am 4ca67e3a: am 61200853: am 295b2051: Merge "libctsopengl_jni: Change char* to unsigned char*"

* commit '4ca67e3a08e1c0acf698719eb7489dad42e51e7d':
  libctsopengl_jni: Change char* to unsigned char*
diff --git a/.gitignore b/.gitignore
index b8a343c..0353c27 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
 .project
 .cproject
 .classpath
+/bin
diff --git a/CtsBuild.mk b/CtsBuild.mk
index 6e57086..f6d0d5c 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -51,3 +51,7 @@
 define cts-get-test-xmls
 	$(foreach name,$(1),$(CTS_TESTCASES_OUT)/$(name).xml)
 endef
+
+define cts-get-executable-paths
+	$(foreach executable,$(1),$(CTS_TESTCASES_OUT)/$(executable))
+endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 3b30e0d..52456e7 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -40,7 +40,6 @@
     CtsHoloDeviceApp \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
-    CtsSampleDeviceApp \
     CtsSomeAccessibilityServices \
     CtsTestStubs \
     SignatureTest \
@@ -104,11 +103,11 @@
     CtsPermission2TestCases \
     CtsPreferenceTestCases \
     CtsPreference2TestCases \
+    CtsPrintTestCases \
     CtsProviderTestCases \
     CtsRenderscriptTestCases \
     CtsRenderscriptGraphicsTestCases \
     CtsRsCppTestCases \
-    CtsSampleDeviceTestCases \
     CtsSaxTestCases \
     CtsSecurityTestCases \
     CtsSpeechTestCases \
@@ -116,6 +115,7 @@
     CtsTextTestCases \
     CtsTextureViewTestCases \
     CtsThemeTestCases \
+    CtsUiAutomationTestCases \
     CtsUtilTestCases \
     CtsViewTestCases \
     CtsWebkitTestCases \
@@ -134,7 +134,6 @@
     CtsHostJank \
     CtsHostUi \
     CtsMonkeyTestCases \
-    CtsSampleHostTestCases \
     CtsUsbTests
 
 # Native test executables that need to have associated test XMLs.
@@ -150,7 +149,11 @@
     CtsUiAutomatorTests
 
 cts_device_jars := \
-    CtsDeviceJank
+    CtsDeviceJank \
+    CtsPrintInstrument
+
+cts_device_executables := \
+    print-instrument
 
 # All the files that will end up under the repository/testcases
 # directory of the final CTS distribution.
@@ -158,7 +161,8 @@
     $(call cts-get-package-paths,$(cts_test_packages)) \
     $(call cts-get-native-paths,$(cts_native_exes)) \
     $(call cts-get-ui-lib-paths,$(cts_ui_tests)) \
-    $(call cts-get-ui-lib-paths,$(cts_device_jars))
+    $(call cts-get-ui-lib-paths,$(cts_device_jars)) \
+    $(call cts-get-executable-paths,$(cts_device_executables))
 
 # All the XMLs that will end up under the repository/testcases
 # and that need to be created before making the final CTS distribution.
@@ -167,6 +171,5 @@
     $(call cts-get-test-xmls,$(cts_native_exes)) \
     $(call cts-get-test-xmls,$(cts_ui_tests))
 
-
 # The following files will be placed in the tools directory of the CTS distribution
 CTS_TOOLS_LIST :=
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 7d165b8..b535384 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -21,7 +21,7 @@
       android:versionName="4.4_r3">
 
     <!-- Using 10+ for more complete NFC support... -->
-    <uses-sdk android:minSdkVersion="12"></uses-sdk>
+    <uses-sdk android:minSdkVersion="19"></uses-sdk>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index bcd00ed..5a0af28 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -18,7 +18,8 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyMeasurementsOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
 
 import java.util.concurrent.TimeUnit;
 
@@ -72,22 +73,24 @@
      * - the values representing the expectation of the test
      * - the mean of values sampled from the sensor
      */
-    private void verifyMeasurements(double ... expectations) throws Throwable {
+    private void verifyMeasurements(float ... expectations) throws Throwable {
         Thread.sleep(500 /*ms*/);
-        VerifyMeasurementsOperation verifyMeasurements = new VerifyMeasurementsOperation(
+        TestSensorOperation verifyMeasurements = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_ACCELEROMETER,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifyMeasurements.addVerification(new MeanVerification(
                 expectations,
-                1.95f /* m / s^2 */);
+                new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
         verifyMeasurements.execute();
         logSuccess();
     }
 
     private void delayedVerifyMeasurements(
             String message,
-            double ... expectations) throws Throwable {
+            float ... expectations) throws Throwable {
         appendText(String.format("\n%s.", message));
         appendText("A sound will be played once the verification is complete...");
         waitForUser();
@@ -100,7 +103,7 @@
         }
     }
 
-    private void verifyMeasurements(String message, double ... expectations) throws Throwable {
+    private void verifyMeasurements(String message, float ... expectations) throws Throwable {
         appendText(String.format("\n%s.", message));
         appendText("Press 'Next' when ready and keep the device steady.");
         waitForUser();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 4bcdd56..066bda4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -18,7 +18,8 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensorTestOperations.VerifySignumOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.SigNumVerification;
 
 /**
  * Semi-automated test that focuses on characteristics associated with Accelerometer measurements.
@@ -96,12 +97,15 @@
         waitForUser();
 
         Thread.sleep(500 /*ms*/);
-        VerifySignumOperation verifySignum = new VerifySignumOperation(
+        TestSensorOperation verifySignum = new TestSensorOperation(
                 getApplicationContext(),
                 Sensor.TYPE_GYROSCOPE,
                 SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifySignum.addVerification(new SigNumVerification(
                 expectations,
-                0.2 /*noiseThreshold*/);
+                new float[]{0.2f, 0.2f, 0.2f} /*noiseThreshold*/));
         verifySignum.execute();
         logSuccess();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index ffed1ab..a131b2b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -19,10 +19,13 @@
 import android.graphics.Color;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
 import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyMagnitudeOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyStandardDeviationOperation;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
 
 /**
  * Semi-automated test that focuses characteristics associated with Accelerometer measurements.
@@ -43,27 +46,34 @@
     }
 
     private void calibrateMagnetometer() {
-        SensorManagerTestVerifier magnetometer = new SensorManagerTestVerifier(
-                this.getApplicationContext(),
-                Sensor.TYPE_MAGNETIC_FIELD,
-                SensorManager.SENSOR_DELAY_NORMAL,
-                0 /*reportLatencyInUs*/) {
+        SensorEventListener2 listener = new SensorEventListener2() {
             @Override
             public void onSensorChanged(SensorEvent event) {
                 float values[] = event.values;
                 clearText();
-                appendText(
-                        "Please calibrate the Magnetometer by moving it in 8 shapes in different " +
-                                "orientations.");
-                appendText(
-                        String.format("->  (%.2f, %.2f, %.2f) uT", values[0], values[1], values[2]),
-                        Color.GRAY);
+                appendText("Please calibrate the Magnetometer by moving it in 8 shapes in "
+                        + "different orientations.");
+                appendText(String.format("->  (%.2f, %.2f, %.2f) uT", values[0], values[1],
+                        values[2]), Color.GRAY);
                 appendText("Then leave the device in a flat surface and press Next...\n");
             }
+
+            @Override
+            public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+
+            @Override
+            public void onFlushCompleted(Sensor sensor) {}
         };
-        magnetometer.registerListener();
-        waitForUser();
-        magnetometer.unregisterListener();
+
+        TestSensorManager magnetometer = new TestSensorManager(
+                this.getApplicationContext(), Sensor.TYPE_MAGNETIC_FIELD,
+                SensorManager.SENSOR_DELAY_NORMAL, 0);
+        try {
+            magnetometer.registerListener(new TestSensorEventListener(listener));
+            waitForUser();
+        } finally {
+            magnetometer.unregisterListener();
+        }
     }
 
     /**
@@ -91,12 +101,15 @@
                 (SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
         float magneticFieldEarthThreshold =
                 expectedMagneticFieldEarth - SensorManager.MAGNETIC_FIELD_EARTH_MIN;
-        VerifyMagnitudeOperation verifyNorm = new VerifyMagnitudeOperation(
+        TestSensorOperation verifyNorm = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        verifyNorm.addVerification(new MagnitudeVerification(
                 expectedMagneticFieldEarth,
-                magneticFieldEarthThreshold);
+                magneticFieldEarthThreshold));
         verifyNorm.execute();
         logSuccess();
     }
@@ -125,12 +138,14 @@
      * the failure to help track down the issue.
      */
     private void verifyStandardDeviation() throws Throwable {
-        VerifyStandardDeviationOperation verifyStdDev = new VerifyStandardDeviationOperation(
+        TestSensorOperation verifyStdDev = new TestSensorOperation(
                 this.getApplicationContext(),
                 Sensor.TYPE_MAGNETIC_FIELD,
                 SensorManager.SENSOR_DELAY_FASTEST,
                 0 /*reportLatencyInUs*/,
-                2f /* uT */);
+                100 /* event count */);
+        verifyStdDev.addVerification(new StandardDeviationVerification(
+                new float[]{2f, 2f, 2f} /* uT */));
         verifyStdDev.execute();
         logSuccess();
     }
diff --git a/apps/cts-usb-accessory/Android.mk b/apps/cts-usb-accessory/Android.mk
index 76022a1..8d18da3 100644
--- a/apps/cts-usb-accessory/Android.mk
+++ b/apps/cts-usb-accessory/Android.mk
@@ -28,7 +28,7 @@
 
 LOCAL_C_INCLUDES += \
 	bionic/libc/kernel/uapi \
-	bionic/libc/kernel/uapi/asm-$(HOST_ARCH) \
+	bionic/libc/kernel/uapi/asm-x86 \
 
 LOCAL_STATIC_LIBRARIES := libusbhost libcutils
 LOCAL_LDLIBS += -lpthread
diff --git a/development/ide/eclipse/.classpath b/development/ide/eclipse/.classpath
index 2ddb4d8..b95d52a 100644
--- a/development/ide/eclipse/.classpath
+++ b/development/ide/eclipse/.classpath
@@ -90,6 +90,7 @@
     <classpathentry kind="src" path="external/easymock/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsAccessibilityServiceTestCases_intermediates/src/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/src"/>
+    <classpathentry kind="src" path="out/target/common/obj/APPS/CtsTestStubs_intermediates/src/renderscript/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifier_intermediates/src"/>
     <classpathentry kind="src" path="out/target/common/obj/APPS/CtsVerifierTests_intermediates/src"/>
 </classpath>
diff --git a/hostsidetests/appsecurity/.gitignore b/hostsidetests/appsecurity/.gitignore
new file mode 100644
index 0000000..5e56e04
--- /dev/null
+++ b/hostsidetests/appsecurity/.gitignore
@@ -0,0 +1 @@
+/bin
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 88b05fb..702002c 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -40,6 +40,8 @@
  */
 public class AppSecurityTests extends DeviceTestCase implements IBuildReceiver {
 
+    private static final String RUNNER = "android.support.test.runner.AndroidJUnitRunner";
+
     // testSharedUidDifferentCerts constants
     private static final String SHARED_UI_APK = "CtsSharedUidInstall.apk";
     private static final String SHARED_UI_PKG = "com.android.cts.shareuidinstall";
@@ -509,7 +511,7 @@
             String testMethodName) throws DeviceNotAvailableException {
 
         RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(pkgName,
-                getDevice().getIDevice());
+                RUNNER, getDevice().getIDevice());
         if (testClassName != null && testMethodName != null) {
             testRunner.setMethodName(testClassName, testMethodName);
         }
@@ -560,7 +562,7 @@
             throws DeviceNotAvailableException {
         // TODO: move this to RemoteAndroidTestRunner once it supports users
         final String cmd = "am instrument --user " + userId + " -w -r -e class " + testClassName
-                + "#" + testMethodName + " " + pkgName + "/android.test.InstrumentationTestRunner";
+                + "#" + testMethodName + " " + pkgName + "/" + RUNNER;
         Log.i(LOG_TAG, "Running " + cmd + " on " + getDevice().getSerialNumber());
 
         CollectingTestListener listener = new CollectingTestListener();
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
index 308992e..2c9c624 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsAppAccessData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
index 3824ef3..ffc104e 100644
--- a/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppAccessData/AndroidManifest.xml
@@ -26,7 +26,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.appaccessdata"
                      android:label="Test to create app data."/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
index 8bcb045..098ce9c 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsAppWithData
 
diff --git a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
index 9decbcd..598ebe7 100644
--- a/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/AppWithData/AndroidManifest.xml
@@ -27,7 +27,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.appwithdata"
                      android:label="Test to create app data."/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
index bc99560..afc8764 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 LOCAL_PACKAGE_NAME := CtsExternalStorageApp
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
index 0ba6684..9d8f615 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.externalstorageapp" />
 
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
index 268ac73..e8ce3b8 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsInstrumentationAppDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
index 8102656..a958c4c 100644
--- a/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/InstrumentationAppDiffCert/AndroidManifest.xml
@@ -34,7 +34,7 @@
     A self-instrumenting test runner, that will try to start the above instrumentation and
     verify it fails.
      -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.instrumentationdiffcertapp"
                      android:label="Test for instrumentation with different cert" />
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
index 48f88b8..1dd109a 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
index 6ace31b..8b599f2 100644
--- a/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/MultiUserStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.multiuserstorageapp" />
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
index 938b325..60d6fad 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareApp/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsPermissionDeclareApp
 
diff --git a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
index acdc20f..ba7285c 100644
--- a/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/PermissionDeclareAppCompat/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := 16
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsPermissionDeclareAppCompat
 
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
index 44e4bef..3e392e3 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
index f6582b9..03884c9 100644
--- a/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ReadExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.readexternalstorageapp" />
 
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk b/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
index 25ba1fe..76187ab 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstall/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSharedUidInstall
 
diff --git a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
index a00b009..c1422ec 100644
--- a/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SharedUidInstallDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSharedUidInstallDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
index 3cd78cf..fb925cd 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstall/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSimpleAppInstall
 
diff --git a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
index 5fbc910..2224d72 100644
--- a/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SimpleAppInstallDiffCert/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsSimpleAppInstallDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
index cc87e29..38a0511 100644
--- a/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/TargetInstrumentationApp/Android.mk
@@ -21,6 +21,7 @@
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsTargetInstrumentationApp
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
index d836042..8878c47 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/Android.mk
@@ -22,6 +22,7 @@
     ../PermissionDeclareApp/src/com/android/cts/permissiondeclareapp/GrantUriPermission.java
 
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_PACKAGE_NAME := CtsUsePermissionDiffCert
 
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
index b7307bb..7acf98f 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/AndroidManifest.xml
@@ -38,5 +38,5 @@
     </application>
 
     <instrumentation android:targetPackage="com.android.cts.usespermissiondiffcertapp"
-            android:name="android.test.InstrumentationTestRunner"/>
+            android:name="android.support.test.runner.AndroidJUnitRunner"/>
 </manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index db26eec..8629a87 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -591,6 +591,7 @@
         grantIntent.setClass(getContext(),
                 service ? ReceiveUriService.class : ReceiveUriActivity.class);
         Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
         intent.setComponent(GRANT_URI_PERM_COMP);
         intent.setAction(service ? GrantUriPermission.ACTION_START_SERVICE
                 : GrantUriPermission.ACTION_START_ACTIVITY);
@@ -598,6 +599,27 @@
         getContext().sendBroadcast(intent);
     }
 
+    private void grantClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_GRANT_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_PACKAGE_NAME, getContext().getPackageName());
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
+    private void revokeClipUriPermissionViaContext(Uri uri, int mode) {
+        Intent intent = new Intent();
+        intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+        intent.setComponent(GRANT_URI_PERM_COMP);
+        intent.setAction(GrantUriPermission.ACTION_REVOKE_URI);
+        intent.putExtra(GrantUriPermission.EXTRA_URI, uri);
+        intent.putExtra(GrantUriPermission.EXTRA_MODE, mode);
+        getContext().sendBroadcast(intent);
+    }
+
     private void assertReadingClipAllowed(ClipData clip) {
         for (int i=0; i<clip.getItemCount(); i++) {
             ClipData.Item item = clip.getItemAt(i);
@@ -1355,4 +1377,166 @@
         getContext().sendOrderedBroadcast(intent, null, receiver, null, 0, null, null);
         receiver.assertSuccess("unexpected outgoing persisted Uri status");
     }
+
+    /**
+     * Validate behavior of prefix permission grants.
+     */
+    public void testGrantPrefixUriPermission() throws Exception {
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo1");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clipMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        // Now give ourselves exact write access
+        ReceiveUriActivity.clearNewIntent();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForNewIntent();
+
+        // Verify we have exact write access, but not prefix write
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipAllowed(clip);
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    public void testGrantPersistablePrefixUriPermission() {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo2");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+
+        // Give ourselves prefix read access
+        ReceiveUriActivity.clearStarted();
+        grantClipUriPermission(clip, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION, false);
+        ReceiveUriActivity.waitForStart();
+
+        // Verify prefix read access
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // Verify we can persist direct grant
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION, before, after);
+
+        // But we can't take anywhere under the prefix
+        try {
+            resolver.takePersistableUriPermission(targetMeow,
+                    Intent.FLAG_GRANT_READ_URI_PERMISSION);
+            fail("taking under prefix should have failed");
+        } catch (SecurityException expected) {
+        }
+
+        // Should still have access regardless of taking
+        assertReadingClipAllowed(clip);
+        assertReadingClipAllowed(clipMeow);
+
+        // And clean up our grants
+        resolver.releasePersistableUriPermission(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        assertNoPersistedUriPermission();
+
+        ReceiveUriActivity.finishCurInstanceSync();
+    }
+
+    /**
+     * Validate behavior of directly granting/revoking permission grants.
+     */
+    public void testDirectGrantRevokeUriPermission() throws Exception {
+        final ContentResolver resolver = getContext().getContentResolver();
+
+        final Uri target = Uri.withAppendedPath(PERM_URI_GRANTING, "foo3");
+        final Uri targetMeow = Uri.withAppendedPath(target, "meow");
+        final Uri targetMeowCat = Uri.withAppendedPath(targetMeow, "cat");
+
+        final ClipData clip = makeSingleClipData(target);
+        final ClipData clipMeow = makeSingleClipData(targetMeow);
+        final ClipData clipMeowCat = makeSingleClipData(targetMeowCat);
+
+        // Make sure we can't see the target
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+
+        // Give ourselves some grants:
+        // /meow/cat  WRITE|PERSISTABLE
+        // /meow      READ|PREFIX
+        // /meow      WRITE
+        grantClipUriPermissionViaContext(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION
+                | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_READ_URI_PERMISSION
+                | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
+        grantClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        long before = System.currentTimeMillis();
+        resolver.takePersistableUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        long after = System.currentTimeMillis();
+        assertPersistedUriPermission(targetMeowCat, Intent.FLAG_GRANT_WRITE_URI_PERMISSION, before, after);
+
+        // Verify they look good
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipAllowed(clipMeow);
+        assertWritingClipAllowed(clipMeowCat);
+
+        // Revoke anyone with write under meow
+        revokeClipUriPermissionViaContext(targetMeow, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
+        SystemClock.sleep(2000);
+
+        // This should have nuked persisted permission at lower level, but it
+        // shoulnd't have touched our prefix read.
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipAllowed(clipMeow);
+        assertReadingClipAllowed(clipMeowCat);
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+
+        // Revoking read at top of tree should nuke everything else
+        revokeClipUriPermissionViaContext(target, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+        SystemClock.sleep(2000);
+        assertReadingClipNotAllowed(clip, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeow, "reading should have failed");
+        assertReadingClipNotAllowed(clipMeowCat, "reading should have failed");
+        assertWritingClipNotAllowed(clip, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeow, "writing should have failed");
+        assertWritingClipNotAllowed(clipMeowCat, "writing should have failed");
+        assertNoPersistedUriPermission();
+    }
 }
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
index 4352bfb..a98fcea 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/Android.mk
@@ -18,6 +18,7 @@
 
 LOCAL_MODULE_TAGS := tests
 LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
     ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
index 82910aa..37e39e9 100644
--- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.writeexternalstorageapp" />
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
diff --git a/hostsidetests/holo/app/Android.mk b/hostsidetests/holo/app/Android.mk
index a5a7bf1..d390418 100644
--- a/hostsidetests/holo/app/Android.mk
+++ b/hostsidetests/holo/app/Android.mk
@@ -26,7 +26,7 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/hostsidetests/holo/app/AndroidManifest.xml b/hostsidetests/holo/app/AndroidManifest.xml
index 70e908c..d27c266 100755
--- a/hostsidetests/holo/app/AndroidManifest.xml
+++ b/hostsidetests/holo/app/AndroidManifest.xml
@@ -34,7 +34,7 @@
     </application>
 
     <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.holo.app"
                      android:label="Generates Holo reference images"/>
 
diff --git a/hostsidetests/usb/SerialTestApp/Android.mk b/hostsidetests/usb/SerialTestApp/Android.mk
index d36b98e..a8f51ad 100644
--- a/hostsidetests/usb/SerialTestApp/Android.mk
+++ b/hostsidetests/usb/SerialTestApp/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
index 0667d60..a75dd75 100644
--- a/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
+++ b/hostsidetests/usb/SerialTestApp/AndroidManifest.xml
@@ -22,5 +22,5 @@
     </application>
     <instrumentation
         android:targetPackage="com.android.cts.usb.serialtest"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" />
 </manifest>
diff --git a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
index f53d210..a8ac3e0 100644
--- a/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
+++ b/hostsidetests/usb/src/com/android/cts/usb/TestUsbTest.java
@@ -51,7 +51,7 @@
 public class TestUsbTest extends DeviceTestCase implements IBuildReceiver {
 
     private static final String LOG_TAG = "TestUsbTest";
-    private static final String CTS_RUNNER = "android.test.InstrumentationCtsTestRunner";
+    private static final String CTS_RUNNER = "android.support.test.runner.AndroidJUnitRunner";
     private static final String PACKAGE_NAME = "com.android.cts.usb.serialtest";
     private static final String APK_NAME="CtsUsbSerialTestApp.apk";
     private ITestDevice mDevice;
diff --git a/libs/deviceutil/Android.mk b/libs/deviceutil/Android.mk
index 1b7db18..d5a2c57 100644
--- a/libs/deviceutil/Android.mk
+++ b/libs/deviceutil/Android.mk
@@ -20,12 +20,12 @@
     $(call all-java-files-under, src) \
     $(call all-java-files-under, ../commonutil/src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE := ctsdeviceutil
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/deviceutil/src/android/cts/util/DeviceReportLog.java b/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
index b707fc8..a3ceecf 100644
--- a/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
+++ b/libs/deviceutil/src/android/cts/util/DeviceReportLog.java
@@ -16,19 +16,24 @@
 
 package android.cts.util;
 
-import com.android.cts.util.ReportLog;
-
 import android.app.Instrumentation;
 import android.os.Bundle;
 import android.util.Log;
 
+import com.android.cts.util.ReportLog;
+
 public class DeviceReportLog extends ReportLog {
     private static final String TAG = "DeviceCtsReport";
     private static final String CTS_RESULT = "CTS_RESULT";
     private static final int INST_STATUS_IN_PROGRESS = 2;
+    private static final int BASE_DEPTH = 4;
 
-    DeviceReportLog() {
-        mDepth = 4;
+    public DeviceReportLog() {
+        mDepth = BASE_DEPTH;
+    }
+
+    public DeviceReportLog(int depth) {
+        mDepth = BASE_DEPTH + depth;
     }
 
     @Override
diff --git a/libs/runner/Android.mk b/libs/runner/Android.mk
index 629c1c2..9642f53 100644
--- a/libs/runner/Android.mk
+++ b/libs/runner/Android.mk
@@ -18,10 +18,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, ../../tests/core/runner/src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_MODULE_TAGS := optional
 
 LOCAL_MODULE := ctstestrunner
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/libs/testserver/src/android/webkit/cts/CtsTestServer.java b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
index 928a68e..6416b73 100644
--- a/libs/testserver/src/android/webkit/cts/CtsTestServer.java
+++ b/libs/testserver/src/android/webkit/cts/CtsTestServer.java
@@ -85,6 +85,7 @@
 import javax.net.ssl.KeyManager;
 import javax.net.ssl.KeyManagerFactory;
 import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLServerSocket;
 import javax.net.ssl.SSLSession;
 import javax.net.ssl.X509TrustManager;
 
@@ -101,20 +102,20 @@
     private static final String DOWNLOAD_ID_PARAMETER = "downloadId";
     private static final String NUM_BYTES_PARAMETER = "numBytes";
 
-    public static final String ASSET_PREFIX = "/assets/";
-    public static final String RAW_PREFIX = "raw/";
-    public static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
-    public static final String APPCACHE_PATH = "/appcache.html";
-    public static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
-    public static final String REDIRECT_PREFIX = "/redirect";
-    public static final String QUERY_REDIRECT_PATH = "/alt_redirect";
-    public static final String DELAY_PREFIX = "/delayed";
-    public static final String BINARY_PREFIX = "/binary";
-    public static final String COOKIE_PREFIX = "/cookie";
-    public static final String AUTH_PREFIX = "/auth";
-    public static final String SHUTDOWN_PREFIX = "/shutdown";
+    private static final String ASSET_PREFIX = "/assets/";
+    private static final String RAW_PREFIX = "raw/";
+    private static final String FAVICON_ASSET_PATH = ASSET_PREFIX + "webkit/favicon.png";
+    private static final String APPCACHE_PATH = "/appcache.html";
+    private static final String APPCACHE_MANIFEST_PATH = "/appcache.manifest";
+    private static final String REDIRECT_PREFIX = "/redirect";
+    private static final String QUERY_REDIRECT_PATH = "/alt_redirect";
+    private static final String DELAY_PREFIX = "/delayed";
+    private static final String BINARY_PREFIX = "/binary";
+    private static final String COOKIE_PREFIX = "/cookie";
+    private static final String AUTH_PREFIX = "/auth";
+    private static final String SHUTDOWN_PREFIX = "/shutdown";
     public static final String NOLENGTH_POSTFIX = "nolength";
-    public static final int DELAY_MILLIS = 2000;
+    private static final int DELAY_MILLIS = 2000;
 
     public static final String AUTH_REALM = "Android CTS";
     public static final String AUTH_USER = "cts";
@@ -126,6 +127,13 @@
     public static final String MESSAGE_403 = "403 forbidden";
     public static final String MESSAGE_404 = "404 not found";
 
+    public enum SslMode {
+        INSECURE,
+        NO_CLIENT_AUTH,
+        WANTS_CLIENT_AUTH,
+        NEEDS_CLIENT_AUTH,
+    }
+
     private static Hashtable<Integer, String> sReasons;
 
     private ServerThread mServerThread;
@@ -133,7 +141,7 @@
     private AssetManager mAssets;
     private Context mContext;
     private Resources mResources;
-    private boolean mSsl;
+    private SslMode mSsl;
     private MimeTypeMap mMap;
     private Vector<String> mQueries;
     private ArrayList<HttpEntity> mRequestEntities;
@@ -168,19 +176,30 @@
      * @throws Exception
      */
     public CtsTestServer(Context context, boolean ssl) throws Exception {
+        this(context, ssl ? SslMode.NO_CLIENT_AUTH : SslMode.INSECURE);
+    }
+
+    /**
+     * Create and start a local HTTP server instance.
+     * @param context The application context to use for fetching assets.
+     * @param sslMode Whether to use SSL, and if so, what client auth (if any) to use.
+     * @throws Exception
+     */
+    public CtsTestServer(Context context, SslMode sslMode) throws Exception {
         mContext = context;
         mAssets = mContext.getAssets();
         mResources = mContext.getResources();
-        mSsl = ssl;
+        mSsl = sslMode;
         mRequestEntities = new ArrayList<HttpEntity>();
         mMap = MimeTypeMap.getSingleton();
         mQueries = new Vector<String>();
         mServerThread = new ServerThread(this, mSsl);
-        if (mSsl) {
-            mServerUri = "https://localhost:" + mServerThread.mSocket.getLocalPort();
+        if (mSsl == SslMode.INSECURE) {
+            mServerUri = "http:";
         } else {
-            mServerUri = "http://localhost:" + mServerThread.mSocket.getLocalPort();
+            mServerUri = "https:";
         }
+        mServerUri += "//localhost:" + mServerThread.mSocket.getLocalPort();
         mServerThread.start();
     }
 
@@ -219,7 +238,9 @@
 
     private URLConnection openConnection(URL url)
             throws IOException, NoSuchAlgorithmException, KeyManagementException {
-        if (mSsl) {
+        if (mSsl == SslMode.INSECURE) {
+            return url.openConnection();
+        } else {
             // Install hostname verifiers and trust managers that don't do
             // anything in order to get around the client not trusting
             // the test server due to a lack of certificates.
@@ -228,13 +249,14 @@
             connection.setHostnameVerifier(new CtsHostnameVerifier());
 
             SSLContext context = SSLContext.getInstance("TLS");
-            CtsTrustManager trustManager = new CtsTrustManager();
-            context.init(null, new CtsTrustManager[] {trustManager}, null);
+            try {
+                context.init(ServerThread.getKeyManagers(), getTrustManagers(), null);
+            } catch (Exception e) {
+                throw new RuntimeException(e);
+            }
             connection.setSSLSocketFactory(context.getSocketFactory());
 
             return connection;
-        } else {
-            return url.openConnection();
         }
     }
 
@@ -246,7 +268,7 @@
      */
     private static class CtsTrustManager implements X509TrustManager {
         public void checkClientTrusted(X509Certificate[] chain, String authType) {
-            // Trust the CtSTestServer...
+            // Trust the CtSTestServer's client...
         }
 
         public void checkServerTrusted(X509Certificate[] chain, String authType) {
@@ -259,6 +281,13 @@
     }
 
     /**
+     * @returns a trust manager array configured to permit any trust decision.
+     */
+    private static CtsTrustManager[] getTrustManagers() {
+        return new CtsTrustManager[] { new CtsTrustManager() };
+    }
+
+    /**
      * {@link HostnameVerifier} that verifies everybody. This permits
      * the client to trust the web server and call
      * {@link CtsTestServer#shutdown()}.
@@ -293,8 +322,20 @@
      * @param path The path of the asset. See {@link AssetManager#open(String)}
      */
     public String getDelayedAssetUrl(String path) {
+        return getDelayedAssetUrl(path, DELAY_MILLIS);
+    }
+
+    /**
+     * Return an artificially delayed absolute URL that refers to the given asset. This can be
+     * used to emulate a slow HTTP server or connection.
+     * @param path The path of the asset. See {@link AssetManager#open(String)}
+     * @param delayMs The number of milliseconds to delay the request
+     */
+    public String getDelayedAssetUrl(String path, int delayMs) {
         StringBuilder sb = new StringBuilder(getBaseUri());
         sb.append(DELAY_PREFIX);
+        sb.append("/");
+        sb.append(delayMs);
         sb.append(ASSET_PREFIX);
         sb.append(path);
         return sb.toString();
@@ -520,12 +561,14 @@
             path = FAVICON_ASSET_PATH;
         }
         if (path.startsWith(DELAY_PREFIX)) {
+            String delayPath = path.substring(DELAY_PREFIX.length() + 1);
+            String delay = delayPath.substring(0, delayPath.indexOf('/'));
+            path = delayPath.substring(delay.length());
             try {
-                Thread.sleep(DELAY_MILLIS);
+                Thread.sleep(Integer.valueOf(delay));
             } catch (InterruptedException ignored) {
                 // ignore
             }
-            path = path.substring(DELAY_PREFIX.length());
         }
         if (path.startsWith(AUTH_PREFIX)) {
             // authentication required
@@ -561,6 +604,8 @@
                     response = createResponse(HttpStatus.SC_OK);
                     response.setEntity(entity);
                     response.addHeader("Content-Disposition", "attachment; filename=test.bin");
+                    response.addHeader("Content-Type", mimeType);
+                    response.addHeader("Content-Length", "" + length);
                 } else {
                     // fall through, return 404 at the end
                 }
@@ -794,7 +839,7 @@
     private static class ServerThread extends Thread {
         private CtsTestServer mServer;
         private ServerSocket mSocket;
-        private boolean mIsSsl;
+        private SslMode mSsl;
         private boolean mIsCancelled;
         private SSLContext mSslContext;
         private ExecutorService mExecutorService = Executors.newFixedThreadPool(20);
@@ -829,13 +874,13 @@
             "1gaEjsC/0wGmmBDg1dTDH+F1p9TInzr3EFuYD0YiQ7YlAHq3cPuyGoLXJ5dXYuSBfhDXJSeddUkl" +
             "k1ufZyOOcskeInQge7jzaRfmKg3U94r+spMEvb0AzDQVOKvjjo1ivxMSgFRZaDb/4qw=";
 
-        private String PASSWORD = "android";
+        private static final String PASSWORD = "android";
 
         /**
          * Loads a keystore from a base64-encoded String. Returns the KeyManager[]
          * for the result.
          */
-        private KeyManager[] getKeyManagers() throws Exception {
+        private static KeyManager[] getKeyManagers() throws Exception {
             byte[] bytes = Base64.decode(SERVER_KEYS_BKS.getBytes());
             InputStream inputStream = new ByteArrayInputStream(bytes);
 
@@ -851,19 +896,24 @@
         }
 
 
-        public ServerThread(CtsTestServer server, boolean ssl) throws Exception {
+        public ServerThread(CtsTestServer server, SslMode sslMode) throws Exception {
             super("ServerThread");
             mServer = server;
-            mIsSsl = ssl;
+            mSsl = sslMode;
             int retry = 3;
             while (true) {
                 try {
-                    if (mIsSsl) {
-                        mSslContext = SSLContext.getInstance("TLS");
-                        mSslContext.init(getKeyManagers(), null, null);
-                        mSocket = mSslContext.getServerSocketFactory().createServerSocket(0);
-                    } else {
+                    if (mSsl == SslMode.INSECURE) {
                         mSocket = new ServerSocket(0);
+                    } else {  // Use SSL
+                        mSslContext = SSLContext.getInstance("TLS");
+                        mSslContext.init(getKeyManagers(), getTrustManagers(), null);
+                        mSocket = mSslContext.getServerSocketFactory().createServerSocket(0);
+                        if (mSsl == SslMode.WANTS_CLIENT_AUTH) {
+                            ((SSLServerSocket) mSocket).setWantClientAuth(true);
+                        } else if (mSsl == SslMode.NEEDS_CLIENT_AUTH) {
+                            ((SSLServerSocket) mSocket).setNeedClientAuth(true);
+                        }
                     }
                     return;
                 } catch (IOException e) {
diff --git a/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
index fe91cb3..06c92d6 100644
--- a/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
+++ b/suite/audio_quality/lib/src/audio/AudioPlaybackLocal.cpp
@@ -42,12 +42,12 @@
     : mHwId(hwId),
       mPcmHandle(NULL)
 {
-    LOGV("AudioPlaybackLocal %x", (unsigned int)this);
+    LOGV("AudioPlaybackLocal %x", (unsigned long)this);
 }
 
 AudioPlaybackLocal::~AudioPlaybackLocal()
 {
-    LOGV("~AudioPlaybackLocal %x", (unsigned int)this);
+    LOGV("~AudioPlaybackLocal %x", (unsigned long)this);
     releaseHw();
 }
 
@@ -108,7 +108,7 @@
 void AudioPlaybackLocal::releaseHw()
 {
     if (mPcmHandle != NULL) {
-        LOGV("releaseHw %x", (unsigned int)this);
+        LOGV("releaseHw %x", (unsigned long)this);
         doStop();
         pcm_close(mPcmHandle);
         mPcmHandle = NULL;
diff --git a/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
index 1325949..eda705d 100644
--- a/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
+++ b/suite/audio_quality/lib/src/audio/AudioRecordingLocal.cpp
@@ -29,12 +29,12 @@
     : mHwId(hwId),
       mPcmHandle(NULL)
 {
-    LOGV("AudioRecordingLocal %x", (unsigned int)this);
+    LOGV("AudioRecordingLocal %x", (unsigned long)this);
 }
 
 AudioRecordingLocal::~AudioRecordingLocal()
 {
-    LOGV("~AudioRecordingLocal %x", (unsigned int)this);
+    LOGV("~AudioRecordingLocal %x", (unsigned long)this);
     releaseHw();
 }
 
@@ -97,7 +97,7 @@
 void AudioRecordingLocal::releaseHw()
 {
     if (mPcmHandle != NULL) {
-        LOGV("releaseHw %x", (unsigned int)this);
+        LOGV("releaseHw %x", (unsigned long)this);
         doStop();
         pcm_close(mPcmHandle);
         mPcmHandle = NULL;
diff --git a/suite/audio_quality/lib/src/task/TaskProcess.cpp b/suite/audio_quality/lib/src/task/TaskProcess.cpp
index f1e47af..061dda5 100644
--- a/suite/audio_quality/lib/src/task/TaskProcess.cpp
+++ b/suite/audio_quality/lib/src/task/TaskProcess.cpp
@@ -271,7 +271,7 @@
             list.push_back(param);
             LOGD(" val %s", param.getParamString().string());
         } else if (isInput && (StringUtil::compare(item[0], "consti") == 0)) {
-            long long value = atoll(item[1].string());
+            int64_t value = atoll(item[1].string());
             TaskCase::Value v(value);
             Param param(v);
             list.push_back(param);
diff --git a/suite/audio_quality/test_description/dut_playback_sample.xml b/suite/audio_quality/test_description/dut_playback_sample.xml
new file mode 100644
index 0000000..f78209e
--- /dev/null
+++ b/suite/audio_quality/test_description/dut_playback_sample.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<case name="dut_playback_sample" version="1.0" description="Sample test which check frequency of DUT's playback">
+	<setup>
+		<!-- prepare sound source id: to be used in output, sine 1000Hz, 4000ms long -->
+		<sound id="sound1" type="sin:32000:1000:4000" preload="1" />
+	</setup>
+	<action>
+		<sequential repeat="1" index="i">
+			<output device="DUT" id="sound1" gain="100" sync="start" waitforcompletion="0" />
+			<sequential repeat="1" index="j">
+				<!-- dummy recording to compensate for possible playback latency -->
+				<input device="host" id="dummy" gain="100" time="1000" sync="complete" />
+				<input device="host" id="host_in_$j" gain="100" time="2000" sync="complete" />
+			</sequential>
+		</sequential>
+		<sequential repeat="1" index="k">
+			<!-- input: host record, signal frequency in Hz, threshold, output: frequency calculated -->
+			<process method="script:playback_sample" input="id:host_in_$k,consti:1000,constf:5.0" output="val:freq_device_$k" />
+		</sequential>
+	</action>
+	<save file="host_in_.*" report="freq_device_.*" />
+</case>
diff --git a/suite/audio_quality/test_description/processing/playback_sample.py b/suite/audio_quality/test_description/processing/playback_sample.py
new file mode 100644
index 0000000..79e8d53
--- /dev/null
+++ b/suite/audio_quality/test_description/processing/playback_sample.py
@@ -0,0 +1,63 @@
+#!/usr/bin/python
+
+# 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.
+
+from consts import *
+
+# Sample test for dut_playback_sample case
+# Input: host recording (mono),
+#        frequency of sine in Hz (i64)
+#        pass level threshold (double)
+# Output: device (double) frequency
+
+def playback_sample(inputData, inputTypes):
+    output = []
+    outputData = []
+    outputTypes = []
+    # basic sanity check
+    inputError = False
+    if (inputTypes[0] != TYPE_MONO):
+        inputError = True
+    if (inputTypes[1] != TYPE_I64):
+        inputError = True
+    if (inputTypes[2] != TYPE_DOUBLE):
+        inputError = True
+    if inputError:
+        output.append(RESULT_ERROR)
+        output.append(outputData)
+        output.append(outputTypes)
+        return output
+
+    hostRecording = inputData[0]
+    signalFrequency = inputData[1]
+    threshold = inputData[2]
+    samplingRate = 44100
+
+    freq = calc_freq(hostRecording, samplingRate)
+    print "Expected Freq ", signalFrequency, "Actual Freq ", freq, "Threshold % ", threshold
+    diff = abs(freq - signalFrequency)
+    if (diff < threshold):
+        output.append(RESULT_PASS)
+    else:
+        output.append(RESULT_OK)
+    outputData.append(freq)
+    outputTypes.append(TYPE_DOUBLE)
+    output.append(outputData)
+    output.append(outputTypes)
+    return output
+
+def calc_freq(recording, samplingRate):
+    #This would calculate the frequency of recording, but is skipped in this sample test for brevity
+    return 32000
\ No newline at end of file
diff --git a/suite/cts/deviceTests/browserbench/Android.mk b/suite/cts/deviceTests/browserbench/Android.mk
index 6b241a9..3696bcd 100644
--- a/suite/cts/deviceTests/browserbench/Android.mk
+++ b/suite/cts/deviceTests/browserbench/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ctstestserver
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/browserbench/AndroidManifest.xml b/suite/cts/deviceTests/browserbench/AndroidManifest.xml
index 16626ad..4bf5b5e 100644
--- a/suite/cts/deviceTests/browserbench/AndroidManifest.xml
+++ b/suite/cts/deviceTests/browserbench/AndroidManifest.xml
@@ -24,6 +24,6 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.browser" />
 </manifest>
diff --git a/suite/cts/deviceTests/dram/Android.mk b/suite/cts/deviceTests/dram/Android.mk
index 861a313..13de747 100644
--- a/suite/cts/deviceTests/dram/Android.mk
+++ b/suite/cts/deviceTests/dram/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsdram_jni
diff --git a/suite/cts/deviceTests/dram/AndroidManifest.xml b/suite/cts/deviceTests/dram/AndroidManifest.xml
index 70f6b11..c9aaf3d 100644
--- a/suite/cts/deviceTests/dram/AndroidManifest.xml
+++ b/suite/cts/deviceTests/dram/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.dram"
             android:label="DRAM bandwidth measurement" />
 </manifest>
diff --git a/suite/cts/deviceTests/filesystemperf/Android.mk b/suite/cts/deviceTests/filesystemperf/Android.mk
index 5f0606e..843d21a 100644
--- a/suite/cts/deviceTests/filesystemperf/Android.mk
+++ b/suite/cts/deviceTests/filesystemperf/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml b/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
index dc90a94..329bf19 100644
--- a/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
+++ b/suite/cts/deviceTests/filesystemperf/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.filesystemperf"
             android:label="UI Latency measurement" />
 </manifest>
diff --git a/suite/cts/deviceTests/opengl/Android.mk b/suite/cts/deviceTests/opengl/Android.mk
index 8617436..7e93dd7 100644
--- a/suite/cts/deviceTests/opengl/Android.mk
+++ b/suite/cts/deviceTests/opengl/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsopengl_jni
diff --git a/suite/cts/deviceTests/opengl/AndroidManifest.xml b/suite/cts/deviceTests/opengl/AndroidManifest.xml
index a0273ec..05fb5be 100644
--- a/suite/cts/deviceTests/opengl/AndroidManifest.xml
+++ b/suite/cts/deviceTests/opengl/AndroidManifest.xml
@@ -46,7 +46,7 @@
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="OpenGL ES 2.0 Benchmark"
         android:targetPackage="com.android.cts.opengl" />
 
diff --git a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
index 6c2c87d..c177129 100644
--- a/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
+++ b/suite/cts/deviceTests/opengl/src/com/android/cts/opengl/primitive/GLPrimitiveBenchmark.java
@@ -118,9 +118,8 @@
         intent.putExtra(GLActivityIntentKeys.INTENT_EXTRA_NUM_ITERATIONS, numIterations);
         intent.putExtra(GLActivityIntentKeys.INTENT_EXTRA_TIMEOUT, timeout);
 
-        GLPrimitiveActivity activity = null;
         setActivityIntent(intent);
-        activity = getActivity();
+        GLPrimitiveActivity activity = getActivity();
         if (activity != null) {
             activity.waitForCompletion();
             double[] fpsValues = activity.mFpsValues;
diff --git a/suite/cts/deviceTests/simplecpu/Android.mk b/suite/cts/deviceTests/simplecpu/Android.mk
index 0cc73cc..cc25223 100644
--- a/suite/cts/deviceTests/simplecpu/Android.mk
+++ b/suite/cts/deviceTests/simplecpu/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libctscpu_jni
diff --git a/suite/cts/deviceTests/simplecpu/AndroidManifest.xml b/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
index 69e4ad2..e5c1c2b 100644
--- a/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
+++ b/suite/cts/deviceTests/simplecpu/AndroidManifest.xml
@@ -23,7 +23,7 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.simplecpu"
             android:label="Very simple CPU benchmarking" />
 </manifest>
diff --git a/suite/cts/deviceTests/ui/Android.mk b/suite/cts/deviceTests/ui/Android.mk
index 17287b2..ee52172 100644
--- a/suite/cts/deviceTests/ui/Android.mk
+++ b/suite/cts/deviceTests/ui/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/ui/AndroidManifest.xml b/suite/cts/deviceTests/ui/AndroidManifest.xml
index 1be3eed..b41008e 100644
--- a/suite/cts/deviceTests/ui/AndroidManifest.xml
+++ b/suite/cts/deviceTests/ui/AndroidManifest.xml
@@ -36,8 +36,12 @@
     </application>
 
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="UI Latency measurement"
-        android:targetPackage="com.android.cts.ui" />
+        android:targetPackage="com.android.cts.ui" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
-</manifest>
\ No newline at end of file
+</manifest>
diff --git a/suite/cts/deviceTests/videoperf/Android.mk b/suite/cts/deviceTests/videoperf/Android.mk
index 6ace48f..cb398a9 100644
--- a/suite/cts/deviceTests/videoperf/Android.mk
+++ b/suite/cts/deviceTests/videoperf/Android.mk
@@ -18,8 +18,6 @@
 # don't include this package in any target
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/videoperf/AndroidManifest.xml b/suite/cts/deviceTests/videoperf/AndroidManifest.xml
index 631141d..ca01298 100644
--- a/suite/cts/deviceTests/videoperf/AndroidManifest.xml
+++ b/suite/cts/deviceTests/videoperf/AndroidManifest.xml
@@ -23,7 +23,11 @@
     <application>
         <uses-library android:name="android.test.runner" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.videoperf"
-            android:label="UI Latency measurement" />
+            android:label="UI Latency measurement" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/suite/cts/hostTests/uihost/appA/Android.mk b/suite/cts/hostTests/uihost/appA/Android.mk
index 48d9009..3e76fdb 100644
--- a/suite/cts/hostTests/uihost/appA/Android.mk
+++ b/suite/cts/hostTests/uihost/appA/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/appB/Android.mk b/suite/cts/hostTests/uihost/appB/Android.mk
index 812637e..13af40f 100644
--- a/suite/cts/hostTests/uihost/appB/Android.mk
+++ b/suite/cts/hostTests/uihost/appB/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/control/Android.mk b/suite/cts/hostTests/uihost/control/Android.mk
index 565e2c0..3770918 100644
--- a/suite/cts/hostTests/uihost/control/Android.mk
+++ b/suite/cts/hostTests/uihost/control/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/hostTests/uihost/control/AndroidManifest.xml b/suite/cts/hostTests/uihost/control/AndroidManifest.xml
index 9901d50..e4b10f2 100644
--- a/suite/cts/hostTests/uihost/control/AndroidManifest.xml
+++ b/suite/cts/hostTests/uihost/control/AndroidManifest.xml
@@ -24,5 +24,9 @@
     </application>
     <instrumentation
         android:targetPackage="com.android.cts.taskswitching.control"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/Android.mk b/tests/Android.mk
index a4cc38f..0c8f71f 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -30,6 +30,7 @@
 # Resource unit tests use a private locale and some densities
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs -c small -c normal -c large -c xlarge \
         -c 320dpi -c 240dpi -c 160dpi -c 32dpi \
+	-c kok,kok_IN,kok_419,kok_419_VARIANT,kok_Knda_419,kok_Knda_419_VARIANT,kok_VARIANT,kok_Knda \
         --preferred-configurations 320dpi --preferred-configurations 240dpi \
         --preferred-configurations 160dpi --preferred-configurations 32dpi
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index b65ded0..cc5832c 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -583,6 +583,12 @@
             android:configChanges="keyboardHidden|orientation|screenSize">
         </activity>
 
+        <activity android:name="android.hardware.camera2.cts.Camera2SurfaceViewStubActivity"
+            android:label="Camera2StubActivity"
+            android:screenOrientation="landscape"
+            android:configChanges="keyboardHidden|orientation|screenSize">
+        </activity>
+
         <activity android:name="android.view.inputmethod.cts.InputMethodStubActivity"
             android:label="InputMethodStubActivity">
             <intent-filter>
diff --git a/tests/ProcessTest/Android.mk b/tests/ProcessTest/Android.mk
index a2958fe..5611b3b 100644
--- a/tests/ProcessTest/Android.mk
+++ b/tests/ProcessTest/Android.mk
@@ -20,8 +20,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_AAPT_FLAGS = -c xx_YY -c cs
 
 LOCAL_SRC_FILES := $(call all-subdir-java-files)
@@ -31,6 +29,8 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/ProcessTest/AndroidManifest.xml b/tests/ProcessTest/AndroidManifest.xml
index b860bde..c7cf635 100644
--- a/tests/ProcessTest/AndroidManifest.xml
+++ b/tests/ProcessTest/AndroidManifest.xml
@@ -18,7 +18,7 @@
        android:sharedUserId="com.android.cts.process.uidpid_test">
 
     <!-- InstrumentationTestRunner for AndroidTests -->
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.process"
                      android:label="Test process"/>
     <application>
diff --git a/tests/SignatureTest/Android.mk b/tests/SignatureTest/Android.mk
index 696f99e..fc794e8 100644
--- a/tests/SignatureTest/Android.mk
+++ b/tests/SignatureTest/Android.mk
@@ -20,12 +20,12 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_PACKAGE_NAME := SignatureTest
 
 LOCAL_SDK_VERSION := current
 
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 # To be passed in on command line
 CTS_API_VERSION ?= current
 ifeq (current,$(CTS_API_VERSION))
diff --git a/tests/SignatureTest/AndroidManifest.xml b/tests/SignatureTest/AndroidManifest.xml
index b4813e6..89d8590 100644
--- a/tests/SignatureTest/AndroidManifest.xml
+++ b/tests/SignatureTest/AndroidManifest.xml
@@ -23,7 +23,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.tests.sigtest"
                      android:label="API Signature Test"/>
 
diff --git a/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java b/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
index c51c6c3..36360d6 100644
--- a/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
+++ b/tests/SignatureTest/src/android/tests/sigtest/JDiffClassDescription.java
@@ -832,19 +832,14 @@
         // Nothing to check if it doesn't extend anything.
         if (mExtendedClass != null) {
             Class<?> superClass = mClass.getSuperclass();
-            if (superClass == null) {
-                // API indicates superclass, reflection doesn't.
-                return false;
-            }
 
-            if (superClass.getCanonicalName().equals(mExtendedClass)) {
-                return true;
+            while (superClass != null) {
+                if (superClass.getCanonicalName().equals(mExtendedClass)) {
+                    return true;
+                }
+                superClass = superClass.getSuperclass();
             }
-
-            if (mAbsoluteClassName.equals("android.hardware.SensorManager")) {
-                // FIXME: Please see Issue 1496822 for more information
-                return true;
-            }
+            // Couldn't find a matching superclass.
             return false;
         }
         return true;
diff --git a/tests/SignatureTest/tests/Android.mk b/tests/SignatureTest/tests/Android.mk
index bdd0a90..0796670 100644
--- a/tests/SignatureTest/tests/Android.mk
+++ b/tests/SignatureTest/tests/Android.mk
@@ -6,8 +6,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 # Include all test java files.
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -19,4 +17,8 @@
 
 LOCAL_DEX_PREOPT := false
 
+LOCAL_SDK_VERSION := current
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
 include $(BUILD_PACKAGE)
diff --git a/tests/SignatureTest/tests/AndroidManifest.xml b/tests/SignatureTest/tests/AndroidManifest.xml
index ab8a6d6..49b3827 100644
--- a/tests/SignatureTest/tests/AndroidManifest.xml
+++ b/tests/SignatureTest/tests/AndroidManifest.xml
@@ -20,7 +20,7 @@
         <uses-library android:name="android.test.runner"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.tests.sigtest"
                      android:label="SignatureTest Functional Testset"/>
 
diff --git a/tests/acceleration/Android.mk b/tests/acceleration/Android.mk
index ef96a24..a6d6022 100644
--- a/tests/acceleration/Android.mk
+++ b/tests/acceleration/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccelerationTestStubs
diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml
index 0d18cef..dde1de8 100644
--- a/tests/accessibility/AndroidManifest.xml
+++ b/tests/accessibility/AndroidManifest.xml
@@ -19,8 +19,6 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
           package="android.view.accessibility.services">
 
-  <uses-permission android:name="android.permission.CAN_REQUEST_TOUCH_EXPLORATION_MODE"/>
-
   <application>
 
     <service android:name=".SpeakingAccessibilityService"
diff --git a/tests/tests/security/assets/selinux_policy.xml b/tests/assets/selinux_policy.xml
similarity index 100%
rename from tests/tests/security/assets/selinux_policy.xml
rename to tests/assets/selinux_policy.xml
diff --git a/tests/assets/webkit/iframe_blank_tag.html b/tests/assets/webkit/iframe_blank_tag.html
index 55ff410..fbc6dc6 100644
--- a/tests/assets/webkit/iframe_blank_tag.html
+++ b/tests/assets/webkit/iframe_blank_tag.html
@@ -16,7 +16,7 @@
 <!DOCTYPE html>
 <html>
 <head>
-  <script type='text/javascript'> window.open('test_hello_world.html'); </script>
+  <script type='text/javascript'> window.open('page_with_link.html'); </script>
 </head>
 </html>
 
diff --git a/tests/assets/webkit/page_with_link.html b/tests/assets/webkit/page_with_link.html
new file mode 100644
index 0000000..50fb78a
--- /dev/null
+++ b/tests/assets/webkit/page_with_link.html
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<html>
+  <body>
+    <a href="http://foo.com" id="link">a link</a>
+  </body>
+</html>
diff --git a/tests/core/libcore/com/AndroidManifest.xml b/tests/core/libcore/com/AndroidManifest.xml
index 4e37ef4..8790a7e 100644
--- a/tests/core/libcore/com/AndroidManifest.xml
+++ b/tests/core/libcore/com/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/conscrypt/AndroidManifest.xml b/tests/core/libcore/conscrypt/AndroidManifest.xml
index 6517a0b..b299793 100644
--- a/tests/core/libcore/conscrypt/AndroidManifest.xml
+++ b/tests/core/libcore/conscrypt/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/dalvik/AndroidManifest.xml b/tests/core/libcore/dalvik/AndroidManifest.xml
index 6def32c..ca34678 100644
--- a/tests/core/libcore/dalvik/AndroidManifest.xml
+++ b/tests/core/libcore/dalvik/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_annotation/AndroidManifest.xml b/tests/core/libcore/harmony_annotation/AndroidManifest.xml
index 0c59b1b..c83ecf2 100644
--- a/tests/core/libcore/harmony_annotation/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_annotation/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_beans/AndroidManifest.xml b/tests/core/libcore/harmony_beans/AndroidManifest.xml
index b4932dd..b9b161c 100644
--- a/tests/core/libcore/harmony_beans/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_beans/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_io/AndroidManifest.xml b/tests/core/libcore/harmony_java_io/AndroidManifest.xml
index 65d64ab..e69d4b4 100644
--- a/tests/core/libcore/harmony_java_io/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_io/AndroidManifest.xml
@@ -22,8 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_lang/AndroidManifest.xml b/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
index a5e499a..1a5a748 100644
--- a/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_lang/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_math/AndroidManifest.xml b/tests/core/libcore/harmony_java_math/AndroidManifest.xml
index f8cd224..4cdf654 100644
--- a/tests/core/libcore/harmony_java_math/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_math/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_net/AndroidManifest.xml b/tests/core/libcore/harmony_java_net/AndroidManifest.xml
index 3c9fd63..db14fa9 100644
--- a/tests/core/libcore/harmony_java_net/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_net/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_nio/AndroidManifest.xml b/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
index 27166d4..0221ebb 100644
--- a/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_nio/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_text/AndroidManifest.xml b/tests/core/libcore/harmony_java_text/AndroidManifest.xml
index 0b6beed..6818053 100644
--- a/tests/core/libcore/harmony_java_text/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_text/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_java_util/AndroidManifest.xml b/tests/core/libcore/harmony_java_util/AndroidManifest.xml
index 72fa3ef..e36468e 100644
--- a/tests/core/libcore/harmony_java_util/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_java_util/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_javax_security/AndroidManifest.xml b/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
index b7b35f2..c927855 100644
--- a/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_javax_security/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_logging/AndroidManifest.xml b/tests/core/libcore/harmony_logging/AndroidManifest.xml
index 94ee60e..8a669e2 100644
--- a/tests/core/libcore/harmony_logging/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_logging/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_prefs/AndroidManifest.xml b/tests/core/libcore/harmony_prefs/AndroidManifest.xml
index f8fdea2..ebcb4ef 100644
--- a/tests/core/libcore/harmony_prefs/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_prefs/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/harmony_sql/AndroidManifest.xml b/tests/core/libcore/harmony_sql/AndroidManifest.xml
index c6c31b2..7cd86da 100644
--- a/tests/core/libcore/harmony_sql/AndroidManifest.xml
+++ b/tests/core/libcore/harmony_sql/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/jsr166/AndroidManifest.xml b/tests/core/libcore/jsr166/AndroidManifest.xml
index 3a0150e..fb4a648 100644
--- a/tests/core/libcore/jsr166/AndroidManifest.xml
+++ b/tests/core/libcore/jsr166/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/libcore/AndroidManifest.xml b/tests/core/libcore/libcore/AndroidManifest.xml
index e4a5d1e..67a3023 100644
--- a/tests/core/libcore/libcore/AndroidManifest.xml
+++ b/tests/core/libcore/libcore/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/org/AndroidManifest.xml b/tests/core/libcore/org/AndroidManifest.xml
index d5b77bd..d705f65 100644
--- a/tests/core/libcore/org/AndroidManifest.xml
+++ b/tests/core/libcore/org/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/sun/AndroidManifest.xml b/tests/core/libcore/sun/AndroidManifest.xml
index cc1a853..9888af3 100644
--- a/tests/core/libcore/sun/AndroidManifest.xml
+++ b/tests/core/libcore/sun/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/libcore/tests/AndroidManifest.xml b/tests/core/libcore/tests/AndroidManifest.xml
index 02f8b4a..f7dab9c 100644
--- a/tests/core/libcore/tests/AndroidManifest.xml
+++ b/tests/core/libcore/tests/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
-                     android:label="cts framework tests"/>
+                     android:label="cts framework tests">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/core/runner/Android.mk b/tests/core/runner/Android.mk
index fb548fc..649b3b4 100644
--- a/tests/core/runner/Android.mk
+++ b/tests/core/runner/Android.mk
@@ -27,6 +27,6 @@
 
 LOCAL_PACKAGE_NAME := android.core.tests.runner
 
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-support-test
 
 include $(BUILD_CTSCORE_PACKAGE)
diff --git a/tests/core/runner/AndroidManifest.xml b/tests/core/runner/AndroidManifest.xml
index e179710..05d210b 100644
--- a/tests/core/runner/AndroidManifest.xml
+++ b/tests/core/runner/AndroidManifest.xml
@@ -22,7 +22,7 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.core.tests.runner"
                      android:label="cts framework tests"/>
 
diff --git a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java b/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
deleted file mode 100644
index d992839..0000000
--- a/tests/core/runner/src/android/test/InstrumentationCtsTestRunner.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.test;
-
-import com.android.internal.util.Predicate;
-import com.android.internal.util.Predicates;
-
-import dalvik.annotation.BrokenTest;
-import dalvik.annotation.SideEffect;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.test.suitebuilder.TestMethod;
-import android.test.suitebuilder.annotation.HasAnnotation;
-import android.util.Log;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.Modifier;
-import java.net.Authenticator;
-import java.net.CookieHandler;
-import java.net.ResponseCache;
-import java.util.List;
-import java.util.Locale;
-import java.util.TimeZone;
-import javax.net.ssl.HostnameVerifier;
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSocketFactory;
-
-import junit.framework.AssertionFailedError;
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestListener;
-
-/**
- * This test runner extends the default InstrumentationTestRunner. It overrides
- * the {@code onCreate(Bundle)} method and sets the system properties necessary
- * for many core tests to run. This is needed because there are some core tests
- * that need writing access to the file system. We also need to set the harness
- * Thread's context ClassLoader. Otherwise some classes and resources will not
- * be found. Finally, we add a means to free memory allocated by a TestCase
- * after its execution.
- *
- * @hide
- */
-public class InstrumentationCtsTestRunner extends InstrumentationTestRunner {
-
-    private static final String TAG = "InstrumentationCtsTestRunner";
-
-    /**
-     * True if (and only if) we are running in single-test mode (as opposed to
-     * batch mode).
-     */
-    private boolean mSingleTest = false;
-
-    private TestEnvironment mEnvironment;
-
-    @Override
-    public void onCreate(Bundle arguments) {
-        // We might want to move this to /sdcard, if is is mounted/writable.
-        File cacheDir = getTargetContext().getCacheDir();
-
-        // Set some properties that the core tests absolutely need.
-        System.setProperty("user.language", "en");
-        System.setProperty("user.region", "US");
-
-        System.setProperty("java.home", cacheDir.getAbsolutePath());
-        System.setProperty("user.home", cacheDir.getAbsolutePath());
-        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
-        System.setProperty("user.dir", cacheDir.getAbsolutePath());
-
-
-        mEnvironment = new TestEnvironment();
-
-        if (arguments != null) {
-            String classArg = arguments.getString(ARGUMENT_TEST_CLASS);
-            mSingleTest = classArg != null && classArg.contains("#");
-        }
-
-        // attempt to disable keyguard,  if current test has permission to do so
-        // TODO: move this to a better place, such as InstrumentationTestRunner ?
-        if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD)
-                == PackageManager.PERMISSION_GRANTED) {
-            Log.i(TAG, "Disabling keyguard");
-            KeyguardManager keyguardManager =
-                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
-            keyguardManager.newKeyguardLock("cts").disableKeyguard();
-        } else {
-            Log.i(TAG, "Test lacks permission to disable keyguard. " +
-                    "UI based tests may fail if keyguard is up");
-        }
-
-        super.onCreate(arguments);
-    }
-
-    @Override
-    protected AndroidTestRunner getAndroidTestRunner() {
-        AndroidTestRunner runner = super.getAndroidTestRunner();
-
-        runner.addTestListener(new TestListener() {
-            /**
-             * The last test class we executed code from.
-             */
-            private Class<?> lastClass;
-
-            @Override
-            public void startTest(Test test) {
-                if (test.getClass() != lastClass) {
-                    lastClass = test.getClass();
-                    printMemory(test.getClass());
-                }
-
-                Thread.currentThread().setContextClassLoader(
-                        test.getClass().getClassLoader());
-
-                mEnvironment.reset();
-            }
-
-            @Override
-            public void endTest(Test test) {
-                if (test instanceof TestCase) {
-                    cleanup((TestCase)test);
-                }
-            }
-
-            @Override
-            public void addError(Test test, Throwable t) {
-                // This space intentionally left blank.
-            }
-
-            @Override
-            public void addFailure(Test test, AssertionFailedError t) {
-                // This space intentionally left blank.
-            }
-
-            /**
-             * Dumps some memory info.
-             */
-            private void printMemory(Class<? extends Test> testClass) {
-                Runtime runtime = Runtime.getRuntime();
-
-                long total = runtime.totalMemory();
-                long free = runtime.freeMemory();
-                long used = total - free;
-
-                Log.d(TAG, "Total memory  : " + total);
-                Log.d(TAG, "Used memory   : " + used);
-                Log.d(TAG, "Free memory   : " + free);
-                Log.d(TAG, "Now executing : " + testClass.getName());
-            }
-
-            /**
-             * Nulls all non-static reference fields in the given test class.
-             * This method helps us with those test classes that don't have an
-             * explicit tearDown() method. Normally the garbage collector should
-             * take care of everything, but since JUnit keeps references to all
-             * test cases, a little help might be a good idea.
-             */
-            private void cleanup(TestCase test) {
-                Class<?> clazz = test.getClass();
-
-                while (clazz != TestCase.class) {
-                    Field[] fields = clazz.getDeclaredFields();
-                    for (int i = 0; i < fields.length; i++) {
-                        Field f = fields[i];
-                        if (!f.getType().isPrimitive() &&
-                                !Modifier.isStatic(f.getModifiers())) {
-                            try {
-                                f.setAccessible(true);
-                                f.set(test, null);
-                            } catch (Exception ignored) {
-                                // Nothing we can do about it.
-                            }
-                        }
-                    }
-
-                    clazz = clazz.getSuperclass();
-                }
-            }
-
-        });
-
-        return runner;
-    }
-
-    // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
-    static class TestEnvironment {
-        private Locale mDefaultLocale;
-        private String mUserHome;
-        private String mJavaIoTmpDir;
-        private HostnameVerifier mHostnameVerifier;
-        private SSLSocketFactory mSslSocketFactory;
-
-        TestEnvironment() {
-            mDefaultLocale = Locale.getDefault();
-            mUserHome = System.getProperty("user.home");
-            mJavaIoTmpDir = System.getProperty("java.io.tmpdir");
-            mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
-            mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
-        }
-
-        void reset() {
-            Locale.setDefault(mDefaultLocale);
-            System.setProperty("user.home", mUserHome);
-            System.setProperty("java.io.tmpdir", mJavaIoTmpDir);
-            Authenticator.setDefault(null);
-            CookieHandler.setDefault(null);
-            ResponseCache.setDefault(null);
-            HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
-            HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
-        }
-    }
-
-    @Override
-    List<Predicate<TestMethod>> getBuilderRequirements() {
-        List<Predicate<TestMethod>> builderRequirements =
-                super.getBuilderRequirements();
-
-        Predicate<TestMethod> brokenTestPredicate =
-                Predicates.not(new HasAnnotation(BrokenTest.class));
-        builderRequirements.add(brokenTestPredicate);
-
-        if (!mSingleTest) {
-            Predicate<TestMethod> sideEffectPredicate =
-                    Predicates.not(new HasAnnotation(SideEffect.class));
-            builderRequirements.add(sideEffectPredicate);
-        }
-        return builderRequirements;
-    }
-}
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
new file mode 100644
index 0000000..a142b29
--- /dev/null
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.runner;
+
+import android.app.Instrumentation;
+import android.app.KeyguardManager;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.support.test.internal.runner.listener.InstrumentationRunListener;
+import android.util.Log;
+
+import junit.framework.TestCase;
+
+import org.junit.runner.Description;
+import org.junit.runner.notification.RunListener;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.net.Authenticator;
+import java.net.CookieHandler;
+import java.net.ResponseCache;
+import java.util.Locale;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+
+/**
+ * A {@link RunListener} for CTS. Sets the system properties necessary for many
+ * core tests to run. This is needed because there are some core tests that need
+ * writing access to the file system.
+ * Finally, we add a means to free memory allocated by a TestCase after its
+ * execution.
+ */
+public class CtsTestRunListener extends InstrumentationRunListener {
+
+    private static final String TAG = "CtsTestRunListener";
+
+    private TestEnvironment mEnvironment;
+    private Class<?> lastClass;
+
+    @Override
+    public void testRunStarted(Description description) throws Exception {
+        mEnvironment = new TestEnvironment();
+
+        // We might want to move this to /sdcard, if is is mounted/writable.
+        File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
+
+        // Set some properties that the core tests absolutely need.
+        System.setProperty("user.language", "en");
+        System.setProperty("user.region", "US");
+
+        System.setProperty("java.home", cacheDir.getAbsolutePath());
+        System.setProperty("user.home", cacheDir.getAbsolutePath());
+        System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+        System.setProperty("user.dir", cacheDir.getAbsolutePath());
+
+        // attempt to disable keyguard, if current test has permission to do so
+        // TODO: move this to a better place, such as InstrumentationTestRunner
+        // ?
+        if (getInstrumentation().getContext().checkCallingOrSelfPermission(
+                android.Manifest.permission.DISABLE_KEYGUARD)
+                == PackageManager.PERMISSION_GRANTED) {
+            Log.i(TAG, "Disabling keyguard");
+            KeyguardManager keyguardManager =
+                    (KeyguardManager) getInstrumentation().getContext().getSystemService(
+                            Context.KEYGUARD_SERVICE);
+            keyguardManager.newKeyguardLock("cts").disableKeyguard();
+        } else {
+            Log.i(TAG, "Test lacks permission to disable keyguard. " +
+                    "UI based tests may fail if keyguard is up");
+        }
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        if (description.getTestClass() != lastClass) {
+            lastClass = description.getTestClass();
+            printMemory(description.getTestClass());
+        }
+
+        mEnvironment.reset();
+    }
+
+    @Override
+    public void testFinished(Description description) {
+        // no way to implement this in JUnit4...
+        // offending test cases that need this logic should probably be cleaned
+        // up individually
+        // if (test instanceof TestCase) {
+        // cleanup((TestCase) test);
+        // }
+    }
+
+    /**
+     * Dumps some memory info.
+     */
+    private void printMemory(Class<?> testClass) {
+        Runtime runtime = Runtime.getRuntime();
+
+        long total = runtime.totalMemory();
+        long free = runtime.freeMemory();
+        long used = total - free;
+
+        Log.d(TAG, "Total memory  : " + total);
+        Log.d(TAG, "Used memory   : " + used);
+        Log.d(TAG, "Free memory   : " + free);
+        Log.d(TAG, "Now executing : " + testClass.getName());
+    }
+
+    /**
+     * Nulls all non-static reference fields in the given test class. This
+     * method helps us with those test classes that don't have an explicit
+     * tearDown() method. Normally the garbage collector should take care of
+     * everything, but since JUnit keeps references to all test cases, a little
+     * help might be a good idea.
+     */
+    private void cleanup(TestCase test) {
+        Class<?> clazz = test.getClass();
+
+        while (clazz != TestCase.class) {
+            Field[] fields = clazz.getDeclaredFields();
+            for (int i = 0; i < fields.length; i++) {
+                Field f = fields[i];
+                if (!f.getType().isPrimitive() &&
+                        !Modifier.isStatic(f.getModifiers())) {
+                    try {
+                        f.setAccessible(true);
+                        f.set(test, null);
+                    } catch (Exception ignored) {
+                        // Nothing we can do about it.
+                    }
+                }
+            }
+
+            clazz = clazz.getSuperclass();
+        }
+    }
+
+    // http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
+    static class TestEnvironment {
+        private Locale mDefaultLocale;
+        private String mUserHome;
+        private String mJavaIoTmpDir;
+        private HostnameVerifier mHostnameVerifier;
+        private SSLSocketFactory mSslSocketFactory;
+
+        TestEnvironment() {
+            mDefaultLocale = Locale.getDefault();
+            mUserHome = System.getProperty("user.home");
+            mJavaIoTmpDir = System.getProperty("java.io.tmpdir");
+            mHostnameVerifier = HttpsURLConnection.getDefaultHostnameVerifier();
+            mSslSocketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();
+        }
+
+        void reset() {
+            Locale.setDefault(mDefaultLocale);
+            System.setProperty("user.home", mUserHome);
+            System.setProperty("java.io.tmpdir", mJavaIoTmpDir);
+            Authenticator.setDefault(null);
+            CookieHandler.setDefault(null);
+            ResponseCache.setDefault(null);
+            HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
+            HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
+        }
+    }
+
+}
diff --git a/tests/deviceadmin/Android.mk b/tests/deviceadmin/Android.mk
index bcc23fc..9ab9cb8 100644
--- a/tests/deviceadmin/Android.mk
+++ b/tests/deviceadmin/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner guava
+LOCAL_JAVA_LIBRARIES := guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tests/deviceadmin/AndroidManifest.xml b/tests/deviceadmin/AndroidManifest.xml
index 2395d99..f70a677 100644
--- a/tests/deviceadmin/AndroidManifest.xml
+++ b/tests/deviceadmin/AndroidManifest.xml
@@ -107,7 +107,7 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.admin"
             android:label="Tests for the device admin APIs."/>
 </manifest>
diff --git a/tests/plans/CTS-flaky.xml b/tests/plans/CTS-flaky.xml
index 5317d96..541a79f 100644
--- a/tests/plans/CTS-flaky.xml
+++ b/tests/plans/CTS-flaky.xml
@@ -8,5 +8,4 @@
   <Entry uri="android.provider" exclude="android.provider.cts.ContactsContract_CommonDataKinds_EventTest#testGetTypeLabel;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testStoreAudioGenresMembersInternal;android.provider.cts.ContactsContract_DataTest#testDataInsert_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_EmailTest#testGetTypeLabel;android.provider.cts.Settings_NameValueTableTest#testPutString;android.provider.cts.CalendarTest#testCalendarEntityQuery;android.provider.cts.ContactsContract_DataUsageTest#testSingleDataUsageFeedback_incrementsCorrectDataItems;android.provider.cts.BrowserTest#testSendString;android.provider.cts.MediaStore_Video_MediaTest#testStoreVideoMediaInternal;android.provider.cts.UserDictionary_WordsTest#testAddWord_deprecated;android.provider.cts.MediaStoreIntentsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DeletedContacts#testQuerySinceTimestamp;android.provider.cts.BrowserTest#testRequestAllIcons;android.provider.cts.ContactsContract_CommonDataKinds_EmailTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testReminders;android.provider.cts.MediaStore_Audio_ArtistsTest#testStoreAudioArtistsExternal;android.provider.cts.ContactsContractIntentsTest#testViewContactDir;android.provider.cts.CalendarTest#testBulkUpdate;android.provider.cts.ContactsContract_PhotoTest#testAddPhoto;android.provider.cts.Settings_NameValueTableTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_hasDeleteLogsForContacts;android.provider.cts.ContactsContract_PhotoTest#testAddEmptyPhoto;android.provider.cts.Settings_SecureTest#testGetPutFloat;android.provider.cts.MediaStore_Images_MediaTest#testGetContentUri;android.provider.cts.ContactsContract_DeletedContacts#testDelete_isUnsupported;android.provider.cts.CalendarTest#testCalendarCreationAndDeletion;android.provider.cts.BrowserTest#testBookmarksTable;android.provider.cts.Settings_SecureTest#testGetUriFor;android.provider.cts.ContactsContract_StatusUpdatesTest#testGetPresenceIconresourceId;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testStoreAudioPlaylistsMembersInternal;android.provider.cts.ContactsContract_CommonDataKinds_EventTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PhonesTest#testGetDisplayLabel;android.provider.cts.ContactsContract_DeletedContacts#testInsert_isUnsupported;android.provider.cts.MediaStore_Audio_AlbumsTest#testStoreAudioAlbumsExternal;android.provider.cts.ContactsContract_DeletedContacts#testAndroidTestCaseSetupProperly;android.provider.cts.BrowserTest#testGetAllVisitedUrls;android.provider.cts.Contacts_ContactMethodsTest#testEncodeAndDecodeProtocol;android.provider.cts.ContactsContract_DeletedContacts#testQueryAll;android.provider.cts.MediaStore_Audio_MediaTest#testStoreAudioMediaInternal;android.provider.cts.ContactsContract_RawContactsTest#testRawContactPsuedoDelete_hasDeleteLogForContact;android.provider.cts.MediaStore_FilesTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Images_MediaTest#testStoreImagesMediaInternal;android.provider.cts.MediaStore_Audio_AlbumsTest#testAlbumArt;android.provider.cts.ContactsContract_ContactsTest#testLookupUri;android.provider.cts.MediaStoreIntentsTest#testPickVideoDir;android.provider.cts.MediaStore_Images_MediaTest#testInsertImageWithBitmap;android.provider.cts.MediaStoreIntentsTest#testViewAudioFile;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testStoreAudioGenresMembersExternal;android.provider.cts.ContactsContract_CommonDataKinds_StructuredPostalTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testInstanceSearch;android.provider.cts.VoicemailContractTest#testStatusTablePermissions;android.provider.cts.ContactsContract_CommonDataKinds_OrganizationTest#testAndroidTestCaseSetupProperly;android.provider.cts.SettingsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_PlaylistsTest#testStoreAudioPlaylistsInternal;android.provider.cts.ContactsContract_DumpFileProviderTest#testQuery_worksWithValidFileName;android.provider.cts.Settings_SecureTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Video_MediaTest#testAndroidTestCaseSetupProperly;android.provider.cts.BrowserTest#testAccessHistory;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByPhonePrefix_returnsCorrectDataRows;android.provider.cts.MediaStore_Images_ThumbnailsTest#testStoreImagesMediaInternal;android.provider.cts.ContactsTest#testCallsTable;android.provider.cts.CalendarTest#testEventCreationAndDeletion;android.provider.cts.VoicemailContractTest#testVoicemailTablePermissions;android.provider.cts.CalendarTest#testEventsUid2445;android.provider.cts.MediaStore_VideoTest#testQuery;android.provider.cts.CalendarTest#testDefaultProjections;android.provider.cts.MediaStoreIntentsTest#testViewVideoDir;android.provider.cts.MediaStore_FilesTest#testGetContentUri;android.provider.cts.ContactsContract_ContactsTest#testInsert_isUnsupported;android.provider.cts.ContactsContract_CommonDataKinds_PhoneTest#testGetTypeLabel;android.provider.cts.VoicemailContractTest#testVoicemailsTable;android.provider.cts.ContactsContract_CommonDataKinds_RelationTest#testGetTypeLabel;android.provider.cts.Settings_SystemTest#testGetDefaultValues;android.provider.cts.ContactsContract_RawContactsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStoreTest#testGetMediaScannerUri;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_removesRecord;android.provider.cts.Settings_SecureTest#testGetPutString;android.provider.cts.ContactsTest#testGroupMembershipTable;android.provider.cts.MediaStore_Audio_MediaTest#testGetContentUri;android.provider.cts.CalendarTest#testFullRecurrenceUpdate;android.provider.cts.MediaStore_FilesTest#testCaseSensitivity;android.provider.cts.MediaStore_Audio_AlbumsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_ArtistsTest#testStoreAudioArtistsInternal;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_hasDeleteLog;android.provider.cts.ContactsContract_DataUsageTest#testMultiIdDataUsageFeedback_incrementsCorrectDataItems;android.provider.cts.CalendarTest#testSyncOnlyInsertEnforcement;android.provider.cts.VoicemailContractTest#testDataColumnUpdate_throwsIllegalArgumentException;android.provider.cts.CalendarTest#testColorWriteRequirements;android.provider.cts.CalendarTest#testWhenByDayQuery;android.provider.cts.ContactsContract_StreamItemsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_AudioTest#testKeyFor;android.provider.cts.ContactsContract_ContactsTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Images_ThumbnailsTest#testQueryInternalThumbnails;android.provider.cts.ContactsContract_DumpFileProviderTest#testOpenFileDescriptor_throwsErrorWithIllegalFileName;android.provider.cts.CalendarTest#testEventColors;android.provider.cts.SettingsTest#testBluetoothDevicesTable;android.provider.cts.ContactsContract_RawContactsTest#testRawContactUpdate_updatesContactUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_RelationTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStoreTest#testGetVersion;android.provider.cts.MediaStore_Audio_GenresTest#testGetContentUri;android.provider.cts.ContactsContract_DataTest#testDataDelete_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsContract_CommonDataKinds_SipAddressTest#testGetTypeLabel;android.provider.cts.BrowserTest#testSaveBookmark;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Video_MediaTest#testGetContentUri;android.provider.cts.CalendarTest#testExtendedProperties;android.provider.cts.Settings_SystemTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testNonAdapterRecurrenceExceptions;android.provider.cts.CalendarTest#testOutOfOrderRecurrenceExceptions;android.provider.cts.CalendarTest#testConversionToRecurring;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testStoreAudioPlaylistsMembersExternal;android.provider.cts.CalendarTest#testMultiRuleRecurrence;android.provider.cts.MediaStoreIntentsTest#testPickAudioDir;android.provider.cts.MediaStore_Audio_GenresTest#testStoreAudioGenresExternal;android.provider.cts.MediaStoreIntentsTest#testViewVideoFile;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemoval_deletesContacts;android.provider.cts.Contacts_ContactMethodsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithGroupRowId;android.provider.cts.TelephonyProviderTest#testOpeningAnyFile;android.provider.cts.MediaStore_Audio_GenresTest#testGetContentUriForAudioId;android.provider.cts.ContactsContract_CommonDataKinds_PhoneTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PeopleTest#testMarkAsContacted;android.provider.cts.MediaStore_FilesTest#testAccess;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testGetTypeLabel;android.provider.cts.SearchRecentSuggestionsTest#testSuggestionsTable;android.provider.cts.CalendarTest#testAttendees;android.provider.cts.SettingsTest#testAccessNonTable;android.provider.cts.MediaStoreIntentsTest#testPickImageDir;android.provider.cts.BrowserTest#testSearchesTable;android.provider.cts.Contacts_SettingsTest#testAccessSetting;android.provider.cts.ContactsContract_StreamItemPhotosTest#testContentPhotoUri;android.provider.cts.ContactsContract_DataTest#testGetLookupUriByDisplayName;android.provider.cts.ContactsContract_StatusUpdatesTest#testInsertStatus;android.provider.cts.MediaStore_Video_ThumbnailsTest#testGetContentUri;android.provider.cts.MediaStore_Audio_GenresTest#testStoreAudioGenresInternal;android.provider.cts.MediaStore_Images_MediaTest#testInsertImageWithImagePath;android.provider.cts.CalendarTest#testForwardRecurrenceExceptions;android.provider.cts.Settings_SecureTest#testUnknownSourcesOffByDefault;android.provider.cts.CalendarTest#testBadRequests;android.provider.cts.ContactsTest#testSettingsTable;android.provider.cts.VoicemailContractTest#testInsert_doesNotUpdateDataColumn;android.provider.cts.ContactsContract_RawContactsTest#testRawContactCreate_updatesContactUpdatedTimestamp;android.provider.cts.MediaStoreIntentsTest#testViewImageDir;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_removesContactRecord;android.provider.cts.MediaStore_Images_ThumbnailsTest#testQueryExternalMiniThumbnails;android.provider.cts.ContactsContract_ContactsTest#testContactUpdate_updatesContactUpdatedTimestamp;android.provider.cts.Settings_SettingNotFoundExceptionTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_RawContactsTest#testGetLookupUriByDisplayName;android.provider.cts.BrowserTest#testAccessSearches;android.provider.cts.ContactsContract_CommonDataKinds_SipAddressTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithUnknownGroupSourceId;android.provider.cts.MediaStore_Audio_MediaTest#testStoreAudioMediaExternal;android.provider.cts.MediaStore_Audio_PlaylistsTest#testGetContentUri;android.provider.cts.ContactsTest#testGroupsTable;android.provider.cts.MediaStore_Audio_AlbumsTest#testGetContentUri;android.provider.cts.Settings_SystemTest#testGetUriFor;android.provider.cts.ContactsContract_StreamItemPhotosTest#testContentDirectoryUri;android.provider.cts.SearchRecentSuggestionsTest#testSearchRecentSuggestions;android.provider.cts.Contacts_ContactMethodsTest#testAddPostalLocation;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testGetContentUri;android.provider.cts.SearchRecentSuggestionsTest#testConstructor;android.provider.cts.ContactsContract_ContactsTest#testMarkAsContacted;android.provider.cts.ContactsTest#testPeopleTable;android.provider.cts.CalendarTest#testCalendarColors;android.provider.cts.CalendarTest#testCalendarIsPrimary;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_hasDeleteLogForContact;android.provider.cts.Settings_SystemTest#testSystemSettings;android.provider.cts.CalendarTest#testRecurrence;android.provider.cts.ContactsContract_GroupMembershipTest#testAddGroupMembershipWithGroupSourceId;android.provider.cts.MediaStore_Audio_Genres_MembersTest#testGetContentUri;android.provider.cts.VoicemailContractTest#testStatusTable;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByFirstName_returnsCorrectDataRows;android.provider.cts.Settings_NameValueTableTest#testGetUriFor;android.provider.cts.ContactsContract_DumpFileProviderTest#testQuery_throwsErrorWithIllegalFileName;android.provider.cts.ContactsTest#testContactMethodsTable;android.provider.cts.ContactsContractIntentsTest#testPickContactDir;android.provider.cts.MediaStore_Audio_AlbumsTest#testStoreAudioAlbumsInternal;android.provider.cts.Contacts_PeopleTest#testAddToGroup;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByEmailPrefix_returnsCorrectDataRows;android.provider.cts.ContactsContract_StatusUpdatesTest#testGetPresencePrecedence;android.provider.cts.BrowserTest#testUpdateVisitedHistory;android.provider.cts.SettingsTest#testSecureTable;android.provider.cts.ContactsContract_StreamItemsTest#testContentUri;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testStoreAudioArtistsAlbumsInternal;android.provider.cts.MediaStore_Video_ThumbnailsTest#testGetThumbnail;android.provider.cts.ContactsContractIntentsTest#testGetContentContactDir;android.provider.cts.SettingsTest#testSystemTable;android.provider.cts.CalendarTest#testEventUpdateAsApp;android.provider.cts.MediaStore_Images_MediaTest#testStoreImagesMediaExternal;android.provider.cts.CalendarTest#testEventsEntityQuery;android.provider.cts.ContactsContractIntentsTest#testAndroidTestCaseSetupProperly;android.provider.cts.CalendarTest#testSyncState;android.provider.cts.MediaStore_Video_ThumbnailsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DataTest#testGetLookupUriBySourceId;android.provider.cts.UserDictionary_WordsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsTest#testExtensionsTable;android.provider.cts.ContactsContract_DataTest#testContactablesUri;android.provider.cts.ContactsContract_CommonDataKinds_ImTest#testGetProtocolLabel;android.provider.cts.Settings_SettingNotFoundExceptionTest#testConstructor;android.provider.cts.CalendarTest#testEventsIsOrganizer;android.provider.cts.ContactsContract_StreamItemsTest#testContentDirectoryUri;android.provider.cts.Contacts_PeopleTest#testAccessPhotoData;android.provider.cts.UserDictionary_WordsTest#testAddWord;android.provider.cts.CalendarTest#testSingleRecurrenceExceptions;android.provider.cts.BrowserTest#testGetAllBookmarks;android.provider.cts.ContactsContract_RawContactsTest#testRawContactDelete_setsDeleteFlag;android.provider.cts.MediaStoreIntentsTest#testViewImageFile;android.provider.cts.Settings_SecureTest#testGetPutLong;android.provider.cts.ContactsContract_DataTest#testContactablesFilterByLastName_returnsCorrectDataRows;android.provider.cts.SearchRecentSuggestionsTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_PhonesTest#testGetDisplayLabelCharSequenceArray;android.provider.cts.SettingsTest#testUserDictionarySettingsExists;android.provider.cts.Contacts_OrganizationsTest#testGetDisplayLabel;android.provider.cts.Settings_SecureTest#testGetDefaultValues;android.provider.cts.ContactsContract_ContactsTest#testContactDelete_marksRawContactsForDeletion;android.provider.cts.ContactsTest#testPhotosTable;android.provider.cts.ContactsContract_ContactsTest#testContentUri;android.provider.cts.MediaStore_Audio_Artists_AlbumsTest#testStoreAudioArtistsAlbumsExternal;android.provider.cts.Contacts_PhonesTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_doesNotDeleteContactAndTimestampUpdated;android.provider.cts.ContactsTest#testOrganizationsTable;android.provider.cts.MediaStore_Audio_Playlists_MembersTest#testGetContentUri;android.provider.cts.MediaStore_Audio_PlaylistsTest#testStoreAudioPlaylistsExternal;android.provider.cts.Contacts_OrganizationsTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsTest#testPhonesTable;android.provider.cts.Settings_SecureTest#testGetPutInt;android.provider.cts.ContactsContract_StatusUpdatesTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_MediaTest#testGetContentUriForPath;android.provider.cts.CalendarTest#testCalendarUpdateAsApp;android.provider.cts.ContactsContract_DeletedContacts#testQueryByContactId;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemoval_hasDeleteLogsForContacts;android.provider.cts.ContactsContract_StreamItemPhotosTest#testAndroidTestCaseSetupProperly;android.provider.cts.MediaStore_Audio_ArtistsTest#testGetContentUri;android.provider.cts.ContactsContract_DumpFileProviderTest#testAndroidTestCaseSetupProperly;android.provider.cts.ContactsContract_DataTest#testDataUpdate_updatesContactLastUpdatedTimestamp;android.provider.cts.ContactsProvider2_AccountRemovalTest#testAccountRemovalWithMergedContact_deletesContacts;android.provider.cts.ContactsContract_RawContactsTest#testGetLookupUriBySourceId;android.provider.cts.MediaStore_Images_ThumbnailsTest#testGetContentUri;android.provider.cts.ContactsContract_DumpFileProviderTest#testOpenFileDescriptor_worksWithValidFileName;android.provider.cts.ContactsContract_CommonDataKinds_StructuredPostalTest#testGetTypeLabel;android.provider.cts.ContactsContract_DataUsageTest#testAndroidTestCaseSetupProperly;android.provider.cts.Contacts_ContactMethodsTest#test;android.provider.cts.MediaStore_Video_MediaTest#testStoreVideoMediaExternal;android.provider.cts.ContactsContract_CommonDataKinds_OrganizationTest#testGetTypeLabel;android.provider.cts.MediaStore_Images_ThumbnailsTest#testStoreImagesMediaExternal;android.provider.cts.ContactsContract_DataTest#testContactablesFilter_doesNotExist_returnsCorrectDataRows;android.provider.cts.ContactsContract_DeletedContacts#testQuery_returnsProperColumns" />
   <Entry uri="android.security" exclude="android.security.cts.KeystoreExploitTest#testAndroidTestCaseSetupProperly;android.security.cts.CharDeviceTest#testExynosKernelMemoryRead;android.security.cts.VoldExploitTest#testAndroidTestCaseSetupProperly;android.security.cts.LoadEffectLibraryTest#testLoadLibrary;android.security.cts.BrowserTest#testAndroidTestCaseSetupProperly;android.security.cts.LinuxRngTest#testDevUrandomMajorMinor;android.security.cts.ServicePermissionsTest#testDumpProtected;android.security.cts.BannedFilesTest#testNoSetuidTcpdump;android.security.cts.SqliteJournalLeakTest#testShm;android.security.cts.LinuxRngTest#testDevRandomMajorMinor;android.security.cts.ClonedSecureRandomTest#testCheckForDuplicateOutput;android.security.cts.BrowserTest#testBrowserPrivateDataAccess;android.security.cts.BrowserTest#testTabExhaustion;android.security.cts.KeystoreExploitTest#testKeystoreCrash;android.security.cts.SqliteJournalLeakTest#testJournal;android.security.cts.BannedFilesTest#testNoCmdClient;android.security.cts.BannedFilesTest#testNoSetuidIp;android.security.cts.BannedFilesTest#testNoSyncAgent;android.security.cts.BannedFilesTest#testNoRootCmdSocket;android.security.cts.ListeningPortsTest#testNoListeningLoopbackTcp6Ports;android.security.cts.KernelSettingsTest#testKptrRestrict;android.security.cts.AslrTest#testVaRandomize;android.security.cts.KernelSettingsTest#testMmapMinAddr;android.security.cts.CertificateTest#testBlockCertificates;android.security.cts.BrowserTest#testTabReuse;android.security.cts.KernelSettingsTest#testSELinuxEnforcing;android.security.cts.ListeningPortsTest#testNoListeningLoopbackUdp6Ports;android.security.cts.NativeCodeTest#testPerfEvent;android.security.cts.KernelSettingsTest#testSetuidDumpable;android.security.cts.PackageSignatureTest#testAndroidTestCaseSetupProperly;android.security.cts.ServicePermissionsTest#testAndroidTestCaseSetupProperly;android.security.cts.CertificateTest#testCertificates;android.security.cts.KernelSettingsTest#testNoConfigGz;android.security.cts.KernelSettingsTest#testDmesgRestrict;android.security.cts.ListeningPortsTest#testAndroidTestCaseSetupProperly;android.security.cts.SqliteJournalLeakTest#testAndroidTestCaseSetupProperly;android.security.cts.ListeningPortsTest#testNoRemotelyAccessibleListeningTcp6Ports;android.security.cts.ListeningPortsTest#testNoListeningLoopbackTcpPorts;android.security.cts.ClonedSecureRandomTest#testAndroidTestCaseSetupProperly;android.security.cts.VoldExploitTest#testTryCommandInjection;android.security.cts.VoldExploitTest#testZergRushCrash;android.security.cts.AslrTest#testOneExecutableIsPie;android.security.cts.VoldExploitTest#testTryToCrashVold;android.security.cts.ListeningPortsTest#testNoListeningLoopbackUdpPorts;android.security.cts.CertificateTest#testAndroidTestCaseSetupProperly;android.security.cts.SqliteJournalLeakTest#testWal;android.security.cts.ListeningPortsTest#testNoRemotelyAccessibleListeningTcpPorts;android.security.cts.CharDeviceTest#testExynosRootingVuln" />
   <Entry uri="android.webkit" exclude="android.webkit.cts.WebViewTest#testDocumentHasImages;android.webkit.cts.WebViewTest#testScrollBarOverlay;android.webkit.cts.WebViewTest#testGoBackAndForward;android.webkit.cts.WebViewTest#testGetContentHeight;android.webkit.cts.WebChromeClientTest#testOnJsConfirm;android.webkit.cts.WebChromeClientTest#testOnProgressChanged;android.webkit.cts.WebViewTest#testAddJavascriptInterfaceNullObject;android.webkit.cts.WebViewTest#testConstructor;android.webkit.cts.WebViewTest#testInvokeZoomPicker;android.webkit.cts.WebSettingsTest#testAccessMinimumLogicalFontSize;android.webkit.cts.WebViewStartupTest#testCookieManagerBlockingUiThread;android.webkit.cts.WebViewTest#testInternals;android.webkit.cts.WebViewClientTest#testShouldOverrideUrlLoading;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestAcceptOnce;android.webkit.cts.WebSettingsTest#testDatabaseDisabled;android.webkit.cts.WebView_WebViewTransportTest#testAccessWebView;android.webkit.cts.WebSettingsTest#testAccessJavaScriptCanOpenWindowsAutomatically;android.webkit.cts.WebSettingsTest#testAppCacheEnabled;android.webkit.cts.WebSettingsTest#testAccessUserAgentString;android.webkit.cts.WebViewTest#testClearHistory;android.webkit.cts.WebSettingsTest#testAccessSerifFontFamily;android.webkit.cts.WebSettingsTest#testAccessLayoutAlgorithm;android.webkit.cts.WebSettingsTest#testAccessFantasyFontFamily;android.webkit.cts.WebViewTest#testAddJavascriptInterface;android.webkit.cts.WebSettingsTest#testAccessCacheMode;android.webkit.cts.WebViewTest#testDebugDump;android.webkit.cts.WebViewTest#testSslErrorProceedResponseNotReusedForDifferentHost;android.webkit.cts.WebSettingsTest#testLocalImageLoads;android.webkit.cts.WebViewTest#testSslErrorProceedResponseReusedForSameHost;android.webkit.cts.HttpAuthHandlerTest#testUseHttpAuthUsernamePassword;android.webkit.cts.WebViewTest#testSetLayoutParams;android.webkit.cts.WebViewTest#testAppInjectedXRequestedWithHeaderIsNotOverwritten;android.webkit.cts.WebSettingsTest#testAccessUseDoubleTree;android.webkit.cts.WebViewTest#testOnReceivedSslErrorCancel;android.webkit.cts.URLUtilTest#testIsHttpUrl;android.webkit.cts.DateSorterTest#testConstants;android.webkit.cts.WebSettingsTest#testAccessFixedFontFamily;android.webkit.cts.WebSettingsTest#testSetRenderPriority;android.webkit.cts.WebViewTest#testRemoveJavascriptInterface;android.webkit.cts.WebViewTest#testAndroidAssetAnchor;android.webkit.cts.WebViewTest#testOnReceivedSslError;android.webkit.cts.CookieTest#testEmptyValue;android.webkit.cts.WebViewTest#testPauseResumeTimers;android.webkit.cts.URLUtilTest#testIsContentUrl;android.webkit.cts.WebChromeClientTest#testBlockWindowsAsync;android.webkit.cts.WebViewTest#testGetVisibleTitleHeight;android.webkit.cts.WebBackForwardListTest#testClone;android.webkit.cts.WebSettingsTest#testAccessDefaultTextEncodingName;android.webkit.cts.URLUtilTest#testGuessUrl;android.webkit.cts.MimeTypeMapTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebChromeClientTest#testOnReceivedIcon;android.webkit.cts.CookieTest#testAndroidTestCaseSetupProperly;android.webkit.cts.CookieManagerTest#testRemoveCookies;android.webkit.cts.WebSettingsTest#testAccessPluginsPath;android.webkit.cts.WebSettingsTest#testAccessAllowFileAccess;android.webkit.cts.WebSettingsTest#testAccessSupportMultipleWindows;android.webkit.cts.WebViewTest#testAppCanInjectHeadersViaImmutableMap;android.webkit.cts.WebViewTest#testSecureSiteSetsCertificate;android.webkit.cts.WebViewTest#testSetWebViewClient;android.webkit.cts.WebViewTest#testSetScrollBarStyle;android.webkit.cts.CookieTest#testDomain;android.webkit.cts.WebViewTest#testZoom;android.webkit.cts.URLUtilTest#testIsDataUrl;android.webkit.cts.CookieManagerTest#testAcceptCookie;android.webkit.cts.WebChromeClientTest#testOnReceivedTitle;android.webkit.cts.URLUtilTest#testIsFileUrl;android.webkit.cts.WebSettingsTest#testAccessJavaScriptEnabled;android.webkit.cts.URLUtilTest#testIsNetworkUrl;android.webkit.cts.WebViewTest#testFindAddress;android.webkit.cts.WebViewTest#testSetNetworkAvailable;android.webkit.cts.WebViewTest#testClearSslPreferences;android.webkit.cts.URLUtilTest#testIsHttpsUrl;android.webkit.cts.MimeTypeMapTest#testGetFileExtensionFromUrl;android.webkit.cts.WebViewTest#testGetOriginalUrl;android.webkit.cts.WebChromeClientTest#testBlockWindowsSync;android.webkit.cts.WebViewTest#testLoadData;android.webkit.cts.WebViewTest#testInsecureSiteClearsCertificate;android.webkit.cts.WebBackForwardListTest#testGetCurrentItem;android.webkit.cts.URLUtilTest#testStripAnchor;android.webkit.cts.URLUtilTest#testGuessFileName;android.webkit.cts.URLUtilTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebViewTest#testEvaluateJavascript;android.webkit.cts.DateSorterTest#testConstructor;android.webkit.cts.WebViewTest#testPageScroll;android.webkit.cts.WebSettingsTest#testIframesWhenAccessFromFileURLsEnabled;android.webkit.cts.WebViewTest#testFlingScroll;android.webkit.cts.WebSettingsTest#testXHRWhenAccessFromFileURLsEnabled;android.webkit.cts.WebChromeClientTest#testOnJsPrompt;android.webkit.cts.WebSettingsTest#testAccessSupportZoom;android.webkit.cts.WebSettingsTest#testLoadsImagesAutomatically;android.webkit.cts.URLUtilTest#testIsValidUrl;android.webkit.cts.WebViewTest#testRequestFocusNodeHref;android.webkit.cts.WebViewTest#testLoadDataWithBaseUrl;android.webkit.cts.WebChromeClientTest#testOnJsAlert;android.webkit.cts.WebSettingsTest#testAccessSansSerifFontFamily;android.webkit.cts.CookieManagerTest#testCookieManager;android.webkit.cts.WebViewTest#testSetMapTrackballToArrowKeys;android.webkit.cts.WebViewTest#testCreatingWebViewCreatesCookieSyncManager;android.webkit.cts.DateSorterTest#testGetIndex;android.webkit.cts.GeolocationTest#testGeolocationPermissions;android.webkit.cts.WebChromeClientTest#testOnJsBeforeUnload;android.webkit.cts.CookieManagerTest#testClone;android.webkit.cts.CookieManagerTest#testGetInstance;android.webkit.cts.WebViewTest#testGetZoomControls;android.webkit.cts.CookieTest#testSubDomain;android.webkit.cts.WebSettingsTest#testUserAgentString_default;android.webkit.cts.MimeTypeMapTest#testGetMimeTypeFromExtension;android.webkit.cts.WebSettingsTest#testBlockNetworkImage;android.webkit.cts.WebViewTest#testPlatformNotifications;android.webkit.cts.URLUtilTest#testIsAboutUrl;android.webkit.cts.WebViewTest#testSetPictureListener;android.webkit.cts.MimeTypeMapTest#testHasMimeType;android.webkit.cts.WebViewTest#testOnReceivedSslErrorProceed;android.webkit.cts.DateSorterTest#testGetLabel;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestAcceptAlways;android.webkit.cts.URLUtilTest#testDecode;android.webkit.cts.HttpAuthHandlerTest#testProceed;android.webkit.cts.WebSettingsTest#testSetNeedInitialFocus;android.webkit.cts.WebSettingsTest#testIframesWhenAccessFromFileURLsDisabled;android.webkit.cts.WebSettingsTest#testAccessCursiveFontFamily;android.webkit.cts.WebViewTest#testFindAll;android.webkit.cts.WebViewTest#testStopLoading;android.webkit.cts.DateSorterTest#testAndroidTestCaseSetupProperly;android.webkit.cts.WebSettingsTest#testAccessDefaultFixedFontSize;android.webkit.cts.CookieManagerTest#testb3167208;android.webkit.cts.WebSettingsTest#testAccessMinimumFontSize;android.webkit.cts.WebSettingsTest#testAccessUseWideViewPort;android.webkit.cts.WebSettingsTest#testAccessSaveFormData;android.webkit.cts.WebViewTest#testRequestChildRectangleOnScreen;android.webkit.cts.URLUtilTest#testIsJavaScriptUrl;android.webkit.cts.WebViewTest#testFindNext;android.webkit.cts.MimeTypeMapTest#testHasExtension;android.webkit.cts.WebViewTest#testSetDownloadListener;android.webkit.cts.WebSettingsTest#testXHRWhenAccessFromFileURLsDisabled;android.webkit.cts.WebViewTest#testDestroy;android.webkit.cts.MimeTypeMapTest#testGetSingleton;android.webkit.cts.WebViewTest#testAndroidAssetQueryParam;android.webkit.cts.WebViewTest#testAccessPluginList;android.webkit.cts.CookieTest#testPath;android.webkit.cts.WebViewTest#testAccessHttpAuthUsernamePassword;android.webkit.cts.WebViewTest#testUseRemovedJavascriptInterface;android.webkit.cts.WebSettingsTest#testAccessTextSize;android.webkit.cts.URLUtilTest#testIsAssetUrl;android.webkit.cts.CookieTest#testInvalidDomain;android.webkit.cts.CookieSyncManagerTest#testCookieSyncManager;android.webkit.cts.URLUtilTest#testComposeSearchUrl;android.webkit.cts.WebChromeClientTest#testWindows;android.webkit.cts.WebViewTest#testRequestImageRef;android.webkit.cts.WebSettingsTest#testAccessDefaultFontSize;android.webkit.cts.WebViewClientTest#testShouldOverrideKeyEvent;android.webkit.cts.WebHistoryItemTest#testWebHistoryItem;android.webkit.cts.WebSettingsTest#testAccessBuiltInZoomControls;android.webkit.cts.WebSettingsTest#testAppCacheDisabled;android.webkit.cts.WebViewTest#testSetWebChromeClient;android.webkit.cts.WebViewTest#testGetHitTestResult;android.webkit.cts.WebSettingsTest#testAccessStandardFontFamily;android.webkit.cts.GeolocationTest#testSimpleGeolocationRequestReject;android.webkit.cts.WebSettingsTest#testBlockNetworkLoads;android.webkit.cts.DateSorterTest#testGetBoundary;android.webkit.cts.WebViewTest#testCapturePicture;android.webkit.cts.WebSettingsTest#testAccessPluginsEnabled;android.webkit.cts.WebViewTest#testSaveAndRestoreState;android.webkit.cts.WebViewTest#testLoadUrl;android.webkit.cts.HttpAuthHandlerTest#testCancel;android.webkit.cts.URLUtilTest#testIsCookielessProxyUrl;android.webkit.cts.WebViewTest#testGetFavicon;android.webkit.cts.MimeTypeMapTest#testGetExtensionFromMimeType" />
-  <Entry uri="android.widget" exclude="android.widget.cts.ImageSwitcherTest#testSetImageDrawable;android.widget.cts.ExpandableListViewTest#testOnSaveInstanceState;android.widget.cts.RemoteViewsTest#testSetFloat;android.widget.cts.ToggleButtonTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testOnGroupCollapsed;android.widget.cts.ListViewTest#testDispatchDraw;android.widget.cts.HorizontalScrollViewTest#testRequestLayout;android.widget.cts.TextViewTest#testDrawableResolution;android.widget.cts.CursorTreeAdapterTest#testGetGroup;android.widget.cts.RemoteViewsTest#testSetBoolean;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildrenCount;android.widget.cts.TimePickerTest#testSetEnabled;android.widget.cts.RemoteViewsTest#testSetChronometer;android.widget.cts.BaseExpandableListAdapterTest#testNotifyDataSetChanged;android.widget.cts.TextViewTest#testConstructor;android.widget.cts.FrameLayoutTest#testGenerateLayoutParams2;android.widget.cts.FrameLayoutTest#testGenerateLayoutParams1;android.widget.cts.DialerFilterTest#testClearText;android.widget.cts.ListViewTest#testFindViewWithTagTraversal;android.widget.cts.TextViewTest#testGetResolvedTextAlignmentWithInheritance;android.widget.cts.SimpleAdapterTest#testSetDropDownViewResource;android.widget.cts.GalleryTest#testShowContextMenu;android.widget.cts.ExpandableListViewTest#testSetChildIndicator;android.widget.cts.GalleryTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testGetGroupCount;android.widget.cts.TextViewTest#testDebug;android.widget.cts.ExpandableListViewTest#testSetIndicatorBounds;android.widget.cts.AutoCompleteTextViewTest#testOnKeyPreIme;android.widget.cts.TextViewTest#testOnTrackballEvent;android.widget.cts.ExpandableListViewTest#testAndroidTestCaseSetupProperly;android.widget.cts.PopupWindowTest#testAccessBackground;android.widget.cts.PopupWindowTest#testGetMaxAvailableHeight;android.widget.cts.TextViewTest#testGetTotalPaddingLeft;android.widget.cts.AutoCompleteTextViewTest#testAccessAdapter;android.widget.cts.AutoCompleteTextViewTest#testPerformCompletion;android.widget.cts.ZoomButtonTest#testDispatchUnhandledMove;android.widget.cts.AbsSpinnerTest#testOnMeasure;android.widget.cts.TextViewTest#testOnWindowFocusChanged;android.widget.cts.PopupWindowTest#testUpdateDimensionAndAlignAnchorViewWithOffsets;android.widget.cts.GridLayoutTest#testCheckLayoutParams;android.widget.cts.TabHostTest#testNewTabSpec;android.widget.cts.ViewAnimatorTest#testGetCurrentView;android.widget.cts.ListViewTest#testDispatchKeyEvent;android.widget.cts.TextViewTest#testSetMinEms;android.widget.cts.FrameLayoutTest#testGatherTransparentRegion;android.widget.cts.AbsListViewTest#testDraw;android.widget.cts.ZoomControlsTest#testHasFocus;android.widget.cts.RemoteViews_ActionExceptionTest#testConstructor;android.widget.cts.ViewFlipperTest#testActivityTestCaseSetUpProperly;android.widget.cts.RemoteViewsTest#testSetString;android.widget.cts.SimpleExpandableListAdapterTest#testConstructor;android.widget.cts.GalleryTest#testComputeHorizontalScrollExtent;android.widget.cts.DialerFilterTest#testGetFilterText;android.widget.cts.RadioGroupTest#testGenerateDefaultLayoutParams;android.widget.cts.DialerFilterTest#testSetFilterWatcher;android.widget.cts.CheckBoxTest#testConstructor;android.widget.cts.ProgressBarTest#testDrawableStateChanged;android.widget.cts.MultiAutoCompleteTextViewTest#testPerformFiltering;android.widget.cts.TextViewTest#testOnKeyMultiple;android.widget.cts.ProgressBarTest#testPostInvalidate;android.widget.cts.SlidingDrawerTest#testOpenAndClose;android.widget.cts.AutoCompleteTextViewTest#testConstructor;android.widget.cts.TextViewTest#testSetLineSpacing;android.widget.cts.ListViewTest#testFindViewTraversal;android.widget.cts.RadioGroupTest#testGetCheckedRadioButtonId;android.widget.cts.TabHostTest#testSetup2;android.widget.cts.TableLayout_LayoutParamsTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testRequestChildFocus;android.widget.cts.TabWidgetTest#testDispatchDraw;android.widget.cts.PopupWindowTest#testUpdate;android.widget.cts.BaseAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.ProgressBarTest#testOnSaveAndRestoreInstanceState;android.widget.cts.TabHostTest#testSetup1;android.widget.cts.TextViewTest#testOnMeasure;android.widget.cts.CompoundButtonTest#testAccessInstanceState;android.widget.cts.TabWidgetTest#testOnFocusChange;android.widget.cts.DialerFilterTest#testOnFinishInflate;android.widget.cts.ImageViewTest#testSetSelected;android.widget.cts.TextViewTest#testDrawableResolution2;android.widget.cts.ExpandableListViewWithHeadersTest#testPreconditions;android.widget.cts.AbsListViewTest#testSetFilterText;android.widget.cts.ExpandableListViewTest#testGetAdapter;android.widget.cts.TextViewTest#testSingleLine;android.widget.cts.HorizontalScrollViewTest#testComputeScroll;android.widget.cts.CursorAdapterTest#testRunQueryOnBackgroundThread;android.widget.cts.ToastTest#testMakeText2;android.widget.cts.CursorAdapterTest#testGetView;android.widget.cts.ViewSwitcherTest#testSetFactory;android.widget.cts.ToastTest#testMakeText1;android.widget.cts.GridViewTest#testAccessStretchMode;android.widget.cts.AutoCompleteTextViewTest#testGetThreshold;android.widget.cts.RemoteViewsTest#testConstructor;android.widget.cts.AbsListViewTest#testCheckLayoutParams;android.widget.cts.ViewAnimatorTest#testSetAnimateFirstView;android.widget.cts.DigitalClockTest#testActivityTestCaseSetUpProperly;android.widget.cts.SlidingDrawerTest#testAnimateOpenAndClose;android.widget.cts.PopupWindowTest#testAccessFocusable;android.widget.cts.TimePickerTest#testSetOnTimeChangedListener;android.widget.cts.ScrollViewTest#testGetMaxScrollAmount;android.widget.cts.CursorAdapterTest#testOnContentChanged;android.widget.cts.TextViewTest#testGetTotalPaddingBottom;android.widget.cts.AnalogClockTest#testOnMeasure;android.widget.cts.RadioGroupTest#testCheck;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetChanged;android.widget.cts.TwoLineListItemTest#testActivityTestCaseSetUpProperly;android.widget.cts.AbsListViewTest#testGetContextMenuInfo;android.widget.cts.ViewAnimatorTest#testAccessOutAnimation;android.widget.cts.SlidingDrawerTest#testConstructor;android.widget.cts.TimePickerTest#testOnSaveInstanceStateAndOnRestoreInstanceState;android.widget.cts.AbsListViewTest#testAccessFastScrollEnabled;android.widget.cts.BaseAdapterTest#testGetItemViewType;android.widget.cts.AbsListViewTest#testSetOnScrollListener;android.widget.cts.ImageViewTest#testSetImageURI;android.widget.cts.RadioGroupTest#testOnFinishInflate;android.widget.cts.TableRowTest#testGenerateLayoutParams;android.widget.cts.DialerFilterTest#testGetLetters;android.widget.cts.HorizontalScrollViewTest#testComputeHorizontalScrollRange;android.widget.cts.TextViewTest#testSetPadding;android.widget.cts.VideoViewTest#testPlayVideo1;android.widget.cts.ArrayAdapterTest#testRemove;android.widget.cts.GridViewTest#testGetNumColumns;android.widget.cts.AbsSpinnerTest#testSetSelectionIntBoolean;android.widget.cts.LayoutDirectionTest#testDirectionForAllLayoutsWithCode;android.widget.cts.SimpleCursorAdapterTest#testAccessStringConversionColumn;android.widget.cts.ViewAnimatorTest#testGetBaseline;android.widget.cts.ChronometerTest#testConstructor;android.widget.cts.ResourceCursorTreeAdapterTest#testConstructor;android.widget.cts.AdapterViewTest#testGetPositionForView;android.widget.cts.GridViewTest#testSetVerticalSpacing;android.widget.cts.ButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ToggleButtonTest#testToggleText;android.widget.cts.RatingBarTest#testSetMax;android.widget.cts.RemoteViewsTest#testSetViewVisibility;android.widget.cts.ScrollViewTest#testOnTouchEvent;android.widget.cts.BaseAdapterTest#testIsEnabled;android.widget.cts.ExpandableListViewTest#testSetOnChildClickListener;android.widget.cts.EditTextTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testMarquee;android.widget.cts.ImageViewTest#testActivityTestCaseSetUpProperly;android.widget.cts.RadioGroupTest#testConstructors;android.widget.cts.DialerFilterTest#testAccessMode;android.widget.cts.DatePickerTest#testInit;android.widget.cts.TextViewTest#testGetTextColors;android.widget.cts.ProgressBarTest#testAccessProgress;android.widget.cts.TextViewTest#testGetPaint;android.widget.cts.SimpleExpandableListAdapterTest#testNewGroupView;android.widget.cts.AdapterView_AdapterContextMenuInfoTest#testConstructor;android.widget.cts.CompoundButtonTest#testConstructor;android.widget.cts.ImageViewTest#testSetImageLevel;android.widget.cts.SimpleCursorTreeAdapterTest#testBindGroupView;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroup;android.widget.cts.TabWidgetTest#testFocusCurrentTab;android.widget.cts.RelativeLayoutTest#testOnLayout;android.widget.cts.ScrollViewTest#testComputeScroll;android.widget.cts.TextViewTest#testAccessHintTextColor;android.widget.cts.TableLayoutTest#testAccessShrinkAllColumns;android.widget.cts.RemoteViewsTest#testGetPackage;android.widget.cts.GridViewTest#testAttachLayoutAnimationParameters;android.widget.cts.LinearLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.CompoundButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetImageDrawable;android.widget.cts.AdapterViewTest#testAccessVisiblePosition;android.widget.cts.ListViewTest#testSaveAndRestoreInstanceState;android.widget.cts.CompoundButtonTest#testToggle;android.widget.cts.TextViewTest#testMoveCursorToVisibleOffset;android.widget.cts.AutoCompleteTextViewTest#testAccessDropDownWidth;android.widget.cts.RadioGroupTest#testGenerateLayoutParams;android.widget.cts.AdapterViewTest#testCanAnimate;android.widget.cts.ScrollViewTest#testOnLayout;android.widget.cts.AutoCompleteTextViewTest#testEnoughToFilter;android.widget.cts.CheckedTextViewTest#testOnCreateDrawableState;android.widget.cts.RelativeLayout_LayoutParamsTest#testDebug;android.widget.cts.ToastTest#testAccessDuration;android.widget.cts.ToggleButtonTest#testSetChecked;android.widget.cts.HeaderViewListAdapterTest#testGetFootersCount;android.widget.cts.GridLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsListViewTest#testAccessSmoothScrollbarEnabled;android.widget.cts.SlidingDrawerTest#testSetOnDrawerOpenListener;android.widget.cts.HeaderViewListAdapterTest#testGetHeadersCount;android.widget.cts.RemoteViewsTest#testSetLong;android.widget.cts.SlidingDrawerTest#testSetOnDrawerCloseListener;android.widget.cts.TextViewTest#testGetResolvedTextDirectionLtrWithInheritance;android.widget.cts.ScrollViewTest#testFullScroll;android.widget.cts.RelativeLayout_LayoutParamsTest#testAccessRule1;android.widget.cts.BaseExpandableListAdapterTest#testAreAllItemsEnabled;android.widget.cts.RelativeLayout_LayoutParamsTest#testAccessRule2;android.widget.cts.RemoteViewsActivityTest#testDerivedClass;android.widget.cts.ToastTest#testShowFailure;android.widget.cts.SimpleAdapterTest#testSetViewImage;android.widget.cts.TextViewTest#testAccessPrivateImeOptions;android.widget.cts.AdapterView_AdapterContextMenuInfoTest#testAndroidTestCaseSetupProperly;android.widget.cts.TwoLineListItemTest#testOnFinishInflate;android.widget.cts.ExpandableListViewTest#testGetExpandableListPosition;android.widget.cts.AbsListViewTest#testAccessListPadding;android.widget.cts.ExpandableListViewTest#testExpandGroup;android.widget.cts.TextViewTest#testGetUrls;android.widget.cts.LinearLayoutTest#testAccessBaselineAlignedChildIndex;android.widget.cts.CheckBoxTest#testAndroidTestCaseSetupProperly;android.widget.cts.ZoomControlsTest#testSetOnZoomInClickListener;android.widget.cts.AbsSpinnerTest#testGetCount;android.widget.cts.SimpleExpandableListAdapterTest#testNewChildView;android.widget.cts.ViewSwitcherTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testReplaceText;android.widget.cts.ScrollViewTest#testAccessFillViewport;android.widget.cts.ToastTest#testShow;android.widget.cts.TextViewTest#testDrawableStateChanged;android.widget.cts.TimePickerTest#testGetBaseline;android.widget.cts.TextViewTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnFocusChanged;android.widget.cts.ImageViewTest#testDrawableStateChanged;android.widget.cts.FrameLayoutTest#testOnLayout;android.widget.cts.ListViewTest#testAccessItemsCanFocus;android.widget.cts.AutoCompleteTextViewTest#testOnCommitCompletion;android.widget.cts.ScrollViewTest#testAccessSmoothScrollingEnabled;android.widget.cts.TextViewTest#testSelection;android.widget.cts.CheckedTextViewTest#testOnDraw;android.widget.cts.ViewAnimatorTest#testConstructor;android.widget.cts.RadioGroupTest#testInternalPassThroughHierarchyChangeListener;android.widget.cts.SimpleCursorAdapterTest#testSetViewImage;android.widget.cts.AdapterViewTest#testGetSelected;android.widget.cts.ExpandableListViewWithHeadersTest#testSelectedPosition;android.widget.cts.ProgressBarTest#testAccessIndeterminateDrawable;android.widget.cts.TableLayoutTest#testSetOnHierarchyChangeListener;android.widget.cts.ExpandableListViewTest#testSetOnItemClickListener;android.widget.cts.ProgressBarTest#testSetVisibility;android.widget.cts.AutoCompleteTextViewTest#testConvertSelectionToString;android.widget.cts.ImageViewTest#testSetImageResource;android.widget.cts.ScrollViewTest#testGetVerticalFadingEdgeStrengths;android.widget.cts.FilterTest#testFilter2;android.widget.cts.CursorTreeAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.FilterTest#testFilter1;android.widget.cts.ZoomControlsTest#testSetIsZoomOutEnabled;android.widget.cts.ScrollViewTest#testMeasureChildWithMargins;android.widget.cts.CompoundButtonTest#testSetOnCheckedChangeListener;android.widget.cts.TextViewTest#testGetEditableText;android.widget.cts.HorizontalScrollViewTest#testOnRequestFocusInDescendants;android.widget.cts.AbsSpinnerTest#testAccessAdapter;android.widget.cts.ChronometerTest#testAccessOnChronometerTickListener;android.widget.cts.HeaderViewListAdapterTest#testConstructor;android.widget.cts.FrameLayoutTest#testSetForegroundGravity;android.widget.cts.AbsSpinnerTest#testGetSelectedView;android.widget.cts.TextViewTest#testSetGetTextAlignment;android.widget.cts.TextViewTest#testGetLayout;android.widget.cts.ImageViewTest#testConstructor;android.widget.cts.ImageViewTest#testInvalidateDrawable;android.widget.cts.PopupWindowTest#testShowAsDropDownWithOffsets;android.widget.cts.PopupWindowTest#testIsAboveAnchor;android.widget.cts.AutoCompleteTextViewTest#testPerformFiltering;android.widget.cts.ViewFlipperTest#testConstructor;android.widget.cts.DatePickerTest#testUpdateDate;android.widget.cts.MultiAutoCompleteTextViewTest#testMultiAutoCompleteTextView;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testConstructor;android.widget.cts.RemoteViewsTest#testSetOnClickPendingIntent;android.widget.cts.HorizontalScrollViewTest#testScrollTo;android.widget.cts.HorizontalScrollViewTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnMeasure;android.widget.cts.TextViewTest#testSetHighlightColor;android.widget.cts.BaseExpandableListAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.ExpandableListViewWithHeadersTest#testConvertionBetweenFlatAndPacked;android.widget.cts.HeaderViewListAdapterTest#testRemoveFooter;android.widget.cts.ListViewTest#testTransientStateStableIds;android.widget.cts.ScrollViewTest#testSmoothScrollBy;android.widget.cts.ViewSwitcherTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testAccessAutoLinkMask;android.widget.cts.ScrollerTest#testScrollModeWithDefaultDuration;android.widget.cts.GalleryTest#testComputeHorizontalScrollRange;android.widget.cts.CheckedTextViewTest#testSetPadding;android.widget.cts.CursorTreeAdapterTest#testGetGroupId;android.widget.cts.RemoteViewsTest#testApply;android.widget.cts.DialerFilterTest#testIsQwertyKeyboard;android.widget.cts.GalleryTest#testShowContextMenuForChild;android.widget.cts.ScrollViewTest#testPageScroll;android.widget.cts.SimpleCursorTreeAdapterTest#testSetViewImage;android.widget.cts.TableLayoutTest#testOnLayout;android.widget.cts.ArrayAdapterTest#testGetPosition;android.widget.cts.TabWidgetTest#testOnSizeChanged;android.widget.cts.SimpleCursorAdapterTest#testNewDropDownView;android.widget.cts.AutoCompleteTextViewTest#testAccessListSelection;android.widget.cts.TextViewTest#testSetText1;android.widget.cts.TextViewTest#testSetText2;android.widget.cts.RemoteViewsActivityTest#testWebView;android.widget.cts.TextViewTest#testSetText3;android.widget.cts.ToastTest#testAccessGravity;android.widget.cts.HorizontalScrollViewTest#testAddViewWithIndex;android.widget.cts.ResourceCursorTreeAdapterTest#testNewChildView;android.widget.cts.HeaderViewListAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.ExpandableListViewTest#testGetPackedPositionForGroup;android.widget.cts.ZoomControlsTest#testShowAndHide;android.widget.cts.CompoundButtonTest#testAccessChecked;android.widget.cts.SimpleAdapterTest#testGetView;android.widget.cts.DialerFilterTest#testGetDigits;android.widget.cts.ArrayAdapterTest#testDataChangeEvent;android.widget.cts.ImageSwitcherTest#testAndroidTestCaseSetupProperly;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildView;android.widget.cts.ZoomControlsTest#testConstructor;android.widget.cts.CompoundButtonTest#testVerifyDrawable;android.widget.cts.TextViewTest#testSetEms;android.widget.cts.AbsSpinnerTest#testGenerateDefaultLayoutParams;android.widget.cts.TextViewTest#testAccessError;android.widget.cts.ZoomButtonTest#testOnTouchEvent;android.widget.cts.TextViewTest#testSetMaxLinesException;android.widget.cts.CompoundButtonTest#testSetButtonDrawableById;android.widget.cts.FrameLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.HorizontalScrollViewTest#testComputeScrollDeltaToGetChildRectOnScreen;android.widget.cts.ExpandableListViewTest#testSetSelectedChild;android.widget.cts.MediaControllerTest#testShow;android.widget.cts.CursorAdapterTest#testGetDropDownView;android.widget.cts.ListViewTest#testNoSelectableItems;android.widget.cts.TextViewTest#testGetBaseLine;android.widget.cts.AbsSpinnerTest#testConstructor;android.widget.cts.ListViewTest#testAccessDividerHeight;android.widget.cts.TabWidgetTest#testChildDrawableStateChanged;android.widget.cts.AbsListViewTest#testAccessSelectedItem;android.widget.cts.ScrollerTest#testTimePassed;android.widget.cts.ArrayAdapterTest#testGetItem;android.widget.cts.ImageViewTest#testVerifyDrawable;android.widget.cts.ProgressBarTest#testConstructor;android.widget.cts.ProgressBarTest#testIncrementSecondaryProgressBy;android.widget.cts.CursorTreeAdapterTest#testSetChildrenCursor;android.widget.cts.FrameLayoutTest#testCheckLayoutParams;android.widget.cts.AbsoluteLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ZoomButtonTest#testOnKeyUp;android.widget.cts.BaseExpandableListAdapterTest#testGetCombinedId;android.widget.cts.ListViewTest#testSetSelection;android.widget.cts.SimpleCursorAdapterTest#testNewView;android.widget.cts.AutoCompleteTextViewTest#testPopupWindow;android.widget.cts.TextView_SaveStateTest#testWriteToParcel;android.widget.cts.TabHost_TabSpecTest#testSetContent2;android.widget.cts.SeekBarTest#testConstructor;android.widget.cts.TabHost_TabSpecTest#testSetContent1;android.widget.cts.TextViewTest#testSetExtractedText;android.widget.cts.TabHost_TabSpecTest#testSetContent3;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupCount;android.widget.cts.TextViewTest#testComputeVerticalScrollRange;android.widget.cts.GridLayoutTest#testAlignment;android.widget.cts.ExpandableListViewTest#testGetPackedPositionForChild;android.widget.cts.FrameLayoutTest#testConstructor;android.widget.cts.CursorAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.RelativeLayoutTest#testSetHorizontalGravity;android.widget.cts.GalleryTest#testDispatchKeyEvent;android.widget.cts.ToastTest#testSetText1;android.widget.cts.HorizontalScrollViewTest#testFullScroll;android.widget.cts.RemoteViewsTest#testSetCharSequence;android.widget.cts.ToastTest#testSetText2;android.widget.cts.HeaderViewListAdapterTest#testRemoveHeader;android.widget.cts.TextSwitcherTest#testSetCurrentText;android.widget.cts.TwoLineListItemTest#testConstructor;android.widget.cts.MediaControllerTest#testMediaController;android.widget.cts.ListViewTest#testConstructor;android.widget.cts.AbsListViewTest#testAccessSelector;android.widget.cts.BaseExpandableListAdapterTest#testDataSetObserver;android.widget.cts.BaseAdapterTest#testHasStableIds;android.widget.cts.TextViewTest#testAccessImeActionLabel;android.widget.cts.CompoundButtonTest#testSetButtonDrawableByDrawable;android.widget.cts.AbsListViewTest#testLayoutChildren;android.widget.cts.ImageViewTest#testClearColorFilter;android.widget.cts.TextViewTest#testGetResolvedTextDirectionRtl;android.widget.cts.CheckedTextViewTest#testChecked;android.widget.cts.MultiAutoCompleteTextViewTest#testReplaceText;android.widget.cts.DigitalClockTest#testOnDetachedFromWindow;android.widget.cts.CursorTreeAdapterTest#testGetChildView;android.widget.cts.FrameLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetMaxWidth;android.widget.cts.TabHostTest#testDispatchWindowFocusChanged;android.widget.cts.DigitalClockTest#testConstructor;android.widget.cts.DialerFilterTest#testOnKeyUpDown;android.widget.cts.TextViewTest#testSetHorizontallyScrolling;android.widget.cts.HeaderViewListAdapterTest#testGetViewTypeCount;android.widget.cts.AbsSeekBarTest#testAccessKeyProgressIncrement;android.widget.cts.AbsSeekBarTest#testSetThumb;android.widget.cts.ProgressBarTest#testOnDraw;android.widget.cts.TextViewTest#testSetText;android.widget.cts.PopupWindowTest#testAccessContentView;android.widget.cts.GridLayoutTest#testConstructor;android.widget.cts.PopupWindowTest#testAccessHeight;android.widget.cts.ToggleButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.ProgressBarTest#testVerifyDrawable;android.widget.cts.TableRowTest#testOnLayout;android.widget.cts.RadioGroupTest#testInternalCheckedStateTracker;android.widget.cts.HorizontalScrollViewTest#testOnSizeChanged;android.widget.cts.ResourceCursorAdapterTest#testNewView;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testFindTokenEnd;android.widget.cts.ZoomButtonTest#testOnLongClick;android.widget.cts.AnalogClockTest#testOnDetachedFromWindow;android.widget.cts.TextViewTest#testSetMaxLines;android.widget.cts.ExpandableListViewTest#testPerformItemClick;android.widget.cts.TextViewTest#testResetTextDirection;android.widget.cts.ScrollViewTest#testScrollTo;android.widget.cts.TableLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ImageViewTest#testSetAdjustViewBounds;android.widget.cts.TableRowTest#testCheckLayoutParams;android.widget.cts.CheckedTextViewTest#testToggle;android.widget.cts.RemoteViewsTest#testSetProgressBar;android.widget.cts.VideoViewTest#testSetOnErrorListener;android.widget.cts.GalleryTest#testDispatchSetPressed;android.widget.cts.HorizontalScrollViewTest#testSmoothScrollTo;android.widget.cts.ZoomControlsTest#testSetZoomSpeed;android.widget.cts.ExpandableListViewTest#testSetAdapter;android.widget.cts.AutoCompleteTextViewTest#testOnFilterComplete;android.widget.cts.ImageViewTest#testGetDrawable;android.widget.cts.CursorTreeAdapterTest#testRunQueryOnBackgroundThread;android.widget.cts.SlidingDrawerTest#testGetHandle;android.widget.cts.BaseAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.MediaControllerTest#testOnTrackballEvent;android.widget.cts.GalleryTest#testGenerateLayoutParams;android.widget.cts.PopupWindowTest#testConstructor;android.widget.cts.GalleryTest#testFoo;android.widget.cts.PopupWindowTest#testAccessAnimationStyle;android.widget.cts.ToggleButtonTest#testAccessTextOn;android.widget.cts.AbsoluteLayoutTest#testGenerateLayoutParams1;android.widget.cts.TextViewTest#testGetDefaultEditable;android.widget.cts.AbsoluteLayoutTest#testGenerateLayoutParams2;android.widget.cts.ArrayAdapterTest#testGetFilter;android.widget.cts.CheckedTextViewTest#testSetCheckMarkDrawableById;android.widget.cts.ScrollerTest#testAbortAnimation;android.widget.cts.LinearLayout_LayoutParamsTest#testConstructor;android.widget.cts.TextViewTest#testComputeVerticalScrollExtent;android.widget.cts.AbsSpinnerTest#testSetSelectionInt;android.widget.cts.TabHostTest#testGetTabContentView;android.widget.cts.TextViewTest#testGetDefaultMovementMethod;android.widget.cts.ScrollViewTest#testRequestChildRectangleOnScreen;android.widget.cts.TabHostTest#testGetCurrentView;android.widget.cts.TextViewTest#testAccessFilters;android.widget.cts.GalleryTest#testCheckLayoutParams;android.widget.cts.RadioGroup_LayoutParamsTest#testConstructor;android.widget.cts.BaseExpandableListAdapterTest#testOnGroupExpanded;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testTerminateToken;android.widget.cts.ScrollViewTest#testConstructor;android.widget.cts.ListViewTest#testLayoutChildren;android.widget.cts.RemoteViewsTest#testSetImageViewUri;android.widget.cts.ViewAnimatorTest#testAccessDisplayedChild;android.widget.cts.ExpandableListViewTest#testCollapseGroup;android.widget.cts.AnalogClockTest#testConstructor;android.widget.cts.CursorAdapterTest#testConvertToString;android.widget.cts.BaseAdapterTest#testIsEmpty;android.widget.cts.ResourceCursorTreeAdapterTest#testNewGroupView;android.widget.cts.ScrollViewTest#testFling;android.widget.cts.SlidingDrawerTest#testOnMeasure;android.widget.cts.TextViewTest#testSetMaxEms;android.widget.cts.LinearLayoutTest#testGenerateLayoutParams;android.widget.cts.PopupWindowTest#testAccessInputMethodMode;android.widget.cts.ListViewTest#testPerformItemClick;android.widget.cts.TextViewTest#testOnTextChanged;android.widget.cts.GridViewTest#testSetSelection;android.widget.cts.CursorAdapterTest#testHasStableIds;android.widget.cts.ProgressBarTest#testSetIndeterminate;android.widget.cts.TableRowTest#testGetVirtualChildCount;android.widget.cts.AutoCompleteTextViewTest#testOnTextChanged;android.widget.cts.TextViewTest#testSetLinesException;android.widget.cts.TextViewTest#testLength;android.widget.cts.RelativeLayoutTest#testSetGravity;android.widget.cts.RelativeLayoutTest#testConstructor;android.widget.cts.TextViewTest#testGetLineHeight;android.widget.cts.GalleryTest#testSetAnimationDuration;android.widget.cts.PopupWindowTest#testAccessTouchable;android.widget.cts.RelativeLayoutTest#testSetVerticalGravity;android.widget.cts.CursorTreeAdapterTest#testConstructor;android.widget.cts.AbsSpinnerTest#testRequestLayout;android.widget.cts.TextViewTest#testOnKeyShortcut;android.widget.cts.TableRowTest#testGetVirtualChildAt;android.widget.cts.SpinnerTest#testConstructor;android.widget.cts.SimpleAdapterTest#testGetDropDownView;android.widget.cts.ScrollViewTest#testAddViewWithLayoutParams;android.widget.cts.HeaderViewListAdapterTest#testGetItemViewType;android.widget.cts.RadioGroupTest#testSetOnCheckedChangeListener;android.widget.cts.ToggleButtonTest#testOnFinishInflate;android.widget.cts.DialerFilterTest#testAppend;android.widget.cts.HorizontalScrollViewTest#testRequestChildRectangleOnScreen;android.widget.cts.RadioButtonTest#testConstructor;android.widget.cts.PopupWindowTest#testDismiss;android.widget.cts.TableLayoutTest#testAddView2;android.widget.cts.TableLayoutTest#testAddView3;android.widget.cts.TabHostTest#testOnTouchModeChanged;android.widget.cts.TableLayoutTest#testAddView4;android.widget.cts.HeaderViewListAdapterTest#testGetWrappedAdapter;android.widget.cts.TableLayoutTest#testAddView1;android.widget.cts.ImageSwitcherTest#testSetImageResource;android.widget.cts.ToggleButtonTest#testDrawableStateChanged;android.widget.cts.EditTextTest#testSetSelectionIndex;android.widget.cts.EditTextTest#testExtendSelection;android.widget.cts.AbsSeekBarTest#testSetMax;android.widget.cts.CursorTreeAdapterTest#testChangeCursor;android.widget.cts.AdapterViewTest#testItemOrItemIdAtPosition;android.widget.cts.HorizontalScrollViewTest#testDispatchKeyEvent;android.widget.cts.TableRowTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsListViewTest#testSetRecyclerListener;android.widget.cts.GalleryTest#testGetContextMenuInfo;android.widget.cts.AbsListViewTest#testAccessStackFromBottom;android.widget.cts.TextViewTest#testAccessText;android.widget.cts.SpinnerTest#testGetBaseline;android.widget.cts.ScrollViewTest#testRequestChildFocus;android.widget.cts.LinearLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.ButtonTest#testConstructor;android.widget.cts.ResourceCursorAdapterTest#testSetViewResource;android.widget.cts.ExpandableListViewBasicTest#testContextMenus;android.widget.cts.FrameLayoutTest#testVerifyDrawable;android.widget.cts.ExpandableListViewTest#testGetPackedPositionType;android.widget.cts.LinearLayoutTest#testLayoutHorizontal;android.widget.cts.ScrollViewTest#testComputeVerticalScrollRange;android.widget.cts.GridViewTest#testOnFocusChanged;android.widget.cts.ZoomControlsTest#testOnTouchEvent;android.widget.cts.GridViewTest#testActivityTestCaseSetUpProperly;android.widget.cts.ListViewTest#testAccessItemChecked;android.widget.cts.AbsoluteLayoutTest#testConstructor;android.widget.cts.TextViewTest#testComputeHorizontalScrollRange;android.widget.cts.HorizontalScrollViewTest#testAddViewWithLayoutParams;android.widget.cts.RemoteViewsTest#testGetLayoutId;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetChangedBoolean;android.widget.cts.TableRow_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.HorizontalScrollViewTest#testMeasureChildWithMargins;android.widget.cts.TextViewTest#testSetIncludeFontPadding;android.widget.cts.HorizontalScrollViewTest#testAddView;android.widget.cts.TimePickerTest#testAccessCurrentMinute;android.widget.cts.TextViewTest#testGetExtendedPaddingBottom;android.widget.cts.BaseExpandableListAdapterTest#testIsEmpty;android.widget.cts.SimpleAdapterTest#testAccessViewBinder;android.widget.cts.SpinnerTest#testOnLayout;android.widget.cts.AutoCompleteTextViewTest#testAccessValidater;android.widget.cts.ScrollViewTest#testAddViewWithIndex;android.widget.cts.FrameLayoutTest#testDrawableStateChanged;android.widget.cts.PopupWindowTest#testSetTouchInterceptor;android.widget.cts.SimpleAdapterTest#testGetItem;android.widget.cts.ImageViewTest#testSetColorFilter1;android.widget.cts.ImageViewTest#testSetColorFilter2;android.widget.cts.SlidingDrawerTest#testDispatchDraw;android.widget.cts.TextViewTest#testSetOnEditorActionListener;android.widget.cts.DatePickerTest#testSetEnabled;android.widget.cts.RemoteViewsTest#testReapply;android.widget.cts.TextViewTest#testCompound;android.widget.cts.AdapterViewTest#testAccessEmptyView;android.widget.cts.Gallery_LayoutParamsTest#testConstructor;android.widget.cts.TextViewTest#testGetResolvedTextDirectionRtlWithInheritance;android.widget.cts.PopupWindowTest#testShowAtLocation;android.widget.cts.TextViewTest#testPerformLongClick;android.widget.cts.ExpandableListViewTest#testSetChildDivider;android.widget.cts.PopupWindowTest#testAccessClippingEnabled;android.widget.cts.RemoteViewsTest#testSetTextViewText;android.widget.cts.ScrollViewTest#testMeasureChild;android.widget.cts.TableLayoutTest#testAccessColumnShrinkable;android.widget.cts.TableRow_LayoutParamsTest#testConstructor;android.widget.cts.GalleryTest#testComputeHorizontalScrollOffset;android.widget.cts.ProgressBarTest#testOnMeasure;android.widget.cts.ProgressBarTest#testAccessProgressDrawable;android.widget.cts.SimpleAdapterTest#testGetFilter;android.widget.cts.RadioGroupTest#testSetOnHierarchyChangeListener;android.widget.cts.AbsListViewTest#testHandleDataChanged;android.widget.cts.AbsListViewTest#testShowContextMenuForChild;android.widget.cts.ExpandableListViewTest#testConstructor;android.widget.cts.HeaderViewListAdapterTest#testIsEmpty;android.widget.cts.AlphabetIndexerTest#testAlphabetIndexer;android.widget.cts.ChronometerTest#testAccessBase;android.widget.cts.TextViewTest#testAccessRawContentType;android.widget.cts.ScrollViewTest#testExecuteKeyEvent;android.widget.cts.SpinnerTest#testAccessPrompt;android.widget.cts.AnalogClockTest#testOnDraw;android.widget.cts.ChronometerTest#testStartAndStop;android.widget.cts.TabWidgetTest#testSetCurrentTab;android.widget.cts.CursorTreeAdapterTest#testGetFilter;android.widget.cts.ImageSwitcherTest#testSetImageURI;android.widget.cts.TabHostTest#testSetOnTabChangedListener;android.widget.cts.ScrollViewTest#testSmoothScrollTo;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupId;android.widget.cts.AbsListViewTest#testAccessCacheColorHint;android.widget.cts.TabHostTest#testSetCurrentTabByTag;android.widget.cts.HeaderViewListAdapterTest#testIsEnabled;android.widget.cts.ImageViewTest#testSetImageState;android.widget.cts.ExpandableListViewTest#testSetSelectedGroup;android.widget.cts.DialerFilterTest#testRemoveFilterWatcher;android.widget.cts.ToggleButtonTest#testAccessTextOff;android.widget.cts.TextViewTest#testSetSelectAllOnFocus;android.widget.cts.TextViewTest#testAccessKeyListener;android.widget.cts.ArrayAdapterTest#testInsert;android.widget.cts.MediaControllerTest#testSetPrevNextListeners;android.widget.cts.PopupWindowTest#testUpdatePositionAndDimension;android.widget.cts.TextViewTest#testOnDraw;android.widget.cts.TextViewTest#testSetFrame;android.widget.cts.ScrollerTest#testAccessFinalX;android.widget.cts.ScrollerTest#testAccessFinalY;android.widget.cts.AdapterViewTest#testOnLayout;android.widget.cts.ScrollViewTest#testOnRequestFocusInDescendants;android.widget.cts.TextViewTest#testCancelLongPress;android.widget.cts.GridViewTest#testAccessAdapter;android.widget.cts.ZoomButtonTest#testSetZoomSpeed;android.widget.cts.ExpandableListViewTest#testGetPackedPositionGroup;android.widget.cts.ExpandableListViewTest#testSetGroupIndicator;android.widget.cts.TextViewTest#testGetResolvedTextDirectionLtr;android.widget.cts.BaseAdapterTest#testGetDropDownView;android.widget.cts.ViewAnimatorTest#testShowPrevious;android.widget.cts.TextViewTest#testOnCreateContextMenu;android.widget.cts.TextViewTest#testAppend;android.widget.cts.DialerFilterTest#testOnFocusChanged;android.widget.cts.SlidingDrawerTest#testOnLayout;android.widget.cts.TextViewTest#testGetFocusedRect;android.widget.cts.TextSwitcherTest#testAddView;android.widget.cts.ExpandableListViewTest#testSetChildIndicatorBounds;android.widget.cts.RemoteViewsTest#testSetImageViewBitmap;android.widget.cts.CursorTreeAdapterTest#testHasStableIds;android.widget.cts.ToastTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testConvertToString;android.widget.cts.AbsListViewTest#testGetTopBottomFadingEdgeStrength;android.widget.cts.EditTextTest#testGetDefaultMovementMethod;android.widget.cts.AbsListViewTest#testAddTouchables;android.widget.cts.SeekBarTest#testSetOnSeekBarChangeListener;android.widget.cts.TextViewTest#testAccessLinkTextColor;android.widget.cts.TextViewTest#testAccessEllipsize;android.widget.cts.RatingBarTest#testAccessNumStars;android.widget.cts.ImageViewTest#testGetBaseline;android.widget.cts.AnalogClockTest#testOnAttachedToWindow;android.widget.cts.LayoutDirectionTest#testDirectionInheritanceForAllLayoutsWithCode;android.widget.cts.AutoCompleteTextViewTest#testAccessItemSelectedListener;android.widget.cts.TextSwitcherTest#testConstructor;android.widget.cts.TextViewTest#testGetResolvedTextAlignment;android.widget.cts.ExpandableListView_ExpandableListContextMenuInfoTest#testConstructor;android.widget.cts.HeaderViewListAdapterTest#testGetFilter;android.widget.cts.AbsSeekBarTest#testConstructor;android.widget.cts.ZoomControlsTest#testSetOnZoomOutClickListener;android.widget.cts.ArrayAdapterTest#testSort;android.widget.cts.HorizontalScrollViewTest#testFling;android.widget.cts.GridViewTest#testConstructor;android.widget.cts.RemoteViewsTest#testNotFeasibleSetters;android.widget.cts.RemoteViewsTest#testWriteToParcel;android.widget.cts.ScrollViewTest#testAddView;android.widget.cts.FilterTest#testConstructor;android.widget.cts.SimpleExpandableListAdapterTest#testGetChildId;android.widget.cts.AutoCompleteTextViewTest#testOnDetachedFromWindow;android.widget.cts.ScrollViewTest#testRequestLayout;android.widget.cts.ImageButtonTest#testConstructor;android.widget.cts.EditTextTest#testSelectAll;android.widget.cts.PopupWindowTest#testSetOnDismissListener;android.widget.cts.TimePickerTest#testAccessCurrentHour;android.widget.cts.ArrayAdapterTest#testCreateFromResource;android.widget.cts.TextViewTest#testSetShadowLayer;android.widget.cts.CompoundButtonTest#testOnCreateDrawableState;android.widget.cts.SimpleCursorAdapterTest#testChangeCursorAndColumns;android.widget.cts.ImageViewTest#testSetFrame;android.widget.cts.ArrayAdapterTest#testSetDropDownViewResouce;android.widget.cts.ResourceCursorAdapterTest#testNewDropDownView;android.widget.cts.ToastTest#testCancel;android.widget.cts.RatingBarTest#testAccessStepSize;android.widget.cts.CheckedTextViewTest#testSetCheckMarkDrawableByDrawable;android.widget.cts.TextViewTest#testInstanceState;android.widget.cts.AbsListViewTest#testGetFocusedRect;android.widget.cts.ViewAnimatorTest#testAddView;android.widget.cts.AdapterViewTest#testUnsupportedMethods;android.widget.cts.TextViewTest#testOnPrivateIMECommand;android.widget.cts.CursorAdapterTest#testAccessCursor;android.widget.cts.TextViewTest#testBringPointIntoView;android.widget.cts.SpinnerTest#testOnClick;android.widget.cts.TextViewTest#testSetEditableFactory;android.widget.cts.ViewSwitcherTest#testGetNextView;android.widget.cts.AbsSeekBarTest#testAccessThumbOffset;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testDebug;android.widget.cts.DatePickerTest#testAccessDate;android.widget.cts.CursorAdapterTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testPageScroll;android.widget.cts.AutoCompleteTextViewTest#testPerformValidation;android.widget.cts.TextViewTest#testAccessLinksClickable;android.widget.cts.SlidingDrawerTest#testGetContent;android.widget.cts.ViewAnimatorTest#testShowNext;android.widget.cts.TextViewTest#testResetTextAlignment;android.widget.cts.ScrollerTest#testFlingMode;android.widget.cts.SlidingDrawerTest#testOnInterceptTouchEvent;android.widget.cts.DatePickerTest#testConstructor;android.widget.cts.TableLayoutTest#testAccessColumnCollapsed;android.widget.cts.AbsSeekBarTest#testVerifyDrawable;android.widget.cts.ScrollViewTest#testOnSizeChanged;android.widget.cts.TimePickerTest#testConstructors;android.widget.cts.TextViewTest#testAccessTransformationMethod;android.widget.cts.SimpleCursorTreeAdapterTest#testBindChildView;android.widget.cts.ScrollerTest#testExtendDuration;android.widget.cts.RatingBarTest#testAccessRating;android.widget.cts.RelativeLayoutTest#testCheckLayoutParams;android.widget.cts.GalleryTest#testGenerateDefaultLayoutParams;android.widget.cts.AbsoluteLayoutTest#testCheckLayoutParams;android.widget.cts.SlidingDrawerTest#testOnFinishInflate;android.widget.cts.EditTextTest#testSetSelectionStartstop;android.widget.cts.ToggleButtonTest#testSetBackgroundDrawable;android.widget.cts.HeaderViewListAdapterTest#testRegisterDataSetObserver;android.widget.cts.TextViewTest#testAccessFreezesText;android.widget.cts.ArrayAdapterTest#testAndroidTestCaseSetupProperly;android.widget.cts.ExpandableListViewTest#testGetSelectedPosition;android.widget.cts.TabHostTest#testGetCurrentTabTag;android.widget.cts.GridViewTest#testOnMeasure;android.widget.cts.HeaderViewListAdapterTest#testGetItemId;android.widget.cts.RadioButtonTest#testToggle;android.widget.cts.TextViewTest#testOnFocusChanged;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.ViewAnimatorTest#testAccessInAnimation;android.widget.cts.AdapterViewTest#testDispatchSaveInstanceState;android.widget.cts.HeaderViewListAdapterTest#testHasStableIds;android.widget.cts.RelativeLayoutTest#testOnMeasure;android.widget.cts.HorizontalScrollViewTest#testSmoothScrollBy;android.widget.cts.ListViewTest#testTransientStateUnstableIds;android.widget.cts.TableRowTest#testConstructor;android.widget.cts.TabHost_TabSpecTest#testSetIndicator2;android.widget.cts.ArrayAdapterTest#testAccessView;android.widget.cts.TabHost_TabSpecTest#testSetIndicator1;android.widget.cts.HorizontalScrollViewTest#testAccessSmoothScrollingEnabled;android.widget.cts.TextViewTest#testSetCursorVisible;android.widget.cts.FrameLayout_LayoutParamsTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testOnAttachedToWindow;android.widget.cts.CursorAdapterTest#testInit;android.widget.cts.SimpleAdapterTest#testGetItemId;android.widget.cts.TableLayout_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.ExpandableListViewBasicTest#testExpandedGroupMovement;android.widget.cts.TextViewTest#testAccessPaintFlags;android.widget.cts.CursorAdapterTest#testGetItemId;android.widget.cts.GalleryTest#testSetSpacing;android.widget.cts.TextViewTest#testSetLines;android.widget.cts.ScrollerTest#testConstructor;android.widget.cts.CompoundButtonTest#testOnDraw;android.widget.cts.ListViewTest#testGetMaxScrollAmount;android.widget.cts.TwoLineListItemTest#testGetTexts;android.widget.cts.AbsListViewTest#testBeforeAndAfterTextChanged;android.widget.cts.AdapterViewTest#testConstructor;android.widget.cts.SimpleAdapterTest#testSetViewText;android.widget.cts.ExpandableListViewTest#testGetSelectedId;android.widget.cts.TableLayoutTest#testOnMeasure;android.widget.cts.DialerFilterTest#testSetDigitsWatcher;android.widget.cts.AbsListViewTest#testPointToPosition;android.widget.cts.ExpandableListViewTest#testGetPackedPositionChild;android.widget.cts.ExpandableListViewBasicTest#testSelectedPosition;android.widget.cts.RemoteViewsTest#testSetBitmap;android.widget.cts.TextViewTest#testFoo;android.widget.cts.EditTextTest#testAccessText;android.widget.cts.AbsListViewTest#testComputeVerticalScrollValues;android.widget.cts.VideoViewTest#testConstructor;android.widget.cts.EditTextTest#testGetDefaultEditable;android.widget.cts.EditTextTest#testConstructor;android.widget.cts.ExpandableListViewWithHeadersTest#testExpandOnFirstGroup;android.widget.cts.RelativeLayout_LayoutParamsTest#testStartEnd;android.widget.cts.ScrollViewTest#testAddViewWithIndexAndLayoutParams;android.widget.cts.ViewFlipperTest#testSetFlipInterval;android.widget.cts.ZoomButtonTest#testConstructor;android.widget.cts.TableRowTest#testOnMeasure;android.widget.cts.TableLayoutTest#testAccessStretchAllColumns;android.widget.cts.GridViewTest#testSetHorizontalSpacing;android.widget.cts.ExpandableListViewWithHeadersTest#testContextMenus;android.widget.cts.ListViewTest#testRequestChildRectangleOnScreen;android.widget.cts.VideoViewTest#testSetMediaController;android.widget.cts.AdapterViewTest#testAccessOnItemClickAndLongClickListener;android.widget.cts.RemoteViewsTest#testSetImageViewResource;android.widget.cts.TextViewTest#testScroll;android.widget.cts.VideoViewTest#testResolveAdjustedSize;android.widget.cts.ScrollViewTest#testOnInterceptTouchEvent;android.widget.cts.ViewAnimatorTest#testRemoveViews;android.widget.cts.AlphabetIndexerTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageViewTest#testSetMaxHeight;android.widget.cts.ImageViewTest#testOnMeasure;android.widget.cts.RadioGroup_LayoutParamsTest#testSetBaseAttributes;android.widget.cts.AbsoluteLayoutTest#testOnMeasure;android.widget.cts.CompoundButtonTest#testPerformClick;android.widget.cts.ExpandableListViewTest#testAccessExpandableListAdapter;android.widget.cts.TextView_SaveStateTest#testToString;android.widget.cts.CursorTreeAdapterTest#testGetGroupView;android.widget.cts.TextViewTest#testPressKey;android.widget.cts.ProgressBarTest#testAccessMax;android.widget.cts.TextViewTest#testBeginEndBatchEdit;android.widget.cts.HeaderViewListAdapterTest#testGetItem;android.widget.cts.LinearLayoutTest#testActivityTestCaseSetUpProperly;android.widget.cts.TextViewTest#testSetGetTextDirection;android.widget.cts.HeaderViewListAdapterTest#testGetView;android.widget.cts.ScrollerTest#testScrollMode;android.widget.cts.ProgressBarTest#testAccessInterpolator;android.widget.cts.TextViewTest#testTextAttr;android.widget.cts.AbsListViewTest#testAccessScrollingCacheEnabled;android.widget.cts.MediaControllerTest#testSetEnabled;android.widget.cts.SimpleCursorAdapterTest#testConvertToString;android.widget.cts.ExpandableListViewTest#testDispatchDraw;android.widget.cts.DigitalClockTest#testOnAttachedToWindow;android.widget.cts.BaseAdapterTest#testGetViewTypeCount;android.widget.cts.CursorAdapterTest#testGetCount;android.widget.cts.ToastTest#testAccessView;android.widget.cts.FrameLayoutTest#testOnMeasure;android.widget.cts.HorizontalScrollViewTest#testGetMaxScrollAmount;android.widget.cts.AbsListViewTest#testSetScrollIndicators;android.widget.cts.TabHostTest#testOnAttachedToAndDetachedFromWindow;android.widget.cts.CursorAdapterTest#testGetFilter;android.widget.cts.CursorTreeAdapterTest#testGetChild;android.widget.cts.SlidingDrawerTest#testSetOnDrawerScrollListener;android.widget.cts.ProgressBarTest#testOnSizeChange;android.widget.cts.TextViewTest#testGetTotalPaddingRight;android.widget.cts.GridViewTest#testPressKey;android.widget.cts.CheckedTextViewTest#testDrawableStateChanged;android.widget.cts.ScrollerTest#testGetDuration;android.widget.cts.TableLayoutTest#testRequestLayout;android.widget.cts.ImageViewTest#testAccessImageMatrix;android.widget.cts.PopupWindowTest#testAccessWidth;android.widget.cts.BaseAdapterTest#testDataSetObserver;android.widget.cts.SpinnerTest#testPerformClick;android.widget.cts.MediaControllerTest#testConstructor;android.widget.cts.SimpleCursorTreeAdapterTest#testConstructor;android.widget.cts.TextViewTest#testGetTextColor;android.widget.cts.DialerFilterTest#testOnModechange;android.widget.cts.AdapterViewTest#testDispatchRestoreInstanceState;android.widget.cts.ImageViewTest#testOnCreateDrawableState;android.widget.cts.CursorTreeAdapterTest#testNotifyDataSetInvalidated;android.widget.cts.SimpleExpandableListAdapterTest#testGetGroupView;android.widget.cts.ListViewTest#testRequestLayout;android.widget.cts.Gallery_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.SimpleExpandableListAdapterTest#testGetChild;android.widget.cts.TableRowTest#testSetOnHierarchyChangeListener;android.widget.cts.TextViewTest#testGetExtendedPaddingTop;android.widget.cts.ResourceCursorAdapterTest#testSetDropDownViewResource;android.widget.cts.LinearLayoutTest#testAccessBaselineAligned;android.widget.cts.CursorTreeAdapterTest#testGetChildId;android.widget.cts.GridViewTest#testSetColumnWidth;android.widget.cts.AbsListViewTest#testAccessTranscriptMode;android.widget.cts.VideoViewTest#testGetBufferPercentage;android.widget.cts.LinearLayoutTest#testConstructor;android.widget.cts.TextViewTest#testAccessContentType;android.widget.cts.SimpleCursorAdapterTest#testBindView;android.widget.cts.SlidingDrawerTest#testOnTouchEvent;android.widget.cts.ListViewTest#testOnKeyUpDown;android.widget.cts.SimpleCursorAdapterTest#testAccessCursorToStringConverter;android.widget.cts.TabWidgetTest#testAddView;android.widget.cts.TextViewTest#testIsInputMethodTarget;android.widget.cts.AbsoluteLayout_LayoutParamsTest#testConstructor;android.widget.cts.RelativeLayoutTest#testGetBaseline;android.widget.cts.TextViewTest#testGetLineCount;android.widget.cts.GridLayoutTest#testActivityTestCaseSetUpProperly;android.widget.cts.ProgressBarTest#testInvalidateDrawable;android.widget.cts.AbsSpinnerTest#testOnSaveAndRestoreInstanceState;android.widget.cts.MultiAutoCompleteTextViewTest#testConstructor;android.widget.cts.ExpandableListViewTest#testGetFlatListPosition;android.widget.cts.ArrayAdapterTest#testAddAllParams;android.widget.cts.CursorTreeAdapterTest#testSetGroupCursor;android.widget.cts.TextViewTest#testDidTouchFocusSelect;android.widget.cts.SimpleCursorAdapterTest#testConstructor;android.widget.cts.TextViewTest#testAccessImeOptions;android.widget.cts.TabHostTest#testClearAllTabs;android.widget.cts.TextViewTest#testGetFadingEdgeStrength;android.widget.cts.TextViewTest#testSetMinLines;android.widget.cts.DialerFilterTest#testConstructor;android.widget.cts.AbsListViewTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testAddViewWithIndexAndLayoutParams;android.widget.cts.TextViewTest#testTextDirectionDefault;android.widget.cts.TextViewTest#testGetLineBounds;android.widget.cts.HorizontalScrollViewTest#testConstructor;android.widget.cts.AutoCompleteTextViewTest#testSetFrame;android.widget.cts.RelativeLayoutTest#testGenerateLayoutParams2;android.widget.cts.RelativeLayoutTest#testGenerateLayoutParams1;android.widget.cts.ArrayAdapterTest#testAdd;android.widget.cts.PopupWindowTest#testUpdateDimensionAndAlignAnchorView;android.widget.cts.ToastTest#testAccessMargin;android.widget.cts.RemoteViewsActivityTest#testGood;android.widget.cts.TextViewTest#testAccessTextSize;android.widget.cts.TableLayoutTest#testColumnStretchableEffect;android.widget.cts.CompoundButtonTest#testDrawableStateChanged;android.widget.cts.CursorAdapterTest#testAccessFilterQueryProvider;android.widget.cts.HorizontalScrollViewTest#testArrowScroll;android.widget.cts.ScrollViewTest#testOnMeasure;android.widget.cts.TextViewTest#testTextChangedListener;android.widget.cts.AutoCompleteTextViewTest#testSetCompletionHint;android.widget.cts.TextViewTest#testAccessTextColor;android.widget.cts.FrameLayoutTest#testAccessMeasureAllChildren;android.widget.cts.LinearLayoutTest#testAccessWeightSum;android.widget.cts.HorizontalScrollViewTest#testOnMeasure;android.widget.cts.ResourceCursorAdapterTest#testConstructor;android.widget.cts.FrameLayoutTest#testOnSizeChanged;android.widget.cts.LinearLayoutTest#testCheckLayoutParams;android.widget.cts.PopupWindowTest#testSetWindowLayoutMode;android.widget.cts.ExpandableListView_ExpandableListContextMenuInfoTest#testAndroidTestCaseSetupProperly;android.widget.cts.ListViewTest#testAccessHeaderView;android.widget.cts.RatingBarTest#testAccessOnRatingBarChangeListener;android.widget.cts.GridViewTest#testSetGravity;android.widget.cts.TextViewTest#testAccessHint;android.widget.cts.HorizontalScrollViewTest#testOnLayout;android.widget.cts.ListViewTest#testAccessAdapter;android.widget.cts.ArrayAdapterTest#testGetItemId;android.widget.cts.ImageButtonTest#testAndroidTestCaseSetupProperly;android.widget.cts.TextViewTest#testOnDetachedFromWindow;android.widget.cts.ProgressBarTest#testAccessSecondaryProgress;android.widget.cts.SimpleExpandableListAdapterTest#testIsChildSelectable;android.widget.cts.TextViewTest#testTextAlignmentDefault;android.widget.cts.TableLayoutTest#testCheckLayoutParams;android.widget.cts.AnalogClockTest#testOnSizeChanged;android.widget.cts.DialerFilterTest#testSetLettersWatcher;android.widget.cts.ViewAnimatorTest#testAccessDisplayedChildBoundary;android.widget.cts.TextViewTest#testSetSpannableFactory;android.widget.cts.ImageSwitcherTest#testConstructor;android.widget.cts.TextViewTest#testAccessMovementMethod;android.widget.cts.AbsSeekBarTest#testDrawableStateChanged;android.widget.cts.RadioGroupTest#testAddView;android.widget.cts.HorizontalScrollViewTest#testOnInterceptTouchEvent;android.widget.cts.TabHostTest#testConstructor;android.widget.cts.CursorTreeAdapterTest#testGetChildrenCount;android.widget.cts.SimpleCursorAdapterTest#testChangeCursor;android.widget.cts.HorizontalScrollViewTest#testAccessFillViewport;android.widget.cts.AbsSeekBarTest#testFoo;android.widget.cts.TableLayoutTest#testConstructor;android.widget.cts.GalleryTest#testSetUnselectedAlpha;android.widget.cts.AbsListViewTest#testInvalidateViews;android.widget.cts.HeaderViewListAdapterTest#testGetCount;android.widget.cts.RatingBarTest#testAccessIndicator;android.widget.cts.RelativeLayout_LayoutParamsTest#testConstructor;android.widget.cts.CursorAdapterTest#testNewDropDownView;android.widget.cts.ChronometerTest#testAccessFormat;android.widget.cts.ExpandableListViewBasicTest#testPreconditions;android.widget.cts.AbsListViewTest#testFoo;android.widget.cts.ListViewTest#testOnTouchEvent;android.widget.cts.TabHostTest#testAddTab;android.widget.cts.AdapterViewTest#testChangeFocusable;android.widget.cts.ListViewTest#testAccessDivider;android.widget.cts.ListViewTest#testAccessFooterView;android.widget.cts.SimpleCursorAdapterTest#testAccessViewBinder;android.widget.cts.ZoomButtonTest#testSetEnabled;android.widget.cts.CheckedTextViewTest#testConstructor;android.widget.cts.GridViewTest#testLayoutChildren;android.widget.cts.ImageViewTest#testSetAlpha;android.widget.cts.TextViewTest#testClearComposingText;android.widget.cts.ZoomControlsTest#testSetIsZoomInEnabled;android.widget.cts.TabWidgetTest#testConstructor;android.widget.cts.TableRowTest#testGenerateLayoutParams2;android.widget.cts.TextViewTest#testSetTextAppearance;android.widget.cts.FilterTest#testConvertResultToString;android.widget.cts.MultiAutoCompleteTextViewTest#testPerformValidation;android.widget.cts.TextViewTest#testHeightAndWidth;android.widget.cts.CursorTreeAdapterTest#testIsChildSelectable;android.widget.cts.ArrayAdapterTest#testAddAllCollection;android.widget.cts.AbsListView_LayoutParamsTest#testConstructors;android.widget.cts.ScrollViewTest#testDispatchKeyEvent;android.widget.cts.TabHostTest#testAccessCurrentTab;android.widget.cts.ViewSwitcherTest#testReset;android.widget.cts.TimePickerTest#testAccessIs24HourView;android.widget.cts.TextViewTest#testGetTotalPaddingTop;android.widget.cts.HorizontalScrollViewTest#testExecuteKeyEvent;android.widget.cts.AbsoluteLayoutTest#testOnLayout;android.widget.cts.ImageViewTest#testAccessScaleType;android.widget.cts.TabHostTest#testGetTabWidget;android.widget.cts.SlidingDrawerTest#testAnimateToggle;android.widget.cts.SlidingDrawerTest#testLockAndUnlock;android.widget.cts.GalleryTest#testSetGravity;android.widget.cts.ListViewTest#testOnFinishInflate;android.widget.cts.ScrollViewTest#testArrowScroll;android.widget.cts.ScrollViewTest#testComputeScrollDeltaToGetChildRectOnScreen;android.widget.cts.TabHostTest#testGetCurrentTabView;android.widget.cts.ExpandableListViewBasicTest#testExpandGroup;android.widget.cts.TextViewTest#testExtractText;android.widget.cts.RemoteViewsTest#testOnLoadClass;android.widget.cts.RadioGroupTest#testCheckLayoutParams;android.widget.cts.TextSwitcherTest#testSetText;android.widget.cts.AbsListViewTest#testGenerateLayoutParams;android.widget.cts.TabWidgetTest#testSetEnabled;android.widget.cts.AutoCompleteTextViewTest#testAccessItemClickListener;android.widget.cts.ChronometerTest#testFoo;android.widget.cts.ExpandableListViewBasicTest#testConvertionBetweenFlatAndPacked;android.widget.cts.DatePickerTest#testOnSaveInstanceState;android.widget.cts.DatePickerTest#testAndroidTestCaseSetupProperly;android.widget.cts.ImageButtonTest#testOnSetAlpha;android.widget.cts.AdapterViewTest#testAccessOnItemSelectedListener;android.widget.cts.BaseExpandableListAdapterTest#testOnGroupCollapsed;android.widget.cts.TableLayoutTest#testGenerateLayoutParams1;android.widget.cts.TableLayoutTest#testGenerateLayoutParams2;android.widget.cts.LinearLayoutTest#testLayoutVertical;android.widget.cts.CursorTreeAdapterTest#testAccessQueryProvider;android.widget.cts.TextViewTest#testSetTextKeepState1;android.widget.cts.ArrayAdapterTest#testConstructor;android.widget.cts.RemoteViewsTest#testDescribeContents;android.widget.cts.RelativeLayoutTest#testGenerateDefaultLayoutParams;android.widget.cts.SimpleExpandableListAdapterTest#testHasStableIds;android.widget.cts.TextViewTest#testOnPreDraw;android.widget.cts.ExpandableListViewBasicTest#testCollapseGroup;android.widget.cts.ExpandableListViewWithHeadersTest#testExpandOnFirstPosition;android.widget.cts.SimpleCursorAdapterTest#testSetViewText;android.widget.cts.RelativeLayoutTest#testSetIgnoreGravity;android.widget.cts.RadioGroup_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.BaseAdapterTest#testAreAllItemsEnabled;android.widget.cts.RemoteViewsTest#testSetInt;android.widget.cts.AutoCompleteTextViewTest#testAccessDropDownAnchor;android.widget.cts.ScrollerTest#testIsFinished;android.widget.cts.TextViewTest#testAccessGravity;android.widget.cts.TableLayoutTest#testAccessColumnStretchable;android.widget.cts.ExpandableListViewTest#testSetOnGroupClickListener;android.widget.cts.AlphabetIndexerTest#testCompare;android.widget.cts.AbsListView_LayoutParamsTest#testAndroidTestCaseSetupProperly;android.widget.cts.LayoutDirectionTest#testLayoutDirectionDefaults;android.widget.cts.HeaderViewListAdapterTest#testAreAllItemsEnabled;android.widget.cts.LinearLayout_LayoutParamsTest#testDebug;android.widget.cts.CursorTreeAdapterTest#testGetCursor;android.widget.cts.ImageViewTest#testOnDraw;android.widget.cts.GalleryTest#testGetChildDrawingOrder;android.widget.cts.TextViewTest#testAccessInputExtras;android.widget.cts.LinearLayoutTest#testGetBaseline;android.widget.cts.RatingBarTest#testConstructor;android.widget.cts.RemoteViewsTest#testSetUri;android.widget.cts.SpinnerTest#testsetPromptId;android.widget.cts.RemoteViewsTest#testSetTextColor;android.widget.cts.VideoViewTest#testGetDuration;android.widget.cts.AdapterViewTest#testGetCount;android.widget.cts.MultiAutoCompleteTextView_CommaTokenizerTest#testFindTokenStart;android.widget.cts.ImageViewTest#testSetImageBitmap;android.widget.cts.HeaderViewListAdapterTest#testUnregisterDataSetObserver;android.widget.cts.PopupWindowTest#testAccessOutsideTouchable;android.widget.cts.EditTextTest#testSetEllipsize;android.widget.cts.SimpleAdapterTest#testGetCount;android.widget.cts.SpinnerTest#testSetOnItemClickListener;android.widget.cts.SlidingDrawerTest#testToggle;android.widget.cts.AbsSpinnerTest#testPointToPosition;android.widget.cts.ViewFlipperTest#testViewFlipper;android.widget.cts.TabHostTest#testDispatchKeyEvent;android.widget.cts.ExpandableListViewTest#testIsGroupExpanded;android.widget.cts.LayoutDirectionTest#testDirectionFromXml;android.widget.cts.TextViewTest#testVerifyDrawable;android.widget.cts.ProgressBarTest#testIncrementProgressBy;android.widget.cts.PopupWindowTest#testShowAsDropDown;android.widget.cts.ListViewTest#testCanAnimate;android.widget.cts.HorizontalScrollViewTest#testMeasureChild;android.widget.cts.RadioGroupTest#testClearCheck;android.widget.cts.SimpleAdapterTest#testConstructor;android.widget.cts.HorizontalScrollViewTest#testGetHorizontalFadingEdgeStrengths;android.widget.cts.CursorAdapterTest#testGetItem" />
 </TestPlan>
diff --git a/tests/plans/CTS-stable.xml b/tests/plans/CTS-stable.xml
index b9e7321..c404310 100644
--- a/tests/plans/CTS-stable.xml
+++ b/tests/plans/CTS-stable.xml
@@ -15,6 +15,20 @@
   <Entry uri="android.core.tests.libcore.package.com"/>
   <Entry uri="android.core.tests.libcore.package.conscrypt"/>
   <Entry uri="android.core.tests.libcore.package.dalvik"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_annotation"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_beans"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_io"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_lang"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_math"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_net"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_nio"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_text"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_java_util"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_javax_security"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_logging"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_prefs"/>
+  <Entry uri="android.core.tests.libcore.package.harmony_sql"/>
+  <Entry uri="android.core.tests.libcore.package.jsr166"/>
   <Entry uri="android.core.tests.libcore.package.libcore"/>
   <Entry uri="android.core.tests.libcore.package.org"/>
   <Entry uri="android.core.tests.libcore.package.sun"/>
@@ -27,11 +41,11 @@
   <Entry uri="android.dreams"/>
   <Entry uri="android.drm"/>
   <Entry uri="android.effect"/>
-  <Entry uri="android.example"/>
   <Entry uri="android.gesture"/>
   <Entry uri="android.graphics"/>
   <Entry uri="android.graphics2"/>
-  <Entry uri="android.hardware" exclude="android.hardware.cts.SensorIntegrationTests#testAccelerometerDoesNotStopGyroscope;android.hardware.cts.SensorIntegrationTests#testsAccelerometerDoesnNotStopMagnetometer;android.hardware.cts.SensorIntegrationTests#testAndroidTestCaseSetupProperly;android.hardware.cts.SensorIntegrationTests#testBatchAndFlush;android.hardware.cts.SensorIntegrationTests#testGyroscopeDoesNotStopAccelerometer;android.hardware.cts.SensorIntegrationTests#testGyroscopeDoesNotStopMagnetometer;android.hardware.cts.SensorIntegrationTests#testMagnetometerDoesNotStopAccelerometer;android.hardware.cts.SensorIntegrationTests#testMagnetometerDoesNotStopGyroscope;android.hardware.cts.SensorMagneticFieldTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorMagneticFieldTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorMagneticFieldTest#testFrequencyAccuracy;android.hardware.cts.SensorMagneticFieldTest#testOneClientSeveralThreads;android.hardware.cts.SensorMagneticFieldTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorMagneticFieldTest#testStandardDeviationWhileStatic;android.hardware.cts.SensorMagneticFieldTest#testStoppingOtherClients;android.hardware.cts.SensorMagneticFieldTest#testStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorAccelerometerTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testFrequencyAccuracy;android.hardware.cts.SensorAccelerometerTest#testOneClientSeveralThreads;android.hardware.cts.SensorAccelerometerTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorGyroscopeTest#testBatchingStoppingOtherClients;android.hardware.cts.SensorGyroscopeTest#testBatchingStoppingOtherClientsBatching;android.hardware.cts.SensorGyroscopeTest#testFrequencyAccuracy;android.hardware.cts.SensorGyroscopeTest#testOneClientSeveralThreads;android.hardware.cts.SensorGyroscopeTest#testOneClientSeveralThreadsBatching;android.hardware.cts.SensorGyroscopeTest#testStandardDeviationWhilStatic;android.hardware.cts.SensorGyroscopeTest#testStoppingOtherClients;android.hardware.cts.SensorGyroscopeTest#testStoppingOtherClientsBatching;android.hardware.cts.SensorAccelerometerTest#testStandardDeviationWhileStatic;android.hardware.cts.SensorAccelerometerTest#testStoppingOtherClients;android.hardware.cts.SensorAccelerometerTest#testStoppingOtherClientsBatching;android.hardware.camera2.cts.CameraDeviceTest#testCameraDeviceRepeatingRequest;android.hardware.camera2.cts.ImageReaderTest#testImageReaderFromCameraJpeg;android.hardware.cts.CameraGLTest#testCameraToSurfaceTextureMetadata;android.hardware.cts.CameraTest#testImmediateZoom;android.hardware.cts.CameraTest#testPreviewCallback;android.hardware.cts.CameraTest#testSmoothZoom;android.hardware.cts.CameraTest#testVideoSnapshot;android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks;android.hardware.cts.CameraGLTest#testSetPreviewTexturePreviewCallback" />
+  <Entry uri="android.hardware" exclude="android.hardware.camera2.cts.CameraDeviceTest#testCameraDeviceRepeatingRequest;android.hardware.camera2.cts.ImageReaderTest#testImageReaderFromCameraJpeg;android.hardware.cts.CameraGLTest#testCameraToSurfaceTextureMetadata;android.hardware.cts.CameraTest#testImmediateZoom;android.hardware.cts.CameraTest#testPreviewCallback;android.hardware.cts.CameraTest#testSmoothZoom;android.hardware.cts.CameraTest#testVideoSnapshot;android.hardware.cts.CameraGLTest#testSetPreviewTextureBothCallbacks;android.hardware.cts.CameraGLTest#testSetPreviewTexturePreviewCallback" />
+  <Entry uri="android.host.holo"/>
   <Entry uri="android.holo"/>
   <Entry uri="android.jni"/>
   <Entry uri="android.keystore"/>
@@ -50,7 +64,8 @@
   <Entry uri="android.permission2"/>
   <Entry uri="android.preference"/>
   <Entry uri="android.preference2"/>
-  <Entry uri="android.provider" exclude="android.provider.cts.CalendarTest#testRemindersAlarm" />
+  <Entry uri="android.print"/>
+  <Entry uri="android.provider"/>
   <Entry uri="android.renderscript"/>
   <Entry uri="android.rscpp"/>
   <Entry uri="android.rsg"/>
@@ -59,7 +74,6 @@
   <Entry uri="android.speech"/>
   <Entry uri="android.telephony"/>
   <Entry uri="android.tests.appsecurity"/>
-  <Entry uri="android.tests.location"/>    
   <Entry uri="android.tests.sigtest"/>
   <Entry uri="android.text"/>
   <Entry uri="android.textureview"/>
@@ -69,13 +83,11 @@
   <Entry uri="android.util"/>
   <Entry uri="android.view"/>
   <Entry uri="android.webkit" exclude="android.webkit.cts.WebViewClientTest#testDoUpdateVisitedHistory;android.webkit.cts.WebViewClientTest#testLoadPage;android.webkit.cts.WebViewClientTest#testOnFormResubmission;android.webkit.cts.WebViewClientTest#testOnReceivedError;android.webkit.cts.WebViewClientTest#testOnReceivedHttpAuthRequest;android.webkit.cts.WebViewClientTest#testOnScaleChanged;android.webkit.cts.WebViewClientTest#testOnUnhandledKeyEvent;android.webkit.cts.WebViewTest#testSetInitialScale" />
-  <Entry uri="android.widget" exclude="android.widget.cts.GridViewTest#testSetNumColumns" />
-  <Entry uri="com.android.cts.bootup"/>
+  <Entry uri="android.widget"/>
   <Entry uri="com.android.cts.browserbench"/>
   <Entry uri="com.android.cts.dram"/>
   <Entry uri="com.android.cts.filesystemperf"/>
-  <Entry uri="com.android.cts.jank.opengl"/>
-  <Entry uri="com.android.cts.jank.ui"/>
+  <Entry uri="com.android.cts.jank"/>
   <Entry uri="com.android.cts.opengl"/>
   <Entry uri="com.android.cts.simplecpu"/>
   <Entry uri="com.android.cts.ui"/>
diff --git a/tests/print/Android.mk b/tests/print/Android.mk
new file mode 100644
index 0000000..fea7dc0
--- /dev/null
+++ b/tests/print/Android.mk
@@ -0,0 +1,36 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+##################################################
+# Build the print instrument library
+##################################################
+include $(CLEAR_VARS)
+LOCAL_MODULE := CtsPrintInstrument
+LOCAL_SRC_FILES := $(call all-subdir-java-files) \
+    src/android/print/cts/IPrivilegedOperations.aidl
+LOCAL_MODULE_TAGS := optional
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_JAVA_LIBRARY)
+
+# Copy the shell script to run the print instrument Jar to the CTS out folder.
+$(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar : $(LOCAL_BUILT_MODULE) | $(ACP) 
+	$(copy-file-to-target)
+
+# Copy the built print instrument library Jar to the CTS out folder.
+$(CTS_TESTCASES_OUT)/print-instrument : $(LOCAL_PATH)/print-instrument | $(ACP)
+	$(copy-file-to-target)
+
diff --git a/tests/print/print-instrument b/tests/print/print-instrument
new file mode 100755
index 0000000..a79cb8a
--- /dev/null
+++ b/tests/print/print-instrument
@@ -0,0 +1,37 @@
+# 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.
+
+# Script to start "print-instrument" on the device
+#
+# The script sets up an alternative dalvik cache when running as
+# non-root. Jar files needs to be dexopt'd to run in Dalvik. For
+# plain jar files, this is done at first use. shell user does not
+# have write permission to default system Dalvik cache so we
+# redirect to an alternative cache.
+
+RUN_BASE=/data/local/tmp
+
+# If not running as root, use an alternative dex cache.
+if [ ${USER_ID} -ne 0 ]; then
+  tmp_cache=${RUN_BASE}/dalvik-cache
+  if [ ! -d ${tmp_cache} ]; then
+    mkdir -p ${tmp_cache}
+  fi
+  export ANDROID_DATA=${RUN_BASE}
+fi
+
+# Run print-instrument.
+export CLASSPATH=${RUN_BASE}/CtsPrintInstrument.jar
+
+exec app_process ${RUN_BASE} android.print.cts.PrintInstrument ${@}
diff --git a/tests/print/src/android/print/cts/IPrivilegedOperations.aidl b/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
new file mode 100644
index 0000000..93c8c3e
--- /dev/null
+++ b/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.print.cts;
+
+interface IPrivilegedOperations {
+    boolean clearApplicationUserData(String packageName);
+}
diff --git a/tests/print/src/android/print/cts/PrintInstrument.java b/tests/print/src/android/print/cts/PrintInstrument.java
new file mode 100644
index 0000000..b154901
--- /dev/null
+++ b/tests/print/src/android/print/cts/PrintInstrument.java
@@ -0,0 +1,279 @@
+/*
+ * 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.print.cts;
+
+import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
+import android.app.IInstrumentationWatcher;
+import android.app.Instrumentation;
+import android.app.UiAutomationConnection;
+import android.content.ComponentName;
+import android.content.pm.IPackageDataObserver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.AndroidException;
+import android.view.IWindowManager;
+
+import com.android.internal.os.BaseCommand;
+
+import java.io.PrintStream;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class PrintInstrument extends BaseCommand {
+
+    private static final String ARG_PRIVILEGED_OPS = "ARG_PRIVILEGED_OPS";
+
+    private IActivityManager mAm;
+
+    public static void main(String[] args) {
+        PrintInstrument instrumenter = new PrintInstrument();
+        instrumenter.run(args);
+    }
+
+    @Override
+    public void onRun() throws Exception {
+        mAm = ActivityManagerNative.getDefault();
+        if (mAm == null) {
+            System.err.println(NO_SYSTEM_ERROR_CODE);
+            throw new AndroidException("Can't connect to activity manager;"
+                    + " is the system running?");
+        }
+
+        String op = nextArgRequired();
+
+        if (op.equals("instrument")) {
+            runInstrument();
+        } else {
+            showError("Error: unknown command '" + op + "'");
+        }
+    }
+
+    @Override
+    public void onShowUsage(PrintStream out) {
+        /* do nothing */
+    }
+
+    @SuppressWarnings("deprecation")
+    private void runInstrument() throws Exception {
+        String profileFile = null;
+        boolean wait = false;
+        boolean rawMode = false;
+        boolean no_window_animation = false;
+        int userId = UserHandle.USER_CURRENT;
+        Bundle args = new Bundle();
+        String argKey = null, argValue = null;
+        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+
+        String opt;
+        while ((opt=nextOption()) != null) {
+            if (opt.equals("-p")) {
+                profileFile = nextArgRequired();
+            } else if (opt.equals("-w")) {
+                wait = true;
+            } else if (opt.equals("-r")) {
+                rawMode = true;
+            } else if (opt.equals("-e")) {
+                argKey = nextArgRequired();
+                argValue = nextArgRequired();
+                args.putString(argKey, argValue);
+            } else if (opt.equals("--no_window_animation")
+                    || opt.equals("--no-window-animation")) {
+                no_window_animation = true;
+            } else if (opt.equals("--user")) {
+                userId = parseUserArg(nextArgRequired());
+            } else {
+                System.err.println("Error: Unknown option: " + opt);
+                return;
+            }
+        }
+
+        if (userId == UserHandle.USER_ALL) {
+            System.err.println("Error: Can't start instrumentation with user 'all'");
+            return;
+        }
+
+        String cnArg = nextArgRequired();
+        ComponentName cn = ComponentName.unflattenFromString(cnArg);
+        if (cn == null) throw new IllegalArgumentException("Bad component name: " + cnArg);
+
+        InstrumentationWatcher watcher = null;
+        UiAutomationConnection connection = null;
+        if (wait) {
+            watcher = new InstrumentationWatcher();
+            watcher.setRawOutput(rawMode);
+            connection = new UiAutomationConnection();
+        }
+
+        float[] oldAnims = null;
+        if (no_window_animation) {
+            oldAnims = wm.getAnimationScales();
+            wm.setAnimationScale(0, 0.0f);
+            wm.setAnimationScale(1, 0.0f);
+        }
+
+        args.putIBinder(ARG_PRIVILEGED_OPS, new PrivilegedOperations(mAm));
+
+        if (!mAm.startInstrumentation(cn, profileFile, 0, args, watcher, connection, userId)) {
+            throw new AndroidException("INSTRUMENTATION_FAILED: " + cn.flattenToString());
+        }
+
+        if (watcher != null) {
+            if (!watcher.waitForFinish()) {
+                System.out.println("INSTRUMENTATION_ABORTED: System has crashed.");
+            }
+        }
+
+        if (oldAnims != null) {
+            wm.setAnimationScales(oldAnims);
+        }
+    }
+
+    private int parseUserArg(String arg) {
+        int userId;
+        if ("all".equals(arg)) {
+            userId = UserHandle.USER_ALL;
+        } else if ("current".equals(arg) || "cur".equals(arg)) {
+            userId = UserHandle.USER_CURRENT;
+        } else {
+            userId = Integer.parseInt(arg);
+        }
+        return userId;
+    }
+
+    private class InstrumentationWatcher extends IInstrumentationWatcher.Stub {
+        private boolean mFinished = false;
+        private boolean mRawMode = false;
+
+        /**
+         * Set or reset "raw mode".  In "raw mode", all bundles are dumped.  In "pretty mode",
+         * if a bundle includes Instrumentation.REPORT_KEY_STREAMRESULT, just print that.
+         * @param rawMode true for raw mode, false for pretty mode.
+         */
+        public void setRawOutput(boolean rawMode) {
+            mRawMode = rawMode;
+        }
+
+        @Override
+        public void instrumentationStatus(ComponentName name, int resultCode, Bundle results) {
+            synchronized (this) {
+                // pretty printer mode?
+                String pretty = null;
+                if (!mRawMode && results != null) {
+                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+                }
+                if (pretty != null) {
+                    System.out.print(pretty);
+                } else {
+                    if (results != null) {
+                        for (String key : results.keySet()) {
+                            System.out.println(
+                                    "INSTRUMENTATION_STATUS: " + key + "=" + results.get(key));
+                        }
+                    }
+                    System.out.println("INSTRUMENTATION_STATUS_CODE: " + resultCode);
+                }
+                notifyAll();
+            }
+        }
+
+        @Override
+        public void instrumentationFinished(ComponentName name, int resultCode,
+                Bundle results) {
+            synchronized (this) {
+                // pretty printer mode?
+                String pretty = null;
+                if (!mRawMode && results != null) {
+                    pretty = results.getString(Instrumentation.REPORT_KEY_STREAMRESULT);
+                }
+                if (pretty != null) {
+                    System.out.println(pretty);
+                } else {
+                    if (results != null) {
+                        for (String key : results.keySet()) {
+                            System.out.println(
+                                    "INSTRUMENTATION_RESULT: " + key + "=" + results.get(key));
+                        }
+                    }
+                    System.out.println("INSTRUMENTATION_CODE: " + resultCode);
+                }
+                mFinished = true;
+                notifyAll();
+            }
+        }
+
+        public boolean waitForFinish() {
+            synchronized (this) {
+                while (!mFinished) {
+                    try {
+                        if (!mAm.asBinder().pingBinder()) {
+                            return false;
+                        }
+                        wait(1000);
+                    } catch (InterruptedException e) {
+                        throw new IllegalStateException(e);
+                    }
+                }
+            }
+            return true;
+        }
+    }
+
+    private static final class PrivilegedOperations extends IPrivilegedOperations.Stub {
+        private final IActivityManager mAm;
+
+        public PrivilegedOperations(IActivityManager am) {
+            mAm = am;
+        }
+
+        @Override
+        public boolean clearApplicationUserData(final String clearedPackageName)
+                throws RemoteException {
+            final long identity = Binder.clearCallingIdentity();
+            try {
+                final AtomicBoolean success = new AtomicBoolean();
+                final CountDownLatch completionLatch = new CountDownLatch(1);
+
+                mAm.clearApplicationUserData(clearedPackageName,
+                        new IPackageDataObserver.Stub() {
+                            @Override
+                            public void onRemoveCompleted(String packageName, boolean succeeded) {
+                                if (clearedPackageName.equals(packageName) && succeeded) {
+                                    success.set(true);
+                                } else {
+                                    success.set(false);
+                                }
+                                completionLatch.countDown();
+                            }
+                }, UserHandle.USER_CURRENT);
+
+                try {
+                    completionLatch.await();
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+
+                return success.get();
+            } finally {
+                Binder.restoreCallingIdentity(identity);
+            }
+        }
+    }
+}
diff --git a/tests/res/drawable/alpha.png b/tests/res/drawable/alpha.png
new file mode 100644
index 0000000..8a88548
--- /dev/null
+++ b/tests/res/drawable/alpha.png
Binary files differ
diff --git a/tests/res/drawable/bitmapdrawable_theme.xml b/tests/res/drawable/bitmapdrawable_theme.xml
new file mode 100644
index 0000000..6df36b3
--- /dev/null
+++ b/tests/res/drawable/bitmapdrawable_theme.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+    android:antialias="?attr/themeBoolean"
+    android:autoMirrored="?attr/themeBoolean"
+    android:dither="?attr/themeBoolean"
+    android:filter="?attr/themeBoolean"
+    android:gravity="?attr/themeGravity"
+    android:mipMap="?attr/themeBoolean"
+    android:src="?attr/themeBitmap"
+    android:tileMode="?attr/themeTileMode" />
diff --git a/tests/res/drawable/colordrawable_theme.xml b/tests/res/drawable/colordrawable_theme.xml
new file mode 100644
index 0000000..00c6fe7
--- /dev/null
+++ b/tests/res/drawable/colordrawable_theme.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<color xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?attr/themeColor" />
diff --git a/tests/res/drawable/google_logo_1.png b/tests/res/drawable/google_logo_1.png
new file mode 100644
index 0000000..6e038fc
--- /dev/null
+++ b/tests/res/drawable/google_logo_1.png
Binary files differ
diff --git a/tests/res/drawable/google_logo_2.webp b/tests/res/drawable/google_logo_2.webp
new file mode 100644
index 0000000..f92c42b
--- /dev/null
+++ b/tests/res/drawable/google_logo_2.webp
Binary files differ
diff --git a/tests/res/drawable/gradientdrawable_radius_base.xml b/tests/res/drawable/gradientdrawable_radius_base.xml
new file mode 100644
index 0000000..ecd50f8
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_radius_base.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <gradient
+        android:centerColor="#ffff0000"
+        android:endColor="#0000ffff"
+        android:gradientRadius="50%"
+        android:startColor="#ffffffff"
+        android:type="radial" />
+
+    <corners android:radius="8px" />
+
+    <padding
+        android:bottom="10px"
+        android:left="4px"
+        android:right="6px"
+        android:top="2px" />
+
+    <size
+        android:height="50px"
+        android:width="50px" />
+
+</shape>
\ No newline at end of file
diff --git a/tests/res/drawable/gradientdrawable_radius_parent.xml b/tests/res/drawable/gradientdrawable_radius_parent.xml
new file mode 100644
index 0000000..73d116a
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_radius_parent.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <gradient
+        android:centerColor="#ffff0000"
+        android:endColor="#0000ffff"
+        android:gradientRadius="50%p"
+        android:startColor="#ffffffff"
+        android:type="radial" />
+
+    <corners android:radius="8px" />
+
+    <padding
+        android:bottom="10px"
+        android:left="4px"
+        android:right="6px"
+        android:top="2px" />
+
+    <size
+        android:height="50px"
+        android:width="50px" />
+
+</shape>
\ No newline at end of file
diff --git a/tests/res/drawable/gradientdrawable_theme.xml b/tests/res/drawable/gradientdrawable_theme.xml
new file mode 100644
index 0000000..68cec62
--- /dev/null
+++ b/tests/res/drawable/gradientdrawable_theme.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <corners
+        android:bottomLeftRadius="?attr/themeDimension"
+        android:bottomRightRadius="?attr/themeDimension"
+        android:topLeftRadius="?attr/themeDimension"
+        android:topRightRadius="?attr/themeDimension" />
+
+    <gradient
+        android:angle="?attr/themeAngle"
+        android:centerColor="?attr/themeColor"
+        android:centerX="?attr/themeFloat"
+        android:centerY="?attr/themeFloat"
+        android:endColor="?attr/themeColor"
+        android:gradientRadius="?attr/themeFloat"
+        android:startColor="?attr/themeColor"
+        android:useLevel="?attr/themeBoolean" />
+
+    <padding
+        android:bottom="?attr/themeDimension"
+        android:left="?attr/themeDimension"
+        android:right="?attr/themeDimension"
+        android:top="?attr/themeDimension" />
+
+    <size
+        android:height="?attr/themeDimension"
+        android:width="?attr/themeDimension" />
+
+    <solid android:color="?attr/themeColor" />
+
+    <stroke android:color="?attr/themeColor" />
+
+</shape>
diff --git a/tests/res/drawable/layerdrawable_theme.xml b/tests/res/drawable/layerdrawable_theme.xml
new file mode 100644
index 0000000..2a678ff
--- /dev/null
+++ b/tests/res/drawable/layerdrawable_theme.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="?attr/themeBoolean" >
+
+    <item android:drawable="@drawable/bitmapdrawable_theme"/>
+    <item>
+        <nine-patch
+            android:autoMirrored="?attr/themeBoolean"
+            android:dither="?attr/themeBoolean"
+            android:src="?attr/themeNinePatch" />
+    </item>
+
+</layer-list>
\ No newline at end of file
diff --git a/tests/res/drawable/ninepatchdrawable_theme.xml b/tests/res/drawable/ninepatchdrawable_theme.xml
new file mode 100644
index 0000000..bb031a5
--- /dev/null
+++ b/tests/res/drawable/ninepatchdrawable_theme.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<nine-patch xmlns:android="http://schemas.android.com/apk/res/android"
+    android:autoMirrored="?attr/themeBoolean"
+    android:dither="?attr/themeBoolean"
+    android:src="?attr/themeNinePatch" />
diff --git a/tests/res/drawable/touchfeedbackdrawable_theme.xml b/tests/res/drawable/touchfeedbackdrawable_theme.xml
new file mode 100644
index 0000000..a2b73cd
--- /dev/null
+++ b/tests/res/drawable/touchfeedbackdrawable_theme.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+    
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+    
+          http://www.apache.org/licenses/LICENSE-2.0
+    
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<touch-feedback xmlns:android="http://schemas.android.com/apk/res/android"
+    android:tint="?attr/themeColor" />
diff --git a/tests/res/drawable/vector_animation_battery.xml b/tests/res/drawable/vector_animation_battery.xml
new file mode 100644
index 0000000..b542c09
--- /dev/null
+++ b/tests/res/drawable/vector_animation_battery.xml
@@ -0,0 +1,73 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="80"
+        android:viewportWidth="40" />
+
+    <group>
+        <path
+            android:name="battery"
+            android:fill="#3388ff"
+            android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502
+                L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625
+                L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46
+                C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625
+                C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002
+                L 32,7.2812502 C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002
+                L 20.28125,2.0000002 z"
+            android:rotation="0"
+            android:stroke="#ff8833"
+            android:strokeWidth="1" />
+        <path
+            android:name="spark"
+            android:fill="#FFFF0000"
+            android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348
+                L 20.096792,37 L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
+    </group>
+    <group>
+        <path
+            android:name="battery"
+            android:fill="#ff8833"
+            android:pathData="M 20.28125,2.0000002 C 17.352748,2.0000002 15,4.3527485 15,7.2812502
+                L 15,8.0000002 L 13.15625,8.0000002 C 9.7507553,8.0000002 7,10.750759 7,14.15625
+                L 7,39.84375 C 7,43.24924 9.7507558,46 13.15625,46 L 33.84375,46
+                C 37.249245,46 39.999999,43.24924 40,39.84375 L 40,14.15625
+                C 40,10.75076 37.249243,8.0000002 33.84375,8.0000002 L 32,8.0000002 L 32,7.2812502
+                C 32,4.3527485 29.647252,2.0000002 26.71875,2.0000002 L 20.28125,2.0000002 z"
+            android:rotation="0"
+            android:stroke="#3388ff"
+            android:strokeWidth="1" />
+        <path
+            android:name="spark"
+            android:fill="#FFFF0000"
+            android:pathData="M 30,18.031528 L 25.579581,23.421071 L 29.370621,26.765348 L 20.096792,37
+                L 21.156922,28.014053 L 17,24.902844 L 20.880632,18 L 30,18.031528 z" />
+    </group>
+
+    <animation
+        android:durations="2000"
+        android:sequence="spark,spark" />
+    <animation
+        android:durations="2000"
+        android:sequence="battery,battery" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_battery_golden.png b/tests/res/drawable/vector_animation_battery_golden.png
new file mode 100644
index 0000000..eba9ad4
--- /dev/null
+++ b/tests/res/drawable/vector_animation_battery_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_clip_circle.xml b/tests/res/drawable/vector_animation_clip_circle.xml
new file mode 100644
index 0000000..112b9d7
--- /dev/null
+++ b/tests/res/drawable/vector_animation_clip_circle.xml
@@ -0,0 +1,116 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="12.25"
+        android:viewportWidth="7.30625" />
+
+    <group>
+        <path
+            android:name="clip1"
+            android:clipToPath="true"
+            android:fill="#112233"
+            android:pathData="
+                M 3.65, 6.125
+                m -.001, 0
+                a .001,.001 0 1,0 .002,0
+                a .001,.001 0 1,0 -.002,0z" />
+        <path
+            android:name="one"
+            android:fill="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0,-1.0Z" />
+        <path
+            android:name="clip2"
+            android:clipToPath="true"
+            android:fill="#112233"
+            android:pathData="
+                M 3.65, 6.125
+                m -6, 0
+                a 6,6 0 1,0 12,0
+                a 6,6 0 1,0 -12,0z" />
+        <path
+            android:name="two"
+            android:fill="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                        q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
+                        q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
+                        q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
+                        q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
+                        q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
+                        q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
+                        q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
+    <group>
+        <path
+            android:name="clip1"
+            android:clipToPath="true"
+            android:fill="#332233"
+            android:pathData="
+                M 3.65, 6.125
+                m -6, 0
+                a 6,6 0 1,0 12,0
+                a 6,6 0 1,0 -12,0z" />
+        <path
+            android:name="one"
+            android:fill="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0,-1.0Z" />
+        <path
+            android:name="clip2"
+            android:clipToPath="true"
+            android:fill="#662233"
+            android:pathData="
+                 M 3.65, 6.125
+                m -.001, 0
+                a .001,.001 0 1,0 .002,0
+                a .001,.001 0 1,0 -.002,0z" />
+        <path
+            android:name="two"
+            android:fill="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                        q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
+                        q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
+                        q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
+                        q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
+                        q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
+                        q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
+                        q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
+                        q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
+
+    <animation
+        android:durations="4000"
+        android:sequence="one,one" />
+    <animation
+        android:durations="4000"
+        android:sequence="two,two" />
+    <animation
+        android:durations="4000"
+        android:sequence="clip1,clip1" />
+    <animation
+        android:durations="4000"
+        android:sequence="clip2,clip2" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_clip_circle_golden.png b/tests/res/drawable/vector_animation_clip_circle_golden.png
new file mode 100644
index 0000000..f22a927
--- /dev/null
+++ b/tests/res/drawable/vector_animation_clip_circle_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_clip_rect.xml b/tests/res/drawable/vector_animation_clip_rect.xml
new file mode 100644
index 0000000..5eff14a
--- /dev/null
+++ b/tests/res/drawable/vector_animation_clip_rect.xml
@@ -0,0 +1,128 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="12.25"
+        android:viewportWidth="7.30625" />
+
+    <group>
+        <path
+            android:name="clip1"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 0
+                l 7.3, 0
+                l 0, 0
+                l -7.3, 0
+                z"
+            android:pivotX="3.65"
+            android:pivotY="6.125"
+            android:rotation="-30" />
+        <path
+            android:name="one"
+            android:fill="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0,-1.0Z" />
+        <path
+            android:name="clip2"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 0
+                l 7.3, 0
+                l 0, 12.25
+                l -7.3, 0
+                z"
+            android:pivotX="3.65"
+            android:pivotY="6.125"
+            android:rotation="-30" />
+        <path
+            android:name="two"
+            android:fill="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
+                q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
+                q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
+                q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
+                q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
+                q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
+                q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
+                q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
+    <group>
+        <path
+            android:name="clip1"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 0
+                l 7.3, 0
+                l 0, 12.25
+                l -7.3, 0
+                z"
+            android:pivotX="3.65"
+            android:pivotY="6.125"
+            android:rotation="-30" />
+        <path
+            android:name="one"
+            android:fill="#ff88ff"
+            android:pathData="M 1.215625,9.5l 1.9375,0.0 0.0,-6.671875 -2.109375,0.421875 0.0,-1.078125
+                l 2.09375,-0.421875 1.1874998,0.0 0.0,7.75 1.9375,0.0 0.0,1.0
+                l -5.046875,0.0 0.0,-1.0Z" />
+        <path
+            android:name="clip2"
+            android:clipToPath="true"
+            android:pathData="
+                M 0, 12.25
+                l 7.3, 0
+                l 0, 12.25
+                l -7.3, 0
+                z"
+            android:pivotX="3.65"
+            android:pivotY="6.125"
+            android:rotation="-30" />
+        <path
+            android:name="two"
+            android:fill="#ff88ff"
+            android:pathData="M 2.534375,9.6875l 4.140625,0.0 0.0,1.0 -5.5625,0.0 0.0,-1.0q 0.671875,-0.6875 1.828125,-1.859375
+                q 1.1718752,-1.1875 1.4687502,-1.53125 0.578125,-0.625 0.796875,-1.0625
+                q 0.234375,-0.453125 0.234375,-0.875 0.0,-0.703125 -0.5,-1.140625
+                q -0.484375,-0.4375 -1.2656252,-0.4375 -0.5625,0.0 -1.1875,0.1875
+                q -0.609375,0.1875 -1.3125,0.59375l 0.0,-1.203125q 0.71875,-0.28125 1.328125,-0.421875
+                q 0.625,-0.15625 1.140625,-0.15625 1.3593752,0.0 2.1718752,0.6875
+                q 0.8125,0.671875 0.8125,1.8125 0.0,0.53125 -0.203125,1.015625
+                q -0.203125,0.484375 -0.734375,1.140625 -0.15625,0.171875 -0.9375,0.984375
+                q -0.78125024,0.8125 -2.2187502,2.265625Z" />
+    </group>
+
+    <animation
+        android:durations="4000"
+        android:sequence="one,one" />
+    <animation
+        android:durations="4000"
+        android:sequence="two,two" />
+    <animation
+        android:durations="4000"
+        android:sequence="clip1,clip1" />
+    <animation
+        android:durations="4000"
+        android:sequence="clip2,clip2" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_clip_rect_golden.png b/tests/res/drawable/vector_animation_clip_rect_golden.png
new file mode 100644
index 0000000..13ac285
--- /dev/null
+++ b/tests/res/drawable/vector_animation_clip_rect_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_rotate_curve.xml b/tests/res/drawable/vector_animation_rotate_curve.xml
new file mode 100644
index 0000000..c4dcfb4
--- /dev/null
+++ b/tests/res/drawable/vector_animation_rotate_curve.xml
@@ -0,0 +1,45 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="400"
+        android:viewportWidth="500" />
+
+    <group>
+        <path
+            android:name="start"
+            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+            android:stroke="#FFFFFF00"
+            android:strokeWidth="10" />
+    </group>
+    <group>
+        <path
+            android:name="end"
+            android:pathData="M100,200 C100,100 250,100 250,200 S400,300 400,200"
+            android:pivotX="250"
+            android:pivotY="200"
+            android:rotation="360"
+            android:strokeWidth="10" />
+    </group>
+
+    <animation android:sequence="start,end" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_rotate_curve_golden.png b/tests/res/drawable/vector_animation_rotate_curve_golden.png
new file mode 100644
index 0000000..55ccb75
--- /dev/null
+++ b/tests/res/drawable/vector_animation_rotate_curve_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_rotate_pie.xml b/tests/res/drawable/vector_animation_rotate_pie.xml
new file mode 100644
index 0000000..4a2ed90
--- /dev/null
+++ b/tests/res/drawable/vector_animation_rotate_pie.xml
@@ -0,0 +1,79 @@
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="400"
+        android:viewportWidth="600" />
+
+    <group>
+        <path
+            android:name="pie1"
+            android:fill="#ffffffff"
+            android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="1" />
+        <path
+            android:name="half"
+            android:fill="#FFFF0000"
+            android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
+            android:pivotX="300"
+            android:pivotY="200"
+            android:rotation="0"
+            android:stroke="#FF0000FF"
+            android:strokeWidth="5" />
+    </group>
+    <group>
+        <path
+            android:name="pie2"
+            android:fill="#ffff0000"
+            android:pathData="M300,200 h-150 a150,150 0 1,0 150,-150 z"
+            android:pivotX="300"
+            android:pivotY="200"
+            android:rotation="360"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="10" />
+        <path
+            android:name="half"
+            android:fill="#FFFFFF00"
+            android:pathData="M275,175 v-150 a150,150 0 0,0 -150,150 z"
+            android:pivotX="300"
+            android:pivotY="200"
+            android:rotation="-360"
+            android:stroke="#FF0000FF"
+            android:strokeWidth="5" />
+    </group>
+
+    <animation
+        android:animate="easeInOut"
+        android:durations="1000"
+        android:repeatCount="2"
+        android:repeatStyle="forward"
+        android:sequence="pie1,pie2"
+        android:startOffset="500" />
+    <animation
+        android:animate="easeInOut"
+        android:durations="1000"
+        android:repeatCount="5"
+        android:repeatStyle="forward"
+        android:sequence="half,half"
+        android:startOffset="500" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_rotate_pie_golden.png b/tests/res/drawable/vector_animation_rotate_pie_golden.png
new file mode 100644
index 0000000..13e6cbd
--- /dev/null
+++ b/tests/res/drawable/vector_animation_rotate_pie_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_trim_path.xml b/tests/res/drawable/vector_animation_trim_path.xml
new file mode 100644
index 0000000..207879d
--- /dev/null
+++ b/tests/res/drawable/vector_animation_trim_path.xml
@@ -0,0 +1,87 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="600"
+        android:viewportWidth="600" />
+
+    <group>
+        <path
+            android:name="pie1"
+            android:pathData="M300,70 a230,230 0 1,0 1,0 z"
+            android:stroke="#FF00FF00"
+            android:strokeWidth="70"
+            android:trimPathEnd=".75"
+            android:trimPathOffset="0"
+            android:trimPathStart="0" />
+        <path
+            android:name="v"
+            android:fill="#FF00FF00"
+            android:pathData="M300,70 l 0,-70 70,70 -70,70z"
+            android:pivotX="300"
+            android:pivotY="300"
+            android:rotation="0" />
+    </group>
+    <group>
+        <path
+            android:name="v"
+            android:pathData="M300,70 l 0,-70 70,70 -70,70z"
+            android:pivotX="300"
+            android:pivotY="300"
+            android:rotation="360" />
+        <path
+            android:name="pie2"
+            android:pathData="M300,70 a230,230 0 1,0 1,0 z"
+            android:pivotX="300"
+            android:pivotY="300"
+            android:rotation="360"
+            android:stroke="#FF00FF00"
+            android:strokeLineCap="round"
+            android:strokeWidth="70"
+            android:trimPathEnd=".5"
+            android:trimPathOffset="0"
+            android:trimPathStart="0" />
+    </group>
+
+    <animation
+        android:animate="easeInOut"
+        android:durations="2000"
+        android:repeatCount="-1"
+        android:repeatStyle="forward"
+        android:sequence="pie1,pie2"
+        android:startOffset="500" />
+    <animation
+        android:animate="easeInOut"
+        android:durations="2000"
+        android:repeatCount="-1"
+        android:repeatStyle="forward"
+        android:sequence="v,v"
+        android:startOffset="500" />
+    <animation
+        android:animate="easeInOut"
+        android:durations="2800"
+        android:limitTo="trimPathEnd"
+        android:repeatCount="-1"
+        android:repeatStyle="reverse"
+        android:sequence="pie1,pie2"
+        android:startOffset="500" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_trim_path_golden.png b/tests/res/drawable/vector_animation_trim_path_golden.png
new file mode 100644
index 0000000..77d6944
--- /dev/null
+++ b/tests/res/drawable/vector_animation_trim_path_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_animation_wifi.xml b/tests/res/drawable/vector_animation_wifi.xml
new file mode 100644
index 0000000..ddb178e
--- /dev/null
+++ b/tests/res/drawable/vector_animation_wifi.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="200"
+        android:viewportWidth="200" />
+
+    <group>
+        <path
+            android:name="bar3"
+            android:fill="#FFFFFFFF"
+            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10
+                c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+        <path
+            android:name="bar2"
+            android:fill="#FF555555"
+            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07
+                C58.403,37.071,39.599,37.071,28.001,48.787z" />
+        <path
+            android:name="bar1"
+            android:fill="#FF555555"
+            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0
+                l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+        <path
+            android:name="bar0"
+            android:fill="#FF555555"
+            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0
+                L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    </group>
+    <group>
+        <path
+            android:name="bar3"
+            android:fill="#FFFFFFFF"
+            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10
+                c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+        <path
+            android:name="bar2"
+            android:fill="#FFFFFFFF"
+            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0l6.999 -7.07
+                C58.403,37.071,39.599,37.071,28.001,48.787z" />
+        <path
+            android:name="bar1"
+            android:fill="#FF555555"
+            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0
+                l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+        <path
+            android:name="bar0"
+            android:fill="#FF555555"
+            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0
+                L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    </group>
+    <group>
+        <path
+            android:name="bar3"
+            android:fill="#FFFFFFFF"
+            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10
+                c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+        <path
+            android:name="bar2"
+            android:fill="#FFFFFFFF"
+            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0
+                l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+        <path
+            android:name="bar1"
+            android:fill="#FFFFFFFF"
+            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0
+                l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+        <path
+            android:name="bar0"
+            android:fill="#FF555555"
+            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0
+                L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    </group>
+    <group>
+        <path
+            android:name="bar3"
+            android:fill="#FFFFFFFF"
+            android:pathData="M49.001,60c-5.466,0 -9.899,4.478 -9.899,10s4.434,10,9.899,10
+                c5.468,0,9.899 -4.478,9.899 -10S54.469,60,49.001,60z" />
+        <path
+            android:name="bar2"
+            android:fill="#FFFFFFFF"
+            android:pathData="M28.001,48.787l7,7.07c7.731 -7.811,20.269 -7.81,28.001,0
+                l6.999 -7.07C58.403,37.071,39.599,37.071,28.001,48.787z" />
+        <path
+            android:name="bar1"
+            android:fill="#FFFFFFFF"
+            android:pathData="M14.001,34.645   L21,41.716c15.464 -15.621,40.536 -15.621,56,0
+                l7.001 -7.071C64.672,15.119,33.33,15.119,14.001,34.645z" />
+        <path
+            android:name="bar0"
+            android:fill="#FFFFFFFF"
+            android:pathData="M0,20.502l6.999,7.071   c23.196 -23.431,60.806 -23.431,84.002,0
+                L98,20.503C70.938 -6.834,27.063 -6.834,0,20.502z" />
+    </group>
+
+    <animation
+        android:durations="500,500,500"
+        android:sequence="bar0,bar0,bar0,bar0" />
+    <animation
+        android:durations="500,500,500"
+        android:sequence="bar1,bar1,bar1,bar1" />
+    <animation
+        android:durations="500,500,500"
+        android:sequence="bar2,bar2,bar2,bar2" />
+    <animation
+        android:durations="500,500,500"
+        android:sequence="bar3,bar3,bar3,bar3" />
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_animation_wifi_golden.png b/tests/res/drawable/vector_animation_wifi_golden.png
new file mode 100644
index 0000000..190015a
--- /dev/null
+++ b/tests/res/drawable/vector_animation_wifi_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_create.xml b/tests/res/drawable/vector_icon_create.xml
new file mode 100644
index 0000000..c8fa089
--- /dev/null
+++ b/tests/res/drawable/vector_icon_create.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="24"
+        android:viewportWidth="24" />
+
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M3.0,17.25L3.0,21.0l3.75,0.0L17.813995,9.936001l-3.75,-3.75L3.0,17.25zM20.707,7.0429993c0.391,-0.391 0.391,-1.023 0.0,-1.414l-2.336,-2.336c-0.391,-0.391 -1.023,-0.391 -1.414,0.0l-1.832,1.832l3.75,3.75L20.707,7.0429993z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_create_golden.png b/tests/res/drawable/vector_icon_create_golden.png
new file mode 100644
index 0000000..943fce5
--- /dev/null
+++ b/tests/res/drawable/vector_icon_create_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_delete.xml b/tests/res/drawable/vector_icon_delete.xml
new file mode 100644
index 0000000..8303ac8
--- /dev/null
+++ b/tests/res/drawable/vector_icon_delete.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="24"
+        android:viewportWidth="24" />
+
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M6.0,19.0c0.0,1.104 0.896,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0,-0.896 2.0,-2.0l0.0,-12.0L6.0,7.0L6.0,19.0zM18.0,4.0l-2.5,0.0l-1.0,-1.0l-5.0,0.0l-1.0,1.0L6.0,4.0C5.4469986,4.0 5.0,4.4469986 5.0,5.0l0.0,1.0l14.0,0.0l0.0,-1.0C19.0,4.4469986 18.552002,4.0 18.0,4.0z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_delete_golden.png b/tests/res/drawable/vector_icon_delete_golden.png
new file mode 100644
index 0000000..b46363e
--- /dev/null
+++ b/tests/res/drawable/vector_icon_delete_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_heart.xml b/tests/res/drawable/vector_icon_heart.xml
new file mode 100644
index 0000000..061c7d8
--- /dev/null
+++ b/tests/res/drawable/vector_icon_heart.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="24"
+        android:viewportWidth="24" />
+
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M16.0,5.0c-1.955,0.0 -3.83,1.268 -4.5,3.0c-0.67,-1.732 -2.547,-3.0 -4.5,-3.0C4.4570007,5.0 2.5,6.931999 2.5,9.5c0.0,3.529 3.793,6.258 9.0,11.5c5.207,-5.242 9.0,-7.971 9.0,-11.5C20.5,6.931999 18.543,5.0 16.0,5.0z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_heart_golden.png b/tests/res/drawable/vector_icon_heart_golden.png
new file mode 100644
index 0000000..7450751
--- /dev/null
+++ b/tests/res/drawable/vector_icon_heart_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_random_path_1.xml b/tests/res/drawable/vector_icon_random_path_1.xml
new file mode 100644
index 0000000..466cd59
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_1.xml
@@ -0,0 +1,55 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="
+                 m 0.0 0.0
+                 c 58.357853 57.648304 47.260395 2.2044754 3.0 3.0
+                 s 61.29288 10.748665 6.0 6.0
+                 s 0.12015152 45.193787 9.0 9.0
+                 s 32.573513 46.862522 12.0 12.0
+                 C 52.051823 62.050003 14.197739 51.99994 15.0 15.0
+                 S 58.365482 51.877937 18.0 18.0
+                 S 26.692455 3.9604378 21.0 21.0
+                 S 21.433464 52.17514 24.0 24.0
+                 M 27.0 27.0
+                 s 0.77630234 20.606667 30.0 30.0
+                 M 33.0 33.0
+                 S 31.06879 21.506374 36.0 36.0
+                 m 39.0 39.0
+                 s 11.699013 23.684185 42.0 42.0
+                 m 45.0 45.0
+                 S 3.7642136 38.589584 48.0 48.0
+                 Q 27.203026 53.329338 51.0 51.0
+                 s 39.229023 15.1781845 54.0 54.0
+                 Q 47.946877 23.706299 57.0 57.0
+                 S 45.63452 56.15198 60.0 60.0 "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_random_path_1_golden.png b/tests/res/drawable/vector_icon_random_path_1_golden.png
new file mode 100644
index 0000000..91776a9
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_1_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_random_path_2.xml b/tests/res/drawable/vector_icon_random_path_2.xml
new file mode 100644
index 0000000..2f1e557
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_2.xml
@@ -0,0 +1,55 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="
+                 m 0.0 0.0
+                 q 4.7088394 36.956432 3.0 3.0
+                 s 29.470345 16.754963 6.0 6.0
+                 q 20.278355 7.4670525 9.0 9.0
+                 S 30.897224 17.732414 12.0 12.0
+                 T 15.0 15.0
+                 s 63.47204 45.67142 18.0 18.0
+                 T 21.0 21.0
+                 S 0.3184204 24.808247 24.0 24.0
+                 t 27.0 27.0
+                 s 39.02275 38.261158 30.0 30.0
+                 t 33.0 33.0
+                 S 50.709816 16.067192 36.0 36.0
+                 a 62.50911 7.7131805 51.932335 0 0 39.0 39.0
+                 s 5.155651 15.749123 42.0 42.0
+                 a 51.87415 40.30564 49.804344 0 0 45.0 45.0
+                 S 16.16534 62.55986 48.0 48.0
+                 A 39.90161 43.904438 41.642593 1 0 51.0 51.0
+                 s 46.258068 32.12831 54.0 54.0
+                 A 22.962704 55.05604 42.912285 1 1 57.0 57.0
+                 S 36.47731 54.216763 60.0 60.0 "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_random_path_2_golden.png b/tests/res/drawable/vector_icon_random_path_2_golden.png
new file mode 100644
index 0000000..9af40a3
--- /dev/null
+++ b/tests/res/drawable/vector_icon_random_path_2_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_repeated_a_1.xml b/tests/res/drawable/vector_icon_repeated_a_1.xml
new file mode 100644
index 0000000..55793b4
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_1.xml
@@ -0,0 +1,49 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="m 45.712063 19.109837
+                H 24.509682
+                a 59.3415 26.877445 22.398209 1 1 3.3506432 1.6524277
+                a 34.922844 36.72583 13.569004 0 0 24.409462 20.931156
+                a 43.47134 32.61542 52.534607 1 0 7.187504 61.509724
+                A 30.621132 41.44202 50.885685 0 0 23.235489 26.638653
+                A 7.251148 15.767811 44.704533 1 1 19.989803 21.33052
+                A 55.645584 46.20288 19.40316 0 1 32.881298 53.410923
+                c 30.649612 4.8525085 21.96682 1.3304634 17.300182 14.747681
+                a 9.375069 44.365055 57.169727 0 0 56.01326 52.59596
+                A 50.071907 37.331825 56.301754 1 0 14.676102 62.04976
+                C 36.531925 4.6217957 47.59332 54.793385 13.562473 13.753647
+                A 2.3695297 42.578487 54.250687 0 1 33.1337 41.511288
+                a 39.4827 38.844944 54.52335 1 1 13.549484 46.81581
+                c 56.943657 51.96854 27.938824 61.148792 24.168636 46.642727
+                "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_a_1_golden.png b/tests/res/drawable/vector_icon_repeated_a_1_golden.png
new file mode 100644
index 0000000..b3acfe7
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_1_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_repeated_a_2.xml b/tests/res/drawable/vector_icon_repeated_a_2.xml
new file mode 100644
index 0000000..5e295fc
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_2.xml
@@ -0,0 +1,51 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="m 45.712063 19.109837
+                H 24.509682
+                A 37.689938 2.3916092 17.462616 1 0 24.958328 48.110596
+                q 45.248383 30.396336 5.777027 3.4086685
+                a 30.966236 62.67946 50.532032 1 0 29.213684 60.63014
+                L 56.16764 8.342098
+                Q 61.172253 1.4613304 4.4721107 38.287144
+                A 6.284897 22.991482 47.409508 1 1 44.10166 60.998764
+                t 36.36881 55.68292
+                a 51.938667 35.22107 22.272938 1 1 28.572739 60.848858
+                A 19.610851 11.569599 51.407906 1 1 56.82705 24.386292
+                T 36.918854 59.542286
+                a 33.191364 10.553429 53.047726 1 0 54.874985 7.409252
+                s 30.186714 42.154182 59.73551 35.50219
+                A 47.9379 5.776497 28.307701 1 1 3.3323975 30.113499
+                a 22.462494 28.096004 55.76455 0 0 25.58981 30.816948
+                S 43.91107 54.679676 19.540264 0.34284973
+                "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_a_2_golden.png b/tests/res/drawable/vector_icon_repeated_a_2_golden.png
new file mode 100644
index 0000000..bbc84b9
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_a_2_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_repeated_cq.xml b/tests/res/drawable/vector_icon_repeated_cq.xml
new file mode 100644
index 0000000..944cf5f
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_cq.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="m 30.81895 41.37989
+                v 31.00579
+                c 24.291603 52.03364 40.6086 24.840137 29.56704 6.5204926
+                45.133224 22.913471 33.052887 21.727486 33.369 61.60278
+                9.647232 22.098152 48.939598 47.470215 53.653687 62.32235
+                C 2.0560722 1.4615479 7.0928993 26.005287 40.137558 36.75628
+                11.246731 32.178127 59.367462 60.34823 57.254383 37.357815
+                47.75605 11.424667 3.3105545 51.886635 56.63027 17.12133
+                q 28.37534 32.85535 25.85654 33.57151
+                10.356537 51.850616 54.085087 35.653175
+                12.530029 52.87991 17.44696 11.780586
+                Q 2.585228 51.92801 60.000664 56.79912
+                54.18275 51.500694 9.375679 23.836113
+                60.35329 59.026245 31.058632 35.14934
+                "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_cq_golden.png b/tests/res/drawable/vector_icon_repeated_cq_golden.png
new file mode 100644
index 0000000..8d73cfd
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_cq_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_repeated_st.xml b/tests/res/drawable/vector_icon_repeated_st.xml
new file mode 100644
index 0000000..d22bc70
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_st.xml
@@ -0,0 +1,48 @@
+<!--
+ 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="128"
+        android:viewportWidth="128" />
+
+    <group>
+        <path
+            android:fill="#FF00FF00"
+            android:pathData="m 20.20005 8.139153
+                h 10.053165
+                s 14.2943 49.612846 35.520653 54.904068
+                50.1405 17.044182 5.470337 40.180553
+                3.125019 34.221123 53.212563 32.862965
+                S 35.985264 35.74349 0.15337753 59.27337
+                2.2951508 44.56783 51.089413 29.829689
+                8.5599785 22.649555 4.3914986 28.139206
+                t 11.932453 44.041077
+                62.629326 7.40921
+                23.302986 54.116184
+                T 43.560753 63.370514
+                40.156204 17.60786
+                40.12051 60.803394
+                "
+            android:stroke="#FF0000FF"
+            android:strokeWidth="1" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_repeated_st_golden.png b/tests/res/drawable/vector_icon_repeated_st_golden.png
new file mode 100644
index 0000000..6094a9a
--- /dev/null
+++ b/tests/res/drawable/vector_icon_repeated_st_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_schedule.xml b/tests/res/drawable/vector_icon_schedule.xml
new file mode 100644
index 0000000..b7540f4
--- /dev/null
+++ b/tests/res/drawable/vector_icon_schedule.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="24"
+        android:viewportWidth="24" />
+
+    <group>
+        <path
+            android:fillOpacity="0.9"
+            android:pathData="M11.994999,2.0C6.4679985,2.0 2.0,6.4780006 2.0,12.0s4.468,10.0 9.995,10.0S22.0,17.522 22.0,12.0S17.521,2.0 11.994999,2.0zM12.0,20.0c-4.42,0.0 -8.0,-3.582 -8.0,-8.0s3.58,-8.0 8.0,-8.0s8.0,3.582 8.0,8.0S16.419998,20.0 12.0,20.0z" />
+        <path
+            android:fillOpacity="0.9"
+            android:pathData="M12.5,6.0l-1.5,0.0 0.0,7.0 5.3029995,3.1819992 0.75,-1.249999 -4.5529995,-2.7320004z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_schedule_golden.png b/tests/res/drawable/vector_icon_schedule_golden.png
new file mode 100644
index 0000000..949916c
--- /dev/null
+++ b/tests/res/drawable/vector_icon_schedule_golden.png
Binary files differ
diff --git a/tests/res/drawable/vector_icon_settings.xml b/tests/res/drawable/vector_icon_settings.xml
new file mode 100644
index 0000000..88ea6d6
--- /dev/null
+++ b/tests/res/drawable/vector_icon_settings.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android" >
+
+    <size
+        android:height="64dp"
+        android:width="64dp" />
+
+    <viewport
+        android:viewportHeight="24"
+        android:viewportWidth="24" />
+
+    <group>
+        <path
+            android:fill="#FF000000"
+            android:pathData="M19.429,12.975998c0.042,-0.32 0.07,-0.645 0.07,-0.976s-0.029,-0.655 -0.07,-0.976l2.113,-1.654c0.188,-0.151 0.243,-0.422 0.118,-0.639l-2.0,-3.463c-0.125,-0.217 -0.386,-0.304 -0.612,-0.218l-2.49,1.004c-0.516,-0.396 -1.081,-0.731 -1.69,-0.984l-0.375,-2.648C14.456,2.1829987 14.25,2.0 14.0,2.0l-4.0,0.0C9.75,2.0 9.544,2.1829987 9.506,2.422001L9.131,5.0699997C8.521,5.322998 7.957,5.6570015 7.44,6.054001L4.952,5.0509987C4.726,4.965 4.464,5.052002 4.34,5.269001l-2.0,3.463C2.2150002,8.947998 2.27,9.219002 2.4580002,9.369999l2.112,1.653C4.528,11.344002 4.5,11.668999 4.5,12.0s0.029,0.656 0.071,0.977L2.4580002,14.630001c-0.188,0.151 -0.243,0.422 -0.118,0.639l2.0,3.463c0.125,0.217 0.386,0.304 0.612,0.218l2.489,-1.004c0.516,0.396 1.081,0.731 1.69,0.984l0.375,2.648C9.544,21.817001 9.75,22.0 10.0,22.0l4.0,0.0c0.25,0.0 0.456,-0.183 0.494,-0.422l0.375,-2.648c0.609,-0.253 1.174,-0.588 1.689,-0.984l2.49,1.004c0.226,0.086 0.487,-0.001 0.612,-0.218l2.0,-3.463c0.125,-0.217 0.07,-0.487 -0.118,-0.639L19.429,12.975998zM12.0,16.0c-2.21,0.0 -4.0,-1.791 -4.0,-4.0c0.0,-2.21 1.79,-4.0 4.0,-4.0c2.208,0.0 4.0,1.79 4.0,4.0C16.0,14.209 14.208,16.0 12.0,16.0z" />
+    </group>
+
+</vector>
\ No newline at end of file
diff --git a/tests/res/drawable/vector_icon_settings_golden.png b/tests/res/drawable/vector_icon_settings_golden.png
new file mode 100644
index 0000000..d12b142
--- /dev/null
+++ b/tests/res/drawable/vector_icon_settings_golden.png
Binary files differ
diff --git a/tests/res/layout/inflater_layout_tags.xml b/tests/res/layout/inflater_layout_tags.xml
new file mode 100644
index 0000000..dc3eb29
--- /dev/null
+++ b/tests/res/layout/inflater_layout_tags.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/viewlayout_root"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <tag
+        android:id="@+id/tag_viewlayout_root"
+        android:value="@string/tag1" />
+
+    <View
+        android:id="@+id/mock_view"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" >
+
+        <tag
+            android:id="@+id/tag_mock_view"
+            android:value="@string/tag2" />
+    </View>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/res/layout/inflater_override_theme_layout.xml b/tests/res/layout/inflater_override_theme_layout.xml
new file mode 100644
index 0000000..2d2a578
--- /dev/null
+++ b/tests/res/layout/inflater_override_theme_layout.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+         http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:theme="@style/Theme_OverrideOuter" >
+
+    <View
+        android:id="@+id/view_outer"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content" />
+
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:theme="@style/Theme_OverrideInner" >
+
+        <View
+            android:id="@+id/view_inner"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+        <View
+            android:id="@+id/view_attr"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:theme="?attr/themeOverrideAttr" />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/tests/res/layout/surface_view_2.xml b/tests/res/layout/surface_view_2.xml
new file mode 100644
index 0000000..fe53c71
--- /dev/null
+++ b/tests/res/layout/surface_view_2.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+     Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="horizontal"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <SurfaceView
+        android:id="@+id/surface_view"
+        android:layout_width="320dp"
+        android:layout_height="240dp"/>
+
+</LinearLayout>
diff --git a/tests/res/raw/testmp3_2.mp3 b/tests/res/raw/testmp3_2.mp3
new file mode 100644
index 0000000..6a70c69
--- /dev/null
+++ b/tests/res/raw/testmp3_2.mp3
Binary files differ
diff --git a/tests/res/values-b+kok+419+VARIANT/configVarying.xml b/tests/res/values-b+kok+419+VARIANT/configVarying.xml
new file mode 100644
index 0000000..3560279
--- /dev/null
+++ b/tests/res/values-b+kok+419+VARIANT/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple kok 419 VARIANT</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok 419 VARIANT</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+419/configVarying.xml b/tests/res/values-b+kok+419/configVarying.xml
new file mode 100644
index 0000000..b6f37f9
--- /dev/null
+++ b/tests/res/values-b+kok+419/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <item type="configVarying" name="simple">simple kok 419</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok 419</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+IN/configVarying.xml b/tests/res/values-b+kok+IN/configVarying.xml
new file mode 100644
index 0000000..967ea47
--- /dev/null
+++ b/tests/res/values-b+kok+IN/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <item type="configVarying" name="simple">simple kok IN</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok IN</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+Knda+419+VARIANT/configVarying.xml b/tests/res/values-b+kok+Knda+419+VARIANT/configVarying.xml
new file mode 100644
index 0000000..2b2bcea
--- /dev/null
+++ b/tests/res/values-b+kok+Knda+419+VARIANT/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple kok Knda 419 VARIANT</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok Knda 419 VARIANT</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+Knda+419/configVarying.xml b/tests/res/values-b+kok+Knda+419/configVarying.xml
new file mode 100644
index 0000000..cbc53b9
--- /dev/null
+++ b/tests/res/values-b+kok+Knda+419/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <item type="configVarying" name="simple">simple kok Knda 419</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok Knda 419</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+Knda/configVarying.xml b/tests/res/values-b+kok+Knda/configVarying.xml
new file mode 100644
index 0000000..f735749
--- /dev/null
+++ b/tests/res/values-b+kok+Knda/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <item type="configVarying" name="simple">simple kok Knda</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok Knda</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok+VARIANT/configVarying.xml b/tests/res/values-b+kok+VARIANT/configVarying.xml
new file mode 100644
index 0000000..6743b7f
--- /dev/null
+++ b/tests/res/values-b+kok+VARIANT/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+    <item type="configVarying" name="simple">simple kok VARIANT</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok VARIANT</item>
+    </bag>
+</resources>
diff --git a/tests/res/values-b+kok/configVarying.xml b/tests/res/values-b+kok/configVarying.xml
new file mode 100644
index 0000000..e1bafa1
--- /dev/null
+++ b/tests/res/values-b+kok/configVarying.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2007 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>
+    <item type="configVarying" name="simple">simple kok</item>
+    <bag type="configVarying" name="bag">
+        <item name="testString">bag kok</item>
+    </bag>
+</resources>
diff --git a/tests/res/values/attrs.xml b/tests/res/values/attrs.xml
index 9793893..4c3d9db 100644
--- a/tests/res/values/attrs.xml
+++ b/tests/res/values/attrs.xml
@@ -125,4 +125,21 @@
         <attr name="textColorHint"/>
         <attr name="textColorLink"/>
     </declare-styleable>
+    <!-- Integer used to uniquely identify theme overrides. -->
+    <attr name="themeType" format="integer"/>
+    <!-- Theme reference used to override parent theme. -->
+    <attr name="themeOverrideAttr" format="reference"/>
+
+    <!-- Drawable theming attributes -->
+    <attr name="themeBoolean" />
+    <attr name="themeColor" />
+    <attr name="themeFloat" />
+    <attr name="themeInteger" />
+    <attr name="themeDimension" />
+    <attr name="themeDrawable" />
+    <attr name="themeBitmap" />
+    <attr name="themeNinePatch" />
+    <attr name="themeGravity" />
+    <attr name="themeTileMode" />
+    <attr name="themeAngle" />
 </resources>
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index 0ab3dd6..ebdb9b9 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -152,6 +152,8 @@
    <string name="version_v3">base</string>
    <string name="authenticator_label">Android CTS</string>
    <string name="search_label">Android CTS</string>
+   <string name="tag1">tag 1</string>
+   <string name="tag2">tag 2</string>
 
    <string name="button">Button</string>
    <string name="holo_test">Holo Test</string>
diff --git a/tests/res/values/styles.xml b/tests/res/values/styles.xml
index f8b56fd..47238a3 100644
--- a/tests/res/values/styles.xml
+++ b/tests/res/values/styles.xml
@@ -14,7 +14,7 @@
      limitations under the License.
 -->
 
-<resources>
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
 
     <style name="Whatever">
         <item name="type1">true</item>
@@ -138,4 +138,31 @@
         <item name="android:panelColorBackground">#ffffffff</item>
     </style>
 
+    <style name="Theme_OverrideOuter">
+        <item name="themeType">1</item>
+    </style>
+
+    <style name="Theme_OverrideInner">
+        <item name="themeType">2</item>
+        <item name="themeOverrideAttr">@style/Theme_OverrideAttr</item>
+    </style>
+
+    <style name="Theme_OverrideAttr">
+        <item name="themeType">3</item>
+    </style>
+    
+    <style name="Theme_ThemedDrawableTest">
+        <item name="themeBoolean">true</item>
+        <item name="themeColor">@android:color/black</item>
+        <item name="themeFloat">1.0</item>
+        <item name="themeAngle">45.0</item>
+        <item name="themeInteger">1</item>
+        <item name="themeDimension">1px</item>
+        <item name="themeDrawable">@drawable/icon_black</item>
+        <item name="themeBitmap">@drawable/icon_black</item>
+        <item name="themeNinePatch">@drawable/ninepatch_0</item>
+        <item name="themeGravity">48</item>
+        <item name="themeTileMode">2</item>
+    </style>
+
 </resources>
diff --git a/tests/sample/Android.mk b/tests/sample/Android.mk
index e1a9408..5e8f571 100755
--- a/tests/sample/Android.mk
+++ b/tests/sample/Android.mk
@@ -22,9 +22,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/sample/AndroidManifest.xml b/tests/sample/AndroidManifest.xml
index ae58f0a..f07ebbe 100755
--- a/tests/sample/AndroidManifest.xml
+++ b/tests/sample/AndroidManifest.xml
@@ -31,9 +31,12 @@
 
     <!--  self-instrumenting test package. -->
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:label="CTS sample tests"
-        android:targetPackage="android.sample.cts" />
-
+        android:targetPackage="android.sample.cts" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java b/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java
new file mode 100644
index 0000000..47b4cba
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/Camera2SurfaceViewStubActivity.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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.camera2.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import com.android.cts.stub.R;
+
+public class Camera2SurfaceViewStubActivity extends Activity implements SurfaceHolder.Callback {
+    private final static String TAG = "Camera2SurfaceViewStubActivity";
+    private final ConditionVariable surfaceChangedDone = new ConditionVariable();
+
+    private SurfaceView mSurfaceView;
+    private int currentWidth = 0;
+    private int currentHeight = 0;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.surface_view_2);
+        mSurfaceView = (SurfaceView) findViewById(R.id.surface_view);
+        mSurfaceView.getHolder().addCallback(this);
+    }
+
+    public SurfaceView getSurfaceView() {
+        return mSurfaceView;
+    }
+
+    public boolean waitForSurfaceSizeChanged(int timeOutMs, int expectWidth, int expectHeight) {
+        if (timeOutMs <= 0 || expectWidth <= 0 || expectHeight <= 0) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "timeout(%d), expectWidth(%d), and expectHeight(%d) " +
+                            "should all be positive numbers",
+                            timeOutMs, expectWidth, expectHeight));
+        }
+
+        int waitTimeMs = timeOutMs;
+        boolean changeSucceeded = false;
+        while (!changeSucceeded && waitTimeMs > 0) {
+            long startTimeMs = System.currentTimeMillis();
+            changeSucceeded = surfaceChangedDone.block(waitTimeMs);
+            if (!changeSucceeded) {
+                Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
+                return changeSucceeded;
+            } else {
+                // Get a surface change callback, need to check if the size is expected.
+                surfaceChangedDone.close();
+                if (currentWidth == expectWidth && currentHeight == expectHeight) {
+                    return changeSucceeded;
+                }
+                // Do a further iteration surface change check as surfaceChanged could be called
+                // again.
+                changeSucceeded = false;
+            }
+            waitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+        }
+
+        // Couldn't get expected surface size change.
+        return false;
+     }
+
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.i(TAG, "Surface Changed to: " + width + "x" + height);
+        currentWidth = width;
+        currentHeight = height;
+        surfaceChangedDone.open();
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+    }
+}
diff --git a/tests/src/android/hardware/camera2/cts/common.rs b/tests/src/android/hardware/camera2/cts/common.rs
new file mode 100644
index 0000000..4c134b3
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/common.rs
@@ -0,0 +1,45 @@
+/*
+ * Copyright 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.
+ */
+
+#ifndef ANDROID_HARDWARE_CAMERA2_CTS_COMMON_RS
+#define ANDROID_HARDWARE_CAMERA2_CTS_COMMON_RS
+
+#pragma version(1)
+#pragma rs java_package_name(android.hardware.camera2.cts)
+#pragma rs_fp_relaxed
+
+typedef uchar3 yuvx_444; // interleaved YUV. (y,u,v) per pixel. use .x/.y/.z to read
+typedef uchar3 yuvf_420; // flexible YUV (4:2:0). use rsGetElementAtYuv to read.
+
+#define convert_yuvx_444 convert_uchar3
+#define convert_yuvf_420 __error_cant_output_flexible_yuv__
+
+#define rsGetElementAt_yuvx_444 rsGetElementAt_uchar3
+#define rsGetElementAt_yuvf_420 __error_cant_output_flexible_yuv__
+
+#define RS_KERNEL __attribute__((kernel))
+
+#ifndef LOG_DEBUG
+#define LOG_DEBUG 0
+#endif
+
+#if LOG_DEBUG
+#define LOGD(string, expr) rsDebug((string), (expr))
+#else
+#define LOGD(string, expr) if (0) rsDebug((string), (expr))
+#endif
+
+#endif // header guard
diff --git a/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs b/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
new file mode 100644
index 0000000..f930f58
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/crop_yuvf_420_to_yuvx_444.rs
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.rs"
+
+// Must be YUV 420 888 (flexible YUV)
+rs_allocation mInput;
+
+// Input globals
+uint32_t src_x;
+uint32_t src_y;
+
+// Crop each pixel from mInput
+yuvx_444 RS_KERNEL crop(uint32_t x, uint32_t y) {
+
+    uchar py = rsGetElementAtYuv_uchar_Y(mInput, x + src_x, y + src_y);
+    uchar pu = rsGetElementAtYuv_uchar_U(mInput, x + src_x, y + src_y);
+    uchar pv = rsGetElementAtYuv_uchar_V(mInput, x + src_x, y + src_y);
+
+    yuvx_444 yuv = { py, pu, pv };
+
+    return yuv;
+}
+
diff --git a/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs b/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
new file mode 100644
index 0000000..052052f
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/means_yuvx_444_1d_to_single.rs
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.rs"
+
+// #define LOG_DEBUG 1
+
+// Must be yuvx_444 (interleaved yuv)
+rs_allocation mInput;
+
+uint32_t width;
+float inv_width; // must be set to 1/w
+
+// Average 1D array -> Single element
+// Input: mInput must be yuvx_444
+yuvx_444 RS_KERNEL means_yuvx_444(void) {
+
+    uint3 sum  = { 0, 0, 0 };
+
+    LOGD("width", width);
+
+    for (uint32_t i = 0; i < width; ++i) {
+        yuvx_444 elem = rsGetElementAt_yuvx_444(mInput, i);
+
+        LOGD("elem", elem);
+        LOGD("i", i);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
diff --git a/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs b/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
new file mode 100644
index 0000000..7dc4e0d
--- /dev/null
+++ b/tests/src/android/hardware/camera2/cts/means_yuvx_444_2d_to_1d.rs
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "common.rs"
+
+// #define LOG_DEBUG 1
+
+// Must be YUV 420 888 (flexible YUV)
+rs_allocation mInput;
+
+uint32_t width;
+float inv_width; // 1/w
+uint32_t src_x; // x-offset from mInput
+uint32_t src_y; // y-offset from mInput
+
+// Average a 2D array -> 1D array (by each row)
+// Input: mInput must be yuvf_420
+yuvx_444 RS_KERNEL means_yuvf_420(uint32_t x) {
+
+    LOGD("x", x);
+
+    uint3 sum = { 0, 0, 0 };
+
+    for (uint32_t i = 0; i < width; ++i) {
+        uchar py = rsGetElementAtYuv_uchar_Y(mInput, src_x + i, src_y + x);
+        uchar pu = rsGetElementAtYuv_uchar_U(mInput, src_x + i, src_y + x);
+        uchar pv = rsGetElementAtYuv_uchar_V(mInput, src_x + i, src_y + x);
+
+        yuvx_444 elem = { py, pu, pv };
+
+        LOGD("elem", elem);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
+// Average a 2D array -> 1D array (by each row)
+// Input: mInput must be yuvx_444
+yuvx_444 RS_KERNEL means_yuvx_444(uint32_t x) {
+
+    LOGD("x", x);
+
+    uint3 sum = { 0, 0, 0 };
+
+    for (uint32_t i = 0; i < width; ++i) {
+        yuvx_444 elem = rsGetElementAt_yuvx_444(mInput, src_x + i, src_y + x);
+
+        LOGD("elem", elem);
+
+        sum.x += elem.x;
+        sum.y += elem.y;
+        sum.z += elem.z;
+    }
+
+    sum.x *= inv_width; // multiply by 1/w
+    sum.y *= inv_width; // multiply by 1/w
+    sum.z *= inv_width; // multiply by 1/w
+
+    yuvx_444 avg = convert_yuvx_444(sum);
+
+    return avg;
+}
+
diff --git a/tests/src/android/webkit/cts/WebViewOnUiThread.java b/tests/src/android/webkit/cts/WebViewOnUiThread.java
index f638ba8..c1032a6 100644
--- a/tests/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/tests/src/android/webkit/cts/WebViewOnUiThread.java
@@ -387,6 +387,7 @@
      * onNewPicture and onProgressChange to reach 100.
      * Test fails if the load timeout elapses.
      * @param url The URL to load.
+     * @param extraHeaders The additional headers to be used in the HTTP request.
      */
     public void loadUrlAndWaitForCompletion(final String url,
             final Map<String, String> extraHeaders) {
@@ -416,6 +417,15 @@
         });
     }
 
+    public void postUrlAndWaitForCompletion(final String url, final byte[] postData) {
+        callAndWait(new Runnable() {
+            @Override
+            public void run() {
+                mWebView.postUrl(url, postData);
+            }
+        });
+    }
+
     public void loadDataAndWaitForCompletion(final String data,
             final String mimeType, final String encoding) {
         callAndWait(new Runnable() {
@@ -694,6 +704,15 @@
         });
     }
 
+    public WebView createWebView() {
+        return getValue(new ValueGetter<WebView>() {
+            @Override
+            public WebView capture() {
+                return new WebView(mWebView.getContext());
+            }
+        });
+    }
+
     public PrintDocumentAdapter createPrintDocumentAdapter() {
         return getValue(new ValueGetter<PrintDocumentAdapter>() {
             @Override
diff --git a/tests/systemAppTest/test/Android.mk b/tests/systemAppTest/test/Android.mk
index 3354886..9be491c 100644
--- a/tests/systemAppTest/test/Android.mk
+++ b/tests/systemAppTest/test/Android.mk
@@ -24,8 +24,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_APPS_PRIVILEGED)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/acceleration/Android.mk b/tests/tests/acceleration/Android.mk
index 30a1f51..d417371 100644
--- a/tests/tests/acceleration/Android.mk
+++ b/tests/tests/acceleration/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/acceleration/AndroidManifest.xml b/tests/tests/acceleration/AndroidManifest.xml
index d08827e..0dd2722 100644
--- a/tests/tests/acceleration/AndroidManifest.xml
+++ b/tests/tests/acceleration/AndroidManifest.xml
@@ -24,8 +24,11 @@
       <uses-library android:name="android.test.runner" />
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="com.android.cts.acceleration.stub"
-                   android:label="Tests for the Hardware Acceleration APIs." />
+                   android:label="Tests for the Hardware Acceleration APIs." >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibility/Android.mk b/tests/tests/accessibility/Android.mk
index abd6f4b..9f98b16 100644
--- a/tests/tests/accessibility/Android.mk
+++ b/tests/tests/accessibility/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccessibilityTestCases
diff --git a/tests/tests/accessibility/AndroidManifest.xml b/tests/tests/accessibility/AndroidManifest.xml
index 53b9cc3..319fb49 100644
--- a/tests/tests/accessibility/AndroidManifest.xml
+++ b/tests/tests/accessibility/AndroidManifest.xml
@@ -24,8 +24,11 @@
       <uses-library android:name="android.test.runner"/>
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="android.view.cts.accessibility"
-                   android:label="Tests for the accessibility APIs."/>
+                   android:label="Tests for the accessibility APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
index 3aaf54e..7862cb4 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityRecordTest.java
@@ -204,7 +204,7 @@
     static void assertNoNewNonStaticFieldsAdded(Class<?> clazz, int expectedCount) {
         int nonStaticFieldCount = 0;
 
-        while (clazz != null) {
+        while (clazz != Object.class) {
             for (Field field : clazz.getDeclaredFields()) {
                 if ((field.getModifiers() & Modifier.STATIC) == 0) {
                     nonStaticFieldCount++;
diff --git a/tests/tests/accessibilityservice/Android.mk b/tests/tests/accessibilityservice/Android.mk
index 73cd288..b27dbcc 100644
--- a/tests/tests/accessibilityservice/Android.mk
+++ b/tests/tests/accessibilityservice/Android.mk
@@ -18,12 +18,12 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccessibilityServiceTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/accessibilityservice/AndroidManifest.xml b/tests/tests/accessibilityservice/AndroidManifest.xml
index ac75553..7c22b96 100644
--- a/tests/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/tests/accessibilityservice/AndroidManifest.xml
@@ -41,12 +41,15 @@
               android:name="android.accessibilityservice.cts.AccessibilityFocusAndInputFocusSyncActivity"/>
 
       <activity android:label="@string/accessibility_text_traversal_test_activity"
-              android:name="android.accessibilityservice.cts.AccessibilityTextTraversalActivity"/> 
+              android:name="android.accessibilityservice.cts.AccessibilityTextTraversalActivity"/>
 
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="com.android.cts.accessibilityservice"
-                   android:label="Tests for the accessibility APIs."/>
+                   android:label="Tests for the accessibility APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
index 7e05e32..81db5be 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEndToEndTest.java
@@ -46,10 +46,8 @@
 
     /**
      * Creates a new instance for testing {@link AccessibilityEndToEndActivity}.
-     *
-     * @throws Exception If any error occurs.
      */
-    public AccessibilityEndToEndTest() throws Exception {
+    public AccessibilityEndToEndTest() {
         super(AccessibilityEndToEndActivity.class);
     }
 
diff --git a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
index 1632489..f01ae7b 100644
--- a/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
+++ b/tests/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowQueryTest.java
@@ -31,6 +31,7 @@
 import android.test.suitebuilder.annotation.MediumTest;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityWindowInfo;
 
 import com.android.cts.accessibilityservice.R;
 
@@ -47,6 +48,8 @@
 public class AccessibilityWindowQueryTest
         extends AccessibilityActivityTestCase<AccessibilityWindowQueryActivity> {
 
+    private static final long TIMEOUT_WINDOW_STATE_IDLE = 500;
+
     public AccessibilityWindowQueryTest() {
         super(AccessibilityWindowQueryActivity.class);
     }
@@ -70,55 +73,326 @@
 
     @MediumTest
     public void testTraverseWindow() throws Exception {
-        try {
-            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
-            info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            getInstrumentation().getUiAutomation().setServiceInfo(info);
+        verifyNodesInAppWindow(getInstrumentation().getUiAutomation().getRootInActiveWindow());
+    }
 
-            // make list of expected nodes
-            List<String> classNameAndTextList = new ArrayList<String>();
-            classNameAndTextList.add("android.widget.FrameLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.FrameLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.LinearLayout");
-            classNameAndTextList.add("android.widget.ButtonB1");
-            classNameAndTextList.add("android.widget.ButtonB2");
-            classNameAndTextList.add("android.widget.ButtonB3");
-            classNameAndTextList.add("android.widget.ButtonB4");
-            classNameAndTextList.add("android.widget.ButtonB5");
-            classNameAndTextList.add("android.widget.ButtonB6");
-            classNameAndTextList.add("android.widget.ButtonB7");
-            classNameAndTextList.add("android.widget.ButtonB8");
-            classNameAndTextList.add("android.widget.ButtonB9");
+    @MediumTest
+    public void testNoWindowsAccessIfFlagNotSet() throws Exception {
+        // Make sure the windows cannot be accessed.
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        assertTrue(uiAutomation.getWindows().isEmpty());
 
-            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
-            fringe.add(getInstrumentation().getUiAutomation().getRootInActiveWindow());
+        // Find a button to click on.
+        final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                .findAccessibilityNodeInfosByViewId(
+                        "com.android.cts.accessibilityservice:id/button1").get(0);
 
-            // do a BFS traversal and check nodes
-            while (!fringe.isEmpty()) {
-                AccessibilityNodeInfo current = fringe.poll();
+        // Argh...
+        final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
 
-                CharSequence text = current.getText();
-                String receivedClassNameAndText = current.getClassName().toString()
-                    + ((text != null) ? text.toString() : "");
-                String expectedClassNameAndText = classNameAndTextList.remove(0);
-
-                assertEquals("Did not get the expected node info",
-                        expectedClassNameAndText, receivedClassNameAndText);
-
-                final int childCount = current.getChildCount();
-                for (int i = 0; i < childCount; i++) {
-                    AccessibilityNodeInfo child = current.getChild(i);
-                    fringe.add(child);
-                }
+        // Click the button.
+        uiAutomation.executeAndWaitForEvent(new Runnable() {
+            @Override
+            public void run() {
+                button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
             }
+        },
+        new UiAutomation.AccessibilityEventFilter() {
+            @Override
+            public boolean accept(AccessibilityEvent event) {
+                if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                    events.add(event);
+                    return true;
+                }
+                return false;
+            }
+        },
+        TIMEOUT_ASYNC_PROCESSING);
+
+        // Make sure the source window cannot be accessed.
+        AccessibilityEvent event = events.get(0);
+        assertNull(event.getSource().getWindow());
+    }
+
+    @MediumTest
+    public void testTraverseAllWindows() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            final UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            getInstrumentation().getUiAutomation().waitForIdle(
+                    TIMEOUT_WINDOW_STATE_IDLE,
+                    TIMEOUT_ASYNC_PROCESSING);
+
+            List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+
+            Rect boundsInScreen = new Rect();
+
+            // Verify the navigation bar window.
+            AccessibilityWindowInfo navBarWindow = windows.get(0);
+            navBarWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(navBarWindow.getType(), AccessibilityWindowInfo.TYPE_SYSTEM);
+            assertFalse(navBarWindow.isFocused());
+            assertFalse(navBarWindow.isActive());
+            assertNull(navBarWindow.getParent());
+            assertSame(0, navBarWindow.getChildCount());
+            assertNotNull(navBarWindow.getRoot());
+
+            // Verify the status bar window.
+            AccessibilityWindowInfo statusBarWindow = windows.get(1);
+            statusBarWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(statusBarWindow.getType(), AccessibilityWindowInfo.TYPE_SYSTEM);
+            assertFalse(statusBarWindow.isFocused());
+            assertFalse(statusBarWindow.isActive());
+            assertNull(statusBarWindow.getParent());
+            assertSame(0, statusBarWindow.getChildCount());
+            assertNotNull(statusBarWindow.getRoot());
+
+            // Verify the application window.
+            AccessibilityWindowInfo appWindow = windows.get(2);
+            appWindow.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, just emptiness check.
+            assertSame(appWindow.getType(), AccessibilityWindowInfo.TYPE_APPLICATION);
+            assertTrue(appWindow.isFocused());
+            assertTrue(appWindow.isActive());
+            assertNull(appWindow.getParent());
+            assertSame(0, appWindow.getChildCount());
+            assertNotNull(appWindow.getRoot());
+
+            verifyNodesInAppWindow(appWindow.getRoot());
         } finally {
-            AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
-            info.flags &= ~AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS;
-            getInstrumentation().getUiAutomation().setServiceInfo(info);
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testTraverseWindowFromEvent() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Find a button to click on.
+            final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button1").get(0);
+
+            // Argh...
+            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
+
+            // Click the button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                        events.add(event);
+                        return true;
+                    }
+                    return false;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+
+            // Get the source window.
+            AccessibilityEvent event = events.get(0);
+            AccessibilityWindowInfo window = event.getSource().getWindow();
+
+            // Verify the application window.
+            Rect boundsInScreen = new Rect();
+            window.getBoundsInScreen(boundsInScreen);
+            assertFalse(boundsInScreen.isEmpty()); // Varies on screen size, so just emptiness check.
+            assertSame(window.getType(), AccessibilityWindowInfo.TYPE_APPLICATION);
+            assertTrue(window.isFocused());
+            assertTrue(window.isActive());
+            assertNull(window.getParent());
+            assertSame(0, window.getChildCount());
+            assertNotNull(window.getRoot());
+
+            // Verify the window content.
+            verifyNodesInAppWindow(window.getRoot());
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithAppWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Find a button to click on.
+            final AccessibilityNodeInfo button1 = uiAutomation.getRootInActiveWindow()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button1").get(0);
+
+            // Argh...
+            final List<AccessibilityEvent> events = new ArrayList<AccessibilityEvent>();
+
+            // Click the button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button1.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
+                        events.add(event);
+                        return true;
+                    }
+                    return false;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+
+            // Get the source window.
+            AccessibilityEvent event = events.get(0);
+            AccessibilityWindowInfo window = event.getSource().getWindow();
+
+            // Find a another button from the event's window.
+            final AccessibilityNodeInfo button2 = window.getRoot()
+                    .findAccessibilityNodeInfosByViewId(
+                            "com.android.cts.accessibilityservice:id/button2").get(0);
+
+            // Click the second button.
+            uiAutomation.executeAndWaitForEvent(new Runnable() {
+                @Override
+                public void run() {
+                    button2.performAction(AccessibilityNodeInfo.ACTION_CLICK);
+                }
+            },
+            new UiAutomation.AccessibilityEventFilter() {
+                @Override
+                public boolean accept(AccessibilityEvent event) {
+                    return event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED;
+                }
+            },
+            TIMEOUT_ASYNC_PROCESSING);
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithNavBarWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+            AccessibilityWindowInfo window = uiAutomation.getWindows().get(0);
+            assertTrue(window.getRoot().performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testInteractWithStatusBarWindow() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+            AccessibilityWindowInfo window = uiAutomation.getWindows().get(1);
+            assertTrue(window.getRoot().performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
+        }
+    }
+
+    @MediumTest
+    public void testSingleAccessibilityFocusAcrossWindows() throws Exception {
+        setAccessInteractiveWindowsFlag();
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            List<AccessibilityWindowInfo> windows = uiAutomation.getWindows();
+
+            AccessibilityNodeInfo firstWindowRoot = windows.get(0).getRoot();
+            AccessibilityNodeInfo secondWindowRoot = windows.get(1).getRoot();
+            AccessibilityNodeInfo thirdWindowRoot = windows.get(2).getRoot();
+
+
+            // Set focus in the first window.
+            assertTrue(firstWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), firstWindowRoot);
+            assertEquals(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), firstWindowRoot);
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Set focus in the second window.
+            assertTrue(secondWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), secondWindowRoot);
+            assertEquals(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), secondWindowRoot);
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Set focus in the third window.
+            assertTrue(thirdWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS));
+
+            // Wait for things to settle.
+            uiAutomation.waitForIdle(TIMEOUT_WINDOW_STATE_IDLE, TIMEOUT_ASYNC_PROCESSING);
+
+            // Make sure there only one accessibility focus.
+            assertEquals(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), thirdWindowRoot);
+            assertEquals(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY), thirdWindowRoot);
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+
+
+            // Clear focus.
+            assertTrue(thirdWindowRoot.performAction(
+                    AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS));
+
+            // Make sure there is not accessibility focus.
+            assertNull(uiAutomation.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(firstWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(secondWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+            assertNull(thirdWindowRoot.findFocus(
+                    AccessibilityNodeInfo.FOCUS_ACCESSIBILITY));
+        } finally {
+            clearAccessInteractiveWindowsFlag();
         }
     }
 
@@ -420,6 +694,20 @@
         }
     }
 
+    private void setAccessInteractiveWindowsFlag () {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        uiAutomation.setServiceInfo(info);
+    }
+
+    private void clearAccessInteractiveWindowsFlag () {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+        AccessibilityServiceInfo info = uiAutomation.getServiceInfo();
+        info.flags &= ~AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        uiAutomation.setServiceInfo(info);
+    }
+
     private void verifyNodesInAppWindow(AccessibilityNodeInfo root) throws Exception {
         try {
             AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
diff --git a/tests/tests/accounts/Android.mk b/tests/tests/accounts/Android.mk
index e4536d4..39dbfb1 100644
--- a/tests/tests/accounts/Android.mk
+++ b/tests/tests/accounts/Android.mk
@@ -21,12 +21,12 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES += android-common ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsAccountManagerTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index 6020636..cf7f7d8 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -39,9 +39,12 @@
         </service>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="android.accounts.cts"
-                     android:label="CTS tests for android.accounts"/>
+                     android:label="CTS tests for android.accounts">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/admin/Android.mk b/tests/tests/admin/Android.mk
index c3645cc..7a5ae34 100644
--- a/tests/tests/admin/Android.mk
+++ b/tests/tests/admin/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner mockito-target
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/admin/AndroidManifest.xml b/tests/tests/admin/AndroidManifest.xml
index 7ce29aa..bbd7918 100644
--- a/tests/tests/admin/AndroidManifest.xml
+++ b/tests/tests/admin/AndroidManifest.xml
@@ -26,8 +26,11 @@
 
   </application>
 
-  <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+  <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                    android:targetPackage="android.deviceadmin.cts"
-                   android:label="Tests for the admin APIs."/>
+                   android:label="Tests for the admin APIs.">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/animation/Android.mk b/tests/tests/animation/Android.mk
index a83bb65..3d8daf7 100644
--- a/tests/tests/animation/Android.mk
+++ b/tests/tests/animation/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/animation/AndroidManifest.xml b/tests/tests/animation/AndroidManifest.xml
index 2212643..fdc5ad9 100644
--- a/tests/tests/animation/AndroidManifest.xml
+++ b/tests/tests/animation/AndroidManifest.xml
@@ -27,8 +27,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.animation"
-                     android:label="CTS tests for android.animation package"/>
+                     android:label="CTS tests for android.animation package">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/app/AndroidManifest.xml b/tests/tests/app/AndroidManifest.xml
index acfc3c8..134df64 100644
--- a/tests/tests/app/AndroidManifest.xml
+++ b/tests/tests/app/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.app"/>
+                     android:label="CTS tests of android.app">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/bluetooth/Android.mk b/tests/tests/bluetooth/Android.mk
index 701730d..4f837fc 100644
--- a/tests/tests/bluetooth/Android.mk
+++ b/tests/tests/bluetooth/Android.mk
@@ -18,16 +18,12 @@
 
 LOCAL_PACKAGE_NAME := CtsBluetoothTestCases
 
-
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
 
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/bluetooth/AndroidManifest.xml b/tests/tests/bluetooth/AndroidManifest.xml
index 9caa267..c9ad122 100644
--- a/tests/tests/bluetooth/AndroidManifest.xml
+++ b/tests/tests/bluetooth/AndroidManifest.xml
@@ -26,9 +26,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.bluetooth"
-                     android:label="CTS tests of bluetooth component"/>
+                     android:label="CTS tests of bluetooth component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/calendarcommon/Android.mk b/tests/tests/calendarcommon/Android.mk
index c825c32..fa4b6c5 100644
--- a/tests/tests/calendarcommon/Android.mk
+++ b/tests/tests/calendarcommon/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/calendarcommon/AndroidManifest.xml b/tests/tests/calendarcommon/AndroidManifest.xml
index dc95af5..17520a3 100644
--- a/tests/tests/calendarcommon/AndroidManifest.xml
+++ b/tests/tests/calendarcommon/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.calendarcommon2"
-                     android:label="CTS tests of calendarcommon"/>
+                     android:label="CTS tests of calendarcommon">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15"></uses-sdk>
 
diff --git a/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java b/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
index a17e3b8..24a04a5 100644
--- a/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
+++ b/tests/tests/calendarcommon/src/android/calendarcommon2/cts/Calendarcommon2Test.java
@@ -16,7 +16,6 @@
 
 package android.calendarcommon2.cts;
 
-import android.test.InstrumentationCtsTestRunner;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import com.android.calendarcommon2.RecurrenceSet;
diff --git a/tests/tests/content/AndroidManifest.xml b/tests/tests/content/AndroidManifest.xml
index 5af05eb..8d57e49 100644
--- a/tests/tests/content/AndroidManifest.xml
+++ b/tests/tests/content/AndroidManifest.xml
@@ -39,8 +39,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.content"/>
+                     android:label="CTS tests of android.content">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/content/src/android/content/res/cts/ConfigTest.java b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
index bcfabe1..2f892b0 100644
--- a/tests/tests/content/src/android/content/res/cts/ConfigTest.java
+++ b/tests/tests/content/src/android/content/res/cts/ConfigTest.java
@@ -35,6 +35,8 @@
     enum Properties {
         LANGUAGE,
         COUNTRY,
+        SCRIPT,
+        VARIANT,
         MCC,
         MNC,
         TOUCHSCREEN,
@@ -88,7 +90,7 @@
         public TotalConfig() {
             mConfig = new Configuration();
             mMetrics = new DisplayMetrics();
-            mConfig.locale = new Locale("++", "++");
+            mConfig.locale = Locale.ROOT;
         }
 
         public void setProperty(final Properties p, final int value) {
@@ -149,12 +151,28 @@
         public void setProperty(final Properties p, final String value) {
             switch(p) {
                 case LANGUAGE:
-                    final String oldCountry = mConfig.locale.getCountry();
-                    mConfig.locale = new Locale(value, oldCountry);
+                    mConfig.locale = new Locale.Builder()
+                            .setLocale(mConfig.locale)
+                            .setLanguage(value)
+                            .build();
                     break;
                 case COUNTRY:
-                    final String oldLanguage = mConfig.locale.getLanguage();
-                    mConfig.locale = new Locale(oldLanguage, value);
+                    mConfig.locale = new Locale.Builder()
+                            .setLocale(mConfig.locale)
+                            .setRegion(value)
+                            .build();
+                    break;
+                case SCRIPT:
+                    mConfig.locale = new Locale.Builder()
+                            .setLocale(mConfig.locale)
+                            .setScript(value)
+                            .build();
+                    break;
+                case VARIANT:
+                    mConfig.locale = new Locale.Builder()
+                            .setLocale(mConfig.locale)
+                            .setVariant(value)
+                            .build();
                     break;
                 default:
                     assert(false);
@@ -1131,4 +1149,69 @@
         assertEquals("base",  mContext.getResources().getString(R.string.version_old));
         assertEquals("v3",  mContext.getResources().getString(R.string.version_v3));
     }
+
+    @MediumTest
+    public void testExtendedLocales() {
+        TotalConfig config = makeClassicConfig();
+        // BCP 47 Locale kok
+        config.setProperty(Properties.LANGUAGE, "kok");
+        Resources res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok"});
+
+        // BCP 47 Locale kok-IN
+        config.setProperty(Properties.COUNTRY, "IN");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok IN");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok IN"});
+
+        // BCP 47 Locale kok-419
+        config.setProperty(Properties.COUNTRY, "419");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok 419");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok 419"});
+
+
+        // BCP 47 Locale kok-419-VARIANT
+        config.setProperty(Properties.VARIANT, "VARIANT");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok 419 VARIANT");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok 419 VARIANT"});
+
+        // BCP 47 Locale kok-Knda
+        config = makeClassicConfig();
+        config.setProperty(Properties.LANGUAGE, "kok");
+        config.setProperty(Properties.SCRIPT, "Knda");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok Knda");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok Knda"});
+
+        // BCP 47 Locale kok-Knda-419
+        config.setProperty(Properties.COUNTRY, "419");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok Knda 419");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok Knda 419"});
+
+        // BCP 47 Locale kok-Knda-419-VARIANT
+        config.setProperty(Properties.VARIANT, "VARIANT");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok Knda 419 VARIANT");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok Knda 419 VARIANT"});
+
+        // BCP 47 Locale kok-VARIANT
+        config = makeClassicConfig();
+        config.setProperty(Properties.LANGUAGE, "kok");
+        config.setProperty(Properties.VARIANT, "VARIANT");
+        res = config.getResources();
+        checkValue(res, R.configVarying.simple, "simple kok VARIANT");
+        checkValue(res, R.configVarying.bag,
+                R.styleable.TestConfig, new String[]{"bag kok VARIANT"});
+    }
 }
diff --git a/tests/tests/database/AndroidManifest.xml b/tests/tests/database/AndroidManifest.xml
index 602f783..fefcc1f 100644
--- a/tests/tests/database/AndroidManifest.xml
+++ b/tests/tests/database/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.database"
-                     android:label="CTS tests of android.database"/>
+                     android:label="CTS tests of android.database">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/display/Android.mk b/tests/tests/display/Android.mk
index ec5b40d..a48a8e3 100644
--- a/tests/tests/display/Android.mk
+++ b/tests/tests/display/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/display/AndroidManifest.xml b/tests/tests/display/AndroidManifest.xml
index d1386d1..0b24754 100644
--- a/tests/tests/display/AndroidManifest.xml
+++ b/tests/tests/display/AndroidManifest.xml
@@ -24,9 +24,13 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.display"
-                     android:label="CTS tests of android.view.display"/>
+                     android:label="CTS tests of android.view.display">
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/dpi/Android.mk b/tests/tests/dpi/Android.mk
index a9dbcc3..fde990b 100644
--- a/tests/tests/dpi/Android.mk
+++ b/tests/tests/dpi/Android.mk
@@ -17,8 +17,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -44,8 +42,8 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_MODULE := android.cts.dpi
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/dpi/AndroidManifest.xml b/tests/tests/dpi/AndroidManifest.xml
index bacfe4a..0197056 100644
--- a/tests/tests/dpi/AndroidManifest.xml
+++ b/tests/tests/dpi/AndroidManifest.xml
@@ -26,7 +26,10 @@
                 android:configChanges="orientation" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dpi"
-                     android:label="CTS tests for DPI"/>
+                     android:label="CTS tests for DPI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/tests/dpi2/Android.mk b/tests/tests/dpi2/Android.mk
index 92ba992..03a687d 100644
--- a/tests/tests/dpi2/Android.mk
+++ b/tests/tests/dpi2/Android.mk
@@ -17,7 +17,6 @@
 
 include $(CLEAR_VARS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
 # We use the DefaultManifestAttributesTest from the android.cts.dpi package.
 LOCAL_STATIC_JAVA_LIBRARIES := android.cts.dpi ctstestrunner
 
@@ -29,8 +28,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# We would set LOCAL_SDK_VERSION := 3 here, but the build system
-# doesn't currently support setting LOCAL_SDK_VERSION to anything but
-# current.
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dpi2/AndroidManifest.xml b/tests/tests/dpi2/AndroidManifest.xml
index 0364b10..6dbdc23 100644
--- a/tests/tests/dpi2/AndroidManifest.xml
+++ b/tests/tests/dpi2/AndroidManifest.xml
@@ -27,7 +27,10 @@
          properly for the screen size attributes. -->
     <uses-sdk android:targetSdkVersion="3" />
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dpi2"
-                     android:label="CTS tests for DPI"/>
+                     android:label="CTS tests for DPI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
diff --git a/tests/tests/dreams/Android.mk b/tests/tests/dreams/Android.mk
index c630d8a..eca3d83 100644
--- a/tests/tests/dreams/Android.mk
+++ b/tests/tests/dreams/Android.mk
@@ -24,14 +24,11 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-# Need access to ServiceManager
+# Need access to ServiceManager - see b/13307221
 #LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/dreams/AndroidManifest.xml b/tests/tests/dreams/AndroidManifest.xml
index fb3e564..b395a4f 100644
--- a/tests/tests/dreams/AndroidManifest.xml
+++ b/tests/tests/dreams/AndroidManifest.xml
@@ -22,9 +22,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.dreams"
-                     android:label="CTS tests for the android.service.dreams package"/>
+                     android:label="CTS tests for the android.service.dreams package">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/drm/Android.mk b/tests/tests/drm/Android.mk
index 8b76cd8..9404f4b 100644
--- a/tests/tests/drm/Android.mk
+++ b/tests/tests/drm/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -35,8 +33,7 @@
 	libctsdrm_jni \
 	libdrmtestplugin
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/drm/AndroidManifest.xml b/tests/tests/drm/AndroidManifest.xml
index 1fc8968..dd70f02 100644
--- a/tests/tests/drm/AndroidManifest.xml
+++ b/tests/tests/drm/AndroidManifest.xml
@@ -22,9 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.drm"/>
+                     android:label="CTS tests of android.drm">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/effect/Android.mk b/tests/tests/effect/Android.mk
index 9e27769..6a9778e 100644
--- a/tests/tests/effect/Android.mk
+++ b/tests/tests/effect/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/effect/AndroidManifest.xml b/tests/tests/effect/AndroidManifest.xml
index 1a346ae..481be14 100644
--- a/tests/tests/effect/AndroidManifest.xml
+++ b/tests/tests/effect/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.effect"
-                     android:label="CTS tests of android.media.effect component"/>
+                     android:label="CTS tests of android.media.effect component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/gesture/Android.mk b/tests/tests/gesture/Android.mk
index 5d44cfc..4a97931 100755
--- a/tests/tests/gesture/Android.mk
+++ b/tests/tests/gesture/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/gesture/AndroidManifest.xml b/tests/tests/gesture/AndroidManifest.xml
index 39e2b90..b288cd2 100755
--- a/tests/tests/gesture/AndroidManifest.xml
+++ b/tests/tests/gesture/AndroidManifest.xml
@@ -24,9 +24,12 @@
     </application>
 
     <!--  self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.gesture"
-                     android:label="CTS tests of android.gesture"/>
+                     android:label="CTS tests of android.gesture">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/graphics/Android.mk b/tests/tests/graphics/Android.mk
index 811267a..cad81b2 100644
--- a/tests/tests/graphics/Android.mk
+++ b/tests/tests/graphics/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,7 +29,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/graphics/AndroidManifest.xml b/tests/tests/graphics/AndroidManifest.xml
index c052a15..0371093 100644
--- a/tests/tests/graphics/AndroidManifest.xml
+++ b/tests/tests/graphics/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.graphics"/>
+                     android:label="CTS tests of android.graphics">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index 91d827c..edb8d73 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -39,7 +39,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.RandomAccessFile;
 
 public class BitmapFactoryTest extends InstrumentationTestCase {
     private Resources mRes;
@@ -58,10 +58,6 @@
             R.drawable.baseline_jpeg, R.drawable.png_test, R.drawable.gif_test,
             R.drawable.bmp_test, R.drawable.webp_test
     };
-    private static String[] NAMES_TEMP_FILES = new String[] {
-        "baseline_temp.jpg", "png_temp.png", "gif_temp.gif",
-        "bmp_temp.bmp", "webp_temp.webp"
-    };
 
     // The width and height of the above image.
     private static int WIDTHS[] = new int[] { 1280, 640, 320, 320, 640 };
@@ -72,6 +68,10 @@
         Config.ARGB_4444};
     private static int[] COLOR_TOLS = new int[] {16, 49, 576};
 
+    private static Config[] COLOR_CONFIGS_RGBA = new Config[] {Config.ARGB_8888,
+        Config.ARGB_4444};
+    private static int[] COLOR_TOLS_RGBA = new int[] {72, 124};
+
     private static int[] RAW_COLORS = new int[] {
         // raw data from R.drawable.premul_data
         Color.argb(255, 0, 0, 0),
@@ -200,11 +200,15 @@
             Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
             assertNotNull(bPng);
             assertEquals(bPng.getConfig(), COLOR_CONFIGS[k]);
+            assertFalse(bPng.isPremultiplied());
+            assertFalse(bPng.hasAlpha());
 
             InputStream iStreamWebp1 = obtainInputStream(R.drawable.webp_test);
             Bitmap bWebp1 = BitmapFactory.decodeStream(iStreamWebp1, null, options);
             assertNotNull(bWebp1);
-            compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true);
+            assertFalse(bWebp1.isPremultiplied());
+            assertFalse(bWebp1.hasAlpha());
+            compareBitmaps(bPng, bWebp1, COLOR_TOLS[k], true, bPng.isPremultiplied());
 
             // Compress the PNG image to WebP format (Quality=90) and decode it back.
             // This will test end-to-end WebP encoding and decoding.
@@ -213,7 +217,46 @@
             InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
             Bitmap bWebp2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
             assertNotNull(bWebp2);
-            compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true);
+            assertFalse(bWebp2.isPremultiplied());
+            assertFalse(bWebp2.hasAlpha());
+            compareBitmaps(bPng, bWebp2, COLOR_TOLS[k], true, bPng.isPremultiplied());
+        }
+    }
+
+    public void testDecodeStream5() throws IOException {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        for (int k = 0; k < COLOR_CONFIGS_RGBA.length; ++k) {
+            options.inPreferredConfig = COLOR_CONFIGS_RGBA[k];
+
+            // Decode the PNG & WebP (google_logo) images. The WebP image has
+            // been encoded from PNG image.
+            InputStream iStreamPng = obtainInputStream(R.drawable.google_logo_1);
+            Bitmap bPng = BitmapFactory.decodeStream(iStreamPng, null, options);
+            assertNotNull(bPng);
+            assertEquals(bPng.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bPng.isPremultiplied());
+            assertTrue(bPng.hasAlpha());
+
+            // Decode the corresponding WebP (transparent) image (google_logo_2.webp).
+            InputStream iStreamWebP1 = obtainInputStream(R.drawable.google_logo_2);
+            Bitmap bWebP1 = BitmapFactory.decodeStream(iStreamWebP1, null, options);
+            assertNotNull(bWebP1);
+            assertEquals(bWebP1.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bWebP1.isPremultiplied());
+            assertTrue(bWebP1.hasAlpha());
+            compareBitmaps(bPng, bWebP1, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
+
+            // Compress the PNG image to WebP format (Quality=90) and decode it back.
+            // This will test end-to-end WebP encoding and decoding.
+            ByteArrayOutputStream oStreamWebp = new ByteArrayOutputStream();
+            assertTrue(bPng.compress(CompressFormat.WEBP, 90, oStreamWebp));
+            InputStream iStreamWebp2 = new ByteArrayInputStream(oStreamWebp.toByteArray());
+            Bitmap bWebP2 = BitmapFactory.decodeStream(iStreamWebp2, null, options);
+            assertNotNull(bWebP2);
+            assertEquals(bWebP2.getConfig(), COLOR_CONFIGS_RGBA[k]);
+            assertTrue(bWebP2.isPremultiplied());
+            assertTrue(bWebP2.hasAlpha());
+            compareBitmaps(bPng, bWebP2, COLOR_TOLS_RGBA[k], true, bPng.isPremultiplied());
         }
     }
 
@@ -240,6 +283,50 @@
         assertEquals(START_WIDTH, b.getWidth());
     }
 
+    public void testDecodeFileDescriptor3() throws IOException {
+        // Arbitrary offsets to use. If the offset of the FD matches the offset of the image,
+        // decoding should succeed, but if they do not match, decoding should fail.
+        long ACTUAL_OFFSETS[] = new long[] { 0, 17 };
+        for (int RES_ID : RES_IDS) {
+            for (int j = 0; j < ACTUAL_OFFSETS.length; ++j) {
+                // FIXME: The purgeable test should attempt to purge the memory
+                // to force a re-decode.
+                for (boolean TEST_PURGEABLE : new boolean[] { false, true }) {
+                    BitmapFactory.Options opts = new BitmapFactory.Options();
+                    opts.inPurgeable = TEST_PURGEABLE;
+                    opts.inInputShareable = TEST_PURGEABLE;
+
+                    long actualOffset = ACTUAL_OFFSETS[j];
+                    String path = obtainPath(RES_ID, actualOffset);
+                    RandomAccessFile file = new RandomAccessFile(path, "r");
+                    FileDescriptor fd = file.getFD();
+                    assertTrue(fd.valid());
+
+                    // Set the offset to ACTUAL_OFFSET
+                    file.seek(actualOffset);
+                    assertEquals(file.getFilePointer(), actualOffset);
+
+                    // Now decode. This should be successful and leave the offset
+                    // unchanged.
+                    Bitmap b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                    assertNotNull(b);
+                    assertEquals(file.getFilePointer(), actualOffset);
+
+                    // Now use the other offset. It should fail to decode, and
+                    // the offset should remain unchanged.
+                    long otherOffset = ACTUAL_OFFSETS[(j + 1) % ACTUAL_OFFSETS.length];
+                    assertFalse(otherOffset == actualOffset);
+                    file.seek(otherOffset);
+                    assertEquals(file.getFilePointer(), otherOffset);
+
+                    b = BitmapFactory.decodeFileDescriptor(fd, null, opts);
+                    assertNull(b);
+                    assertEquals(file.getFilePointer(), otherOffset);
+                }
+            }
+        }
+    }
+
     public void testDecodeFile1() throws IOException {
         Bitmap b = BitmapFactory.decodeFile(obtainPath(), mOpt1);
         assertNotNull(b);
@@ -278,6 +365,50 @@
         assertTrue(pass.isMutable());
     }
 
+    /**
+     * Create bitmap sized to load unscaled resources: start, pass, and alpha
+     */
+    private Bitmap createBitmapForReuse(int pixelCount) {
+        Bitmap bitmap = Bitmap.createBitmap(pixelCount, 1, Config.ARGB_8888);
+        bitmap.eraseColor(Color.BLACK);
+        bitmap.setHasAlpha(false);
+        return bitmap;
+    }
+
+    /**
+     * Decode resource with ResId into reuse bitmap without scaling, verifying expected hasAlpha
+     */
+    private void decodeResourceWithReuse(Bitmap reuse, int resId, boolean hasAlpha) {
+        BitmapFactory.Options options = new BitmapFactory.Options();
+        options.inMutable = true;
+        options.inSampleSize = 1;
+        options.inScaled = false;
+        options.inBitmap = reuse;
+        Bitmap output = BitmapFactory.decodeResource(mRes, resId, options);
+        assertSame(reuse, output);
+        assertEquals(output.hasAlpha(), hasAlpha);
+    }
+
+    public void testDecodeReuseHasAlpha() throws IOException {
+        final int bitmapSize = 31; // size in pixels of start, pass, and alpha resources
+        final int pixelCount = bitmapSize * bitmapSize;
+
+        // Test reuse, hasAlpha false and true
+        Bitmap bitmap = createBitmapForReuse(pixelCount);
+        decodeResourceWithReuse(bitmap, R.drawable.start, false);
+        decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
+
+        // Test pre-reconfigure, hasAlpha false and true
+        bitmap = createBitmapForReuse(pixelCount);
+        bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
+        bitmap.setHasAlpha(true);
+        decodeResourceWithReuse(bitmap, R.drawable.start, false);
+
+        bitmap = createBitmapForReuse(pixelCount);
+        bitmap.reconfigure(bitmapSize, bitmapSize, Config.ARGB_8888);
+        decodeResourceWithReuse(bitmap, R.drawable.alpha, true);
+    }
+
     public void testDecodeReuseFormats() throws IOException {
         // reuse should support all image formats
         for (int i = 0; i < RES_IDS.length; ++i) {
@@ -401,6 +532,47 @@
         assertFalse(purgeableBitmap.getAllocationByteCount() == 0);
     }
 
+    private int mDefaultCreationDensity;
+    private void verifyScaled(Bitmap b) {
+        assertEquals(b.getWidth(), START_WIDTH * 2);
+        assertEquals(b.getDensity(), 2);
+    }
+
+    private void verifyUnscaled(Bitmap b) {
+        assertEquals(b.getWidth(), START_WIDTH);
+        assertEquals(b.getDensity(), mDefaultCreationDensity);
+    }
+
+    public void testDecodeScaling() {
+        BitmapFactory.Options defaultOpt = new BitmapFactory.Options();
+
+        BitmapFactory.Options unscaledOpt = new BitmapFactory.Options();
+        unscaledOpt.inScaled = false;
+
+        BitmapFactory.Options scaledOpt = new BitmapFactory.Options();
+        scaledOpt.inScaled = true;
+        scaledOpt.inDensity = 1;
+        scaledOpt.inTargetDensity = 2;
+
+        mDefaultCreationDensity = Bitmap.createBitmap(1, 1, Config.ARGB_8888).getDensity();
+
+        byte[] bytes = obtainArray();
+
+        verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length));
+        verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, null));
+        verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, unscaledOpt));
+        verifyUnscaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, defaultOpt));
+
+        verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream()));
+        verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, null));
+        verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, unscaledOpt));
+        verifyUnscaled(BitmapFactory.decodeStream(obtainInputStream(), null, defaultOpt));
+
+        // scaling should only occur if Options are passed with inScaled=true
+        verifyScaled(BitmapFactory.decodeByteArray(bytes, 0, bytes.length, scaledOpt));
+        verifyScaled(BitmapFactory.decodeStream(obtainInputStream(), null, scaledOpt));
+    }
+
     private byte[] obtainArray() {
         ByteArrayOutputStream stm = new ByteArrayOutputStream();
         Options opt = new BitmapFactory.Options();
@@ -426,17 +598,32 @@
     }
 
     private String obtainPath() throws IOException {
+        return obtainPath(R.drawable.start, 0);
+    }
+
+    /*
+     * Create a new file and return a path to it.
+     * @param resId Original file. It will be copied into the new file.
+     * @param offset Number of zeroes to write to the new file before the
+     *               copied file. This allows testing decodeFileDescriptor
+     *               with an offset. Must be less than or equal to 1024
+     */
+    private String obtainPath(int resId, long offset) throws IOException {
         File dir = getInstrumentation().getTargetContext().getFilesDir();
         dir.mkdirs();
+        // The suffix does not necessarily represent theactual file type.
         File file = new File(dir, "test.jpg");
         if (!file.createNewFile()) {
             if (!file.exists()) {
                 fail("Failed to create new File!");
             }
         }
-        InputStream is = obtainInputStream();
+        InputStream is = obtainInputStream(resId);
         FileOutputStream fOutput = new FileOutputStream(file);
         byte[] dataBuffer = new byte[1024];
+        // Write a bunch of zeroes before the image.
+        assertTrue(offset <= 1024);
+        fOutput.write(dataBuffer, 0, (int) offset);
         int readLength = 0;
         while ((readLength = is.read(dataBuffer)) != -1) {
             fOutput.write(dataBuffer, 0, readLength);
@@ -450,7 +637,7 @@
     // lessThanMargin is to indicate whether we expect the mean square error
     // to be "less than" or "no less than".
     private void compareBitmaps(Bitmap expected, Bitmap actual,
-            int mseMargin, boolean lessThanMargin) {
+            int mseMargin, boolean lessThanMargin, boolean isPremultiplied) {
         final int width = expected.getWidth();
         final int height = expected.getHeight();
 
@@ -463,30 +650,49 @@
         int[] expectedColors = new int [width * height];
         int[] actualColors = new int [width * height];
 
+        // Bitmap.getPixels() returns colors with non-premultiplied ARGB values.
         expected.getPixels(expectedColors, 0, width, 0, 0, width, height);
         actual.getPixels(actualColors, 0, width, 0, 0, width, height);
 
         for (int row = 0; row < height; ++row) {
             for (int col = 0; col < width; ++col) {
                 int idx = row * width + col;
-                mse += distance(expectedColors[idx], actualColors[idx]);
+                mse += distance(expectedColors[idx], actualColors[idx], isPremultiplied);
             }
         }
         mse /= width * height;
 
         if (lessThanMargin) {
-            assertTrue("MSE too large for normal case: " + mse,
+            assertTrue("MSE " + mse +  "larger than the threshold: " + mseMargin,
                     mse <= mseMargin);
         } else {
-            assertFalse("MSE too small for abnormal case: " + mse,
+            assertFalse("MSE " + mse +  "smaller than the threshold: " + mseMargin,
                     mse <= mseMargin);
         }
     }
 
-    private double distance(int expect, int actual) {
-        final int r = Color.red(actual) - Color.red(expect);
-        final int g = Color.green(actual) - Color.green(expect);
-        final int b = Color.blue(actual) - Color.blue(expect);
-        return r * r + g * g + b * b;
+    private int multiplyAlpha(int color, int alpha) {
+        return (color * alpha + 127) / 255;
+    }
+
+    // For the Bitmap with Alpha, multiply the Alpha values to get the effective
+    // RGB colors and then compute the color-distance.
+    private double distance(int expect, int actual, boolean isPremultiplied) {
+        if (isPremultiplied) {
+            final int a1 = Color.alpha(actual);
+            final int a2 = Color.alpha(expect);
+            final int r = multiplyAlpha(Color.red(actual), a1) -
+                    multiplyAlpha(Color.red(expect), a2);
+            final int g = multiplyAlpha(Color.green(actual), a1) -
+                    multiplyAlpha(Color.green(expect), a2);
+            final int b = multiplyAlpha(Color.blue(actual), a1) -
+                    multiplyAlpha(Color.blue(expect), a2);
+            return r * r + g * g + b * b;
+        } else {
+            final int r = Color.red(actual) - Color.red(expect);
+            final int g = Color.green(actual) - Color.green(expect);
+            final int b = Color.blue(actual) - Color.blue(expect);
+            return r * r + g * g + b * b;
+        }
     }
 }
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
index c981db3..5ef710f 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapRegionDecoderTest.java
@@ -88,7 +88,7 @@
     private int mMseMargin = 3 * (1 * 1);
 
     // MSE margin for WebP Region-Decoding for 'Config.RGB_565' is little bigger.
-    private int mMseMarginWebPConfigRgb565 = 5;
+    private int mMseMarginWebPConfigRgb565 = 8;
 
 
     @Override
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
index 0672db6..26cdbb6 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/DrawableContainerTest.java
@@ -186,10 +186,10 @@
         assertConstantStateNotSet();
         assertNull(mDrawableContainer.getCurrent());
 
+        mDrawableContainer.setConstantState(mDrawableContainerState);
         mDrawableContainer.setColorFilter(null);
         mDrawableContainer.setColorFilter(new ColorFilter());
 
-        mDrawableContainer.setConstantState(mDrawableContainerState);
         MockDrawable dr = new MockDrawable();
         addAndSelectDrawable(dr);
 
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
index f2d7b82..75639c2 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/GradientDrawableTest.java
@@ -18,11 +18,11 @@
 
 import com.android.cts.stub.R;
 
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.graphics.Canvas;
 import android.graphics.ColorFilter;
 import android.graphics.PixelFormat;
@@ -248,6 +248,26 @@
         }
     }
 
+    public void testInflateGradientRadius() throws XmlPullParserException, IOException {
+        Rect parentBounds = new Rect(0, 0, 100, 100);
+        Resources resources = mContext.getResources();
+
+        GradientDrawable gradientDrawable;
+        float radius;
+
+        gradientDrawable = (GradientDrawable) resources.getDrawable(
+                R.drawable.gradientdrawable_radius_base);
+        gradientDrawable.setBounds(parentBounds);
+        radius = gradientDrawable.getGradientRadius();
+        assertEquals(25.0f, radius, 0.0f);
+
+        gradientDrawable = (GradientDrawable) resources.getDrawable(
+                R.drawable.gradientdrawable_radius_parent);
+        gradientDrawable.setBounds(parentBounds);
+        radius = gradientDrawable.getGradientRadius();
+        assertEquals(50.0f, radius, 0.0f);
+    }
+
     public void testGetIntrinsicWidth() {
         GradientDrawable gradientDrawable = new GradientDrawable();
         gradientDrawable.setSize(6, 4);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
index b23c7fa..84cf41b 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/NinePatchDrawableTest.java
@@ -18,8 +18,6 @@
 
 import com.android.cts.stub.R;
 
-import dalvik.annotation.KnownFailure;
-
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -239,9 +237,9 @@
         assertEquals(9, mNinePatchDrawable.getMinimumHeight());
     }
 
-    @KnownFailure("Bug 2834281 - Bitmap#hasAlpha seems to return true for "
-        + "images without alpha.")
-    public void testGetOpacity() {
+    // Known failure: Bug 2834281 - Bitmap#hasAlpha seems to return true for
+    // images without alpha
+    public void suppress_testGetOpacity() {
         assertEquals(PixelFormat.OPAQUE, mNinePatchDrawable.getOpacity());
 
         mNinePatchDrawable = getNinePatchDrawable(R.drawable.ninepatch_1);
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
new file mode 100644
index 0000000..c8a5e24
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ThemedDrawableTest.java
@@ -0,0 +1,149 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import android.annotation.TargetApi;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.graphics.Shader.TileMode;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.GradientDrawable;
+import android.graphics.drawable.LayerDrawable;
+import android.graphics.drawable.NinePatchDrawable;
+import android.graphics.drawable.TouchFeedbackDrawable;
+import android.test.AndroidTestCase;
+import android.util.SparseIntArray;
+import android.view.ContextThemeWrapper;
+import android.view.Gravity;
+
+import com.android.cts.stub.R;
+
+@TargetApi(19)
+public class ThemedDrawableTest extends AndroidTestCase {
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        mContext.setTheme(R.style.Theme_ThemedDrawableTest);
+    }
+
+    public void testBitmapDrawable() {
+        BitmapDrawable d = (BitmapDrawable) mContext.getDrawable(R.drawable.bitmapdrawable_theme);
+
+        internalTestBitmapDrawable(d);
+    }
+
+    private void internalTestBitmapDrawable(BitmapDrawable d) {
+        assertEquals(true, d.hasAntiAlias());
+        assertEquals(true, d.isAutoMirrored());
+        // assertEquals(true, d.hasDither());
+        // assertEquals(true, d.hasFilter());
+        assertEquals(Gravity.TOP, d.getGravity());
+        assertEquals(true, d.hasMipMap());
+        assertNotNull(d.getBitmap());
+        assertEquals(TileMode.MIRROR, d.getTileModeX());
+        assertEquals(TileMode.MIRROR, d.getTileModeY());
+    }
+
+    public void testColorDrawable() {
+        ColorDrawable d = (ColorDrawable) mContext.getDrawable(R.drawable.colordrawable_theme);
+
+        assertEquals(Color.BLACK, d.getColor());
+    }
+
+    public void testGradientDrawable() {
+        GradientDrawable d = (GradientDrawable) mContext.getDrawable(
+                R.drawable.gradientdrawable_theme);
+
+        // Corners
+        // assertEquals(1, d.getCornerRadius(0));
+        // assertEquals(1, d.getCornerRadius(1));
+        // assertEquals(1, d.getCornerRadius(2));
+        // assertEquals(1, d.getCornerRadius(3));
+
+        // Gradient
+        // int[] colors = d.getColors(null);
+        // for (int i = 0; i < color.length; i++) {
+        // assertEquals(Color.BLACK, colors[i]);
+        // }
+        // assertEquals(1.0f, d.getGradientAngle());
+        // assertEquals(1.0, d.getGradientCenterX());
+        // assertEquals(1.0, d.getGradientCenterY());
+        // assertEquals(1.0, d.getGradientRadius());
+        // assertEquals(false, d.getUseLevel());
+
+        // Padding
+        Rect padding = new Rect();
+        assertTrue(d.getPadding(padding));
+        assertEquals(1, padding.left);
+        assertEquals(1, padding.top);
+        assertEquals(1, padding.bottom);
+        assertEquals(1, padding.right);
+
+        // Size
+        assertEquals(1, d.getIntrinsicHeight());
+        assertEquals(1, d.getIntrinsicWidth());
+
+        // Solid
+        // assertEquals(true, d.hasSolidColor());
+        // assertEquals(Color.BLACK, d.getColor());
+
+        // Stroke
+        // assertEquals(1.0, d.getStrokeWidth());
+        // assertEquals(Color.BLACK, d.getStrokeColor());
+        // assertEquals(1.0, d.getStrokeDashWidth());
+        // assertEquals(1.0, d.getStrokeDashGap());
+    }
+
+    public void testNinePatchDrawable() {
+        NinePatchDrawable d = (NinePatchDrawable) mContext.getDrawable(
+                R.drawable.ninepatchdrawable_theme);
+
+        internalTestNinePatchDrawable(d);
+    }
+
+    private void internalTestNinePatchDrawable(NinePatchDrawable d) {
+        assertEquals(true, d.isAutoMirrored());
+        // assertEquals(true, d.hasDither());
+        // assertNotNull(d.getNinePatch());
+    }
+
+    public void testTouchFeedbackDrawable() {
+        TouchFeedbackDrawable d = (TouchFeedbackDrawable) mContext.getDrawable(
+                R.drawable.touchfeedbackdrawable_theme);
+
+        // assertEquals(Color.BLACK, d.getPressColor());
+    }
+    
+    public void testLayerDrawable() {
+        LayerDrawable d = (LayerDrawable) mContext.getDrawable(R.drawable.layerdrawable_theme);
+
+        // Layer autoMirror values are set to the parent's autoMirror value, so
+        // make sure the container is using the expected value.
+        assertEquals(true, d.isAutoMirrored());
+
+        BitmapDrawable bitmapDrawable  = (BitmapDrawable) d.getDrawable(0);
+        internalTestBitmapDrawable(bitmapDrawable);
+
+        NinePatchDrawable ninePatchDrawable = (NinePatchDrawable) d.getDrawable(1);
+        internalTestNinePatchDrawable(ninePatchDrawable);
+    }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
new file mode 100644
index 0000000..46b53cb
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/VectorDrawableTest.java
@@ -0,0 +1,216 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.drawable.VectorDrawable;
+import android.test.AndroidTestCase;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.util.Xml;
+
+import com.android.cts.stub.R;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+public class VectorDrawableTest extends AndroidTestCase {
+    private static final String LOGTAG = VectorDrawableTest.class.getSimpleName();
+    private int[] mIconResIds = new int[] {
+            R.drawable.vector_icon_create,
+            R.drawable.vector_icon_delete,
+            R.drawable.vector_icon_heart,
+            R.drawable.vector_icon_schedule,
+            R.drawable.vector_icon_settings,
+            R.drawable.vector_icon_random_path_1,
+            R.drawable.vector_icon_random_path_2,
+            R.drawable.vector_icon_repeated_cq,
+            R.drawable.vector_icon_repeated_st,
+            R.drawable.vector_icon_repeated_a_1,
+            R.drawable.vector_icon_repeated_a_2,
+    };
+
+    private int[] mGoldenImages = new int[] {
+            R.drawable.vector_icon_create_golden,
+            R.drawable.vector_icon_delete_golden,
+            R.drawable.vector_icon_heart_golden,
+            R.drawable.vector_icon_schedule_golden,
+            R.drawable.vector_icon_settings_golden,
+            R.drawable.vector_icon_random_path_1_golden,
+            R.drawable.vector_icon_random_path_2_golden,
+            R.drawable.vector_icon_repeated_cq_golden,
+            R.drawable.vector_icon_repeated_st_golden,
+            R.drawable.vector_icon_repeated_a_1_golden,
+            R.drawable.vector_icon_repeated_a_2_golden,
+    };
+
+    private int[] mAnimatedIconResIds = new int[] {
+            R.drawable.vector_animation_battery,
+            R.drawable.vector_animation_clip_circle,
+            R.drawable.vector_animation_clip_rect,
+            R.drawable.vector_animation_rotate_curve,
+            R.drawable.vector_animation_rotate_pie,
+            R.drawable.vector_animation_trim_path,
+            R.drawable.vector_animation_wifi,
+    };
+
+    // These golden images are capturing the snapshot of the animated vector drawables
+    // at 50% of the time interval.
+    private int[] mAnimatedGoldenImages = new int[] {
+            R.drawable.vector_animation_battery_golden,
+            R.drawable.vector_animation_clip_circle_golden,
+            R.drawable.vector_animation_clip_rect_golden,
+            R.drawable.vector_animation_rotate_curve_golden,
+            R.drawable.vector_animation_rotate_pie_golden,
+            R.drawable.vector_animation_trim_path_golden,
+            R.drawable.vector_animation_wifi_golden,
+    };
+
+    private static final int IMAGE_WIDTH = 64;
+    private static final int IMAGE_HEIGHT = 64;
+    // A small value is actually making sure that the values are matching
+    // exactly with the golden image.
+    // We can increase the threshold if the Skia is drawing with some variance
+    // on different devices. So far, the tests show they are matching correctly.
+    private static final float PIXEL_ERROR_THRESHOLD = 0.00001f;
+
+    private static final boolean DBG_DUMP_PNG = false;
+
+    private Resources mResources;
+    private VectorDrawable mVectorDrawable;
+    private BitmapFactory.Options mOptions;
+    private Bitmap mBitmap;
+    private Canvas mCanvas;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        final int width = IMAGE_WIDTH;
+        final int height = IMAGE_HEIGHT;
+
+        mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+        mCanvas = new Canvas(mBitmap);
+        mVectorDrawable = new VectorDrawable();
+        mVectorDrawable.setBounds(0, 0, width, height);
+
+        mResources = mContext.getResources();
+
+        mOptions = new BitmapFactory.Options();
+        mOptions.inScaled = false;
+    }
+
+    public void testSimpleVectorDrawables() throws Exception {
+        verifyVectorDrawables(mIconResIds, mGoldenImages, 0);
+    }
+
+    public void testAnimatedVectorDrawables() throws Exception {
+        verifyVectorDrawables(mAnimatedIconResIds, mAnimatedGoldenImages, 0.5f);
+    }
+
+    private void verifyVectorDrawables(int[] resIds, int[] goldenImages, float fraction) throws Exception {
+        for (int i = 0; i < resIds.length; i++) {
+            // Setup VectorDrawable from xml file and draw into the bitmap.
+            // TODO: use the VectorDrawable.create() function if it is
+            // publicized.
+            XmlPullParser xpp = mResources.getXml(resIds[i]);
+            AttributeSet attrs = Xml.asAttributeSet(xpp);
+
+            mVectorDrawable.inflate(mResources, xpp, attrs);
+            mVectorDrawable.setAnimationFraction(fraction);
+
+            mBitmap.eraseColor(0);
+            mVectorDrawable.draw(mCanvas);
+
+            if (DBG_DUMP_PNG) {
+                saveVectorDrawableIntoPNG(mBitmap, resIds, i);
+            } else {
+                // Start to compare
+                Bitmap golden = BitmapFactory.decodeResource(mResources, goldenImages[i], mOptions);
+                compareImages(mBitmap, golden, mResources.getString(resIds[i]));
+            }
+        }
+    }
+
+    // This is only for debugging or golden image (re)generation purpose.
+    private void saveVectorDrawableIntoPNG(Bitmap bitmap, int[] resIds, int index) throws IOException {
+        // Save the image to the disk.
+        FileOutputStream out = null;
+        try {
+            String originalFilePath = mResources.getString(resIds[index]);
+            File originalFile = new File(originalFilePath);
+            String fileFullName = originalFile.getName();
+            String fileTitle = fileFullName.substring(0, fileFullName.lastIndexOf("."));
+            String outputFilename = "/sdcard/temp/" + fileTitle + "_golden.png";
+            File outputFile = new File(outputFilename);
+            if (!outputFile.exists()) {
+                outputFile.createNewFile();
+            }
+
+            out = new FileOutputStream(outputFile, false);
+            bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
+            Log.v(LOGTAG, "Write test No." + index + " to file successfully.");
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    private void compareImages(Bitmap ideal, Bitmap given, String filename) {
+        int idealWidth = ideal.getWidth();
+        int idealHeight = ideal.getHeight();
+
+        assertTrue(idealWidth == given.getWidth());
+        assertTrue(idealHeight == given.getHeight());
+
+        int totalDiffPixelCount = 0;
+        float totalPixelCount = idealWidth * idealHeight;
+        for (int x = 0; x < idealWidth; x++) {
+            for (int y = 0; y < idealHeight; y++) {
+                int idealColor = ideal.getPixel(x, y);
+                int givenColor = given.getPixel(x, y);
+                if (idealColor == givenColor)
+                    continue;
+
+                float totalError = 0;
+                totalError += Math.abs(Color.red(idealColor) - Color.red(givenColor));
+                totalError += Math.abs(Color.green(idealColor) - Color.green(givenColor));
+                totalError += Math.abs(Color.blue(idealColor) - Color.blue(givenColor));
+                totalError += Math.abs(Color.alpha(idealColor) - Color.alpha(givenColor));
+
+                if ((totalError / 1024.0f) >= PIXEL_ERROR_THRESHOLD) {
+                    fail((filename + ": totalError is " + totalError));
+                }
+
+                totalDiffPixelCount++;
+            }
+        }
+        if ((totalDiffPixelCount / totalPixelCount) >= PIXEL_ERROR_THRESHOLD) {
+            fail((filename +": totalDiffPixelCount is " + totalDiffPixelCount));
+        }
+
+    }
+}
diff --git a/tests/tests/graphics2/Android.mk b/tests/tests/graphics2/Android.mk
index b3e7340..a3cdafa 100644
--- a/tests/tests/graphics2/Android.mk
+++ b/tests/tests/graphics2/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/graphics2/AndroidManifest.xml b/tests/tests/graphics2/AndroidManifest.xml
index 2392100..67557ad 100644
--- a/tests/tests/graphics2/AndroidManifest.xml
+++ b/tests/tests/graphics2/AndroidManifest.xml
@@ -24,7 +24,10 @@
 
     <instrumentation
         android:targetPackage="com.android.cts.graphics2"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/hardware/Android.mk b/tests/tests/hardware/Android.mk
index e0ad6e5..110b291 100644
--- a/tests/tests/hardware/Android.mk
+++ b/tests/tests/hardware/Android.mk
@@ -32,9 +32,7 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner mockito-target android-ex-camera2
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner mockito-target android-ex-camera2
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
@@ -42,7 +40,8 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13281332 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES := android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/hardware/AndroidManifest.xml b/tests/tests/hardware/AndroidManifest.xml
index 971d6c7..11ca9c0 100644
--- a/tests/tests/hardware/AndroidManifest.xml
+++ b/tests/tests/hardware/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.hardware"/>
+                     android:label="CTS tests of android.hardware">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
new file mode 100644
index 0000000..b27e0d6
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
@@ -0,0 +1,844 @@
+/*
+ * Copyright 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.camera2.cts;
+
+import static android.graphics.ImageFormat.YUV_420_888;
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Rational;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.helpers.MaybeNull;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
+import android.hardware.camera2.cts.rs.ScriptGraph;
+import android.hardware.camera2.cts.rs.ScriptYuvCrop;
+import android.hardware.camera2.cts.rs.ScriptYuvMeans1d;
+import android.hardware.camera2.cts.rs.ScriptYuvMeans2dTo1d;
+import android.hardware.camera2.cts.rs.ScriptYuvToRgb;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.renderscript.Allocation;
+import android.renderscript.Script.LaunchOptions;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Suite of tests for camera2 -> RenderScript APIs.
+ *
+ * <p>It uses CameraDevice as producer, camera sends the data to the surface provided by
+ * Allocation. Only the below format is tested:</p>
+ *
+ * <p>YUV_420_888: flexible YUV420, it is a mandatory format for camera.</p>
+ */
+public class AllocationTest extends AndroidTestCase {
+    private static final String TAG = "AllocationTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private CameraManager mCameraManager;
+    private CameraDevice mCamera;
+    private BlockingStateListener mCameraListener;
+    private String[] mCameraIds;
+
+    private Handler mHandler;
+    private HandlerThread mHandlerThread;
+
+    private CameraIterable mCameraIterable;
+    private SizeIterable mSizeIterable;
+    private ResultIterable mResultIterable;
+
+    @Override
+    public synchronized void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+
+        RenderScriptSingleton.setContext(context);
+        // TODO: call clearContext
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCameraIds = mCameraManager.getCameraIdList();
+        mHandlerThread = new HandlerThread("AllocationTest");
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+
+        mCameraIterable = new CameraIterable();
+        mSizeIterable = new SizeIterable();
+        mResultIterable = new ResultIterable();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        MaybeNull.close(mCamera);
+
+        // TODO: Clean up RenderScript context in a static test run finished method.
+        // Or alternatively count the # of test methods that are in this test,
+        // once we reach that count, it's time to call the last tear down
+
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        super.tearDown();
+    }
+
+    /**
+     * Update the request with a default manual request template.
+     *
+     * @param request A builder for a CaptureRequest
+     * @param sensitivity ISO gain units (e.g. 100)
+     * @param expTimeNs Exposure time in nanoseconds
+     */
+    private static void setManualCaptureRequest(CaptureRequest.Builder request, int sensitivity,
+            long expTimeNs) {
+        final Rational ONE = new Rational(1, 1);
+        final Rational ZERO = new Rational(0, 1);
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Create manual capture request, sensitivity = %d, expTime = %f",
+                    sensitivity, expTimeNs / (1000.0 * 1000)));
+        }
+
+        request.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AWB_MODE, CaptureRequest.CONTROL_AWB_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+        request.set(CaptureRequest.CONTROL_EFFECT_MODE, CaptureRequest.CONTROL_EFFECT_MODE_OFF);
+        request.set(CaptureRequest.SENSOR_FRAME_DURATION, 0L);
+        request.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
+        request.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTimeNs);
+        request.set(CaptureRequest.COLOR_CORRECTION_MODE,
+                CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
+
+        // Identity transform
+        request.set(CaptureRequest.COLOR_CORRECTION_TRANSFORM,
+            new Rational[] {
+                ONE, ZERO, ZERO,
+                ZERO, ONE, ZERO,
+                ZERO, ZERO, ONE
+            });
+
+        // Identity gains
+        request.set(CaptureRequest.COLOR_CORRECTION_GAINS, new float[] { 1.0f, 1.0f, 1.0f, 1.0f });
+        request.set(CaptureRequest.TONEMAP_MODE, CaptureRequest.TONEMAP_MODE_FAST);
+    }
+
+    /**
+     * Calculate the absolute crop window from a {@link Size},
+     * and configure {@link LaunchOptions} for it.
+     */
+    // TODO: split patch crop window and the application against a particular size into 2 classes
+    public static class Patch {
+        /**
+         * Create a new {@link Patch} from relative crop coordinates.
+         *
+         * <p>All float values must be normalized coordinates between [0, 1].</p>
+         *
+         * @param size Size of the original rectangle that is being cropped.
+         * @param xNorm The X coordinate defining the left side of the rectangle (in [0, 1]).
+         * @param yNorm The Y coordinate defining the top side of the rectangle (in [0, 1]).
+         * @param wNorm The width of the crop rectangle (normalized between [0, 1]).
+         * @param hNorm The height of the crop rectangle (normalized between [0, 1]).
+         *
+         * @throws NullPointerException if size was {@code null}.
+         * @throws AssertionError if any of the normalized coordinates were out of range
+         */
+        public Patch(Size size, float xNorm, float yNorm, float wNorm, float hNorm) {
+            checkNotNull("size", size);
+
+            assertInRange(xNorm, 0.0f, 1.0f);
+            assertInRange(yNorm, 0.0f, 1.0f);
+            assertInRange(wNorm, 0.0f, 1.0f);
+            assertInRange(hNorm, 0.0f, 1.0f);
+
+            wFull = size.getWidth();
+            hFull = size.getWidth();
+
+            xTile = (int)Math.ceil(xNorm * wFull);
+            yTile = (int)Math.ceil(yNorm * hFull);
+
+            wTile = (int)Math.ceil(wNorm * wFull);
+            hTile = (int)Math.ceil(hNorm * hFull);
+
+            mSourceSize = size;
+        }
+
+        /**
+         * Get the original size used to create this {@link Patch}.
+         *
+         * @return source size
+         */
+        public Size getSourceSize() {
+            return mSourceSize;
+        }
+
+        /**
+         * Get the cropped size after applying the normalized crop window.
+         *
+         * @return cropped size
+         */
+        public Size getSize() {
+            return new Size(wFull, hFull);
+        }
+
+        /**
+         * Get the {@link LaunchOptions} that can be used with a {@link android.renderscript.Script}
+         * to apply a kernel over a subset of an {@link Allocation}.
+         *
+         * @return launch options
+         */
+        public LaunchOptions getLaunchOptions() {
+            return (new LaunchOptions())
+                    .setX(xTile, xTile + wTile)
+                    .setY(yTile, yTile + hTile);
+        }
+
+        /**
+         * Get the cropped width after applying the normalized crop window.
+         *
+         * @return cropped width
+         */
+        public int getWidth() {
+            return wTile;
+        }
+
+        /**
+         * Get the cropped height after applying the normalized crop window.
+         *
+         * @return cropped height
+         */
+        public int getHeight() {
+            return hTile;
+        }
+
+        /**
+         * Convert to a {@link RectF} where each corner is represented by a
+         * normalized coordinate in between [0.0, 1.0] inclusive.
+         *
+         * @return a new rectangle
+         */
+        public RectF toRectF() {
+            return new RectF(
+                    xTile * 1.0f / wFull,
+                    yTile * 1.0f / hFull,
+                    (xTile + wTile) * 1.0f / wFull,
+                    (yTile + hTile) * 1.0f / hFull);
+        }
+
+        private final Size mSourceSize;
+        private final int wFull;
+        private final int hFull;
+        private final int xTile;
+        private final int yTile;
+        private final int wTile;
+        private final int hTile;
+    }
+
+    /**
+     * Convert a single YUV pixel (3 byte elements) to an RGB pixel.
+     *
+     * <p>The color channels must be in the following order:
+     * <ul><li>Y - 0th channel
+     * <li>U - 1st channel
+     * <li>V - 2nd channel
+     * </ul></p>
+     *
+     * <p>Each channel has data in the range 0-255.</p>
+     *
+     * <p>Output data is a 3-element pixel with each channel in the range of [0,1].
+     * Each channel is saturated to avoid over/underflow.</p>
+     *
+     * <p>The conversion is done using JFIF File Interchange Format's "Conversion to and from RGB":
+     * <ul>
+     * <li>R = Y + 1.042 (Cr - 128)
+     * <li>G = Y - 0.34414 (Cb - 128) - 0.71414 (Cr - 128)
+     * <li>B = Y + 1.772 (Cb - 128)
+     * </ul>
+     *
+     * Where Cr and Cb are aliases of V and U respectively.
+     * </p>
+     *
+     * @param yuvData An array of a YUV pixel (at least 3 bytes large)
+     *
+     * @return an RGB888 pixel with each channel in the range of [0,1]
+     */
+    private static float[] convertPixelYuvToRgb(byte[] yuvData) {
+        final int CHANNELS = 3; // yuv
+        final float COLOR_RANGE = 256f;
+
+        assertTrue("YUV pixel must be at least 3 bytes large", CHANNELS <= yuvData.length);
+
+        float[] rgb = new float[CHANNELS];
+
+        float y = yuvData[0] & 0xFF;  // Y channel
+        float cb = yuvData[1] & 0xFF; // U channel
+        float cr = yuvData[2] & 0xFF; // V channel
+
+        // convert YUV -> RGB (from JFIF's "Conversion to and from RGB" section)
+        float r = y + 1.402f * (cr - 128);
+        float g = y - 0.34414f * (cb - 128) - 0.71414f * (cr - 128);
+        float b = y + 1.772f * (cb - 128);
+
+        // normalize [0,255] -> [0,1]
+        rgb[0] = r / COLOR_RANGE;
+        rgb[1] = g / COLOR_RANGE;
+        rgb[2] = b / COLOR_RANGE;
+
+        // Clamp to range [0,1]
+        for (int i = 0; i < CHANNELS; ++i) {
+            rgb[i] = Math.max(0.0f, Math.min(1.0f, rgb[i]));
+        }
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("RGB calculated (r,g,b) = (%f, %f, %f)", rgb[0], rgb[1],
+                    rgb[2]));
+        }
+
+        return rgb;
+    }
+
+    /**
+     * Configure the camera with the target surface;
+     * create a capture request builder with {@code cameraTarget} as the sole surface target.
+     *
+     * <p>Outputs are configured with the new surface targets, and this function blocks until
+     * the camera has finished configuring.</p>
+     *
+     * <p>The capture request is created from the {@link CameraDevice#TEMPLATE_PREVIEW} template.
+     * No other keys are set.
+     * </p>
+     */
+    private CaptureRequest.Builder configureAndCreateRequestForSurface(Surface cameraTarget)
+            throws CameraAccessException {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+        assertNotNull("Failed to get Surface", cameraTarget);
+        outputSurfaces.add(cameraTarget);
+
+        mCamera.configureOutputs(outputSurfaces);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+
+        CaptureRequest.Builder captureBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        assertNotNull("Fail to create captureRequest", captureBuilder);
+        captureBuilder.addTarget(cameraTarget);
+
+        if (VERBOSE) Log.v(TAG, "configureAndCreateRequestForSurface - done");
+
+        return captureBuilder;
+    }
+
+    /**
+     * Submit a single request to the camera, block until the buffer is available.
+     *
+     * <p>Upon return from this function, script has been executed against the latest buffer.
+     * </p>
+     */
+    private void captureSingleShotAndExecute(CaptureRequest request, ScriptGraph graph)
+            throws CameraAccessException {
+        checkNotNull("request", request);
+        checkNotNull("graph", graph);
+
+        mCamera.capture(request, new CameraDevice.CaptureListener() {
+            @Override
+            public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
+                    CaptureResult result) {
+                if (VERBOSE) Log.v(TAG, "Capture completed");
+            }
+        }, mHandler);
+
+        if (VERBOSE) Log.v(TAG, "Waiting for single shot buffer");
+        graph.advanceInputWaiting();
+        if (VERBOSE) Log.v(TAG, "Got the buffer");
+        graph.execute();
+    }
+
+    private void stopCapture() throws CameraAccessException {
+        if (VERBOSE) Log.v(TAG, "Stopping capture and waiting for idle");
+        // Stop repeat, wait for captures to complete, and disconnect from surfaces
+        mCamera.configureOutputs(/*outputs*/null);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
+    }
+
+    /**
+     * Extremely dumb validator. Makes sure there is at least one non-zero RGB pixel value.
+     */
+    private void validateInputOutputNotZeroes(ScriptGraph scriptGraph, Size size) {
+        final int BPP = 8; // bits per pixel
+
+        int width = size.getWidth();
+        int height = size.getHeight();
+        /**
+         * Check the input allocation is sane.
+         * - Byte size matches what we expect.
+         * - The input is not all zeroes.
+         */
+
+        // Check that input data was updated first. If it wasn't, the rest of the test will fail.
+        byte[] data = scriptGraph.getInputData();
+        assertArrayNotAllZeroes("Input allocation data was not updated", data);
+
+        // Minimal required size to represent YUV 4:2:0 image
+        int packedSize =
+                width * height * ImageFormat.getBitsPerPixel(YUV_420_888) / BPP;
+        if (VERBOSE) Log.v(TAG, "Expected image size = " + packedSize);
+        int actualSize = data.length;
+        // Actual size may be larger due to strides or planes being non-contiguous
+        assertTrue(
+                String.format(
+                        "YUV 420 packed size (%d) should be at least as large as the actual size " +
+                        "(%d)", packedSize, actualSize), packedSize <= actualSize);
+        /**
+         * Check the output allocation by converting to RGBA.
+         * - Byte size matches what we expect
+         * - The output is not all zeroes
+         */
+        final int RGBA_CHANNELS = 4;
+
+        int actualSizeOut = scriptGraph.getOutputAllocation().getBytesSize();
+        int packedSizeOut = width * height * RGBA_CHANNELS;
+
+        byte[] dataOut = scriptGraph.getOutputData();
+        assertEquals("RGB mismatched byte[] and expected size",
+                packedSizeOut, dataOut.length);
+
+        if (VERBOSE) {
+            Log.v(TAG, "checkAllocationByConvertingToRgba - RGB data size " + dataOut.length);
+        }
+
+        assertArrayNotAllZeroes("RGBA data was not updated", dataOut);
+        // RGBA8888 stride should be equal to the width
+        assertEquals("RGBA 8888 mismatched byte[] and expected size", packedSizeOut, actualSizeOut);
+
+        if (VERBOSE) Log.v(TAG, "validating Buffer , size = " + actualSize);
+    }
+
+    public void testAllocationFromCameraFlexibleYuv() throws Exception {
+
+        /** number of frame (for streaming requests) to be verified. */
+        final int NUM_FRAME_VERIFIED = 1;
+
+        mCameraIterable.forEachCamera(new CameraBlock() {
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+
+                // Iterate over each size in the camera
+                mSizeIterable.forEachSize(YUV_420_888, new SizeBlock() {
+                    @Override
+                    public void run(final Size size) throws CameraAccessException {
+                        // Create a script graph that converts YUV to RGB
+                        final ScriptGraph scriptGraph = ScriptGraph.create()
+                                .configureInputWithSurface(size, YUV_420_888)
+                                .chainScript(ScriptYuvToRgb.class)
+                                .buildGraph();
+
+                        if (VERBOSE) Log.v(TAG, "Prepared ScriptYuvToRgb for size " + size);
+
+                        // Run the graph against camera input and validate we get some input
+                        try {
+                            CaptureRequest request =
+                                    configureAndCreateRequestForSurface(scriptGraph.getInputSurface()).build();
+
+                            // Block until we get 1 result, then iterate over the result
+                            mResultIterable.forEachResultRepeating(
+                                    request, NUM_FRAME_VERIFIED, new ResultBlock() {
+                                @Override
+                                public void run(CaptureResult result) throws CameraAccessException {
+                                    scriptGraph.advanceInputWaiting();
+                                    scriptGraph.execute();
+                                    validateInputOutputNotZeroes(scriptGraph, size);
+                                    scriptGraph.advanceInputAndDrop();
+                                }
+                            });
+
+                            stopCapture();
+                        } finally {
+                            scriptGraph.close();
+                        }
+                    }
+                });
+            }
+        });
+    }
+
+    /**
+     * Take two shots and ensure per-frame-control with exposure/gain is working correctly.
+     *
+     * <p>Takes a shot with very low ISO and exposure time. Expect it to be black.</p>
+     *
+     * <p>Take a shot with very high ISO and exposure time. Expect it to be white.</p>
+     *
+     * @throws Exception
+     */
+    public void testBlackWhite() throws CameraAccessException {
+
+        /** low iso + low exposure (first shot) */
+        final float THRESHOLD_LOW = 0.025f;
+        /** high iso + high exposure (second shot) */
+        final float THRESHOLD_HIGH = 0.975f;
+
+        mCameraIterable.forEachCamera(/*fullHwLevel*/true, new CameraBlock() {
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+
+                final Size maxSize = getMaxSize(
+                        getSupportedSizeForFormat(YUV_420_888, camera.getId(), mCameraManager));
+                final StaticMetadata staticInfo =
+                        new StaticMetadata(mCameraManager.getCameraCharacteristics(camera.getId()));
+
+                ScriptGraph scriptGraph = createGraphForYuvCroppedMeans(maxSize);
+
+                CaptureRequest.Builder req =
+                        configureAndCreateRequestForSurface(scriptGraph.getInputSurface());
+
+                // Take a shot with very low ISO and exposure time. Expect it to be black.
+                int minimumSensitivity = staticInfo.getSensitivityMinimumOrDefault();
+                long minimumExposure = staticInfo.getExposureMinimumOrDefault();
+                setManualCaptureRequest(req, minimumSensitivity, minimumExposure);
+
+                CaptureRequest lowIsoExposureShot = req.build();
+                captureSingleShotAndExecute(lowIsoExposureShot, scriptGraph);
+
+                float[] blackMeans = convertPixelYuvToRgb(scriptGraph.getOutputData());
+
+                // Take a shot with very high ISO and exposure time. Expect it to be white.
+                int maximumSensitivity = staticInfo.getSensitivityMaximumOrDefault();
+                long maximumExposure = staticInfo.getExposureMaximumOrDefault();
+                setManualCaptureRequest(req, maximumSensitivity, maximumExposure);
+
+                CaptureRequest highIsoExposureShot = req.build();
+                captureSingleShotAndExecute(highIsoExposureShot, scriptGraph);
+
+                float[] whiteMeans = convertPixelYuvToRgb(scriptGraph.getOutputData());
+
+                // low iso + low exposure (first shot)
+                assertArrayWithinUpperBound("Black means too high", blackMeans, THRESHOLD_LOW);
+
+                // high iso + high exposure (second shot)
+                assertArrayWithinLowerBound("White means too low", whiteMeans, THRESHOLD_HIGH);
+            }
+        });
+    }
+
+    /**
+     * Test that the android.sensitivity.parameter is applied.
+     */
+    public void testParamSensitivity() throws CameraAccessException {
+        final float THRESHOLD_MAX_MIN_DIFF = 0.3f;
+        final float THRESHOLD_MAX_MIN_RATIO = 2.0f;
+        final int NUM_STEPS = 5;
+        final long EXPOSURE_TIME_NS = 2000000; // 2 seconds
+        final int RGB_CHANNELS = 3;
+
+        final List<float[]> rgbMeans = new ArrayList<float[]>();
+
+        mCameraIterable.forEachCamera(/*fullHwLevel*/true, new CameraBlock() {
+            @Override
+            public void run(CameraDevice camera) throws CameraAccessException {
+
+                final Size maxSize = getMaxSize(
+                        getSupportedSizeForFormat(YUV_420_888, camera.getId(), mCameraManager));
+                final StaticMetadata staticInfo =
+                        new StaticMetadata(mCameraManager.getCameraCharacteristics(camera.getId()));
+
+                final int sensitivityMin = staticInfo.getSensitivityMinimumOrDefault();
+                final int sensitivityMax = staticInfo.getSensitivityMaximumOrDefault();
+
+                // List each sensitivity from min-max in NUM_STEPS increments
+                int[] sensitivities = new int[NUM_STEPS];
+                for (int i = 0; i < NUM_STEPS; ++i) {
+                    int delta = (sensitivityMax - sensitivityMin) / (NUM_STEPS - 1);
+                    sensitivities[i] = sensitivityMin + delta * i;
+                }
+
+                ScriptGraph scriptGraph = createGraphForYuvCroppedMeans(maxSize);
+
+                CaptureRequest.Builder req =
+                        configureAndCreateRequestForSurface(scriptGraph.getInputSurface());
+
+                // Take burst shots with increasing sensitivity one after other.
+                for (int i = 0; i < NUM_STEPS; ++i) {
+                    setManualCaptureRequest(req, sensitivities[i], EXPOSURE_TIME_NS);
+                    captureSingleShotAndExecute(req.build(), scriptGraph);
+                    float[] means = convertPixelYuvToRgb(scriptGraph.getOutputData());
+                    rgbMeans.add(means);
+
+                    if (VERBOSE) {
+                        Log.v(TAG, "testParamSensitivity - captured image " + i +
+                                " with RGB means: " + Arrays.toString(means));
+                    }
+                }
+
+                // Test that every consecutive image gets brighter.
+                for (int i = 0; i < rgbMeans.size() - 1; ++i) {
+                    float[] curMeans = rgbMeans.get(i);
+                    float[] nextMeans = rgbMeans.get(i+1);
+
+                    assertArrayNotGreater(
+                            String.format("Shot with sensitivity %d should not have higher " +
+                                    "average means than shot with sensitivity %d",
+                                    sensitivities[i], sensitivities[i+1]),
+                            curMeans, nextMeans);
+                }
+
+                // Test the min-max diff and ratios are within expected thresholds
+                float[] lastMeans = rgbMeans.get(NUM_STEPS - 1);
+                float[] firstMeans = rgbMeans.get(/*location*/0);
+                for (int i = 0; i < RGB_CHANNELS; ++i) {
+                    assertTrue(
+                            String.format("Sensitivity max-min diff too small (max=%f, min=%f)",
+                                    lastMeans[i], firstMeans[i]),
+                            lastMeans[i] - firstMeans[i] > THRESHOLD_MAX_MIN_DIFF);
+                    assertTrue(
+                            String.format("Sensitivity max-min ratio too small (max=%f, min=%f)",
+                                    lastMeans[i], firstMeans[i]),
+                            lastMeans[i] / firstMeans[i] > THRESHOLD_MAX_MIN_RATIO);
+                }
+            }
+        });
+
+    }
+
+    /**
+     * Common script graph for manual-capture based tests that determine the average pixel
+     * values of a cropped sub-region.
+     *
+     * <p>Processing chain:
+     *
+     * <pre>
+     * input:  YUV_420_888 surface
+     * output: mean YUV value of a central section of the image,
+     *         YUV 4:4:4 encoded as U8_3
+     * steps:
+     *      1) crop [0.45,0.45] - [0.55, 0.55]
+     *      2) average columns
+     *      3) average rows
+     * </pre>
+     * </p>
+     */
+    private static ScriptGraph createGraphForYuvCroppedMeans(final Size size) {
+        ScriptGraph scriptGraph = ScriptGraph.create()
+                .configureInputWithSurface(size, YUV_420_888)
+                .configureScript(ScriptYuvCrop.class)
+                    .set(ScriptYuvCrop.CROP_WINDOW,
+                            new Patch(size, /*x*/0.45f, /*y*/0.45f, /*w*/0.1f, /*h*/0.1f).toRectF())
+                    .buildScript()
+                .chainScript(ScriptYuvMeans2dTo1d.class)
+                .chainScript(ScriptYuvMeans1d.class)
+                // TODO: Make a script for YUV 444 -> RGB 888 conversion
+                .buildGraph();
+        return scriptGraph;
+    }
+
+    /*
+     * TODO: Refactor below code into separate classes and to not depend on AllocationTest
+     * inner variables.
+     *
+     * TODO: add javadocs to below methods
+     *
+     * TODO: Figure out if there's some elegant way to compose these forEaches together, so that
+     * the callers don't have to do a ton of nesting
+     */
+
+    interface CameraBlock {
+        void run(CameraDevice camera) throws CameraAccessException;
+    }
+
+    class CameraIterable {
+        public void forEachCamera(CameraBlock runnable)
+                throws CameraAccessException {
+            forEachCamera(/*fullHwLevel*/false, runnable);
+        }
+
+        public void forEachCamera(boolean fullHwLevel, CameraBlock runnable)
+                throws CameraAccessException {
+            assertNotNull("No camera manager", mCameraManager);
+            assertNotNull("No camera IDs", mCameraIds);
+
+            for (int i = 0; i < mCameraIds.length; i++) {
+                // Don't execute the runnable against non-FULL cameras if FULL is required
+                CameraCharacteristics properties =
+                        mCameraManager.getCameraCharacteristics(mCameraIds[i]);
+                StaticMetadata staticInfo = new StaticMetadata(properties);
+                if (fullHwLevel && !staticInfo.isHardwareLevelFull()) {
+                    Log.i(TAG, String.format(
+                            "Skipping this test for camera %d, needs FULL hw level",
+                            mCameraIds[i]));
+                    continue;
+                }
+
+                // FIXME: hammerhead FFC thinks its FULL but doesnt have per-frame-control
+                if (fullHwLevel &&
+                        staticInfo.getCharacteristics().get(CameraCharacteristics.LENS_FACING)
+                        != CameraMetadata.LENS_FACING_BACK
+                        && "hammerhead".equals(android.os.Build.PRODUCT)) {
+                    Log.w(TAG,
+                            "FIXME: Front facing camera claims to support per-frame-control " +
+                            "but doesn't for product " + android.os.Build.PRODUCT);
+                    continue;
+                }
+
+                // Open camera and execute test
+                Log.i(TAG, "Testing Camera " + mCameraIds[i]);
+                try {
+                    openDevice(mCameraIds[i]);
+
+                    runnable.run(mCamera);
+                } finally {
+                    closeDevice(mCameraIds[i]);
+                }
+            }
+        }
+
+        private void openDevice(String cameraId) {
+            if (mCamera != null) {
+                throw new IllegalStateException("Already have open camera device");
+            }
+            try {
+                mCamera = openCamera(
+                    mCameraManager, cameraId, mCameraListener, mHandler);
+            } catch (CameraAccessException e) {
+                fail("Fail to open camera synchronously, " + Log.getStackTraceString(e));
+            } catch (BlockingOpenException e) {
+                fail("Fail to open camera asynchronously, " + Log.getStackTraceString(e));
+            }
+            mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
+        }
+
+        private void closeDevice(String cameraId) {
+            if (mCamera != null) {
+                mCamera.close();
+                mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+                mCamera = null;
+            }
+        }
+    }
+
+    interface SizeBlock {
+        void run(Size size) throws CameraAccessException;
+    }
+
+    class SizeIterable {
+        public void forEachSize(int format, SizeBlock runnable) throws CameraAccessException {
+            assertNotNull("No camera opened", mCamera);
+            assertNotNull("No camera manager", mCameraManager);
+
+            CameraCharacteristics properties =
+                    mCameraManager.getCameraCharacteristics(mCamera.getId());
+
+            assertNotNull("Can't get camera properties!", properties);
+
+            int[] availableFormats = properties.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
+            assertArrayNotEmpty(availableFormats,
+                    "availableFormats should not be empty");
+            Arrays.sort(availableFormats);
+            assertTrue("Can't find the format " + format + " in supported formats " +
+                    Arrays.toString(availableFormats),
+                    Arrays.binarySearch(availableFormats, format) >= 0);
+
+            Size[] availableSizes = getSupportedSizeForFormat(format, mCamera.getId(),
+                    mCameraManager);
+            assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
+
+            for (Size size : availableSizes) {
+
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing size " + size.toString() +
+                            " for camera " + mCamera.getId());
+                }
+                runnable.run(size);
+            }
+        }
+    }
+
+    interface ResultBlock {
+        void run(CaptureResult result) throws CameraAccessException;
+    }
+
+    class ResultIterable {
+        public void forEachResultOnce(CaptureRequest request, ResultBlock block)
+                throws CameraAccessException {
+            forEachResult(request, /*count*/1, /*repeating*/false, block);
+        }
+
+        public void forEachResultRepeating(CaptureRequest request, int count, ResultBlock block)
+                throws CameraAccessException {
+            forEachResult(request, count, /*repeating*/true, block);
+        }
+
+        public void forEachResult(CaptureRequest request, int count, boolean repeating,
+                ResultBlock block) throws CameraAccessException {
+
+            // TODO: start capture, i.e. configureOutputs
+
+            SimpleCaptureListener listener = new SimpleCaptureListener();
+
+            if (!repeating) {
+                for (int i = 0; i < count; ++i) {
+                    mCamera.capture(request, listener, mHandler);
+                }
+            } else {
+                mCamera.setRepeatingRequest(request, listener, mHandler);
+            }
+
+            // Assume that the device is already IDLE.
+            mCameraListener.waitForState(BlockingStateListener.STATE_ACTIVE,
+                    CAMERA_ACTIVE_TIMEOUT_MS);
+
+            for (int i = 0; i < count; ++i) {
+                if (VERBOSE) {
+                    Log.v(TAG, String.format("Testing with result %d of %d for camera %s",
+                            i, count, mCamera.getId()));
+                }
+
+                CaptureResult result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                block.run(result);
+            }
+
+            if (repeating) {
+                mCamera.stopRepeating();
+                mCameraListener.waitForState(BlockingStateListener.STATE_IDLE,
+                        CAMERA_IDLE_TIMEOUT_MS);
+            }
+
+            // TODO: Make a Configure decorator or some such for configureOutputs
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
deleted file mode 100644
index 6a708e3..0000000
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.cts;
-
-import android.content.Context;
-import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraCharacteristics;
-import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
-import android.hardware.camera2.CameraMetadata;
-import android.hardware.camera2.CaptureFailure;
-import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
-import android.hardware.camera2.Size;
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-import android.media.ImageReader;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.test.AndroidTestCase;
-import android.util.Log;
-import android.view.Surface;
-
-import com.android.ex.camera2.blocking.BlockingStateListener;
-import static com.android.ex.camera2.blocking.BlockingStateListener.*;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-public class CameraCaptureResultTest extends AndroidTestCase {
-    private static final String TAG = "CameraCaptureResultTest";
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    private CameraManager mCameraManager;
-    private HandlerThread mHandlerThread;
-    private Handler mHandler;
-    private ImageReader mImageReader;
-    private Surface mSurface;
-    private BlockingStateListener mCameraListener;
-
-    private static final int MAX_NUM_IMAGES = 5;
-    private static final int NUM_FRAMES_VERIFIED = 300;
-    private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
-
-    // List that includes all public keys from CaptureResult
-    List<CameraMetadata.Key<?>> mAllKeys;
-
-    // List tracking the failed test keys.
-    List<CameraMetadata.Key<?>> mFailedKeys = new ArrayList<CameraMetadata.Key<?>>();
-
-    @Override
-    public void setContext(Context context) {
-        mAllKeys = getAllCaptureResultKeys();
-        super.setContext(context);
-    }
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager", mCameraManager);
-        mHandlerThread = new HandlerThread(TAG);
-        mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
-        mCameraListener = new BlockingStateListener();
-        mFailedKeys.clear();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        mHandlerThread.quitSafely();
-        super.tearDown();
-    }
-
-    public void testCameraCaptureResultAllKeys() throws Exception {
-        /**
-         * Hardcode a key waiver list for the keys we want to skip the sanity check.
-         * FIXME: We need get ride of this list, see bug 11116270.
-         */
-        List<CameraMetadata.Key<?>> waiverkeys = new ArrayList<CameraMetadata.Key<?>>();
-        waiverkeys.add(CaptureResult.EDGE_MODE);
-        waiverkeys.add(CaptureResult.JPEG_GPS_COORDINATES);
-        waiverkeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
-        waiverkeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
-        waiverkeys.add(CaptureResult.JPEG_ORIENTATION);
-        waiverkeys.add(CaptureResult.JPEG_QUALITY);
-        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
-        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
-        waiverkeys.add(CaptureResult.SENSOR_TEMPERATURE);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
-        waiverkeys.add(CaptureResult.TONEMAP_CURVE_RED);
-        waiverkeys.add(CaptureResult.TONEMAP_MODE);
-        waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
-        waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
-        waiverkeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
-
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
-            assertNotNull("CameraCharacteristics shouldn't be null", props);
-            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
-            if (hwLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) {
-                continue;
-            }
-            // TODO: check for LIMITED keys
-
-            CameraDevice camera = null;
-            try {
-                Size[] sizes = CameraTestUtils.getSupportedSizeForFormat(
-                        ImageFormat.YUV_420_888, ids[i], mCameraManager);
-                CameraTestUtils.assertArrayNotEmpty(sizes, "Available sizes shouldn't be empty");
-                createDefaultSurface(sizes[0]);
-
-                if (VERBOSE) {
-                    Log.v(TAG, "Testing camera " + ids[i] + "for size " + sizes[0].toString());
-                }
-
-                camera = CameraTestUtils.openCamera(
-                        mCameraManager, ids[i], mCameraListener, mHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
-                mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
-
-                List<Surface> outputSurfaces = new ArrayList<Surface>(1);
-                outputSurfaces.add(mSurface);
-                camera.configureOutputs(outputSurfaces);
-                mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-                mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
-
-                CaptureRequest.Builder requestBuilder =
-                        camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-                assertNotNull("Failed to create capture request", requestBuilder);
-                requestBuilder.addTarget(mSurface);
-
-                // Enable face detection if supported
-                byte[] faceModes = props.get(
-                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
-                assertNotNull("Available face detection modes shouldn't be null", faceModes);
-                for (int m = 0; m < faceModes.length; m++) {
-                    if (faceModes[m] == CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL) {
-                        if (VERBOSE) {
-                            Log.v(TAG, "testCameraCaptureResultAllKeys - " +
-                                    "setting facedetection mode to full");
-                        }
-                        requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
-                                (int)faceModes[m]);
-                    }
-                }
-
-                // Enable lensShading mode, it should be supported by full mode device.
-                requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
-                        CameraMetadata.STATISTICS_LENS_SHADING_MAP_MODE_ON);
-
-                SimpleCaptureListener captureListener = new SimpleCaptureListener();
-                camera.setRepeatingRequest(requestBuilder.build(), captureListener, mHandler);
-
-                for (int m = 0; m < NUM_FRAMES_VERIFIED; m++) {
-                    if(VERBOSE) {
-                        Log.v(TAG, "Testing frame " + m);
-                    }
-                    validateCaptureResult(
-                            captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS),
-                            waiverkeys);
-                }
-
-                // Stop repeat, wait for captures to complete, and disconnect from surfaces
-                camera.configureOutputs(/*outputs*/ null);
-                mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-                mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
-                // Camera has disconnected, clear out the reader
-                mSurface.release();
-                mImageReader.close();
-            } finally {
-                if (camera != null) {
-                    camera.close();
-                }
-            }
-
-        }
-    }
-
-    private void validateCaptureResult(CaptureResult result,
-            List<CameraMetadata.Key<?>> skippedKeys) throws Exception {
-        for (CameraMetadata.Key<?> key : mAllKeys) {
-            if (!skippedKeys.contains(key) && result.get(key) == null) {
-                mFailedKeys.add(key);
-            }
-        }
-
-        StringBuffer failedKeyNames = new StringBuffer("Below Keys have null values:\n");
-        for (CameraMetadata.Key<?> key : mFailedKeys) {
-            failedKeyNames.append(key.getName() + "\n");
-        }
-
-        assertTrue("Some keys have null values, " + failedKeyNames.toString(),
-                mFailedKeys.isEmpty());
-    }
-
-    private static class SimpleCaptureListener extends CameraDevice.CaptureListener {
-        LinkedBlockingQueue<CaptureResult> mQueue = new LinkedBlockingQueue<CaptureResult>();
-
-        @Override
-        public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp)
-        {
-        }
-
-        @Override
-        public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
-                CaptureResult result) {
-            try {
-                mQueue.put(result);
-            } catch (InterruptedException e) {
-                throw new UnsupportedOperationException(
-                        "Can't handle InterruptedException in onCaptureCompleted");
-            }
-        }
-
-        @Override
-        public void onCaptureFailed(CameraDevice camera, CaptureRequest request,
-                CaptureFailure failure) {
-        }
-
-        @Override
-        public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId,
-                int frameNumber) {
-        }
-
-        public CaptureResult getCaptureResult(long timeout) throws InterruptedException {
-            CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
-            assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
-            return result;
-        }
-    }
-
-    private void createDefaultSurface(Size sz) {
-        mImageReader =
-                ImageReader.newInstance(sz.getWidth(),
-                        sz.getHeight(),
-                        ImageFormat.YUV_420_888,
-                        MAX_NUM_IMAGES);
-        mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler);
-        mSurface = mImageReader.getSurface();
-    }
-
-    /**
-     * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
-     * @hide keys.
-     *
-     */
-
-    /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
-     * The key entries below this point are generated from metadata
-     * definitions in /system/media/camera/docs. Do not modify by hand or
-     * modify the comment blocks at the start or end.
-     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
-
-    private static List<CameraMetadata.Key<?>> getAllCaptureResultKeys() {
-        ArrayList<CameraMetadata.Key<?>> resultKeys = new ArrayList<CameraMetadata.Key<?>>();
-        resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
-        resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
-        resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_AF_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
-        resultKeys.add(CaptureResult.CONTROL_MODE);
-        resultKeys.add(CaptureResult.CONTROL_AE_STATE);
-        resultKeys.add(CaptureResult.CONTROL_AF_STATE);
-        resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
-        resultKeys.add(CaptureResult.EDGE_MODE);
-        resultKeys.add(CaptureResult.FLASH_MODE);
-        resultKeys.add(CaptureResult.FLASH_STATE);
-        resultKeys.add(CaptureResult.JPEG_GPS_COORDINATES);
-        resultKeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
-        resultKeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
-        resultKeys.add(CaptureResult.JPEG_ORIENTATION);
-        resultKeys.add(CaptureResult.JPEG_QUALITY);
-        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
-        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
-        resultKeys.add(CaptureResult.LENS_APERTURE);
-        resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
-        resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
-        resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
-        resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
-        resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
-        resultKeys.add(CaptureResult.LENS_STATE);
-        resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
-        resultKeys.add(CaptureResult.REQUEST_FRAME_COUNT);
-        resultKeys.add(CaptureResult.SCALER_CROP_REGION);
-        resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
-        resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
-        resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
-        resultKeys.add(CaptureResult.SENSOR_TIMESTAMP);
-        resultKeys.add(CaptureResult.SENSOR_TEMPERATURE);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_IDS);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_LANDMARKS);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_RECTANGLES);
-        resultKeys.add(CaptureResult.STATISTICS_FACE_SCORES);
-        resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP);
-        resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
-        resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
-        resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
-        resultKeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
-        resultKeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
-        resultKeys.add(CaptureResult.TONEMAP_CURVE_RED);
-        resultKeys.add(CaptureResult.TONEMAP_MODE);
-        resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
-
-        // Add STATISTICS_FACES key separately here because it is not
-        // defined in metadata xml file.
-        resultKeys.add(CaptureResult.STATISTICS_FACES);
-
-        return resultKeys;
-    }
-
-    /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
-     * End generated code
-     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
-}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
index 1b892ba..614576c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCharacteristicsTest.java
@@ -80,6 +80,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidControlAeAvailableModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.control.aeAvailableModes",
+                        props.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.control.aeAvailableModes", allKeys.contains(
+                        CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidControlAeAvailableTargetFpsRanges() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -287,6 +310,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidEdgeAvailableEdgeModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.edge.availableEdgeModes",
+                        props.get(CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.edge.availableEdgeModes", allKeys.contains(
+                        CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidFlashInfoAvailable() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -310,6 +356,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidHotPixelAvailableHotPixelModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.hotPixel.availableHotPixelModes",
+                        props.get(CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.hotPixel.availableHotPixelModes", allKeys.contains(
+                        CameraCharacteristics.HOT_PIXEL_AVAILABLE_HOT_PIXEL_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidJpegAvailableThumbnailSizes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -455,6 +524,10 @@
             assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
                                         props);
 
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
             {
 
                 assertNotNull("Invalid property: android.lens.info.hyperfocalDistance",
@@ -517,6 +590,52 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidLensInfoFocusDistanceCalibration() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.lens.info.focusDistanceCalibration",
+                        props.get(CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.lens.info.focusDistanceCalibration", allKeys.contains(
+                        CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidNoiseReductionAvailableNoiseReductionModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.noiseReduction.availableNoiseReductionModes",
+                        props.get(CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.noiseReduction.availableNoiseReductionModes", allKeys.contains(
+                        CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidRequestMaxNumOutputStreams() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -540,6 +659,98 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidRequestMaxNumInputStreams() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.maxNumInputStreams",
+                        props.get(CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.maxNumInputStreams", allKeys.contains(
+                        CameraCharacteristics.REQUEST_MAX_NUM_INPUT_STREAMS));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidRequestPipelineMaxDepth() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.pipelineMaxDepth",
+                        props.get(CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.pipelineMaxDepth", allKeys.contains(
+                        CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidRequestPartialResultCount() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.partialResultCount",
+                        props.get(CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.partialResultCount", allKeys.contains(
+                        CameraCharacteristics.REQUEST_PARTIAL_RESULT_COUNT));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidRequestAvailableCapabilities() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.request.availableCapabilities",
+                        props.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.request.availableCapabilities", allKeys.contains(
+                        CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidScalerAvailableFormats() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -678,6 +889,306 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidScalerAvailableInputOutputFormatsMap() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.scaler.availableInputOutputFormatsMap",
+                        props.get(CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.scaler.availableInputOutputFormatsMap", allKeys.contains(
+                        CameraCharacteristics.SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidScalerAvailableStreamConfigurations() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.scaler.availableStreamConfigurations",
+                        props.get(CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.scaler.availableStreamConfigurations", allKeys.contains(
+                        CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidScalerAvailableMinFrameDurations() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.scaler.availableMinFrameDurations",
+                        props.get(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.scaler.availableMinFrameDurations", allKeys.contains(
+                        CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidScalerAvailableStallDurations() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.scaler.availableStallDurations",
+                        props.get(CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.scaler.availableStallDurations", allKeys.contains(
+                        CameraCharacteristics.SCALER_AVAILABLE_STALL_DURATIONS));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorReferenceIlluminant1() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sensor.referenceIlluminant1",
+                        props.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.referenceIlluminant1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorReferenceIlluminant2() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sensor.referenceIlluminant2",
+                        props.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.referenceIlluminant2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorCalibrationTransform1() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.calibrationTransform1",
+                        props.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.calibrationTransform1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorCalibrationTransform2() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.calibrationTransform2",
+                        props.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.calibrationTransform2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorColorTransform1() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.colorTransform1",
+                        props.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.colorTransform1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_COLOR_TRANSFORM1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorColorTransform2() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.colorTransform2",
+                        props.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.colorTransform2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_COLOR_TRANSFORM2));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorForwardMatrix1() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.forwardMatrix1",
+                        props.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.forwardMatrix1", allKeys.contains(
+                        CameraCharacteristics.SENSOR_FORWARD_MATRIX1));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorForwardMatrix2() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.forwardMatrix2",
+                        props.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.forwardMatrix2", allKeys.contains(
+                        CameraCharacteristics.SENSOR_FORWARD_MATRIX2));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorBaseGainFactor() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -705,6 +1216,33 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorBlackLevelPattern() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.blackLevelPattern",
+                        props.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.blackLevelPattern", allKeys.contains(
+                        CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorMaxAnalogSensitivity() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -755,6 +1293,60 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorProfileHueSatMapDimensions() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.profileHueSatMapDimensions",
+                        props.get(CameraCharacteristics.SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.profileHueSatMapDimensions", allKeys.contains(
+                        CameraCharacteristics.SENSOR_PROFILE_HUE_SAT_MAP_DIMENSIONS));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorAvailableTestPatternModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            assertNotNull("No hardware level reported! android.info.supportedHardwareLevel",
+                    hwLevel);
+            if (hwLevel == CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+            {
+
+                assertNotNull("Invalid property: android.sensor.availableTestPatternModes",
+                        props.get(CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.availableTestPatternModes", allKeys.contains(
+                        CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorInfoActiveArraySize() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -801,6 +1393,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorInfoColorFilterArrangement() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sensor.info.colorFilterArrangement",
+                        props.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.info.colorFilterArrangement", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidSensorInfoExposureTimeRange() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -870,6 +1485,52 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidSensorInfoPixelArraySize() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sensor.info.pixelArraySize",
+                        props.get(CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.info.pixelArraySize", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE));
+
+            }
+
+        }
+    }
+
+    public void testCameraCharacteristicsAndroidSensorInfoWhiteLevel() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sensor.info.whiteLevel",
+                        props.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sensor.info.whiteLevel", allKeys.contains(
+                        CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidStatisticsInfoAvailableFaceDetectModes() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -916,6 +1577,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidStatisticsInfoAvailableHotPixelMapModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.statistics.info.availableHotPixelMapModes",
+                        props.get(CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.statistics.info.availableHotPixelMapModes", allKeys.contains(
+                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidTonemapMaxCurvePoints() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -939,6 +1623,29 @@
         }
     }
 
+    public void testCameraCharacteristicsAndroidTonemapAvailableToneMapModes() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.tonemap.availableToneMapModes",
+                        props.get(CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.tonemap.availableToneMapModes", allKeys.contains(
+                        CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES));
+
+            }
+
+        }
+    }
+
     public void testCameraCharacteristicsAndroidInfoSupportedHardwareLevel() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
@@ -961,5 +1668,28 @@
 
         }
     }
+
+    public void testCameraCharacteristicsAndroidSyncMaxLatency() throws Exception {
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull(String.format("Can't get camera characteristics from: ID %s", ids[i]),
+                                        props);
+
+            {
+
+                assertNotNull("Invalid property: android.sync.maxLatency",
+                        props.get(CameraCharacteristics.SYNC_MAX_LATENCY));
+
+                List<Key<?>> allKeys = props.getKeys();
+                assertNotNull(String.format("Can't get camera characteristics keys from: ID %s",
+                        ids[i], props));
+                assertTrue("Key not in keys list: android.sync.maxLatency", allKeys.contains(
+                        CameraCharacteristics.SYNC_MAX_LATENCY));
+
+            }
+
+        }
+    }
 }
 
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index f68b10a..55cd5d2 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -19,62 +19,46 @@
 import static android.hardware.camera2.cts.CameraTestUtils.*;
 import static com.android.ex.camera2.blocking.BlockingStateListener.*;
 import static org.mockito.Mockito.*;
+import static android.hardware.camera2.CameraMetadata.*;
+import static android.hardware.camera2.CaptureRequest.*;
 
 import android.content.Context;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CaptureFailure;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.CaptureResult;
-import android.media.Image;
-import android.media.ImageReader;
-import android.os.Handler;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.os.SystemClock;
-import android.test.AndroidTestCase;
 import android.util.Log;
 import android.view.Surface;
 
 import com.android.ex.camera2.blocking.BlockingStateListener;
+
 import org.mockito.ArgumentMatcher;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 /**
  * <p>Basic test for CameraDevice APIs.</p>
  */
-public class CameraDeviceTest extends AndroidTestCase {
+public class CameraDeviceTest extends Camera2AndroidTestCase {
     private static final String TAG = "CameraDeviceTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
-    private CameraManager mCameraManager;
-    private BlockingStateListener mCameraListener;
-    private CameraTestThread mLooperThread;
-    private Handler mCallbackHandler;
-    private int mLatestState = STATE_UNINITIALIZED;
-
-    /**
-     * The error triggered flag starts out as false, and it will flip to true if any errors
-     * are ever caught; it won't be reset to false after that happens. This is due to the
-     * fact that when multiple tests are run back to back (as they are here), it's hard
-     * to associate the asynchronous error with the test that caused it (so we won't even try).
-     */
-    private boolean mErrorTriggered = false;
-    private ImageReader mReader;
-    private CameraTestThread mDummyThread;
-    private Surface mSurface;
-
-    private static final int CAMERA_CONFIGURE_TIMEOUT_MS = 2000;
-    private static final int CAPTURE_WAIT_TIMEOUT_MS = 2000;
     private static final int ERROR_LISTENER_WAIT_TIMEOUT_MS = 1000;
     private static final int REPEATING_CAPTURE_EXPECTED_RESULT_COUNT = 5;
-    // VGA size capture is required by CDD.
-    private static final int DEFAULT_CAPTURE_WIDTH = 640;
-    private static final int DEFAULT_CAPTURE_HEIGHT = 480;
     private static final int MAX_NUM_IMAGES = 5;
+    private static final int MIN_FPS_REQUIRED_FOR_STREAMING = 20;
+    private static final int AE_REGION_INDEX = 0;
+    private static final int AWB_REGION_INDEX = 1;
+    private static final int AF_REGION_INDEX = 2;
+
+    private BlockingStateListener mCameraMockListener;
+    private int mLatestState = STATE_UNINITIALIZED;
 
     private static int[] mTemplates = new int[] {
             CameraDevice.TEMPLATE_PREVIEW,
@@ -87,18 +71,11 @@
     public void setContext(Context context) {
         super.setContext(context);
         /**
-         * Workaround for mockito and JB-MR2 incompatibility
-         *
-         * Avoid java.lang.IllegalArgumentException: dexcache == null
-         * https://code.google.com/p/dexmaker/issues/detail?id=2
-         */
-        System.setProperty("dexmaker.dexcache", mContext.getCacheDir().toString());
-        /**
-         * Create errorlistener in context scope, to catch asynchronous device error.
+         * Create error listener in context scope, to catch asynchronous device error.
          * Use spy object here since we want to use the SimpleDeviceListener callback
          * implementation (spy doesn't stub the functions unless we ask it to do so).
          */
-        mCameraListener = spy(new BlockingStateListener());
+        mCameraMockListener = spy(new BlockingStateListener());
     }
 
     @Override
@@ -110,102 +87,356 @@
          * fail the rest of the tests. This is especially needed when error
          * callback is fired too late.
          */
-        verify(mCameraListener, never())
+        verify(mCameraMockListener, never())
                 .onError(
                     any(CameraDevice.class),
                     anyInt());
-        verify(mCameraListener, never())
+        verify(mCameraMockListener, never())
                 .onDisconnected(
                     any(CameraDevice.class));
 
-        mCameraManager = (CameraManager)mContext.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager", mCameraManager);
-        createDefaultSurface();
-        mLooperThread = new CameraTestThread();
-        mCallbackHandler = mLooperThread.start();
+        mCameraListener = mCameraMockListener;
+        createDefaultImageReader(DEFAULT_CAPTURE_SIZE, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                new ImageDropperListener());
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mDummyThread.close();
-        mReader.close();
         super.tearDown();
     }
 
-    public void testCameraDeviceCreateCaptureBuilder() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
-            try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device ID: %s", ids[i]), camera);
+    /**
+     * <p>
+     * Test camera capture request preview capture template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are auto.</li>
+     * <li>All sensor settings are not null.</li>
+     * <li>All ISP processing settings should be non-manual, and the camera
+     * device should make sure the stable frame rate is guaranteed for the given
+     * settings.</li>
+     * </ul>
+     */
+    public void testCameraDevicePreviewTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_PREVIEW);
+        }
 
+        // TODO: test the frame rate sustainability in preview use case test.
+    }
+
+    /**
+     * <p>
+     * Test camera capture request still capture template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are auto.</li>
+     * <li>All sensor settings are not null.</li>
+     * <li>All ISP processing settings should be non-manual, and the camera
+     * device should make sure the high quality takes priority to the stable
+     * frame rate for the given settings.</li>
+     * </ul>
+     */
+    public void testCameraDeviceStillTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_STILL_CAPTURE);
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera capture video recording template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly.
+     * It has the similar requirement as preview, with one difference:
+     * </p>
+     * <ul>
+     * <li>Frame rate should be stable, for example, wide fps range like [7, 30]
+     * is a bad setting.</li>
+     */
+    public void testCameraDeviceRecordingTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_RECORD);
+        }
+
+        // TODO: test the frame rate sustainability in recording use case test.
+    }
+
+    /**
+     *<p>Test camera capture video snapshot template.</p>
+     *
+     * <p>The request template returned by the camera device must include a necessary set of
+     * metadata keys, and their values must be set correctly. It has the similar requirement
+     * as recording, with an additional requirement: the settings should maximize image quality
+     * without compromising stable frame rate.</p>
+     */
+    public void testCameraDeviceVideoSnapShotTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+        }
+
+        // TODO: test the frame rate sustainability in video snapshot use case test.
+    }
+
+    /**
+     *<p>Test camera capture request zero shutter lag template.</p>
+     *
+     * <p>The request template returned by the camera device must include a necessary set of
+     * metadata keys, and their values must be set correctly. It has the similar requirement
+     * as preview, with an additional requirement: </p>
+     */
+    public void testCameraDeviceZSLTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG);
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera capture request manual template.
+     * </p>
+     *
+     * <p>
+     * The request template returned by the camera device must include a
+     * necessary set of metadata keys, and their values must be set correctly. It
+     * mainly requires below settings:
+     * </p>
+     * <ul>
+     * <li>All 3A settings are manual.</li>
+     * <li>ISP processing parameters are set to preview quality.</li>
+     * <li>The manual capture parameters (exposure, sensitivity, and so on) are
+     * set to reasonable defaults.</li>
+     * </ul>
+     */
+    public void testCameraDeviceManualTemplate() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            captureTemplateTestByCamera(mCameraIds[i], CameraDevice.TEMPLATE_MANUAL);
+        }
+    }
+
+    public void testCameraDeviceCreateCaptureBuilder() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
                 /**
                  * Test: that each template type is supported, and that its required fields are
                  * present.
                  */
                 for (int j = 0; j < mTemplates.length; j++) {
-                    CaptureRequest.Builder capReq = camera.createCaptureRequest(mTemplates[j]);
+                    CaptureRequest.Builder capReq = mCamera.createCaptureRequest(mTemplates[j]);
                     assertNotNull("Failed to create capture request", capReq);
                     assertNotNull("Missing field: SENSOR_EXPOSURE_TIME",
                             capReq.get(CaptureRequest.SENSOR_EXPOSURE_TIME));
                     assertNotNull("Missing field: SENSOR_SENSITIVITY",
                             capReq.get(CaptureRequest.SENSOR_SENSITIVITY));
-
-                    // TODO: Add more tests to check more fields.
                 }
             }
             finally {
-                if (camera != null) {
-                    camera.close();
-                }
+                closeDevice(mCameraIds[i], mCameraMockListener);
             }
         }
     }
 
     public void testCameraDeviceSetErrorListener() throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
+        for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
-                        mCameraListener, mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
-
+                openDevice(mCameraIds[i], mCameraMockListener);
                 /**
                  * Test: that the error listener can be set without problems.
                  * Also, wait some time to check if device doesn't run into error.
                  */
                 SystemClock.sleep(ERROR_LISTENER_WAIT_TIMEOUT_MS);
-                verify(mCameraListener, never())
+                verify(mCameraMockListener, never())
                         .onError(
                                 any(CameraDevice.class),
                                 anyInt());
             }
             finally {
-                if (camera != null) {
-                    camera.close();
-                }
+                closeDevice(mCameraIds[i], mCameraMockListener);
             }
         }
     }
 
     public void testCameraDeviceCapture() throws Exception {
-        runCaptureTest(false, false);
+        runCaptureTest(/*burst*/false, /*repeating*/false, /*flush*/false);
     }
 
     public void testCameraDeviceCaptureBurst() throws Exception {
-        runCaptureTest(true, false);
+        runCaptureTest(/*burst*/true, /*repeating*/false, /*flush*/false);
     }
 
     public void testCameraDeviceRepeatingRequest() throws Exception {
-        runCaptureTest(false, true);
+        runCaptureTest(/*burst*/false, /*repeating*/true, /*flush*/false);
     }
 
     public void testCameraDeviceRepeatingBurst() throws Exception {
-        runCaptureTest(true, true);
+        runCaptureTest(/*burst*/true, /*repeating*/true, /*flush*/false);
+    }
+
+    /**
+     * Test {@link CameraDevice#flush} API.
+     *
+     * <p>
+     * Flush is the fastest way to idle the camera device for reconfiguration
+     * with {@link #configureOutputs}, at the cost of discarding in-progress
+     * work. Once the flush is complete, the idle callback will be called.
+     * </p>
+     */
+    public void testCameraDeviceFlush() throws Exception {
+        runCaptureTest(/*burst*/false, /*repeating*/true, /*flush*/true);
+        runCaptureTest(/*burst*/true, /*repeating*/true, /*flush*/true);
+        /**
+         * TODO: this is only basic test of flush. we probably should also test below cases:
+         *
+         * 1. Performance. Make sure flush is faster than stopRepeating, we can test each one
+         * a couple of times, then compare the average. Also, for flush() alone, we should make
+         * sure it doesn't take too long time (e.g. <100ms for full devices, <500ms for limited
+         * devices), after the flush, we should be able to get all results back very quickly.
+         * This can be done in performance test.
+         *
+         * 2. Make sure all in-flight request comes back after flush, e.g. submit a couple of
+         * long exposure single captures, then flush, then check if we can get the pending
+         * request back quickly.
+         *
+         * 3. Also need check onCaptureSequenceCompleted for repeating burst after flush().
+         */
+    }
+
+    /**
+     * Test invalid capture (e.g. null or empty capture request).
+     */
+    public void testInvalidCapture() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i], mCameraMockListener);
+                waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
+
+                prepareCapture();
+
+                invalidRequestCaptureTestByCamera();
+            }
+            finally {
+                closeDevice(mCameraIds[i], mCameraMockListener);
+            }
+        }
+    }
+
+    private void invalidRequestCaptureTestByCamera() throws Exception {
+        List<CaptureRequest> emptyRequests = new ArrayList<CaptureRequest>();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest unConfiguredRequest = requestBuilder.build();
+        List<CaptureRequest> unConfiguredRequests = new ArrayList<CaptureRequest>();
+        unConfiguredRequests.add(unConfiguredRequest);
+
+        try {
+            // Test: CameraDevice capture should throw IAE for null request.
+            mCamera.capture(/*request*/null, /*listener*/null, mHandler);
+            mCollector.addMessage(
+                    "CameraDevice capture should throw IllegalArgumentException for null request");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice capture should throw IAE for request
+            // without surface configured.
+            mCamera.capture(unConfiguredRequest, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice capture should throw " +
+                    "IllegalArgumentException for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice setRepeatingRequest should throw IAE for null request.
+            mCamera.setRepeatingRequest(/*request*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice setRepeatingRequest should throw" +
+                    "IllegalArgumentException for null request");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice setRepeatingRequest should throw IAE for for request
+            // without surface configured.
+            mCamera.setRepeatingRequest(unConfiguredRequest, /*listener*/null, mHandler);
+            mCollector.addMessage("Capture zero burst should throw IllegalArgumentException" +
+                    "for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice captureBurst should throw IAE for null request list.
+            mCamera.captureBurst(/*requests*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice captureBurst should throw" +
+                    "IllegalArgumentException for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice captureBurst should throw IAE for empty request list.
+            mCamera.captureBurst(emptyRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice captureBurst should throw" +
+                    " IllegalArgumentException for empty request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice captureBurst should throw IAE for request
+            // without surface configured.
+            mCamera.captureBurst(unConfiguredRequests, /*listener*/null, mHandler);
+            fail("CameraDevice captureBurst should throw IllegalArgumentException" +
+                    "for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice setRepeatingBurst should throw IAE for null request list.
+            mCamera.setRepeatingBurst(/*requests*/null, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice setRepeatingBurst should throw" +
+                    "IllegalArgumentException for null request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice setRepeatingBurst should throw IAE for empty request list.
+            mCamera.setRepeatingBurst(emptyRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice setRepeatingBurst should throw" +
+                    "IllegalArgumentException for empty request list");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
+
+        try {
+            // Test: CameraDevice setRepeatingBurst should throw IAE for request
+            // without surface configured.
+            mCamera.setRepeatingBurst(unConfiguredRequests, /*listener*/null, mHandler);
+            mCollector.addMessage("CameraDevice setRepeatingBurst should throw" +
+                    "IllegalArgumentException for request without surface configured");
+        } catch (IllegalArgumentException e) {
+            // Pass.
+        }
     }
 
     private class IsCameraMetadataNotEmpty<T extends CameraMetadata>
@@ -225,31 +456,33 @@
         }
     }
 
-    private void runCaptureTest(boolean burst, boolean repeating) throws Exception {
-        String[] ids = mCameraManager.getCameraIdList();
-        for (int i = 0; i < ids.length; i++) {
-            CameraDevice camera = null;
+    /**
+     * Run capture test with different test configurations.
+     *
+     * @param burst If the test uses {@link CameraDevice#captureBurst} or
+     * {@link CameraDevice#setRepeatingBurst} to capture the burst.
+     * @param repeating If the test uses {@link CameraDevice#setRepeatingBurst} or
+     * {@link CameraDevice#setRepeatingRequest} for repeating capture.
+     * @param flush If the test uses {@link CameraDevice#flush} to stop the repeating capture.
+     * It has no effect if repeating is false.
+     */
+    private void runCaptureTest(boolean burst, boolean repeating, boolean flush) throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
             try {
-                camera = CameraTestUtils.openCamera(mCameraManager, ids[i],
-                        mCameraListener, mCallbackHandler);
-                assertNotNull(
-                        String.format("Failed to open camera device %s", ids[i]), camera);
+                openDevice(mCameraIds[i], mCameraMockListener);
                 waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
 
-                prepareCapture(camera);
+                prepareCapture();
 
                 if (!burst) {
                     // Test: that a single capture of each template type succeeds.
                     for (int j = 0; j < mTemplates.length; j++) {
-                        captureSingleShot(camera, ids[i], mTemplates[j], repeating);
+                        captureSingleShot(mCameraIds[i], mTemplates[j], repeating, flush);
                     }
                 }
                 else {
-                    // Test: burst of zero shots
-                    captureBurstShot(camera, ids[i], mTemplates, 0, repeating);
-
                     // Test: burst of one shot
-                    captureBurstShot(camera, ids[i], mTemplates, 1, repeating);
+                    captureBurstShot(mCameraIds[i], mTemplates, 1, repeating, flush);
 
                     int[] templates = new int[] {
                             CameraDevice.TEMPLATE_STILL_CAPTURE,
@@ -260,36 +493,34 @@
                             };
 
                     // Test: burst of 5 shots of the same template type
-                    captureBurstShot(camera, ids[i], templates, templates.length, repeating);
+                    captureBurstShot(mCameraIds[i], templates, templates.length, repeating, flush);
 
                     // Test: burst of 5 shots of different template types
-                    captureBurstShot(camera, ids[i], mTemplates, mTemplates.length, repeating);
+                    captureBurstShot(
+                            mCameraIds[i], mTemplates, mTemplates.length, repeating, flush);
                 }
-                verify(mCameraListener, never())
+                verify(mCameraMockListener, never())
                         .onError(
                                 any(CameraDevice.class),
                                 anyInt());
             }
             finally {
-                if (camera != null) {
-                    camera.close();
-                }
+                closeDevice(mCameraIds[i], mCameraMockListener);
             }
         }
     }
 
     private void captureSingleShot(
-            CameraDevice camera,
             String id,
             int template,
-            boolean repeating) throws Exception {
+            boolean repeating, boolean flush) throws Exception {
 
         assertEquals("Bad initial state for preparing to capture",
                 mLatestState, STATE_IDLE);
 
-        CaptureRequest.Builder requestBuilder = camera.createCaptureRequest(template);
+        CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(template);
         assertNotNull("Failed to create capture request", requestBuilder);
-        requestBuilder.addTarget(mSurface);
+        requestBuilder.addTarget(mReaderSurface);
         CameraDevice.CaptureListener mockCaptureListener =
                 mock(CameraDevice.CaptureListener.class);
 
@@ -297,30 +528,29 @@
             Log.v(TAG, String.format("Capturing shot for device %s, template %d",
                     id, template));
         }
-        if (!repeating) {
-            camera.capture(requestBuilder.build(), mockCaptureListener, mCallbackHandler);
-        }
-        else {
-            camera.setRepeatingRequest(requestBuilder.build(), mockCaptureListener,
-                    mCallbackHandler);
-        }
+
+        startCapture(requestBuilder.build(), repeating, mockCaptureListener, mHandler);
         waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
 
         int expectedCaptureResultCount = repeating ? REPEATING_CAPTURE_EXPECTED_RESULT_COUNT : 1;
-        verifyCaptureResults(camera, mockCaptureListener, expectedCaptureResultCount);
+        verifyCaptureResults(mockCaptureListener, expectedCaptureResultCount);
 
         if (repeating) {
-            camera.stopRepeating();
+            if (flush) {
+                mCamera.flush();
+            } else {
+                mCamera.stopRepeating();
+            }
         }
         waitForState(STATE_IDLE, CAMERA_CONFIGURE_TIMEOUT_MS);
     }
 
     private void captureBurstShot(
-            CameraDevice camera,
             String id,
             int[] templates,
             int len,
-            boolean repeating) throws Exception {
+            boolean repeating,
+            boolean flush) throws Exception {
 
         assertEquals("Bad initial state for preparing to capture",
                 mLatestState, STATE_IDLE);
@@ -328,9 +558,9 @@
         assertTrue("Invalid args to capture function", len <= templates.length);
         List<CaptureRequest> requests = new ArrayList<CaptureRequest>();
         for (int i = 0; i < len; i++) {
-            CaptureRequest.Builder requestBuilder = camera.createCaptureRequest(templates[i]);
+            CaptureRequest.Builder requestBuilder = mCamera.createCaptureRequest(templates[i]);
             assertNotNull("Failed to create capture request", requestBuilder);
-            requestBuilder.addTarget(mSurface);
+            requestBuilder.addTarget(mReaderSurface);
             requests.add(requestBuilder.build());
         }
         CameraDevice.CaptureListener mockCaptureListener =
@@ -341,10 +571,10 @@
         }
 
         if (!repeating) {
-            camera.captureBurst(requests, mockCaptureListener, mCallbackHandler);
+            mCamera.captureBurst(requests, mockCaptureListener, mHandler);
         }
         else {
-            camera.setRepeatingBurst(requests, mockCaptureListener, mCallbackHandler);
+            mCamera.setRepeatingBurst(requests, mockCaptureListener, mHandler);
         }
         waitForState(STATE_ACTIVE, CAMERA_CONFIGURE_TIMEOUT_MS);
 
@@ -353,88 +583,309 @@
             expectedResultCount *= REPEATING_CAPTURE_EXPECTED_RESULT_COUNT;
         }
 
-        verifyCaptureResults(camera, mockCaptureListener, expectedResultCount);
+        verifyCaptureResults(mockCaptureListener, expectedResultCount);
 
         if (repeating) {
-            camera.stopRepeating();
+            if (flush) {
+                mCamera.flush();
+            } else {
+                mCamera.stopRepeating();
+            }
         }
         waitForState(STATE_IDLE, CAMERA_CONFIGURE_TIMEOUT_MS);
     }
 
     // Precondition: Device must be in known IDLE/UNCONFIGURED state (has been waited for)
-    private void prepareCapture(CameraDevice camera) throws Exception {
+    private void prepareCapture() throws Exception {
         assertTrue("Bad initial state for preparing to capture",
                 mLatestState == STATE_IDLE || mLatestState == STATE_UNCONFIGURED);
 
         List<Surface> outputSurfaces = new ArrayList<Surface>(1);
-        outputSurfaces.add(mSurface);
-        camera.configureOutputs(outputSurfaces);
+        outputSurfaces.add(mReaderSurface);
+        mCamera.configureOutputs(outputSurfaces);
         waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
         waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
-    }
-
-    /**
-     * Dummy listener that release the image immediately once it is available.
-     * It can be used for the case where we don't care the image data at all.
-     * TODO: move it to the CameraTestUtil class.
-     */
-    private class ImageDropperListener implements ImageReader.OnImageAvailableListener {
-        @Override
-        public void onImageAvailable(ImageReader reader) {
-            Image image = null;
-            try {
-                image = reader.acquireNextImage();
-            } finally {
-                if (image != null) {
-                    image.close();
-                }
-            }
-        }
-    }
-
-    private void createDefaultSurface() throws Exception {
-        mReader =
-                ImageReader.newInstance(DEFAULT_CAPTURE_WIDTH,
-                        DEFAULT_CAPTURE_HEIGHT,
-                        ImageFormat.YUV_420_888,
-                        MAX_NUM_IMAGES);
-        mSurface = mReader.getSurface();
-        // Create dummy image listener since we don't care the image data in this test.
-        ImageReader.OnImageAvailableListener listener = new ImageDropperListener();
-        mDummyThread = new CameraTestThread();
-        mReader.setOnImageAvailableListener(listener, mDummyThread.start());
-    }
+}
 
     private void waitForState(int state, long timeout) {
-        mCameraListener.waitForState(state, timeout);
+        mCameraMockListener.waitForState(state, timeout);
         mLatestState = state;
     }
 
     private void verifyCaptureResults(
-            CameraDevice camera,
             CameraDevice.CaptureListener mockListener,
             int expectResultCount) {
         // Should receive expected number of capture results.
         verify(mockListener,
                 timeout(CAPTURE_WAIT_TIMEOUT_MS).atLeast(expectResultCount))
                         .onCaptureCompleted(
-                                eq(camera),
+                                eq(mCamera),
                                 isA(CaptureRequest.class),
                                 argThat(new IsCameraMetadataNotEmpty<CaptureResult>()));
         // Should not receive any capture failed callbacks.
         verify(mockListener, never())
                         .onCaptureFailed(
-                                eq(camera),
+                                eq(mCamera),
                                 argThat(new IsCameraMetadataNotEmpty<CaptureRequest>()),
                                 isA(CaptureFailure.class));
         // Should receive expected number of capture shutter calls
         verify(mockListener,
                 atLeast(expectResultCount))
                         .onCaptureStarted(
-                               eq(camera),
+                               eq(mCamera),
                                isA(CaptureRequest.class),
                                anyLong());
 
     }
 
+    private void checkFpsRange(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        Key<int[]> fpsRangeKey = CONTROL_AE_TARGET_FPS_RANGE;
+        int[] fpsRange;
+        if ((fpsRange = mCollector.expectKeyValueNotNull(request, fpsRangeKey)) == null) {
+            return;
+        }
+
+        // TODO: Use generated array dimensions
+        final int CONTROL_AE_TARGET_FPS_RANGE_SIZE = 2;
+        final int CONTROL_AE_TARGET_FPS_RANGE_MIN = 0;
+        final int CONTROL_AE_TARGET_FPS_RANGE_MAX = 1;
+
+        String cause = "Failed with fps range size check";
+        if (!mCollector.expectEquals(cause, CONTROL_AE_TARGET_FPS_RANGE_SIZE, fpsRange.length)) {
+            return;
+        }
+
+        int minFps = fpsRange[CONTROL_AE_TARGET_FPS_RANGE_MIN];
+        int maxFps = fpsRange[CONTROL_AE_TARGET_FPS_RANGE_MAX];
+        int[] availableFpsRange = props
+                .get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+        boolean foundRange = false;
+        for (int i = 0; i < availableFpsRange.length; i += CONTROL_AE_TARGET_FPS_RANGE_SIZE) {
+            if (minFps == availableFpsRange[i + CONTROL_AE_TARGET_FPS_RANGE_MIN]
+                    && maxFps == availableFpsRange[i + CONTROL_AE_TARGET_FPS_RANGE_MAX]) {
+                foundRange = true;
+                break;
+            }
+        }
+        if (!foundRange) {
+            mCollector.addMessage(String.format("Unable to find the fps range (%d, %d)",
+                    minFps, maxFps));
+            return;
+        }
+
+        if (template != CameraDevice.TEMPLATE_MANUAL &&
+                template != CameraDevice.TEMPLATE_STILL_CAPTURE) {
+            if (maxFps < MIN_FPS_REQUIRED_FOR_STREAMING) {
+                mCollector.addMessage("Max fps should be at least "
+                        + MIN_FPS_REQUIRED_FOR_STREAMING);
+                return;
+            }
+
+            // Need give fixed frame rate for video recording template.
+            if (template == CameraDevice.TEMPLATE_RECORD) {
+                if (maxFps != minFps) {
+                    mCollector.addMessage("Video recording frame rate should be fixed");
+                }
+            }
+        }
+    }
+
+    private void checkAfMode(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        boolean hasFocuser =
+                props.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE) > 0f;
+
+        if (!hasFocuser) {
+            return;
+        }
+
+        int targetAfMode = CONTROL_AF_MODE_AUTO;
+        byte[] availableAfMode = props.get(CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES);
+        if (template == CameraDevice.TEMPLATE_PREVIEW ||
+                template == CameraDevice.TEMPLATE_STILL_CAPTURE ||
+                template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG) {
+            // Default to CONTINUOUS_PICTURE if it is available, otherwise AUTO.
+            for (int i = 0; i < availableAfMode.length; i++) {
+                if (availableAfMode[i] == CONTROL_AF_MODE_CONTINUOUS_PICTURE) {
+                    targetAfMode = CONTROL_AF_MODE_CONTINUOUS_PICTURE;
+                    break;
+                }
+            }
+        } else if (template == CameraDevice.TEMPLATE_RECORD ||
+                template == CameraDevice.TEMPLATE_VIDEO_SNAPSHOT) {
+            // Default to CONTINUOUS_VIDEO if it is available, otherwise AUTO.
+            for (int i = 0; i < availableAfMode.length; i++) {
+                if (availableAfMode[i] == CONTROL_AF_MODE_CONTINUOUS_VIDEO) {
+                    targetAfMode = CONTROL_AF_MODE_CONTINUOUS_VIDEO;
+                    break;
+                }
+            }
+        } else if (template == CameraDevice.TEMPLATE_MANUAL) {
+            targetAfMode = CONTROL_AF_MODE_OFF;
+        }
+
+        mCollector.expectKeyValueEquals(request, CONTROL_AF_MODE, targetAfMode);
+        mCollector.expectKeyValueNotNull(request, LENS_FOCUS_DISTANCE);
+    }
+
+    /**
+     * <p>Check if the request settings are suitable for a given request template.</p>
+     *
+     * <p>This function doesn't fail the test immediately, it updates the
+     * test pass/fail status and appends the failure message to the error collector each key.</p>
+     *
+     * @param request The request to be checked.
+     * @param template The capture template targeted by this request.
+     * @param props The CameraCharacteristics this request is checked against with.
+     */
+    private void checkRequestForTemplate(CaptureRequest.Builder request, int template,
+            CameraCharacteristics props) {
+        // 3A settings--control.mode.
+        if (template != CameraDevice.TEMPLATE_MANUAL) {
+            mCollector.expectKeyValueEquals(request, CONTROL_MODE, CONTROL_MODE_AUTO);
+        }
+
+        // 3A settings--AE/AWB/AF.
+        int[] maxRegions = props.get(CameraCharacteristics.CONTROL_MAX_REGIONS);
+        checkAfMode(request, template, props);
+        checkFpsRange(request, template, props);
+        if (template == CameraDevice.TEMPLATE_MANUAL) {
+            mCollector.expectKeyValueEquals(request, CONTROL_MODE, CONTROL_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE, CONTROL_AWB_MODE_OFF);
+
+        } else {
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE, CONTROL_AE_MODE_ON);
+            mCollector.expectKeyValueNotEquals(request, CONTROL_AE_ANTIBANDING_MODE,
+                    CONTROL_AE_ANTIBANDING_MODE_OFF);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
+            mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
+                    CONTROL_AE_PRECAPTURE_TRIGGER_IDLE);
+
+            mCollector.expectKeyValueEquals(request, CONTROL_AF_TRIGGER, CONTROL_AF_TRIGGER_IDLE);
+
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_MODE, CONTROL_AWB_MODE_AUTO);
+            mCollector.expectKeyValueEquals(request, CONTROL_AWB_LOCK, false);
+
+            // Check 3A regions.
+            if (VERBOSE) {
+                Log.v(TAG, "maxRegions is: " + Arrays.toString(maxRegions));
+            }
+            if (maxRegions[AE_REGION_INDEX] > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AE_REGIONS);
+            }
+            if (maxRegions[AWB_REGION_INDEX] > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AWB_REGIONS);
+            }
+            if (maxRegions[AF_REGION_INDEX] > 0) {
+                mCollector.expectKeyValueNotNull(request, CONTROL_AF_REGIONS);
+            }
+        }
+
+        // Sensor settings.
+        float[] availableApertures =
+                props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES);
+        if (availableApertures.length > 1) {
+            mCollector.expectKeyValueNotNull(request, LENS_APERTURE);
+        }
+
+        float[] availableFilters =
+                props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FILTER_DENSITIES);
+        if (availableFilters.length > 1) {
+            mCollector.expectKeyValueNotNull(request, LENS_FILTER_DENSITY);
+        }
+
+        float[] availableFocalLen =
+                props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
+        if (availableFocalLen.length > 1) {
+            mCollector.expectKeyValueNotNull(request, LENS_FOCAL_LENGTH);
+        }
+
+        byte[] availableOIS =
+                props.get(CameraCharacteristics.LENS_INFO_AVAILABLE_OPTICAL_STABILIZATION);
+        if (availableOIS.length > 1) {
+            mCollector.expectKeyValueNotNull(request, LENS_OPTICAL_STABILIZATION_MODE);
+        }
+
+        mCollector.expectKeyValueEquals(request, BLACK_LEVEL_LOCK, false);
+        mCollector.expectKeyValueNotNull(request, SENSOR_FRAME_DURATION);
+        mCollector.expectKeyValueNotNull(request, SENSOR_EXPOSURE_TIME);
+        mCollector.expectKeyValueNotNull(request, SENSOR_SENSITIVITY);
+
+        // ISP-processing settings.
+        mCollector.expectKeyValueEquals(
+                request, STATISTICS_FACE_DETECT_MODE, STATISTICS_FACE_DETECT_MODE_OFF);
+        mCollector.expectKeyValueEquals(request, FLASH_MODE, FLASH_MODE_OFF);
+        mCollector.expectKeyValueEquals(
+                request, STATISTICS_LENS_SHADING_MAP_MODE, STATISTICS_LENS_SHADING_MAP_MODE_OFF);
+
+        if (template == CameraDevice.TEMPLATE_STILL_CAPTURE) {
+            // Not enforce high quality here, as some devices may not effectively have high quality
+            // mode.
+            mCollector.expectKeyValueNotEquals(
+                    request, COLOR_CORRECTION_MODE, COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
+
+            List<Byte> availableEdgeModes =
+                    Arrays.asList(toObject(mStaticInfo.getAvailableEdgeModesChecked()));
+            if (availableEdgeModes.contains((byte)EDGE_MODE_HIGH_QUALITY)) {
+                mCollector.expectKeyValueEquals(request, EDGE_MODE, EDGE_MODE_HIGH_QUALITY);
+            } else if (availableEdgeModes.contains((byte)EDGE_MODE_FAST)) {
+                mCollector.expectKeyValueEquals(request, EDGE_MODE, EDGE_MODE_FAST);
+            } else {
+                mCollector.expectKeyValueEquals(request, EDGE_MODE, EDGE_MODE_OFF);
+            }
+
+            List<Byte> availableNoiseReductionModes =
+                    Arrays.asList(toObject(mStaticInfo.getAvailableNoiseReductionModesChecked()));
+            if (availableNoiseReductionModes.contains((byte)NOISE_REDUCTION_MODE_HIGH_QUALITY)) {
+                mCollector.expectKeyValueEquals(
+                        request, NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_HIGH_QUALITY);
+            } else if (availableNoiseReductionModes.contains((byte)NOISE_REDUCTION_MODE_FAST)) {
+                mCollector.expectKeyValueEquals(
+                        request, NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_FAST);
+            } else {
+                mCollector.expectKeyValueEquals(
+                        request, NOISE_REDUCTION_MODE, NOISE_REDUCTION_MODE_OFF);
+            }
+
+            List<Byte> availableToneMapModes =
+                    Arrays.asList(toObject(mStaticInfo.getAvailableToneMapModesChecked()));
+            if (availableToneMapModes.contains((byte)TONEMAP_MODE_HIGH_QUALITY)) {
+                mCollector.expectKeyValueEquals(request, TONEMAP_MODE, TONEMAP_MODE_HIGH_QUALITY);
+            } else {
+                mCollector.expectKeyValueEquals(request, TONEMAP_MODE, TONEMAP_MODE_FAST);
+            }
+        } else {
+            mCollector.expectKeyValueNotNull(request, EDGE_MODE);
+            mCollector.expectKeyValueNotNull(request, NOISE_REDUCTION_MODE);
+            mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE, TONEMAP_MODE_CONTRAST_CURVE);
+        }
+
+        mCollector.expectKeyValueEquals(request, CONTROL_CAPTURE_INTENT, template);
+
+        // TODO: use the list of keys from CameraCharacteristics to avoid expecting
+        //       keys which are not available by this CameraDevice.
+    }
+
+    private void captureTemplateTestByCamera(String cameraId, int template) throws Exception {
+        try {
+            openDevice(cameraId, mCameraMockListener);
+
+            assertTrue("Camera template " + template + " is out of range!",
+                    template >= CameraDevice.TEMPLATE_PREVIEW
+                            && template <= CameraDevice.TEMPLATE_MANUAL);
+
+            mCollector.setCameraId(cameraId);
+            CaptureRequest.Builder request = mCamera.createCaptureRequest(template);
+            assertNotNull("Failed to create capture request for template " + template, request);
+
+            CameraCharacteristics props = mStaticInfo.getCharacteristics();
+            checkRequestForTemplate(request, template, props);
+        }
+        finally {
+            closeDevice(cameraId, mCameraMockListener);
+        }
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 7091cac..9719278 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -16,17 +16,32 @@
 
 package android.hardware.camera2.cts;
 
+import static org.mockito.Mockito.*;
+import static org.mockito.AdditionalMatchers.not;
+import static org.mockito.AdditionalMatchers.and;
+
 import android.content.Context;
 import android.content.pm.PackageManager;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.StateListener;
 import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.cts.CameraTestUtils.MockStateListener;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
 import android.os.Handler;
+import android.os.HandlerThread;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.InOrder;
+
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 
 /**
  * <p>Basic test for CameraManager class.</p>
@@ -34,14 +49,15 @@
 public class CameraManagerTest extends AndroidTestCase {
     private static final String TAG = "CameraManagerTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
     private static final int NUM_CAMERA_REOPENS = 10;
 
     private PackageManager mPackageManager;
     private CameraManager mCameraManager;
     private NoopCameraListener mListener;
-    private CameraTestThread mLooperThread;
+    private HandlerThread mHandlerThread;
     private Handler mHandler;
+    private BlockingStateListener mCameraListener;
+    private CameraErrorCollector mCollector;
 
     @Override
     public void setContext(Context context) {
@@ -57,16 +73,45 @@
     protected void setUp() throws Exception {
         super.setUp();
 
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
+        mCameraListener = spy(new BlockingStateListener());
+
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCollector = new CameraErrorCollector();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        mLooperThread.close();
+        mHandlerThread.quitSafely();
         mHandler = null;
 
-        super.tearDown();
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Verifies that the reason is in the range of public-only codes.
+     */
+    private static int checkCameraAccessExceptionReason(CameraAccessException e) {
+        int reason = e.getReason();
+
+        switch (reason) {
+            case CameraAccessException.CAMERA_DISABLED:
+            case CameraAccessException.CAMERA_DISCONNECTED:
+            case CameraAccessException.CAMERA_ERROR:
+                return reason;
+        }
+
+        fail("Invalid CameraAccessException code: " + reason);
+
+        return -1; // unreachable
     }
 
     public void testCameraManagerGetDeviceIdList() throws Exception {
@@ -135,12 +180,12 @@
         for (int i = 0; i < ids.length; i++) {
             invalidId.append(ids[i]);
         }
+
         try {
-            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(
+            mCameraManager.getCameraCharacteristics(
                 invalidId.toString());
             fail(String.format("Accepted invalid camera ID: %s", invalidId.toString()));
-        }
-        catch (IllegalArgumentException e) {
+        } catch (IllegalArgumentException e) {
             // This is the exception that should be thrown in this case.
         }
     }
@@ -150,84 +195,263 @@
         String[] ids = mCameraManager.getCameraIdList();
         for (int i = 0; i < ids.length; i++) {
             for (int j = 0; j < NUM_CAMERA_REOPENS; j++) {
-                CameraDevice camera = CameraTestUtils.openCamera(mCameraManager, ids[i], mHandler);
-                assertNotNull(
-                    String.format("Failed to open camera device ID: %s", ids[i]), camera);
-                camera.close();
+                CameraDevice camera = null;
+                try {
+                    MockStateListener mockListener = MockStateListener.mock();
+                    mCameraListener = new BlockingStateListener(mockListener);
+
+                    mCameraManager.openCamera(ids[i], mCameraListener, mHandler);
+
+                    // Block until unConfigured
+                    mCameraListener.waitForState(BlockingStateListener.STATE_UNCONFIGURED,
+                            CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                    // Ensure state transitions are in right order:
+                    // -- 1) Opened
+                    // -- 2) Unconfigured
+                    // Ensure no other state transitions have occurred:
+                    camera = verifyCameraStateOpenedThenUnconfigured(ids[i], mockListener);
+                } finally {
+                    if (camera != null) {
+                        camera.close();
+                    }
+                }
             }
         }
     }
 
     /**
-     * Test: that all camera devices can be open at the same time, or the appropriate
-     * exception is thrown if this can't be done.
+     * Test: one or more camera devices can be open at the same time, or the right error state
+     * is set if this can't be done.
      */
     public void testCameraManagerOpenAllCameras() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
-        CameraDevice[] cameras = new CameraDevice[ids.length];
+        assertNotNull("Camera ids shouldn't be null", ids);
+
+        // Skip test if the device doesn't have multiple cameras.
+        if (ids.length <= 1) {
+            return;
+        }
+
+        List<CameraDevice> cameraList = new ArrayList<CameraDevice>();
+        List<MockStateListener> listenerList = new ArrayList<MockStateListener>();
         try {
             for (int i = 0; i < ids.length; i++) {
-                try {
-                    cameras[i] = CameraTestUtils.openCamera(mCameraManager, ids[i], mHandler);
+                // Ignore state changes from other cameras
+                MockStateListener mockListener = MockStateListener.mock();
+                mCameraListener = new BlockingStateListener(mockListener);
 
-                    /**
-                     * If the camera can't be opened, should throw an exception, rather than
-                     * returning null.
-                     */
-                    assertNotNull(
-                        String.format("Failed to open camera device ID: %s", ids[i]),
-                        cameras[i]);
+                /**
+                 * Track whether or not we got a synchronous error from openCamera.
+                 *
+                 * A synchronous error must also be accompanied by an asynchronous
+                 * StateListener#onError callback.
+                 */
+                boolean expectingError = false;
+
+                String cameraId = ids[i];
+                try {
+                    mCameraManager.openCamera(cameraId, mCameraListener,
+                            mHandler);
+                } catch (CameraAccessException e) {
+                    if (checkCameraAccessExceptionReason(e) == CameraAccessException.CAMERA_ERROR) {
+                        expectingError = true;
+                    } else {
+                        // TODO: We should handle a Disabled camera by passing here and elsewhere
+                        fail("Camera must not be disconnected or disabled for this test" + ids[i]);
+                    }
                 }
-                catch (CameraAccessException e) {
-                    /**
-                     * This is the expected behavior if the camera can't be opened due to
-                     * limitations on how many devices can be open simultaneously.
-                     */
-                    assertEquals(
-                        String.format("Invalid exception reason: %s", e.getReason()),
-                        CameraAccessException.MAX_CAMERAS_IN_USE, e.getReason());
+
+                List<Integer> expectedStates = new ArrayList<Integer>();
+                expectedStates.add(BlockingStateListener.STATE_UNCONFIGURED);
+                expectedStates.add(BlockingStateListener.STATE_ERROR);
+                int state = mCameraListener.waitForAnyOfStates(
+                        expectedStates, CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                // It's possible that we got an asynchronous error transition only. This is ok.
+                if (expectingError) {
+                    assertEquals("Throwing a CAMERA_ERROR exception must be accompanied with a " +
+                            "StateListener#onError callback",
+                            BlockingStateListener.STATE_ERROR, state);
                 }
+
+                /**
+                 * Two situations are considered passing:
+                 * 1) The camera opened successfully.
+                 *     => No error must be set.
+                 * 2) The camera did not open because there were too many other cameras opened.
+                 *     => Only MAX_CAMERAS_IN_USE error must be set.
+                 *
+                 * Any other situation is considered a failure.
+                 *
+                 * For simplicity we treat disconnecting asynchronously as a failure, so
+                 * camera devices should not be physically unplugged during this test.
+                 */
+
+                CameraDevice camera;
+                if (state == BlockingStateListener.STATE_ERROR) {
+                    // Camera did not open because too many other cameras were opened
+                    // => onError called exactly once with a non-null camera
+                    assertTrue("At least one camera must be opened successfully",
+                            cameraList.size() > 0);
+
+                    ArgumentCaptor<CameraDevice> argument =
+                            ArgumentCaptor.forClass(CameraDevice.class);
+
+                    verify(mockListener)
+                            .onError(
+                                    argument.capture(),
+                                    eq(CameraDevice.StateListener.ERROR_MAX_CAMERAS_IN_USE));
+                    verifyNoMoreInteractions(mockListener);
+
+                    camera = argument.getValue();
+                    assertNotNull("Expected a non-null camera for the error transition for ID: "
+                            + ids[i], camera);
+                } else if (state == BlockingStateListener.STATE_UNCONFIGURED) {
+                    // Camera opened successfully.
+                    // => onOpened+onUnconfigured called exactly once with same argument
+                    camera = verifyCameraStateOpenedThenUnconfigured(cameraId,
+                            mockListener);
+                } else {
+                    fail("Unexpected state " + state);
+                    camera = null; // unreachable. but need this for java compiler
+                }
+
+                // Keep track of cameras so we can close it later
+                cameraList.add(camera);
+                listenerList.add(mockListener);
+            }
+        } finally {
+            for (CameraDevice camera : cameraList) {
+                camera.close();
             }
         }
-        finally {
-            for (int i = 0; i < ids.length; i++) {
-                if (cameras[i] != null) {
-                    cameras[i].close();
-                }
-            }
+
+        /*
+         * Ensure that no state transitions have bled through from one camera to another
+         * after closing the cameras.
+         */
+        int i = 0;
+        for (MockStateListener listener : listenerList) {
+            CameraDevice camera = cameraList.get(i);
+
+            verify(listener).onClosed(eq(camera));
+            verifyNoMoreInteractions(listener);
+            // Only a #close can happen on the camera since we were done with it.
+            // Also nothing else should've happened between the close and the open.
         }
     }
 
-    // Test: that opening the same device multiple times throws the right exception.
+    /**
+     * Verifies the camera in this listener was opened and then unconfigured exactly once.
+     *
+     * <p>This assumes that no other action to the camera has been done (e.g.
+     * it hasn't been configured, or closed, or disconnected). Verification is
+     * performed immediately without any timeouts.</p>
+     *
+     * <p>This checks that the state has previously changed first for opened and then unconfigured.
+     * Any other state transitions will fail. A test failure is thrown if verification fails.</p>
+     *
+     * @param cameraId Camera identifier
+     * @param listener Listener which was passed to {@link CameraManager#openCamera}
+     *
+     * @return The camera device (non-{@code null}).
+     */
+    private static CameraDevice verifyCameraStateOpenedThenUnconfigured(String cameraId,
+            MockStateListener listener) {
+        ArgumentCaptor<CameraDevice> argument =
+                ArgumentCaptor.forClass(CameraDevice.class);
+        InOrder inOrder = inOrder(listener);
+
+        /**
+         * State transitions (in that order):
+         *  1) onOpened
+         *  2) onUnconfigured
+         *
+         * No other transitions must occur for successful #openCamera
+         */
+        inOrder.verify(listener)
+                .onOpened(argument.capture());
+
+        CameraDevice camera = argument.getValue();
+        assertNotNull(
+                String.format("Failed to unconfigure camera device ID: %s", cameraId),
+                camera);
+
+        inOrder.verify(listener)
+                .onUnconfigured(argument.capture());
+
+        assertEquals(String.format("Opened camera did not match unconfigured camera " +
+                "for camera device ID: %s", cameraId),
+                camera,
+                argument.getValue());
+        // Do not use inOrder here since that would skip anything called before onOpened
+        verifyNoMoreInteractions(listener);
+
+        return camera;
+    }
+
+    /**
+     * Test: that opening the same device multiple times and make sure the right
+     * error state is set.
+     */
     public void testCameraManagerOpenCameraTwice() throws Exception {
         String[] ids = mCameraManager.getCameraIdList();
-        CameraDevice[] cameras = new CameraDevice[2];
-        if (ids.length > 0) {
+
+        // Test across every camera device.
+        for (int i = 0; i < ids.length; ++i) {
+            CameraDevice successCamera = null;
+            mCollector.setCameraId(ids[i]);
+
             try {
-                cameras[0] = CameraTestUtils.openCamera(mCameraManager, ids[0], mHandler);
-                assertNotNull(
-                    String.format("Failed to open camera device ID: %s", ids[0]),
-                    cameras[0]);
+                MockStateListener mockSuccessListener = MockStateListener.mock();
+                MockStateListener mockFailListener = MockStateListener.mock();
+
+                BlockingStateListener successListener =
+                        new BlockingStateListener(mockSuccessListener);
+                BlockingStateListener failListener =
+                        new BlockingStateListener(mockFailListener);
+
+                mCameraManager.openCamera(ids[i], successListener, mHandler);
+
                 try {
-                    cameras[1] = CameraTestUtils.openCamera(mCameraManager, ids[0], mHandler);
-                    fail(String.format("Opened the same camera device twice ID: %s",
-                        ids[0]));
+                    mCameraManager.openCamera(ids[i], mCameraListener,
+                            mHandler);
+                } catch (CameraAccessException e) {
+                    // Optional (but common). Camera might fail asynchronously only.
+                    // Don't assert here, otherwise, all subsequent tests will fail because the
+                    // opened camera is never closed.
+                    mCollector.expectEquals(
+                            "If second camera open fails immediately, must be due to"
+                            + "camera being busy for ID: " + ids[i],
+                            CameraAccessException.CAMERA_ERROR, e.getReason());
                 }
-                catch (CameraAccessException e) {
-                    /**
-                     * This is the expected behavior if the camera device is attempted to
-                     * be opened more than once.
-                     */
-                    assertEquals(
-                        String.format("Invalid exception reason: %s", e.getReason()),
-                        CameraAccessException.CAMERA_IN_USE, e.getReason());
-                }
-            }
-            finally {
-                for (int i = 0; i < 2; i++) {
-                    if (cameras[i] != null) {
-                        cameras[i].close();
-                    }
+
+                successListener.waitForState(BlockingStateListener.STATE_OPENED,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+                // Have to get the successCamera here, otherwise, it won't be
+                // closed if STATE_ERROR timeout exception occurs.
+                ArgumentCaptor<CameraDevice> argument =
+                        ArgumentCaptor.forClass(CameraDevice.class);
+                verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture());
+                successCamera = argument.getValue();
+                successListener.waitForState(BlockingStateListener.STATE_UNCONFIGURED,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                failListener.waitForState(BlockingStateListener.STATE_ERROR,
+                        CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+
+                successCamera = verifyCameraStateOpenedThenUnconfigured(
+                        ids[i], mockSuccessListener);
+
+                verify(mockFailListener)
+                        .onError(
+                                and(notNull(CameraDevice.class), not(successCamera)),
+                                StateListener.ERROR_CAMERA_IN_USE);
+                verifyNoMoreInteractions(mockFailListener);
+            } finally {
+                if (successCamera != null) {
+                    successCamera.close();
                 }
             }
         }
@@ -252,13 +476,12 @@
      * a listener that isn't registered should have no effect.
      */
     public void testCameraManagerListener() throws Exception {
-        CameraTestThread callbackThread = new CameraTestThread();
-        Handler callbackHandler = callbackThread.start();
+        mCameraManager.removeAvailabilityListener(mListener);
+        mCameraManager.addAvailabilityListener(mListener, mHandler);
+        mCameraManager.addAvailabilityListener(mListener, mHandler);
+        mCameraManager.removeAvailabilityListener(mListener);
+        mCameraManager.removeAvailabilityListener(mListener);
 
-        mCameraManager.removeAvailabilityListener(mListener);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
-        mCameraManager.addAvailabilityListener(mListener, callbackHandler);
-        mCameraManager.removeAvailabilityListener(mListener);
-        mCameraManager.removeAvailabilityListener(mListener);
+        // TODO: test the listener callbacks
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java
deleted file mode 100644
index 9516ead..0000000
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestThread.java
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.camera2.cts;
-
-import android.os.ConditionVariable;
-import android.os.Handler;
-import android.os.Looper;
-import android.util.Log;
-
-import java.util.concurrent.TimeoutException;
-
-/**
- * Camera test thread wrapper for handling camera callbacks
- */
-public class CameraTestThread implements AutoCloseable {
-    private static final String TAG = "CameraTestThread";
-    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-    // Timeout for initializing looper and opening camera in Milliseconds.
-    private static final long WAIT_FOR_COMMAND_TO_COMPLETE = 5000;
-    private Looper mLooper = null;
-    private Handler mHandler = null;
-
-    /**
-     * Create and start a looper thread, return the Handler
-     */
-    public synchronized Handler start() throws Exception {
-        final ConditionVariable startDone = new ConditionVariable();
-        if (mLooper != null || mHandler !=null) {
-            Log.w(TAG, "Looper thread already started");
-            return mHandler;
-        }
-
-        new Thread() {
-            @Override
-            public void run() {
-                if (VERBOSE) Log.v(TAG, "start loopRun");
-                Looper.prepare();
-                // Save the looper so that we can terminate this thread
-                // after we are done with it.
-                mLooper = Looper.myLooper();
-                mHandler = new Handler();
-                startDone.open();
-                Looper.loop();
-                if (VERBOSE) Log.v(TAG, "createLooperThread: finished");
-            }
-        }.start();
-
-        if (VERBOSE) Log.v(TAG, "start waiting for looper");
-        if (!startDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
-            throw new TimeoutException("createLooperThread: start timeout");
-        }
-        return mHandler;
-    }
-
-    /**
-     * Terminate the looper thread
-     */
-    public synchronized void close() throws Exception {
-        if (mLooper == null || mHandler == null) {
-            Log.w(TAG, "Looper thread doesn't start yet");
-            return;
-        }
-
-        if (VERBOSE) Log.v(TAG, "Terminate looper thread");
-        mLooper.quit();
-        mLooper.getThread().join();
-        mLooper = null;
-        mHandler = null;
-    }
-
-    @Override
-    protected void finalize() throws Throwable {
-        try {
-            close();
-        } finally {
-            super.finalize();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index 7f10cb8..5086340 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -16,43 +16,75 @@
 
 package android.hardware.camera2.cts;
 
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
 import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
 import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
 import android.hardware.camera2.Size;
+import android.hardware.camera2.CameraMetadata.Key;
 import android.media.Image;
 import android.media.ImageReader;
 import android.media.Image.Plane;
 import android.os.Handler;
 import android.util.Log;
+import android.view.Surface;
 
 import com.android.ex.camera2.blocking.BlockingCameraManager;
 import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
 
 import junit.framework.Assert;
 
+import org.mockito.Mockito;
+
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Array;
 import java.nio.ByteBuffer;
+import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A package private utility class for wrapping up the camera2 cts test common utility functions
  */
-class CameraTestUtils extends Assert {
+public class CameraTestUtils extends Assert {
     private static final String TAG = "CameraTestUtils";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    // Only test the preview and video size that is no larger than 1080p.
+    public static final Size PREVIEW_SIZE_BOUND = new Size(1920, 1080);
     // Default timeouts for reaching various states
-    public static final int CAMERA_OPEN_TIMEOUT_MS = 500;
+    public static final int CAMERA_OPEN_TIMEOUT_MS = 2000;
+    public static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
     public static final int CAMERA_IDLE_TIMEOUT_MS = 2000;
-    public static final int CAMERA_ACTIVE_TIMEOUT_MS = 500;
-    public static final int CAMERA_BUSY_TIMEOUT_MS = 500;
+    public static final int CAMERA_ACTIVE_TIMEOUT_MS = 1000;
+    public static final int CAMERA_BUSY_TIMEOUT_MS = 1000;
+    public static final int CAMERA_UNCONFIGURED_TIMEOUT_MS = 1000;
+    public static final int CAMERA_CONFIGURE_TIMEOUT_MS = 2000;
+    public static final int CAPTURE_RESULT_TIMEOUT_MS = 1000;
+    public static final int CAPTURE_IMAGE_TIMEOUT_MS = 3000;
 
+    /**
+     * Dummy listener that release the image immediately once it is available.
+     *
+     * <p>
+     * It can be used for the case where we don't care the image data at all.
+     * </p>
+     */
     public static class ImageDropperListener implements ImageReader.OnImageAvailableListener {
         @Override
         public void onImageAvailable(ImageReader reader) {
@@ -67,6 +99,108 @@
         }
     }
 
+    public static class SimpleImageReaderListener
+            implements ImageReader.OnImageAvailableListener {
+        private final LinkedBlockingQueue<Image> mQueue =
+                new LinkedBlockingQueue<Image>();
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            try {
+                mQueue.put(reader.acquireNextImage());
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        /**
+         * Get an image from the image reader.
+         *
+         * @param timeout Timeout value for the wait.
+         * @return The image from the image reader.
+         */
+        public Image getImage(long timeout) throws InterruptedException {
+            Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+            assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
+            return image;
+        }
+    }
+
+    public static class SimpleCaptureListener extends CameraDevice.CaptureListener {
+        private final LinkedBlockingQueue<CaptureResult> mQueue =
+                new LinkedBlockingQueue<CaptureResult>();
+
+        @Override
+        public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp)
+        {
+        }
+
+        @Override
+        public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
+                CaptureResult result) {
+            try {
+                mQueue.put(result);
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onCaptureCompleted");
+            }
+        }
+
+        @Override
+        public void onCaptureFailed(CameraDevice camera, CaptureRequest request,
+                CaptureFailure failure) {
+        }
+
+        @Override
+        public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId,
+                int frameNumber) {
+        }
+
+        public CaptureResult getCaptureResult(long timeout) {
+            try {
+                CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+                assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
+                return result;
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException("Unhandled interrupted exception", e);
+            }
+        }
+
+        /**
+         * Get the {@link #CaptureResult capture result} for a given
+         * {@link #CaptureRequest capture request}.
+         *
+         * @param myRequest The {@link #CaptureRequest capture request} whose
+         *            corresponding {@link #CaptureResult capture result} was
+         *            being waited for
+         * @param numResultsWait Number of frames to wait for the capture result
+         *            before timeout.
+         * @throws TimeoutRuntimeException If more than numResultsWait results are
+         *            seen before the result matching myRequest arrives, or each
+         *            individual wait for result times out after
+         *            {@value #CAPTURE_RESULT_TIMEOUT_MS}ms.
+         */
+        public CaptureResult getCaptureResultForRequest(CaptureRequest myRequest,
+                int numResultsWait) {
+            if (numResultsWait < 0) {
+                throw new IllegalArgumentException("numResultsWait must be no less than 0");
+            }
+
+            CaptureResult result;
+            int i = 0;
+            do {
+                result = getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                if (result.getRequest().equals(myRequest)) {
+                    return result;
+                }
+            } while (i++ < numResultsWait);
+
+            throw new TimeoutRuntimeException("Unable to get the expected capture result after "
+                    + "waiting for " + numResultsWait + " results");
+        }
+    }
+
     /**
      * Block until the camera is opened.
      *
@@ -74,9 +208,15 @@
      * an AssertionError if it fails to open the camera device.</p>
      *
      * @return CameraDevice opened camera device
-     * @throws BlockingOpenException
      *
-     * @throws AssertionError if the camera fails to open (or times out)
+     * @throws IllegalArgumentException
+     *            If the handler is null, or if the handler's looper is current.
+     * @throws CameraAccessException
+     *            If open fails immediately.
+     * @throws BlockingOpenException
+     *            If open fails after blocking for some amount of time.
+     * @throws TimeoutRuntimeException
+     *            If opening times out. Typically unrecoverable.
      */
     public static CameraDevice openCamera(CameraManager manager, String cameraId,
             CameraDevice.StateListener listener, Handler handler) throws CameraAccessException,
@@ -101,9 +241,14 @@
      * <p>Don't use this to test #onDisconnected/#onError since this will throw
      * an AssertionError if it fails to open the camera device.</p>
      *
-     * @return CameraDevice opened camera device
-     *
-     * @throws AssertionError if the camera fails to open (or times out)
+     * @throws IllegalArgumentException
+     *            If the handler is null, or if the handler's looper is current.
+     * @throws CameraAccessException
+     *            If open fails immediately.
+     * @throws BlockingOpenException
+     *            If open fails after blocking for some amount of time.
+     * @throws TimeoutRuntimeException
+     *            If opening times out. Typically unrecoverable.
      */
     public static CameraDevice openCamera(CameraManager manager, String cameraId, Handler handler)
             throws CameraAccessException,
@@ -111,6 +256,24 @@
         return openCamera(manager, cameraId, /*listener*/null, handler);
     }
 
+    /**
+     * Configure camera output surfaces.
+     *
+     * @param camera The CameraDevice to be configured.
+     * @param outputSurfaces The surface list that used for camera output.
+     * @param listener The callback CameraDevice will notify when capture results are available.
+     */
+    public static void configureCameraOutputs(CameraDevice camera, List<Surface> outputSurfaces,
+            BlockingStateListener listener) throws CameraAccessException {
+        camera.configureOutputs(outputSurfaces);
+        listener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        if (outputSurfaces == null || outputSurfaces.size() == 0) {
+            listener.waitForState(STATE_UNCONFIGURED, CAMERA_UNCONFIGURED_TIMEOUT_MS);
+        } else {
+            listener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+        }
+    }
+
     public static <T> void assertArrayNotEmpty(T arr, String message) {
         assertTrue(message, arr != null && Array.getLength(arr) > 0);
     }
@@ -121,9 +284,7 @@
     public static void checkYuvFormat(int format) {
         if ((format != ImageFormat.YUV_420_888) &&
                 (format != ImageFormat.NV21) &&
-                (format != ImageFormat.YV12) &&
-                (format != ImageFormat.Y8) &&
-                (format != ImageFormat.Y16)) {
+                (format != ImageFormat.YV12)) {
             fail("Wrong formats: " + format);
         }
     }
@@ -183,7 +344,6 @@
             buffer = planes[i].getBuffer();
             assertNotNull("Fail to get bytebuffer from plane", buffer);
             rowStride = planes[i].getRowStride();
-            assertTrue("rowStride should be no less than width", rowStride >= width);
             pixelStride = planes[i].getPixelStride();
             assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
             if (VERBOSE) {
@@ -216,6 +376,7 @@
                 }
             }
             if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
+            buffer.rewind();
         }
         return data;
     }
@@ -223,7 +384,7 @@
     /**
      * <p>Check android image format validity for an image, only support below formats:</p>
      *
-     * <p>YUV_420_888/NV21/YV12/Y8/Y16, can add more for future</p>
+     * <p>YUV_420_888/NV21/YV12, can add more for future</p>
      */
     public static void checkAndroidImageFormat(Image image) {
         int format = image.getFormat();
@@ -234,11 +395,8 @@
             case ImageFormat.YV12:
                 assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
                 break;
-            case ImageFormat.Y8:
-            case ImageFormat.Y16:
-                assertEquals("Y8/Y16 Image should have 1 plane", 1, planes.length);
-                break;
             case ImageFormat.JPEG:
+            case ImageFormat.RAW_SENSOR:
                 assertEquals("Jpeg Image should have one plane", 1, planes.length);
                 break;
             default:
@@ -264,7 +422,7 @@
     }
 
     public static Size[] getSupportedSizeForFormat(int format, String cameraId,
-            CameraManager cameraManager) throws Exception {
+            CameraManager cameraManager) throws CameraAccessException {
         CameraMetadata.Key<Size[]> key = null;
         CameraCharacteristics properties = cameraManager.getCameraCharacteristics(cameraId);
         assertNotNull("Can't get camera characteristics!", properties);
@@ -278,8 +436,6 @@
             case ImageFormat.YUV_420_888:
             case ImageFormat.YV12:
             case ImageFormat.NV21:
-            case ImageFormat.Y8:
-            case ImageFormat.Y16:
                 key = CameraCharacteristics.SCALER_AVAILABLE_PROCESSED_SIZES;
                 break;
             default:
@@ -287,7 +443,375 @@
                         String.format("Invalid format specified 0x%x", format));
         }
         Size[] availableSizes = properties.get(key);
+        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
         if (VERBOSE) Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(availableSizes));
         return availableSizes;
     }
-}
\ No newline at end of file
+
+    /**
+     * Size comparator that compares the number of pixels it covers.
+     *
+     * <p>If two the areas of two sizes are same, compare the widths.</p>
+     */
+    public static class SizeComparator implements Comparator<Size> {
+        @Override
+        public int compare(Size lhs, Size rhs) {
+            long left = lhs.getWidth() * lhs.getHeight();
+            long right = rhs.getWidth() * rhs.getHeight();
+            if (left == right) {
+                left = lhs.getWidth();
+                right = rhs.getWidth();
+            }
+            return (left < right) ? -1 : (left > right ? 1 : 0);
+        }
+    }
+
+    /**
+     * Get sorted size list in descending order. Remove the sizes larger than
+     * the bound. If the bound is null, don't do the size bound filtering.
+     */
+    static public List<Size> getSupportedPreviewSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+    }
+
+    /**
+     * Get a sorted list of sizes from a given size list.
+     *
+     * <p>
+     * The size is compare by area it covers, if the areas are same, then
+     * compare the widths.
+     * </p>
+     *
+     * @param sizeList The input size list to be sorted
+     * @param ascending True if the order is ascending, otherwise descending order
+     * @return The ordered list of sizes
+     */
+    static public List<Size> getAscendingOrderSizes(final List<Size> sizeList, boolean ascending) {
+        if (sizeList == null) {
+            throw new IllegalArgumentException("sizeList shouldn't be null");
+        }
+
+        Comparator<Size> comparator = new SizeComparator();
+        List<Size> sortedSizes = new ArrayList<Size>();
+        sortedSizes.addAll(sizeList);
+        Collections.sort(sortedSizes, comparator);
+        if (!ascending) {
+            Collections.reverse(sortedSizes);
+        }
+
+        return sortedSizes;
+    }
+
+    /**
+     * Get sorted (descending order) size list for given format. Remove the sizes larger than
+     * the bound. If the bound is null, don't do the size bound filtering.
+     */
+    static private List<Size> getSortedSizesForFormat(String cameraId,
+            CameraManager cameraManager, int format, Size bound) throws CameraAccessException {
+        Comparator<Size> comparator = new SizeComparator();
+        Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager);
+        List<Size> sortedSizes = null;
+        if (bound != null) {
+            sortedSizes = new ArrayList<Size>(/*capacity*/1);
+            for (Size sz : sizes) {
+                if (comparator.compare(sz, bound) <= 0) {
+                    sortedSizes.add(sz);
+                }
+            }
+        } else {
+            sortedSizes = Arrays.asList(sizes);
+        }
+        assertTrue("Supported size list should have at least one element",
+                sortedSizes.size() > 0);
+
+        Collections.sort(sortedSizes, comparator);
+        // Make it in descending order.
+        Collections.reverse(sortedSizes);
+        return sortedSizes;
+    }
+
+    /**
+     * Get supported video size list for a given camera device.
+     *
+     * <p>
+     * Filter out the sizes that are larger than the bound. If the bound is
+     * null, don't do the size bound filtering.
+     * </p>
+     */
+    static public List<Size> getSupportedVideoSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+    }
+
+    /**
+     * Get supported video size list (descending order) for a given camera device.
+     *
+     * <p>
+     * Filter out the sizes that are larger than the bound. If the bound is
+     * null, don't do the size bound filtering.
+     * </p>
+     */
+    static public List<Size> getSupportedStillSizes(String cameraId,
+            CameraManager cameraManager, Size bound) throws CameraAccessException {
+        return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.JPEG, bound);
+    }
+
+    static public Size getMinPreviewSize(String cameraId, CameraManager cameraManager)
+            throws CameraAccessException {
+        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, null);
+        return sizes.get(sizes.size() - 1);
+    }
+
+    /**
+     * Get max supported preview size for a camera device.
+     */
+    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager)
+            throws CameraAccessException {
+        return getMaxPreviewSize(cameraId, cameraManager, /*bound*/null);
+    }
+
+    /**
+     * Get max preview size for a camera device in the supported sizes that are no larger
+     * than the bound.
+     */
+    static public Size getMaxPreviewSize(String cameraId, CameraManager cameraManager, Size bound)
+            throws CameraAccessException {
+        List<Size> sizes = getSupportedPreviewSizes(cameraId, cameraManager, bound);
+        return sizes.get(0);
+    }
+
+    /**
+     * Get the largest size by area.
+     *
+     * @param sizes an array of sizes, must have at least 1 element
+     *
+     * @return Largest Size
+     *
+     * @throws IllegalArgumentException if sizes was null or had 0 elements
+     */
+    public static Size getMaxSize(Size[] sizes) {
+        if (sizes == null || sizes.length == 0) {
+            throw new IllegalArgumentException("sizes was empty");
+        }
+
+        Size sz = sizes[0];
+        for (Size size : sizes) {
+            if (size.getWidth() * size.getHeight() > sz.getWidth() * sz.getHeight()) {
+                sz = size;
+            }
+        }
+
+        return sz;
+    }
+
+    /**
+     * Get object array from byte array.
+     *
+     * @param array Input byte array to be converted
+     * @return Byte object array converted from input byte array
+     */
+    public static Byte[] toObject(byte[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Byte.class);
+    }
+
+    /**
+     * Get object array from int array.
+     *
+     * @param array Input int array to be converted
+     * @return Integer object array converted from input int array
+     */
+    public static Integer[] toObject(int[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Integer.class);
+    }
+
+    /**
+     * Get object array from float array.
+     *
+     * @param array Input float array to be converted
+     * @return Float object array converted from input float array
+     */
+    public static Float[] toObject(float[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Float.class);
+    }
+
+    /**
+     * Get object array from double array.
+     *
+     * @param array Input double array to be converted
+     * @return Double object array converted from input double array
+     */
+    public static Double[] toObject(double[] array) {
+        return convertPrimitiveArrayToObjectArray(array, Double.class);
+    }
+
+    /**
+     * Convert a primitive input array into its object array version (e.g. from int[] to Integer[]).
+     *
+     * @param array Input array object
+     * @param wrapperClass The boxed class it converts to
+     * @return Boxed version of primitive array
+     */
+    private static <T> T[] convertPrimitiveArrayToObjectArray(final Object array,
+            final Class<T> wrapperClass) {
+        // getLength does the null check and isArray check already.
+        int arrayLength = Array.getLength(array);
+        if (arrayLength == 0) {
+            throw new IllegalArgumentException("Input array shouldn't be empty");
+        }
+
+        @SuppressWarnings("unchecked")
+        final T[] result = (T[]) Array.newInstance(wrapperClass, arrayLength);
+        for (int i = 0; i < arrayLength; i++) {
+            Array.set(result, i, Array.get(array, i));
+        }
+        return result;
+    }
+
+    /**
+     * Validate image based on format and size.
+     * <p>
+     * Only RAW_SENSOR, YUV420_888 and JPEG formats are supported. Calling this
+     * method with other formats will cause a UnsupportedOperationException.
+     * </p>
+     *
+     * @param image The image to be validated.
+     * @param width The image width.
+     * @param height The image height.
+     * @param format The image format.
+     * @param filePath The debug dump file path, null if don't want to dump to
+     *            file.
+     * @throws UnsupportedOperationException if calling with format other than
+     *             RAW_SENSOR, YUV420_888 or JPEG.
+     */
+    public static void validateImage(Image image, int width, int height, int format,
+            String filePath) {
+        checkImage(image, width, height, format);
+
+        /**
+         * TODO: validate timestamp:
+         * 1. capture result timestamp against the image timestamp (need
+         * consider frame drops)
+         * 2. timestamps should be monotonically increasing for different requests
+         */
+        if(VERBOSE) Log.v(TAG, "validating Image");
+        byte[] data = getDataFromImage(image);
+        assertTrue("Invalid image data", data != null && data.length > 0);
+
+        switch (format) {
+            case ImageFormat.JPEG:
+                validateJpegData(data, width, height, filePath);
+                break;
+            case ImageFormat.YUV_420_888:
+                validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+                break;
+            case ImageFormat.RAW_SENSOR:
+                validateRaw16Data(data, width, height, format, image.getTimestamp(), filePath);
+                break;
+            default:
+                throw new UnsupportedOperationException("Unsupported format for validation: "
+                        + format);
+        }
+    }
+
+    /**
+     * Provide a mock for {@link CameraDevice.StateListener}.
+     *
+     * <p>Only useful because mockito can't mock {@link CameraDevice.StateListener} which is an
+     * abstract class.</p>
+     *
+     * <p>
+     * Use this instead of other classes when needing to verify interactions, since
+     * trying to spy on {@link BlockingStateListener} (or others) will cause unnecessary extra
+     * interactions which will cause false test failures.
+     * </p>
+     *
+     */
+    public static class MockStateListener extends CameraDevice.StateListener {
+
+        @Override
+        public void onOpened(CameraDevice camera) {
+        }
+
+        @Override
+        public void onDisconnected(CameraDevice camera) {
+        }
+
+        @Override
+        public void onError(CameraDevice camera, int error) {
+        }
+
+        private MockStateListener() {}
+
+        /**
+         * Create a Mockito-ready mocked StateListener.
+         */
+        public static MockStateListener mock() {
+            return Mockito.spy(new MockStateListener());
+        }
+    }
+
+    private static void validateJpegData(byte[] jpegData, int width, int height, String filePath) {
+        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
+        // DecodeBound mode: only parse the frame header to get width/height.
+        // it doesn't decode the pixel.
+        bmpOptions.inJustDecodeBounds = true;
+        BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
+        assertEquals(width, bmpOptions.outWidth);
+        assertEquals(height, bmpOptions.outHeight);
+
+        // Pixel decoding mode: decode whole image. check if the image data
+        // is decodable here.
+        assertNotNull("Decoding jpeg failed",
+                BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + ".jpeg";
+            dumpFile(fileName, jpegData);
+        }
+    }
+
+    private static void validateYuvData(byte[] yuvData, int width, int height, int format,
+            long ts, String filePath) {
+        checkYuvFormat(format);
+        if (VERBOSE) Log.v(TAG, "Validating YUV data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
+
+        // TODO: Can add data validation for test pattern.
+
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv";
+            dumpFile(fileName, yuvData);
+        }
+    }
+
+    private static void validateRaw16Data(byte[] rawData, int width, int height, int format,
+            long ts, String filePath) {
+        if (VERBOSE) Log.v(TAG, "Validating raw data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, rawData.length);
+
+        // TODO: Can add data validation for test pattern.
+
+        if (DEBUG && filePath != null) {
+            String fileName =
+                    filePath + "/" + width + "x" + height + "_" + ts / 1e6 + ".raw16";
+            dumpFile(fileName, rawData);
+        }
+
+        return;
+    }
+
+    public static <T> T getValueNotNull(CaptureResult result, Key<T> key) {
+        if (result == null) {
+            throw new IllegalArgumentException("Result must not be null");
+        }
+
+        T value = result.get(key);
+        assertNotNull("Value of Key " + key.getName() + "shouldn't be null", value);
+        return value;
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
new file mode 100644
index 0000000..99143fd
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -0,0 +1,1451 @@
+/*
+ * Copyright 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.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static android.hardware.camera2.CameraCharacteristics.*;
+
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Face;
+import android.hardware.camera2.Rational;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * <p>
+ * Basic test for camera CaptureRequest key controls.
+ * </p>
+ * <p>
+ * Several test categories are covered: manual sensor control, 3A control,
+ * manual ISP control and other per-frame control and synchronization.
+ * </p>
+ */
+public class CaptureRequestTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "CaptureRequestTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int NUM_FRAMES_VERIFIED = 15;
+    private static final int NUM_FACE_DETECTION_FRAMES_VERIFIED = 60;
+    /** 30ms exposure time must be supported by full capability devices. */
+    private static final long DEFAULT_EXP_TIME_NS = 30000000L;
+    private static final int DEFAULT_SENSITIVITY = 100;
+    private static final int RGGB_COLOR_CHANNEL_COUNT = 4;
+    private static final int MAX_SHADING_MAP_SIZE = 64 * 64 * RGGB_COLOR_CHANNEL_COUNT;
+    private static final int MIN_SHADING_MAP_SIZE = 1 * 1 * RGGB_COLOR_CHANNEL_COUNT;
+    private static final long IGORE_REQUESTED_EXPOSURE_TIME_CHECK = -1L;
+    private static final long EXPOSURE_TIME_BOUNDARY_50HZ_NS = 10000000L; // 10ms
+    private static final long EXPOSURE_TIME_BOUNDARY_60HZ_NS = 8300000L; // 8.3ms, Approximation.
+    private static final long EXPOSURE_TIME_ERROR_MARGIN_NS = 100000L; // 100us, Approximation.
+    private static final int SENSITIVITY_ERROR_MARGIN = 10; // 10
+    private static final int DEFAULT_NUM_EXPOSURE_TIME_STEPS = 3;
+    private static final int DEFAULT_NUM_SENSITIVITY_STEPS = 16;
+    private static final int DEFAULT_SENSITIVITY_STEP_SIZE = 100;
+    private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
+    private static final int NUM_TEST_FOCUS_DISTANCES = 10;
+    private static final float FOCUS_DISTANCE_ERROR_PERCENT = 0.05f; // 5 percent error margin
+
+    // Linear tone mapping curve example.
+    private static final float[] TONEMAP_CURVE_LINEAR = {0, 0, 1.0f, 1.0f};
+    // Standard sRGB tone mapping, per IEC 61966-2-1:1999, with 16 control points.
+    private static final float[] TONEMAP_CURVE_SRGB = {
+            0.0000f, 0.0000f, 0.0667f, 0.2864f, 0.1333f, 0.4007f, 0.2000f, 0.4845f,
+            0.2667f, 0.5532f, 0.3333f, 0.6125f, 0.4000f, 0.6652f, 0.4667f, 0.7130f,
+            0.5333f, 0.7569f, 0.6000f, 0.7977f, 0.6667f, 0.8360f, 0.7333f, 0.8721f,
+            0.8000f, 0.9063f, 0.8667f, 0.9389f, 0.9333f, 0.9701f, 1.0000f, 1.0000f
+    };
+    private final Rational ZERO_R = new Rational(0, 1);
+    private final Rational ONE_R = new Rational(1, 1);
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test black level lock when exposure value change.
+     * <p>
+     * When {@link CaptureRequest#BLACK_LEVEL_LOCK} is true in a request, the
+     * camera device should lock the black level. When the exposure values are changed,
+     * the camera may require reset black level Since changes to certain capture
+     * parameters (such as exposure time) may require resetting of black level
+     * compensation. However, the black level must remain locked after exposure
+     * value changes (when requests have lock ON).
+     * </p>
+     */
+    public void testBlackLevelLock() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                if (!mStaticInfo.isHardwareLevelFull()) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                // Start with default manual exposure time, with black level being locked.
+                requestBuilder.set(CaptureRequest.BLACK_LEVEL_LOCK, true);
+                changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
+
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+                startPreview(requestBuilder, previewSz, listener);
+
+                // No lock OFF state is allowed as the exposure is not changed.
+                verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/0);
+
+                // Double the exposure time and gain, with black level still being locked.
+                changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS * 2, DEFAULT_SENSITIVITY * 2);
+                startPreview(requestBuilder, previewSz, listener);
+
+                // Allow at most one lock OFF state as the exposure is changed once.
+                verifyBlackLevelLockResults(listener, NUM_FRAMES_VERIFIED, /*maxLockOffCnt*/1);
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Basic lens shading map request test.
+     * <p>
+     * When {@link CaptureRequest#SHADING_MODE} is set to OFF, no lens shading correction will
+     * be applied by the camera device, and an identity lens shading map data
+     * will be provided if {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} is ON.
+     * </p>
+     * <p>
+     * When {@link CaptureRequest#SHADING_MODE} is set to other modes, lens shading correction
+     * will be applied by the camera device. The lens shading map data can be
+     * requested by setting {@link CaptureRequest#STATISTICS_LENS_SHADING_MAP_MODE} to ON.
+     * </p>
+     */
+    public void testLensShadingMap() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                if (!mStaticInfo.isHardwareLevelFull()) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                // Shading map mode OFF, lensShadingMapMode ON, camera device
+                // should output unity maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_OFF);
+                requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+                        STATISTICS_LENS_SHADING_MAP_MODE_ON);
+
+                Size mapSz = mStaticInfo.getCharacteristics().get(LENS_INFO_SHADING_MAP_SIZE);
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, mapSz, SHADING_MODE_OFF);
+
+                // Shading map mode FAST, lensShadingMapMode ON, camera device
+                // should output valid maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_FAST);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+
+                // Allow at most one lock OFF state as the exposure is changed once.
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, mapSz, SHADING_MODE_FAST);
+
+                // Shading map mode HIGH_QUALITY, lensShadingMapMode ON, camera device
+                // should output valid maps.
+                requestBuilder.set(CaptureRequest.SHADING_MODE, SHADING_MODE_HIGH_QUALITY);
+
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, previewSz, listener);
+
+                verifyShadingMap(listener, NUM_FRAMES_VERIFIED, mapSz, SHADING_MODE_HIGH_QUALITY);
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test {@link CaptureRequest#CONTROL_AE_ANTIBANDING_MODE} control.
+     * <p>
+     * Test all available anti-banding modes, check if the exposure time adjustment is
+     * correct.
+     * </p>
+     */
+    public void testAntiBandingModes() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                if (!mStaticInfo.isHardwareLevelFull()) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+
+                byte[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
+
+                Size previewSz =
+                        getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+                for (byte mode : modes) {
+                    antiBandingTestByMode(listener, previewSz, mode);
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+
+    }
+
+    /**
+     * Test AE mode and lock.
+     *
+     * <p>
+     * For AE lock, when it is locked, exposure parameters shouldn't be changed.
+     * For AE modes, each mode should satisfy the per frame controls defined in
+     * API specifications.
+     * </p>
+     */
+    public void testAeModeAndLock() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                // Can only test full capability because test relies on per frame control
+                // and synchronization.
+                if (!mStaticInfo.isHardwareLevelFull()) {
+                    continue;
+                }
+
+                Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+                // Update preview surface with given size for all sub-tests.
+                updatePreviewSurface(maxPreviewSz);
+
+                // Test aeMode and lock
+                byte[] aeModes = mStaticInfo.getAeAvailableModesChecked();
+                for (byte mode : aeModes) {
+                    aeModeAndLockTestByMode(mode);
+                }
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /** Test {@link CaptureRequest#FLASH_MODE} control.
+     * <p>
+     * For each {@link CaptureRequest#FLASH_MODE} mode, test the flash control
+     * and {@link CaptureResult#FLASH_STATE} result.
+     * </p>
+     */
+    public void testFlashControl() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                // Can only test full capability because test relies on per frame control
+                // and synchronization.
+                if (!mStaticInfo.isHardwareLevelFull()) {
+                    continue;
+                }
+
+                SimpleCaptureListener listener = new SimpleCaptureListener();
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+                Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+                startPreview(requestBuilder, maxPreviewSz, listener);
+
+                // Flash control can only be used when the AE mode is ON or OFF.
+                flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_ON);
+                flashTestByAeMode(listener, CaptureRequest.CONTROL_AE_MODE_OFF);
+
+                stopPreview();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test face detection modes and results.
+     */
+    public void testFaceDetection() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                faceDetectionTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test tone map modes and controls.
+     */
+    public void testToneMapControl() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                openDevice(mCameraIds[i]);
+
+                toneMapTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test color correction modes and controls.
+     */
+    public void testColorCorrectionControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                colorCorrectionTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    public void testEdgeModeControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.isPerFrameControlSupported()) {
+                    Log.i(TAG, "Camera " + id + "Doesn't support per frame control");
+                    continue;
+                }
+
+                edgeModesTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test focus distance control.
+     */
+    public void testFocusDistanceControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.isPerFrameControlSupported() || !mStaticInfo.hasFocuser()) {
+                    Log.i(TAG, "Camera " + id
+                            + "Doesn't support per frame control or has no focuser");
+                    continue;
+                }
+
+                focusDistanceTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    public void testNoiseReductionModeControl() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+                if (!mStaticInfo.isPerFrameControlSupported()) {
+                    Log.i(TAG, "Camera " + id + "Doesn't support per frame control");
+                    continue;
+                }
+
+                noiseReductionModeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    // TODO: add 3A state machine test.
+
+    private void noiseReductionModeTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        byte[] availableModes = mStaticInfo.getAvailableNoiseReductionModesChecked();
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        for (byte mode : availableModes) {
+            requestBuilder.set(CaptureRequest.NOISE_REDUCTION_MODE, (int)mode);
+            resultListener = new SimpleCaptureListener();
+            mCamera.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+
+            verifyCaptureResultForKey(CaptureResult.NOISE_REDUCTION_MODE, (int)mode,
+                    resultListener, NUM_FRAMES_VERIFIED);
+
+            // Test that OFF and FAST mode should not slow down the frame rate.
+            if (mode == CaptureRequest.NOISE_REDUCTION_MODE_OFF ||
+                    mode == CaptureRequest.NOISE_REDUCTION_MODE_FAST) {
+                verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
+            }
+        }
+
+        stopPreview();
+    }
+
+    private void focusDistanceTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        float[] testDistances = getFocusDistanceTestValuesInOrder();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_OFF);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        CaptureRequest request;
+        float[] resultDistances = new float[testDistances.length];
+        for (int i = 0; i < testDistances.length; i++) {
+            requestBuilder.set(CaptureRequest.LENS_FOCUS_DISTANCE, testDistances[i]);
+            request = requestBuilder.build();
+            resultListener = new SimpleCaptureListener();
+            mCamera.setRepeatingRequest(request, resultListener, mHandler);
+            resultDistances[i] = verifyFocusDistanceControl(testDistances[i], request,
+                    resultListener);
+            if (VERBOSE) {
+                Log.v(TAG, "Capture request focus distance: " + testDistances[i] + " result: "
+                        + resultDistances[i]);
+            }
+        }
+
+        // Verify the monotonicity
+        mCollector.checkArrayMonotonicityAndNotAllEqual(CameraTestUtils.toObject(resultDistances),
+                /*ascendingOrder*/true);
+    }
+
+    /**
+     * Verify focus distance control.
+     *
+     * @param distance The focus distance requested
+     * @param request The capture request to control the manual focus distance
+     * @param resultListener The capture listener to recieve capture result callbacks
+     * @return the result focus distance
+     */
+    private float verifyFocusDistanceControl(float distance, CaptureRequest request,
+            SimpleCaptureListener resultListener) {
+        // Need make sure the result corresponding to the request is back, then check.
+        CaptureResult result =
+                resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        // Then wait for the lens.state to be stationary.
+        waitForResultValue(resultListener, CaptureResult.LENS_STATE,
+                CaptureResult.LENS_STATE_STATIONARY, NUM_RESULTS_WAIT_TIMEOUT);
+        // Then check the focus distance.
+        result = resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        Float resultDistance = getValueNotNull(result, CaptureResult.LENS_FOCUS_DISTANCE);
+        if (mStaticInfo.getFocusDistanceCalibrationChecked() ==
+                CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED) {
+            // TODO: what's more to test for CALIBRATED devices?
+        }
+
+        float minValue = 0;
+        float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
+        mCollector.expectInRange("Result focus distance is out of range",
+                resultDistance, minValue, maxValue);
+
+        return resultDistance;
+    }
+
+    /**
+     * Verify edge mode control results.
+     */
+    private void edgeModesTestByCamera() throws Exception {
+        Size maxPrevSize = mOrderedPreviewSizes.get(0);
+        byte[] edgeModes = mStaticInfo.getAvailableEdgeModesChecked();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPrevSize, resultListener);
+
+        for (byte mode : edgeModes) {
+            requestBuilder.set(CaptureRequest.EDGE_MODE, (int)mode);
+            resultListener = new SimpleCaptureListener();
+            mCamera.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+
+            verifyCaptureResultForKey(CaptureResult.EDGE_MODE, (int)mode, resultListener,
+                    NUM_FRAMES_VERIFIED);
+
+            // Test that OFF and FAST mode should not slow down the frame rate.
+            if (mode == CaptureRequest.EDGE_MODE_OFF ||
+                    mode == CaptureRequest.EDGE_MODE_FAST) {
+                verifyFpsNotSlowDown(requestBuilder, NUM_FRAMES_VERIFIED);
+            }
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Test color correction controls.
+     *
+     * <p>Test different color correction modes. For TRANSFORM_MATRIX, only test
+     * the unit gain and identity transform.</p>
+     */
+    private void colorCorrectionTestByCamera() throws Exception {
+        CaptureRequest request;
+        CaptureResult result;
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+        updatePreviewSurface(maxPreviewSz);
+        CaptureRequest.Builder manualRequestBuilder = createRequestForPreview();
+        CaptureRequest.Builder previewRequestBuilder = createRequestForPreview();
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+
+        startPreview(previewRequestBuilder, maxPreviewSz, listener);
+
+        // Default preview result should give valid color correction metadata.
+        result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        validateColorCorrectionResult(result);
+
+        // TRANSFORM_MATRIX mode
+        // Only test unit gain and identity transform
+        float[] UNIT_GAIN = {1.0f, 1.0f, 1.0f, 1.0f};
+        Rational[] IDENTITY_TRANSFORM = {
+                ONE_R, ZERO_R, ZERO_R,
+                ZERO_R, ONE_R, ZERO_R,
+                ZERO_R, ZERO_R, ONE_R
+        };
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE,
+                CaptureRequest.COLOR_CORRECTION_MODE_TRANSFORM_MATRIX);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_GAINS, UNIT_GAIN);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_TRANSFORM, IDENTITY_TRANSFORM);
+        request = manualRequestBuilder.build();
+        mCamera.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        float[] gains = result.get(CaptureResult.COLOR_CORRECTION_GAINS);
+        Rational[] transform = result.get(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+        validateColorCorrectionResult(result);
+        mCollector.expectEquals("Color correction gain result/request mismatch",
+                CameraTestUtils.toObject(UNIT_GAIN), CameraTestUtils.toObject(gains));
+        mCollector.expectEquals("Color correction gain result/request mismatch",
+                IDENTITY_TRANSFORM, transform);
+
+        // FAST mode
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE,
+                CaptureRequest.COLOR_CORRECTION_MODE_FAST);
+        request = manualRequestBuilder.build();
+        mCamera.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        validateColorCorrectionResult(result);
+
+        // HIGH_QUALITY mode
+        manualRequestBuilder.set(CaptureRequest.CONTROL_MODE, CaptureRequest.CONTROL_MODE_AUTO);
+        manualRequestBuilder.set(CaptureRequest.COLOR_CORRECTION_MODE,
+                CaptureRequest.COLOR_CORRECTION_MODE_FAST);
+        request = manualRequestBuilder.build();
+        mCamera.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+        validateColorCorrectionResult(result);
+    }
+
+    private void validateColorCorrectionResult(CaptureResult result) {
+        float[] ZERO_GAINS = {0, 0, 0, 0};
+        final int TRANSFORM_SIZE = 9;
+        Rational[] zeroTransform = new Rational[TRANSFORM_SIZE];
+        Arrays.fill(zeroTransform, ZERO_R);
+
+        float[] resultGain;
+        if ((resultGain = mCollector.expectKeyValueNotNull(result,
+                CaptureResult.COLOR_CORRECTION_GAINS)) != null) {
+            mCollector.expectEquals("Color correction gain size in incorrect",
+                    ZERO_GAINS.length, resultGain.length);
+            mCollector.expectKeyValueNotEquals(result,
+                    CaptureResult.COLOR_CORRECTION_GAINS, ZERO_GAINS);
+        }
+
+        Rational[] resultTransform;
+        if ((resultTransform = mCollector.expectKeyValueNotNull(result,
+                CaptureResult.COLOR_CORRECTION_TRANSFORM)) != null) {
+            mCollector.expectEquals("Color correction transform size is incorrect",
+                    zeroTransform.length, resultTransform.length);
+            mCollector.expectKeyValueNotEquals(result,
+                    CaptureResult.COLOR_CORRECTION_TRANSFORM, zeroTransform);
+        }
+    }
+
+    /**
+     * Test flash mode control by AE mode.
+     * <p>
+     * Only allow AE mode ON or OFF, because other AE mode could run into conflict with
+     * flash manual control. This function expects the camera to already have an active
+     * repeating request and be sending results to the listener.
+     * </p>
+     *
+     * @param listener The Capture listener that is used to wait for capture result
+     * @param aeMode The AE mode for flash to test with
+     */
+    private void flashTestByAeMode(SimpleCaptureListener listener, int aeMode) throws Exception {
+        CaptureRequest request;
+        CaptureResult result;
+        final int NUM_FLASH_REQUESTS_TESTED = 10;
+        CaptureRequest.Builder requestBuilder = createRequestForPreview();
+
+        if (aeMode == CaptureRequest.CONTROL_AE_MODE_ON) {
+            requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, aeMode);
+        } else if (aeMode == CaptureRequest.CONTROL_AE_MODE_OFF) {
+            changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
+        } else {
+            throw new IllegalArgumentException("This test only works when AE mode is ON or OFF");
+        }
+
+        // For camera that doesn't have flash unit, flash state should always be UNAVAILABLE.
+        if (mStaticInfo.getFlashInfoChecked() == false) {
+            for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
+                result = listener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+                mCollector.expectEquals("No flash unit available, flash state must be UNAVAILABLE"
+                        + "for AE mode " + aeMode, CaptureResult.FLASH_STATE_UNAVAILABLE,
+                        result.get(CaptureResult.FLASH_STATE));
+            }
+
+            return;
+        }
+
+        // Test flash SINGLE mode control. Wait for flash state to be READY first.
+        waitForResultValue(listener, CaptureResult.FLASH_STATE, CaptureResult.FLASH_STATE_READY,
+                NUM_RESULTS_WAIT_TIMEOUT);
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_SINGLE);
+        request = requestBuilder.build();
+        mCamera.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request,
+                NUM_RESULTS_WAIT_TIMEOUT);
+        // Result mode must be SINGLE, state must be FIRED.
+        mCollector.expectEquals("Flash mode result must be SINGLE",
+                CaptureResult.FLASH_MODE_SINGLE, result.get(CaptureResult.FLASH_MODE));
+        mCollector.expectEquals("Flash state result must be FIRED",
+                CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+
+        // Test flash TORCH mode control.
+        CaptureRequest[] requests = new CaptureRequest[NUM_FLASH_REQUESTS_TESTED];
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
+        for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
+            requests[i] = requestBuilder.build();
+            mCamera.capture(requests[i], listener, mHandler);
+        }
+        // Verify the results
+        for (int i = 0; i < NUM_FLASH_REQUESTS_TESTED; i++) {
+            result = listener.getCaptureResultForRequest(requests[i],
+                    NUM_RESULTS_WAIT_TIMEOUT);
+
+            // Result mode must be TORCH, state must be FIRED
+            mCollector.expectEquals("Flash mode result must be TORCH",
+                    CaptureResult.FLASH_MODE_TORCH, result.get(CaptureResult.FLASH_MODE));
+            mCollector.expectEquals("Flash state result must be FIRED",
+                    CaptureResult.FLASH_STATE_FIRED, result.get(CaptureResult.FLASH_STATE));
+        }
+
+        // Test flash OFF mode control
+        requestBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
+        request = requestBuilder.build();
+        mCamera.capture(request, listener, mHandler);
+        result = listener.getCaptureResultForRequest(request,
+                NUM_RESULTS_WAIT_TIMEOUT);
+        mCollector.expectEquals("Flash mode result must be OFF", CaptureResult.FLASH_MODE_OFF,
+                result.get(CaptureResult.FLASH_MODE));
+    }
+
+    private void verifyAntiBandingMode(SimpleCaptureListener listener, int numFramesVerified,
+            int mode, boolean isAeManual, long requestExpTime) throws Exception {
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            Long resultExpTime = result.get(CaptureRequest.SENSOR_EXPOSURE_TIME);
+            assertNotNull("Exposure time shouldn't be null", resultExpTime);
+            Integer flicker = result.get(CaptureResult.STATISTICS_SCENE_FLICKER);
+            // Scene flicker result should be always available.
+            assertNotNull("Scene flicker must not be null", flicker);
+            assertTrue("Scene flicker is invalid", flicker >= STATISTICS_SCENE_FLICKER_NONE &&
+                    flicker <= STATISTICS_SCENE_FLICKER_60HZ);
+
+            if (isAeManual) {
+                // First, round down not up, second, need close enough.
+                validateExposureTime(requestExpTime, resultExpTime);
+                return;
+            }
+
+            long expectedExpTime = resultExpTime; // Default, no exposure adjustment.
+            if (mode == CONTROL_AE_ANTIBANDING_MODE_50HZ) {
+                // result exposure time must be adjusted by 50Hz illuminant source.
+                expectedExpTime =
+                        resultExpTime - (resultExpTime % EXPOSURE_TIME_BOUNDARY_50HZ_NS);
+            } else if (mode == CONTROL_AE_ANTIBANDING_MODE_60HZ) {
+                // result exposure time must be adjusted by 60Hz illuminant source.
+                expectedExpTime =
+                        resultExpTime - (resultExpTime % EXPOSURE_TIME_BOUNDARY_60HZ_NS);
+            } else if (mode == CONTROL_AE_ANTIBANDING_MODE_AUTO){
+                /**
+                 * Use STATISTICS_SCENE_FLICKER to tell the illuminant source
+                 * and do the exposure adjustment.
+                 */
+                expectedExpTime = resultExpTime;
+                if (flicker == STATISTICS_SCENE_FLICKER_60HZ) {
+                    expectedExpTime = resultExpTime
+                            - (resultExpTime % EXPOSURE_TIME_BOUNDARY_60HZ_NS);
+                } else if (flicker == STATISTICS_SCENE_FLICKER_50HZ) {
+                    expectedExpTime = resultExpTime
+                            - (resultExpTime % EXPOSURE_TIME_BOUNDARY_50HZ_NS);
+                }
+            }
+
+            if (Math.abs(resultExpTime - expectedExpTime) > EXPOSURE_TIME_ERROR_MARGIN_NS) {
+                mCollector.addMessage(String.format("Result exposure time %dns diverges too much"
+                        + " from expected exposure time %dns for mode %d when AE is auto",
+                        resultExpTime, expectedExpTime, mode));
+            }
+        }
+    }
+
+    private void antiBandingTestByMode(SimpleCaptureListener listener, Size size, int mode)
+            throws Exception {
+        if(VERBOSE) {
+            Log.v(TAG, "Anti-banding test for mode " + mode + " for camera " + mCamera.getId());
+        }
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE, mode);
+
+        // Test auto AE mode anti-banding behavior
+        startPreview(requestBuilder, size, listener);
+        verifyAntiBandingMode(listener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/false,
+                IGORE_REQUESTED_EXPOSURE_TIME_CHECK);
+
+        // Test manual AE mode anti-banding behavior
+        // 65ms, must be supported by full capability devices.
+        final long TEST_MANUAL_EXP_TIME_NS = 65000000L;
+        changeExposure(requestBuilder, TEST_MANUAL_EXP_TIME_NS);
+        startPreview(requestBuilder, size, listener);
+        verifyAntiBandingMode(listener, NUM_FRAMES_VERIFIED, mode, /*isAeManual*/true,
+                TEST_MANUAL_EXP_TIME_NS);
+
+        stopPreview();
+    }
+
+    /**
+     * Test the all available AE modes and AE lock.
+     * <p>
+     * For manual AE mode, test iterates through different sensitivities and
+     * exposure times, validate the result exposure time correctness. For
+     * CONTROL_AE_MODE_ON_ALWAYS_FLASH mode, the AE lock and flash are tested.
+     * For the rest of the AUTO mode, AE lock is tested.
+     * </p>
+     *
+     * @param mode
+     */
+    private void aeModeAndLockTestByMode(int mode)
+            throws Exception {
+        switch (mode) {
+            case CONTROL_AE_MODE_OFF:
+                // Test manual exposure control.
+                aeManualControlTest();
+                break;
+            case CONTROL_AE_MODE_ON:
+            case CONTROL_AE_MODE_ON_AUTO_FLASH:
+            case CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE:
+            case CONTROL_AE_MODE_ON_ALWAYS_FLASH:
+                // Test AE lock for above AUTO modes.
+                aeAutoModeTestLock(mode);
+                break;
+            default:
+                throw new UnsupportedOperationException("Unhandled AE mode " + mode);
+        }
+    }
+
+    /**
+     * Test AE auto modes.
+     * <p>
+     * Use single request rather than repeating request to test AE lock per frame control.
+     * </p>
+     */
+    private void aeAutoModeTestLock(int mode) throws Exception {
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, mode);
+        configurePreviewOutput(requestBuilder);
+
+        final int MAX_NUM_CAPTURES_BEFORE_LOCK = 5;
+        for (int i = 1; i <= MAX_NUM_CAPTURES_BEFORE_LOCK; i++) {
+            autoAeMultipleCapturesThenTestLock(requestBuilder, mode, i);
+        }
+    }
+
+    /**
+     * Issue multiple auto AE captures, then lock AE, validate the AE lock vs.
+     * the last capture result before the AE lock.
+     */
+    private void autoAeMultipleCapturesThenTestLock(
+            CaptureRequest.Builder requestBuilder, int aeMode, int numCapturesBeforeLock)
+            throws Exception {
+        if (numCapturesBeforeLock < 1) {
+            throw new IllegalArgumentException("numCapturesBeforeLock must be no less than 1");
+        }
+        if (VERBOSE) {
+            Log.v(TAG, "Camera " + mCamera.getId() + ": Testing auto AE mode and lock for mode "
+                    + aeMode + " with " + numCapturesBeforeLock + " captures before lock");
+        }
+
+        SimpleCaptureListener listener =  new SimpleCaptureListener();
+        CaptureResult latestResult = null;
+
+        CaptureRequest request = requestBuilder.build();
+        for (int i = 0; i < numCapturesBeforeLock; i++) {
+            // Fire a capture, auto AE, lock off.
+            mCamera.capture(request, listener, mHandler);
+        }
+        // Then fire a capture to lock the AE,
+        requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+        mCamera.capture(requestBuilder.build(), listener, mHandler);
+
+        // Get the latest exposure values of the last AE lock off requests.
+        for (int i = 0; i < numCapturesBeforeLock; i++) {
+            latestResult = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        }
+        int sensitivity = getValueNotNull(latestResult, CaptureResult.SENSOR_SENSITIVITY);
+        long expTime = getValueNotNull(latestResult, CaptureResult.SENSOR_EXPOSURE_TIME);
+
+        // Get the AE lock on result and validate the exposure values.
+        latestResult = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        int sensitivityLocked = getValueNotNull(latestResult, CaptureResult.SENSOR_SENSITIVITY);
+        long expTimeLocked = getValueNotNull(latestResult, CaptureResult.SENSOR_EXPOSURE_TIME);
+        mCollector.expectEquals("Locked exposure time shouldn't be changed for AE auto mode "
+                + aeMode + "after " + numCapturesBeforeLock + " captures", expTime, expTimeLocked);
+        mCollector.expectEquals("Locked sensitivity shouldn't be changed for AE auto mode " + aeMode
+                + "after " + numCapturesBeforeLock + " captures", sensitivity, sensitivityLocked);
+    }
+
+    /**
+     * Iterate through exposure times and sensitivities for manual AE control.
+     * <p>
+     * Use single request rather than repeating request to test manual exposure
+     * value change per frame control.
+     * </p>
+     */
+    private void aeManualControlTest()
+            throws Exception {
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
+        configurePreviewOutput(requestBuilder);
+        SimpleCaptureListener listener =  new SimpleCaptureListener();
+
+        long[] expTimes = getExposureTimeTestValues();
+        int[] sensitivities = getSensitivityTestValues();
+        // Submit single request at a time, then verify the result.
+        for (int i = 0; i < expTimes.length; i++) {
+            for (int j = 0; j < sensitivities.length; j++) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Camera " + mCamera.getId() + ": Testing sensitivity "
+                            + sensitivities[j] + ", exposure time " + expTimes[i] + "ns");
+                }
+
+                changeExposure(requestBuilder, expTimes[i], sensitivities[j]);
+                mCamera.capture(requestBuilder.build(), listener, mHandler);
+
+                CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                long resultExpTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+                int resultSensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
+                validateExposureTime(expTimes[i], resultExpTime);
+                validateSensitivity(sensitivities[j], resultSensitivity);
+                validateFrameDurationForCapture(result);
+            }
+        }
+        // TODO: Add another case to test where we can submit all requests, then wait for
+        // results, which will hide the pipeline latency. this is not only faster, but also
+        // test high speed per frame control and synchronization.
+    }
+
+
+    /**
+     * Verify black level lock control.
+     */
+    private void verifyBlackLevelLockResults(SimpleCaptureListener listener, int numFramesVerified,
+            int maxLockOffCnt) throws Exception {
+        int noLockCnt = 0;
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            Boolean blackLevelLock = result.get(CaptureResult.BLACK_LEVEL_LOCK);
+            assertNotNull("Black level lock result shouldn't be null", blackLevelLock);
+
+            // Count the lock == false result, which could possibly occur at most once.
+            if (blackLevelLock == false) {
+                noLockCnt++;
+            }
+
+            if(VERBOSE) {
+                Log.v(TAG, "Black level lock result: " + blackLevelLock);
+            }
+        }
+        assertTrue("Black level lock OFF occurs " + noLockCnt + " times,  expect at most "
+                + maxLockOffCnt + " for camera " + mCamera.getId(), noLockCnt <= maxLockOffCnt);
+    }
+
+    /**
+     * Verify shading map for different shading modes.
+     */
+    private void verifyShadingMap(SimpleCaptureListener listener, int numFramesVerified,
+            Size mapSize, int shadingMode) throws Exception {
+        int numElementsInMap = mapSize.getWidth() * mapSize.getHeight() * RGGB_COLOR_CHANNEL_COUNT;
+        float[] unityMap = new float[numElementsInMap];
+        Arrays.fill(unityMap, 1.0f);
+
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Shading mode result doesn't match request",
+                    shadingMode, result.get(CaptureResult.SHADING_MODE));
+            float[] map = result.get(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+            assertNotNull("Map must not be null", map);
+            assertTrue("Map size " + map.length + " must be " + numElementsInMap,
+                    map.length == numElementsInMap);
+            assertFalse(String.format(
+                    "Map size %d should be less than %d", numElementsInMap, MAX_SHADING_MAP_SIZE),
+                    numElementsInMap >= MAX_SHADING_MAP_SIZE);
+            assertFalse(String.format("Map size %d should be no less than %d", numElementsInMap,
+                    MIN_SHADING_MAP_SIZE), numElementsInMap < MIN_SHADING_MAP_SIZE);
+
+            if (shadingMode == CaptureRequest.SHADING_MODE_FAST ||
+                    shadingMode == CaptureRequest.SHADING_MODE_HIGH_QUALITY) {
+                // shading mode is FAST or HIGH_QUALITY, expect to receive a map with all
+                // elements >= 1.0f
+
+                int badValueCnt = 0;
+                // Detect the bad values of the map data.
+                for (int j = 0; j < numElementsInMap; j++) {
+                    if (Float.isNaN(map[j]) || map[j] < 1.0f) {
+                        badValueCnt++;
+                    }
+                }
+                assertEquals("Number of value in the map is " + badValueCnt + " out of "
+                        + numElementsInMap, /*expected*/0, /*actual*/badValueCnt);
+            } else if (shadingMode == CaptureRequest.SHADING_MODE_OFF) {
+                // shading mode is OFF, expect to receive a unity map.
+                assertTrue("Result map " + Arrays.toString(map) + " must be an unity map",
+                        Arrays.equals(unityMap, map));
+            }
+        }
+    }
+
+    /**
+     * Test face detection for a camera.
+     */
+    private void faceDetectionTestByCamera() throws Exception {
+        // Can only test full capability because test relies on per frame control
+        // and synchronization.
+        if (!mStaticInfo.isHardwareLevelFull()) {
+            return;
+        }
+        byte[] faceDetectModes = mStaticInfo.getAvailableFaceDetectModesChecked();
+
+        SimpleCaptureListener listener;
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+        for (byte mode : faceDetectModes) {
+            requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE, (int)mode);
+            if (VERBOSE) {
+                Log.v(TAG, "Start testing face detection mode " + mode);
+            }
+
+            // Create a new listener for each run to avoid the results from one run spill
+            // into another run.
+            listener = new SimpleCaptureListener();
+            startPreview(requestBuilder, maxPreviewSz, listener);
+            verifyFaceDetectionResults(listener, NUM_FACE_DETECTION_FRAMES_VERIFIED, mode);
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Verify face detection results for different face detection modes.
+     *
+     * @param listener The listener to get capture result
+     * @param numFramesVerified Number of results to be verified
+     * @param faceDetectionMode Face detection mode to be verified against
+     */
+    private void verifyFaceDetectionResults(SimpleCaptureListener listener, int numFramesVerified,
+            int faceDetectionMode) {
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Result face detection mode should match the request",
+                    faceDetectionMode, result.get(CaptureResult.STATISTICS_FACE_DETECT_MODE));
+
+            Face[] faces = result.get(CaptureResult.STATISTICS_FACES);
+            List<Integer> faceIds = new ArrayList<Integer>(faces.length);
+            List<Integer> faceScores = new ArrayList<Integer>(faces.length);
+            if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_OFF) {
+                mCollector.expectEquals("Number of detection faces should always 0 for OFF mode",
+                        0, faces.length);
+            } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_SIMPLE) {
+                for (Face face : faces) {
+                    mCollector.expectNotNull("Face rectangle shouldn't be null", face.getBounds());
+                    faceScores.add(face.getScore());
+                    mCollector.expectTrue("Face id is expected to be -1 for SIMPLE mode",
+                            face.getId() == Face.ID_UNSUPPORTED);
+                }
+            } else if (faceDetectionMode == CaptureResult.STATISTICS_FACE_DETECT_MODE_FULL) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Number of faces detected: " + faces.length);
+                }
+
+                for (Face face : faces) {
+                    Rect faceBound = null;
+                    boolean faceRectAvailable =  mCollector.expectTrue("Face rectangle "
+                            + "shouldn't be null", face.getBounds() != null);
+                    if (!faceRectAvailable) {
+                        continue;
+                    }
+                    faceBound = face.getBounds();
+
+                    faceScores.add(face.getScore());
+                    faceIds.add(face.getId());
+
+                    mCollector.expectTrue("Face id is shouldn't be -1 for FULL mode",
+                            face.getId() != Face.ID_UNSUPPORTED);
+                    boolean leftEyeAvailable =
+                            mCollector.expectTrue("Left eye position shouldn't be null",
+                                    face.getLeftEyePosition() != null);
+                    boolean rightEyeAvailable =
+                            mCollector.expectTrue("Right eye position shouldn't be null",
+                                    face.getRightEyePosition() != null);
+                    boolean mouthAvailable =
+                            mCollector.expectTrue("Mouth position shouldn't be null",
+                            face.getMouthPosition() != null);
+                    // Eyes/mouth position should be inside of the face rect.
+                    if (leftEyeAvailable) {
+                        Point leftEye = face.getLeftEyePosition();
+                        mCollector.expectTrue("Left eye " + leftEye.toString() + "should be"
+                                + "inside of face rect " + faceBound.toString(),
+                                faceBound.contains(leftEye.x, leftEye.y));
+                    }
+                    if (rightEyeAvailable) {
+                        Point rightEye = face.getRightEyePosition();
+                        mCollector.expectTrue("Right eye " + rightEye.toString() + "should be"
+                                + "inside of face rect " + faceBound.toString(),
+                                faceBound.contains(rightEye.x, rightEye.y));
+                    }
+                    if (mouthAvailable) {
+                        Point mouth = face.getMouthPosition();
+                        mCollector.expectTrue("Mouth " + mouth.toString() +  " should be inside of"
+                                + " face rect " + faceBound.toString(),
+                                faceBound.contains(mouth.x, mouth.y));
+                    }
+                }
+            }
+            mCollector.expectValuesInRange("Face scores are invalid", faceIds,
+                    Face.SCORE_MIN, Face.SCORE_MAX);
+            mCollector.expectValuesUnique("Face ids are invalid", faceIds);
+        }
+    }
+
+    /**
+     * Test tone map mode and result by camera
+     */
+    private void toneMapTestByCamera() throws Exception {
+        // Can only test full capability because test relies on per frame control
+        // and synchronization.
+        if (!mStaticInfo.isHardwareLevelFull()) {
+            return;
+        }
+
+        SimpleCaptureListener listener;
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+
+        byte[] toneMapModes = mStaticInfo.getAvailableToneMapModesChecked();
+        for (byte mode : toneMapModes) {
+            requestBuilder.set(CaptureRequest.TONEMAP_MODE, (int)mode);
+            if (VERBOSE) {
+                Log.v(TAG, "Testing tonemap mode " + mode);
+            }
+
+            if (mode == CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE) {
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_RED, TONEMAP_CURVE_LINEAR);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_GREEN, TONEMAP_CURVE_LINEAR);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_BLUE, TONEMAP_CURVE_LINEAR);
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        TONEMAP_CURVE_LINEAR);
+
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_RED, TONEMAP_CURVE_SRGB);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_GREEN, TONEMAP_CURVE_SRGB);
+                requestBuilder.set(CaptureRequest.TONEMAP_CURVE_BLUE, TONEMAP_CURVE_SRGB);
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        TONEMAP_CURVE_SRGB);
+            } else {
+                // Create a new listener for each run to avoid the results from one run spill
+                // into another run.
+                listener = new SimpleCaptureListener();
+                startPreview(requestBuilder, maxPreviewSz, listener);
+                verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
+                        /*inputToneCurve*/null);
+            }
+        }
+
+        stopPreview();
+    }
+
+    /**
+     * Verify tonemap results.
+     * <p>
+     * Assumes R,G,B channels use the same tone curve
+     * </p>
+     *
+     * @param listener The capture listener used to get the capture results
+     * @param numFramesVerified Number of results to be verified
+     * @param tonemapMode Tonemap mode to verify
+     * @param inputToneCurve Tonemap curve used by all 3 channels, ignored when
+     * map mode is not CONTRAST_CURVE.
+     */
+    private void verifyToneMapModeResults(SimpleCaptureListener listener, int numFramesVerified,
+            int tonemapMode, float[] inputToneCurve) {
+        final int MIN_TONEMAP_CURVE_POINTS = 2;
+        final Float ZERO = new Float(0);
+        final Float ONE = new Float(1.0f);
+
+        int maxCurvePoints = mStaticInfo.getMaxTonemapCurvePointChecked();
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            mCollector.expectEquals("Capture result tonemap mode should match request", tonemapMode,
+                    result.get(CaptureRequest.TONEMAP_MODE));
+            float[] mapRed = result.get(CaptureResult.TONEMAP_CURVE_RED);
+            float[] mapGreen = result.get(CaptureResult.TONEMAP_CURVE_GREEN);
+            float[] mapBlue = result.get(CaptureResult.TONEMAP_CURVE_BLUE);
+            boolean redAvailable =
+                    mCollector.expectTrue("Tonemap curve red shouldn't be null for mode "
+                            + tonemapMode, mapRed != null);
+            boolean greenAvailable =
+                    mCollector.expectTrue("Tonemap curve red shouldn't be null for mode "
+                            + tonemapMode, mapGreen != null);
+            boolean blueAvailable =
+                    mCollector.expectTrue("Tonemap curve red shouldn't be null for mode "
+                            + tonemapMode, mapBlue != null);
+            if (tonemapMode == CaptureResult.TONEMAP_MODE_CONTRAST_CURVE) {
+                /**
+                 * TODO: need figure out a good way to measure the difference
+                 * between request and result, as they may have different array
+                 * size.
+                 */
+            }
+
+            // Tonemap curve result availability and basic sanity check for all modes.
+            if (redAvailable) {
+                mCollector.expectValuesInRange("Tonemap curve red values are out of range",
+                        CameraTestUtils.toObject(mapRed), /*min*/ZERO, /*max*/ONE);
+                mCollector.expectInRange("Tonemap curve red length is out of range",
+                        mapRed.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+            }
+            if (greenAvailable) {
+                mCollector.expectValuesInRange("Tonemap curve green values are out of range",
+                        CameraTestUtils.toObject(mapGreen), /*min*/ZERO, /*max*/ONE);
+                mCollector.expectInRange("Tonemap curve green length is out of range",
+                        mapGreen.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+            }
+            if (blueAvailable) {
+                mCollector.expectValuesInRange("Tonemap curve blue values are out of range",
+                        CameraTestUtils.toObject(mapBlue), /*min*/ZERO, /*max*/ONE);
+                mCollector.expectInRange("Tonemap curve blue length is out of range",
+                        mapBlue.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
+            }
+        }
+    }
+
+    //----------------------------------------------------------------
+    //---------Below are common functions for all tests.--------------
+    //----------------------------------------------------------------
+
+    /**
+     * Enable exposure manual control and change exposure and sensitivity and
+     * clamp the value into the supported range.
+     */
+    private void changeExposure(CaptureRequest.Builder requestBuilder,
+            long expTime, int sensitivity) {
+        expTime = mStaticInfo.getExposureClampToRange(expTime);
+        sensitivity = mStaticInfo.getSensitivityClampToRange(sensitivity);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CONTROL_AE_MODE_OFF);
+        requestBuilder.set(CaptureRequest.SENSOR_EXPOSURE_TIME, expTime);
+        requestBuilder.set(CaptureRequest.SENSOR_SENSITIVITY, sensitivity);
+    }
+    /**
+     * Enable exposure manual control and change exposure time and
+     * clamp the value into the supported range.
+     *
+     * <p>The sensitivity is set to default value.</p>
+     */
+    private void changeExposure(CaptureRequest.Builder requestBuilder, long expTime) {
+        changeExposure(requestBuilder, expTime, DEFAULT_SENSITIVITY);
+    }
+
+    /**
+     * Enable exposure manual control and change sensitivity and
+     * clamp the value into the supported range.
+     *
+     * <p>The exposure time is set to default value.</p>
+     */
+    private void changeExposure(CaptureRequest.Builder requestBuilder, int sensitivity) {
+        changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, sensitivity);
+    }
+
+    /**
+     * Get the exposure time array that contains multiple exposure time steps in
+     * the exposure time range.
+     */
+    private long[] getExposureTimeTestValues() {
+        long[] testValues = new long[DEFAULT_NUM_EXPOSURE_TIME_STEPS + 1];
+        long maxExpTime = mStaticInfo.getExposureMaximumOrDefault(DEFAULT_EXP_TIME_NS);
+        long minxExpTime = mStaticInfo.getExposureMinimumOrDefault(DEFAULT_EXP_TIME_NS);
+
+        long range = maxExpTime - minxExpTime;
+        double stepSize = range / (double)DEFAULT_NUM_EXPOSURE_TIME_STEPS;
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minxExpTime + (long)(stepSize * i);
+            testValues[i] = mStaticInfo.getExposureClampToRange(testValues[i]);
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Generate test focus distances in range of [0, minFocusDistance] in increasing order.
+     */
+    private float[] getFocusDistanceTestValuesInOrder() {
+        float[] testValues = new float[NUM_TEST_FOCUS_DISTANCES + 1];
+        float minValue = 0;
+        float maxValue = mStaticInfo.getMinimumFocusDistanceChecked();
+
+        float range = maxValue - minValue;
+        float stepSize = range / NUM_TEST_FOCUS_DISTANCES;
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minValue + stepSize * i;
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Get the sensitivity array that contains multiple sensitivity steps in the
+     * sensitivity range.
+     * <p>
+     * Sensitivity number of test values is determined by
+     * {@value #DEFAULT_SENSITIVITY_STEP_SIZE} and sensitivity range, and
+     * bounded by {@value #DEFAULT_NUM_SENSITIVITY_STEPS}.
+     * </p>
+     */
+    private int[] getSensitivityTestValues() {
+        int maxSensitivity = mStaticInfo.getSensitivityMaximumOrDefault(
+                DEFAULT_SENSITIVITY);
+        int minSensitivity = mStaticInfo.getSensitivityMinimumOrDefault(
+                DEFAULT_SENSITIVITY);
+
+        int range = maxSensitivity - minSensitivity;
+        int stepSize = DEFAULT_SENSITIVITY_STEP_SIZE;
+        int numSteps = range / stepSize;
+        // Bound the test steps to avoid supper long test.
+        if (numSteps > DEFAULT_NUM_SENSITIVITY_STEPS) {
+            numSteps = DEFAULT_NUM_SENSITIVITY_STEPS;
+            stepSize = range / numSteps;
+        }
+        int[] testValues = new int[numSteps + 1];
+        for (int i = 0; i < testValues.length; i++) {
+            testValues[i] = minSensitivity + stepSize * i;
+            testValues[i] = mStaticInfo.getSensitivityClampToRange(testValues[i]);
+        }
+
+        return testValues;
+    }
+
+    /**
+     * Validate the AE manual control exposure time.
+     *
+     * <p>Exposure should be close enough, and only round down if they are not equal.</p>
+     *
+     * @param request Request exposure time
+     * @param result Result exposure time
+     */
+    private void validateExposureTime(long request, long result) {
+        long expTimeDelta = request - result;
+        // First, round down not up, second, need close enough.
+        mCollector.expectTrue("Exposture time is invalid for AE manaul control test, request: "
+                + request + " result: " + result,
+                expTimeDelta < EXPOSURE_TIME_ERROR_MARGIN_NS && expTimeDelta >= 0);
+    }
+
+    /**
+     * Validate AE manual control sensitivity.
+     *
+     * @param request Request sensitivity
+     * @param result Result sensitivity
+     */
+    private void validateSensitivity(int request, int result) {
+        int sensitivityDelta = request - result;
+        // First, round down not up, second, need close enough.
+        mCollector.expectTrue("Sensitivity is invalid for AE manaul control test, request: "
+                + request + " result: " + result,
+                sensitivityDelta < SENSITIVITY_ERROR_MARGIN && sensitivityDelta >= 0);
+    }
+
+    /**
+     * Validate frame duration for a given capture.
+     *
+     * <p>Frame duration should be longer than exposure time.</p>
+     *
+     * @param result The capture result for a given capture
+     */
+    private void validateFrameDurationForCapture(CaptureResult result) {
+        long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+        long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+        if (VERBOSE) {
+            Log.v(TAG, "frame duration: " + frameDuration + " Exposure time: " + expTime);
+        }
+
+        mCollector.expectTrue(String.format("Frame duration (%d) should be longer than exposure"
+                + " time (%d) for a given capture", frameDuration, expTime),
+                frameDuration >= expTime);
+    }
+
+    private <T> T getValueNotNull(CaptureResult result, Key<T> key) {
+        T value = result.get(key);
+        assertNotNull("Value of Key " + key.getName() + " shouldn't be null", value);
+        return value;
+    }
+
+    /**
+     * Basic verification for the control mode capture result.
+     *
+     * @param key The capture result key to be verified against
+     * @param requestMode The request mode for this result
+     * @param listener The capture listener to get capture results
+     * @param numFramesVerified The number of capture results to be verified
+     */
+    private <T> void verifyCaptureResultForKey(Key<T> key, T requestMode,
+            SimpleCaptureListener listener, int numFramesVerified) {
+        for (int i = 0; i < numFramesVerified; i++) {
+            CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            T resultMode = getValueNotNull(result, key);
+            mCollector.expectEquals("Key " + key.getName() + " result should match request",
+                    requestMode, resultMode);
+        }
+    }
+
+    /**
+     * Verify if the fps is slow down for given input request with certain
+     * controls inside.
+     * <p>
+     * This method selects a max preview size for each fps range, and then
+     * configure the preview stream. Preview is started with the max preview
+     * size, and then verify if the result frame duration is in the frame
+     * duration range.
+     * </p>
+     *
+     * @param requestBuilder The request builder that contains post-processing
+     *            controls that could impact the output frame rate, such as
+     *            {@link CaptureRequest.NOISE_REDUCTION_MODE}. The value of
+     *            these controls must be set to some values such that the frame
+     *            rate is not slow down.
+     * @param numFramesVerified The number of frames to be verified
+     */
+    private void verifyFpsNotSlowDown(CaptureRequest.Builder requestBuilder,
+            int numFramesVerified)  throws Exception {
+        int[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
+        final int FPS_RANGE_SIZE = 2;
+        int[] fpsRange = new int[FPS_RANGE_SIZE];
+        SimpleCaptureListener resultListener;
+
+        for (int i = 0; i < fpsRanges.length; i += FPS_RANGE_SIZE) {
+            fpsRange[0] = fpsRanges[i];
+            fpsRange[1] = fpsRanges[i + 1];
+            Size previewSz = getMaxPreviewSizeForFpsRange(fpsRange);
+            // If unable to find a preview size, then log the failure, and skip this run.
+            if (!mCollector.expectTrue(String.format(
+                    "Unable to find a preview size supporting given fps range [%d, %d]",
+                    fpsRange[0], fpsRange[1]), previewSz != null)) {
+                continue;
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG, String.format("Test fps range [%d, %d] for preview size %s",
+                        fpsRange[0], fpsRange[1], previewSz.toString()));
+            }
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+            resultListener = new SimpleCaptureListener();
+            startPreview(requestBuilder, previewSz, resultListener);
+            long[] frameDurationRange =
+                    new long[]{(long) (1e9 / fpsRange[1]), (long) (1e9 / fpsRange[0])};
+            for (int j = 0; j < numFramesVerified; j++) {
+                CaptureResult result =
+                        resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+                long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+                mCollector.expectInRange(
+                        "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+                        frameDuration,
+                        (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
+                        (long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
+            }
+        }
+
+        mCamera.stopRepeating();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
new file mode 100644
index 0000000..9dc40af
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -0,0 +1,261 @@
+/*
+ * Copyright 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CaptureResultTest extends Camera2AndroidTestCase {
+    private static final String TAG = "CaptureResultTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int MAX_NUM_IMAGES = 5;
+    private static final int NUM_FRAMES_VERIFIED = 300;
+    private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
+
+    // List that includes all public keys from CaptureResult
+    List<CameraMetadata.Key<?>> mAllKeys;
+
+    // List tracking the failed test keys.
+    List<CameraMetadata.Key<?>> mFailedKeys = new ArrayList<CameraMetadata.Key<?>>();
+
+    @Override
+    public void setContext(Context context) {
+        mAllKeys = getAllCaptureResultKeys();
+        super.setContext(context);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mFailedKeys.clear();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * <p>
+     * Basic non-null check test for multiple capture results.
+     * </p>
+     * <p>
+     * When capturing many frames, some camera devices may return some results that have null keys
+     * randomly, which is an API violation and could cause application crash randomly. This test
+     * runs a typical flexible yuv capture many times, and checks if there is any null entries in
+     * a capture result.
+     * </p>
+     */
+    public void testCameraCaptureResultAllKeys() throws Exception {
+        /**
+         * Hardcode a key waiver list for the keys that are allowed to be null.
+         * FIXME: We need get ride of this list, see bug 11116270.
+         */
+        List<CameraMetadata.Key<?>> waiverkeys = new ArrayList<CameraMetadata.Key<?>>();
+        waiverkeys.add(CaptureResult.JPEG_GPS_COORDINATES);
+        waiverkeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+        waiverkeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
+        waiverkeys.add(CaptureResult.JPEG_ORIENTATION);
+        waiverkeys.add(CaptureResult.JPEG_QUALITY);
+        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+        waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+        waiverkeys.add(CaptureResult.SENSOR_TEMPERATURE);
+
+        String[] ids = mCameraManager.getCameraIdList();
+        for (int i = 0; i < ids.length; i++) {
+            CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+            assertNotNull("CameraCharacteristics shouldn't be null", props);
+            Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+            if (hwLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) {
+                continue;
+            }
+            // TODO: check for LIMITED keys
+
+            try {
+                // Create image reader and surface.
+                Size sz = getMaxPreviewSize(ids[i], mCameraManager);
+                createDefaultImageReader(sz, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                        new ImageDropperListener());
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing camera " + ids[i] + "for size " + sz.toString());
+                }
+
+                // Open camera.
+                openDevice(ids[i]);
+
+                // Configure output streams.
+                List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+                outputSurfaces.add(mReaderSurface);
+                configureCameraOutputs(mCamera, outputSurfaces, mCameraListener);;
+
+                CaptureRequest.Builder requestBuilder =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                assertNotNull("Failed to create capture request", requestBuilder);
+                requestBuilder.addTarget(mReaderSurface);
+
+                // Enable face detection if supported
+                byte[] faceModes = props.get(
+                        CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
+                assertNotNull("Available face detection modes shouldn't be null", faceModes);
+                for (int m = 0; m < faceModes.length; m++) {
+                    if (faceModes[m] == CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL) {
+                        if (VERBOSE) {
+                            Log.v(TAG, "testCameraCaptureResultAllKeys - " +
+                                    "setting facedetection mode to full");
+                        }
+                        requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
+                                (int)faceModes[m]);
+                    }
+                }
+
+                // Enable lensShading mode, it should be supported by full mode device.
+                requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+                        CameraMetadata.STATISTICS_LENS_SHADING_MAP_MODE_ON);
+
+                // Start capture
+                SimpleCaptureListener captureListener = new SimpleCaptureListener();
+                startCapture(requestBuilder.build(), /*repeating*/true, captureListener, mHandler);
+
+                // Verify results
+                validateCaptureResult(captureListener, waiverkeys, NUM_FRAMES_VERIFIED);
+
+                stopCapture(/*fast*/false);
+            } finally {
+                closeDevice(ids[i]);
+                closeDefaultImageReader();
+            }
+
+        }
+    }
+
+    private void validateCaptureResult(SimpleCaptureListener captureListener,
+            List<CameraMetadata.Key<?>> skippedKeys, int numFramesVerified) throws Exception {
+        CaptureResult result = null;
+        for (int i = 0; i < numFramesVerified; i++) {
+            result = captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            for (CameraMetadata.Key<?> key : mAllKeys) {
+                if (!skippedKeys.contains(key) && result.get(key) == null) {
+                    mFailedKeys.add(key);
+                }
+            }
+
+            StringBuffer failedKeyNames = new StringBuffer("Below Keys have null values:\n");
+            for (CameraMetadata.Key<?> key : mFailedKeys) {
+                failedKeyNames.append(key.getName() + "\n");
+            }
+
+            assertTrue("Some keys have null values, " + failedKeyNames.toString(),
+                    mFailedKeys.isEmpty());
+        }
+    }
+
+    /**
+     * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
+     * @hide keys.
+     *
+     */
+
+    /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+     * The key entries below this point are generated from metadata
+     * definitions in /system/media/camera/docs. Do not modify by hand or
+     * modify the comment blocks at the start or end.
+     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
+
+    private static List<CameraMetadata.Key<?>> getAllCaptureResultKeys() {
+        ArrayList<CameraMetadata.Key<?>> resultKeys = new ArrayList<CameraMetadata.Key<?>>();
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+        resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
+        resultKeys.add(CaptureResult.CONTROL_AE_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_AF_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
+        resultKeys.add(CaptureResult.CONTROL_MODE);
+        resultKeys.add(CaptureResult.CONTROL_AE_STATE);
+        resultKeys.add(CaptureResult.CONTROL_AF_STATE);
+        resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
+        resultKeys.add(CaptureResult.EDGE_MODE);
+        resultKeys.add(CaptureResult.FLASH_MODE);
+        resultKeys.add(CaptureResult.FLASH_STATE);
+        resultKeys.add(CaptureResult.HOT_PIXEL_MODE);
+        resultKeys.add(CaptureResult.JPEG_GPS_COORDINATES);
+        resultKeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+        resultKeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
+        resultKeys.add(CaptureResult.JPEG_ORIENTATION);
+        resultKeys.add(CaptureResult.JPEG_QUALITY);
+        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+        resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+        resultKeys.add(CaptureResult.LENS_APERTURE);
+        resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+        resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
+        resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+        resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
+        resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
+        resultKeys.add(CaptureResult.LENS_STATE);
+        resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
+        resultKeys.add(CaptureResult.REQUEST_FRAME_COUNT);
+        resultKeys.add(CaptureResult.REQUEST_PIPELINE_DEPTH);
+        resultKeys.add(CaptureResult.SCALER_CROP_REGION);
+        resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+        resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
+        resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+        resultKeys.add(CaptureResult.SENSOR_TIMESTAMP);
+        resultKeys.add(CaptureResult.SENSOR_TEMPERATURE);
+        resultKeys.add(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+        resultKeys.add(CaptureResult.SENSOR_PROFILE_HUE_SAT_MAP);
+        resultKeys.add(CaptureResult.SENSOR_PROFILE_TONE_CURVE);
+        resultKeys.add(CaptureResult.SENSOR_GREEN_SPLIT);
+        resultKeys.add(CaptureResult.SENSOR_TEST_PATTERN_MODE);
+        resultKeys.add(CaptureResult.SHADING_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP_MODE);
+        resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+        resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
+        resultKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
+        resultKeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
+        resultKeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
+        resultKeys.add(CaptureResult.TONEMAP_CURVE_RED);
+        resultKeys.add(CaptureResult.TONEMAP_MODE);
+        resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
+
+        // Add STATISTICS_FACES key separately here because it is not
+        // defined in metadata xml file.
+        resultKeys.add(CaptureResult.STATISTICS_FACES);
+
+        return resultKeys;
+    }
+
+    /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+     * End generated code
+     *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 28cb13e..733c7d2 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -17,30 +17,21 @@
 package android.hardware.camera2.cts;
 
 import static android.hardware.camera2.cts.CameraTestUtils.*;
-import static com.android.ex.camera2.blocking.BlockingStateListener.*;
 
 import android.content.Context;
-import android.graphics.BitmapFactory;
 import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraAccessException;
 import android.hardware.camera2.CameraDevice;
-import android.hardware.camera2.CameraManager;
 import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
 import android.media.Image;
 import android.media.ImageReader;
-import android.os.Environment;
-import android.os.Handler;
-import android.test.AndroidTestCase;
+import android.os.ConditionVariable;
 import android.util.Log;
 import android.view.Surface;
 
-import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
-import com.android.ex.camera2.blocking.BlockingStateListener;
-
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -53,78 +44,67 @@
  * <p>Some invalid access test. </p>
  * <p>TODO: Add more format tests? </p>
  */
-public class ImageReaderTest extends AndroidTestCase {
+public class ImageReaderTest extends Camera2AndroidTestCase {
     private static final String TAG = "ImageReaderTest";
     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-    private static final boolean DUMP_FILE = false;
-    private static final String DEBUG_FILE_NAME_BASE =
-            Environment.getExternalStorageDirectory().getPath();
     // number of frame (for streaming requests) to be verified.
     // TODO: Need extend it to bigger number
     private static final int NUM_FRAME_VERIFIED = 1;
     // Max number of images can be accessed simultaneously from ImageReader.
     private static final int MAX_NUM_IMAGES = 5;
 
-    private CameraManager mCameraManager;
-    private CameraDevice mCamera;
-    private BlockingStateListener mCameraListener;
-    private String[] mCameraIds;
-    private ImageReader mReader = null;
-    private Handler mHandler = null;
-    private SimpleImageListener mListener = null;
-    private CameraTestThread mLooperThread = null;
+    private SimpleImageListener mListener;
 
     @Override
     public void setContext(Context context) {
         super.setContext(context);
-        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
-        assertNotNull("Can't connect to camera manager!", mCameraManager);
     }
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
-        mCameraIds = mCameraManager.getCameraIdList();
-        mLooperThread = new CameraTestThread();
-        mHandler = mLooperThread.start();
-        mCameraListener = new BlockingStateListener();
     }
 
     @Override
     protected void tearDown() throws Exception {
-        if (mCamera != null) {
-            mCamera.close();
-            mCamera = null;
-        }
-        if (mReader != null) {
-            mReader.close();
-            mReader = null;
-        }
-        mLooperThread.close();
-        mHandler = null;
         super.tearDown();
     }
 
     public void testImageReaderFromCameraFlexibleYuv() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
-            Log.i(TAG, "Testing Camera " + mCameraIds[i]);
-            openDevice(mCameraIds[i]);
-            bufferFormatTestByCamera(ImageFormat.YUV_420_888, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+            try {
+                Log.i(TAG, "Testing Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+                bufferFormatTestByCamera(ImageFormat.YUV_420_888);
+            } finally {
+                closeDevice(mCameraIds[i]);
+            }
         }
     }
 
     public void testImageReaderFromCameraJpeg() throws Exception {
         for (int i = 0; i < mCameraIds.length; i++) {
-            Log.v(TAG, "Testing Camera " + mCameraIds[i]);
-            openDevice(mCameraIds[i]);
-            bufferFormatTestByCamera(ImageFormat.JPEG, mCameraIds[i]);
-            closeDevice(mCameraIds[i]);
+            try {
+                Log.v(TAG, "Testing Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+                bufferFormatTestByCamera(ImageFormat.JPEG);
+            } finally {
+                closeDevice(mCameraIds[i]);
+            }
         }
     }
 
-    public void testImageReaderFromCameraRaw() {
-        // TODO: can test this once raw is supported
+    public void testImageReaderFromCameraRaw() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "Testing raw capture for camera " + id);
+                openDevice(id);
+
+                bufferFormatTestByCamera(ImageFormat.RAW_SENSOR);
+            } finally {
+                closeDevice(id);
+            }
+        }
     }
 
     public void testImageReaderInvalidAccessTest() {
@@ -132,226 +112,229 @@
         // exceptions
     }
 
-    private void bufferFormatTestByCamera(int format, String cameraId) throws Exception {
-        CameraCharacteristics properties = mCameraManager.getCameraCharacteristics(cameraId);
-        assertNotNull("Can't get camera properties!", properties);
+    /**
+     * Test two image stream (YUV420_888 and JPEG) capture by using ImageReader.
+     *
+     * <p>Both stream formats are mandatory for Camera2 API</p>
+     */
+    public void testImageReaderYuvAndJpeg() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "YUV and JPEG testing for camera " + id);
+                openDevice(id);
 
-        /**
-         * TODO: cleanup the color format mess, we probably need define formats
-         * in Image class instead of using ImageFormat for camera. also,
-         * probably make sense to change the available format type from Enum[]
-         * to int[]. It'll also be nice to put this into a helper function and
-         * move to util class.
-         */
-        int[] availableFormats = properties.get(CameraCharacteristics.SCALER_AVAILABLE_FORMATS);
-        assertArrayNotEmpty(availableFormats,
-                "availableFormats should not be empty");
-        Arrays.sort(availableFormats);
-        assertTrue("Can't find the format " + format + " in supported formats " +
-                Arrays.toString(availableFormats),
-                Arrays.binarySearch(availableFormats, format) >= 0);
+                bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
 
-        Size[] availableSizes = getSupportedSizeForFormat(format, mCamera.getId(), mCameraManager);
-        assertArrayNotEmpty(availableSizes, "availableSizes should not be empty");
+    /**
+     * Test two image stream (YUV420_888 and RAW_SENSOR) capture by using ImageReader.
+     *
+     */
+    public void testImageReaderYuvAndRaw() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.v(TAG, "YUV and RAW testing for camera " + id);
+                openDevice(id);
+
+                bufferFormatWithYuvTestByCamera(ImageFormat.RAW_SENSOR);
+            } finally {
+                closeDevice(id);
+            }
+        }
+    }
+
+
+    /**
+     * Test capture a given format stream with yuv stream simultaneously.
+     *
+     * <p>Use fixed yuv size, varies targeted format capture size. Single capture is tested.</p>
+     *
+     * @param format The capture format to be tested along with yuv format.
+     */
+    private void bufferFormatWithYuvTestByCamera(int format) throws Exception {
+        if (format != ImageFormat.JPEG && format != ImageFormat.RAW_SENSOR
+                && format != ImageFormat.YUV_420_888) {
+            throw new IllegalArgumentException("Unsupported format: " + format);
+        }
+
+        final int NUM_SINGLE_CAPTURE_TESTED = MAX_NUM_IMAGES - 1;
+        Size maxYuvSz = mOrderedPreviewSizes.get(0);
+        Size[] targetCaptureSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+
+        for (Size captureSz : targetCaptureSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing yuv size " + maxYuvSz.toString() + " and capture size "
+                        + captureSz.toString() + " for camera " + mCamera.getId());
+            }
+
+            ImageReader captureReader = null;
+            ImageReader yuvReader = null;
+            try {
+                // Create YUV image reader
+                SimpleImageReaderListener yuvListener  = new SimpleImageReaderListener();
+                yuvReader = createImageReader(maxYuvSz, ImageFormat.YUV_420_888, MAX_NUM_IMAGES,
+                        yuvListener);
+                Surface yuvSurface = yuvReader.getSurface();
+
+                // Create capture image reader
+                SimpleImageReaderListener captureListener = new SimpleImageReaderListener();
+                captureReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
+                        captureListener);
+                Surface captureSurface = captureReader.getSurface();
+
+                // Capture images.
+                List<Surface> outputSurfaces = new ArrayList<Surface>();
+                outputSurfaces.add(yuvSurface);
+                outputSurfaces.add(captureSurface);
+                CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
+                SimpleCaptureListener resultListener = new SimpleCaptureListener();
+
+                for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
+                    startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+                }
+
+                // Verify capture result and images
+                for (int i = 0; i < NUM_SINGLE_CAPTURE_TESTED; i++) {
+                    resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the capture result back for " + i + "th capture");
+                    }
+
+                    Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the yuv image back for " + i + "th capture");
+                    }
+
+                    Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+                    if (VERBOSE) {
+                        Log.v(TAG, " Got the capture image back for " + i + "th capture");
+                    }
+
+                    //Validate captured images.
+                    CameraTestUtils.validateImage(yuvImage, maxYuvSz.getWidth(),
+                            maxYuvSz.getHeight(), ImageFormat.YUV_420_888, /*filePath*/null);
+                    CameraTestUtils.validateImage(captureImage, captureSz.getWidth(),
+                            captureSz.getHeight(), format, /*filePath*/null);
+                }
+
+                // Stop capture, delete the streams.
+                stopCapture(/*fast*/false);
+            } finally {
+                closeImageReader(captureReader);
+                captureReader = null;
+                closeImageReader(yuvReader);
+                yuvReader = null;
+            }
+        }
+    }
+
+    private void bufferFormatTestByCamera(int format) throws Exception {
+
+        Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
 
         // for each resolution, test imageReader:
         for (Size sz : availableSizes) {
-            if (VERBOSE) Log.v(TAG, "Testing size " + sz.toString() + " for camera " + cameraId);
+            try {
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing size " + sz.toString() + " format " + format
+                            + " for camera " + mCamera.getId());
+                }
 
-            prepareImageReader(sz, format);
+                // Create ImageReader.
+                mListener  = new SimpleImageListener();
+                createDefaultImageReader(sz, format, MAX_NUM_IMAGES, mListener);
 
-            CaptureRequest request = prepareCaptureRequest(format);
+                // Start capture.
+                CaptureRequest request = prepareCaptureRequest();
+                boolean repeating =
+                        (format != ImageFormat.JPEG && format != ImageFormat.RAW_SENSOR);
+                startCapture(request, repeating, null, null);
 
-            captureAndValidateImage(request, sz, format);
+                // Validate images.
+                validateImage(sz, format);
 
-            stopCapture();
+                // stop capture.
+                stopCapture(/*fast*/false);
+            } finally {
+                closeDefaultImageReader();
+            }
+
         }
     }
 
-    private class SimpleImageListener implements ImageReader.OnImageAvailableListener {
-        private int mPendingImages = 0;
-        private final Object mImageSyncObject = new Object();
-
+    private final class SimpleImageListener implements ImageReader.OnImageAvailableListener {
+        private final ConditionVariable imageAvailable = new ConditionVariable();
         @Override
         public void onImageAvailable(ImageReader reader) {
+            if (mReader != reader) {
+                return;
+            }
+
             if (VERBOSE) Log.v(TAG, "new image available");
-            synchronized (mImageSyncObject) {
-                mPendingImages++;
-                mImageSyncObject.notifyAll();
-            }
+            imageAvailable.open();
         }
 
-        public boolean isImagePending() {
-            synchronized (mImageSyncObject) {
-                return (mPendingImages > 0);
-            }
-        }
-
-        public void waitForImage() {
-            final int TIMEOUT_MS = 5000;
-            synchronized (mImageSyncObject) {
-                while (mPendingImages == 0) {
-                    try {
-                        if (VERBOSE)
-                            Log.d(TAG, "waiting for next image");
-                        mImageSyncObject.wait(TIMEOUT_MS);
-                        if (mPendingImages == 0) {
-                            fail("wait for next image timed out");
-                        }
-                    } catch (InterruptedException ie) {
-                        throw new RuntimeException(ie);
-                    }
-                }
-                mPendingImages--;
+        public void waitForAnyImageAvailable(long timeout) {
+            if (imageAvailable.block(timeout)) {
+                imageAvailable.close();
+            } else {
+                fail("wait for image available timed out after " + timeout + "ms");
             }
         }
     }
 
-    private void prepareImageReader(Size sz, int format) throws Exception {
-        int width = sz.getWidth();
-        int height = sz.getHeight();
-        mReader = ImageReader.newInstance(width, height, format, MAX_NUM_IMAGES);
-        mListener  = new SimpleImageListener();
-        mReader.setOnImageAvailableListener(mListener, mHandler);
-        if (VERBOSE) Log.v(TAG, "Preparing ImageReader size " + sz.toString());
-    }
-
-    private CaptureRequest prepareCaptureRequest(int format) throws Exception {
-        List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+    private CaptureRequest prepareCaptureRequest() throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
         Surface surface = mReader.getSurface();
         assertNotNull("Fail to get surface from ImageReader", surface);
         outputSurfaces.add(surface);
-        mCamera.configureOutputs(outputSurfaces);
-        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-        mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+        return prepareCaptureRequestForSurfaces(outputSurfaces).build();
+    }
+
+    private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
+            throws Exception {
+        configureCameraOutputs(mCamera, surfaces, mCameraListener);
 
         CaptureRequest.Builder captureBuilder =
                 mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
         assertNotNull("Fail to get captureRequest", captureBuilder);
-        captureBuilder.addTarget(mReader.getSurface());
+        for (Surface surface : surfaces) {
+            captureBuilder.addTarget(surface);
+        }
 
-        return captureBuilder.build();
+        return captureBuilder;
     }
 
-    private void captureAndValidateImage(CaptureRequest request,
-            Size sz, int format) throws Exception {
+    private void validateImage(Size sz, int format) throws Exception {
         // TODO: Add more format here, and wrap each one as a function.
         Image img;
-        int captureCount = NUM_FRAME_VERIFIED;
 
+        int captureCount = NUM_FRAME_VERIFIED;
         // Only verify single image for still capture
         if (format == ImageFormat.JPEG) {
             captureCount = 1;
-            mCamera.capture(request, null, null);
-        } else {
-            mCamera.setRepeatingRequest(request, null, null);
         }
 
         for (int i = 0; i < captureCount; i++) {
             assertNotNull("Image listener is null", mListener);
             if (VERBOSE) Log.v(TAG, "Waiting for an Image");
-            mListener.waitForImage();
-            img = mReader.acquireNextImage();
-            if (VERBOSE) Log.v(TAG, "Got next image");
-            validateImage(img, sz.getWidth(), sz.getHeight(), format);
+            mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
+            /**
+             * Acquire the latest image in case the validation is slower than
+             * the image producing rate.
+             */
+            img = mReader.acquireLatestImage();
+            assertNotNull("Unable to acquire the latest image", img);
+            if (VERBOSE) Log.v(TAG, "Got the latest image");
+            CameraTestUtils.validateImage(img, sz.getWidth(), sz.getHeight(), format,
+                    DEBUG_FILE_NAME_BASE);
             img.close();
-            // Return the pending images to producer in case the validation is slower
-            // than the image producing rate. Otherwise, it could cause the producer
-            // starvation.
-            while (mListener.isImagePending()) {
-                mListener.waitForImage();
-                img = mReader.acquireNextImage();
-                img.close();
-            }
-        }
-    }
-
-    private void stopCapture() throws CameraAccessException {
-        if (VERBOSE) Log.v(TAG, "Stopping capture and waiting for idle");
-        // Stop repeat, wait for captures to complete, and disconnect from surfaces
-        mCamera.configureOutputs(/*outputs*/ null);
-        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
-        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
-        // Camera has disconnected, clear out the reader
-        mReader.close();
-        mReader = null;
-        mListener = null;
-    }
-
-    private void openDevice(String cameraId) {
-        if (mCamera != null) {
-            throw new IllegalStateException("Already have open camera device");
-        }
-        try {
-            mCamera = CameraTestUtils.openCamera(
-                mCameraManager, cameraId, mCameraListener, mHandler);
-        } catch (CameraAccessException e) {
-            mCamera = null;
-            fail("Fail to open camera, " + Log.getStackTraceString(e));
-        } catch (BlockingOpenException e) {
-            mCamera = null;
-            fail("Fail to open camera, " + Log.getStackTraceString(e));
-        }
-        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
-    }
-
-    private void closeDevice(String cameraId) {
-        mCamera.close();
-        mCamera = null;
-    }
-
-    private void validateImage(Image image, int width, int height, int format) {
-        checkImage(image, width, height, format);
-
-        /**
-         * TODO: validate timestamp:
-         * 1. capture result timestamp against the image timestamp (need
-         * consider frame drops)
-         * 2. timestamps should be monotonically increasing for different requests
-         */
-        if(VERBOSE) Log.v(TAG, "validating Image");
-        byte[] data = getDataFromImage(image);
-        assertTrue("Invalid image data", data != null && data.length > 0);
-
-        if (format == ImageFormat.JPEG) {
-            validateJpegData(data, width, height);
-        } else {
-            validateYuvData(data, width, height, format, image.getTimestamp());
-        }
-    }
-
-    private void validateJpegData(byte[] jpegData, int width, int height) {
-        BitmapFactory.Options bmpOptions = new BitmapFactory.Options();
-        // DecodeBound mode: only parse the frame header to get width/height.
-        // it doesn't decode the pixel.
-        bmpOptions.inJustDecodeBounds = true;
-        BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length, bmpOptions);
-        assertEquals(width, bmpOptions.outWidth);
-        assertEquals(height, bmpOptions.outHeight);
-
-        // Pixel decoding mode: decode whole image. check if the image data
-        // is decodable here.
-        assertNotNull("Decoding jpeg failed",
-                BitmapFactory.decodeByteArray(jpegData, 0, jpegData.length));
-        if (DUMP_FILE) {
-            String fileName =
-                    DEBUG_FILE_NAME_BASE + width + "x" + height + ".yuv";
-            dumpFile(fileName, jpegData);
-        }
-    }
-
-    private void validateYuvData(byte[] yuvData, int width, int height, int format, long ts) {
-        checkYuvFormat(format);
-        if (VERBOSE) Log.v(TAG, "Validating YUV data");
-        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
-        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
-
-        // TODO: Can add data validation if we have test pattern(tracked by b/9625427)
-
-        if (DUMP_FILE) {
-            String fileName =
-                    DEBUG_FILE_NAME_BASE + "/" + width + "x" + height + "_" + ts / 1e6 + ".yuv";
-            dumpFile(fileName, yuvData);
         }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
new file mode 100644
index 0000000..e6cb543
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -0,0 +1,627 @@
+/*
+ * 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.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.media.CamcorderProfile;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaPlayer;
+import android.media.MediaRecorder;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.SystemClock;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.Surface;
+
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * CameraDevice video recording use case tests by using MediaRecorder and
+ * MediaCodec.
+ */
+@LargeTest
+public class RecordingTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "RecordingTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG_DUMP = false;
+    private static final Size VIDEO_SIZE_BOUND = new Size(1920, 1080);
+    private static final int RECORDING_DURATION_MS = 2000;
+    private static final int DURATION_MARGIN_MS = 400;
+    private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
+    private static final int BIT_RATE_1080P = 16000000;
+    private static final int BIT_RATE_MIN = 64000;
+    private static final int BIT_RATE_MAX = 40000000;
+    private static final int VIDEO_FRAME_RATE = 30;
+    private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
+    private static final int[] mCamcorderProfileList = {
+            CamcorderProfile.QUALITY_1080P,
+            CamcorderProfile.QUALITY_480P,
+            CamcorderProfile.QUALITY_720P,
+            CamcorderProfile.QUALITY_CIF,
+            CamcorderProfile.QUALITY_LOW,
+            CamcorderProfile.QUALITY_HIGH,
+            CamcorderProfile.QUALITY_QCIF,
+            CamcorderProfile.QUALITY_QVGA,
+    };
+
+    private List<Size> mSupportedVideoSizes;
+    private Surface mRecordingSurface;
+    private Surface mPreviewSurface;
+    private MediaRecorder mMediaRecorder;
+    private Size mPreviewSz = new Size(0, 0);
+    private String mOutMediaFileName;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * <p>
+     * Test basic camera recording.
+     * </p>
+     * <p>
+     * This test covers the typical basic use case of camera recording.
+     * MediaRecorder is used to record the audio and video, CamcorderProfile is
+     * used to configure the MediaRecorder. It goes through the pre-defined
+     * CamcorderProfile list, test each profile configuration and validate the
+     * recorded video. Preview is set to the video size.
+     * </p>
+     */
+    public void testBasicRecording() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+                mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
+                        VIDEO_SIZE_BOUND);
+
+                basicRecordingTestByCamera();
+            } finally {
+                closeDevice();
+                releasRecorder();
+            }
+        }
+    }
+
+    /**
+     * <p>
+     * Test camera recording for all supported sizes by using MediaRecorder.
+     * </p>
+     * <p>
+     * This test covers camera recording for all supported sizes by camera. MediaRecorder
+     * is used to encode the video. Preview is set to the video size. Recorded videos are
+     * validated according to the recording configuration.
+     * </p>
+     */
+    public void testSupportedVideoSizes() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing supported video size recording for camera " + mCameraIds[i]);
+                // Re-use the MediaRecorder object for the same camera device.
+                mMediaRecorder = new MediaRecorder();
+                openDevice(mCameraIds[i]);
+
+                mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
+                        VIDEO_SIZE_BOUND);
+
+                recordingSizeTestByCamera();
+            } finally {
+                closeDevice();
+                releasRecorder();
+            }
+        }
+    }
+
+    /**
+     * Test different start/stop orders of Camera and Recorder.
+     *
+     * <p>The recording should be working fine for any kind of start/stop orders.</p>
+     */
+    public void testCameraRecorderOrdering() {
+        // TODO: need implement
+    }
+
+    /**
+     * <p>
+     * Test camera recording for all supported sizes by using MediaCodec.
+     * </p>
+     * <p>
+     * This test covers video only recording for all supported sizes (camera and
+     * encoder). MediaCodec is used to encode the video. The recorded videos are
+     * validated according to the recording configuration.
+     * </p>
+     */
+    public void testMediaCodecRecording() throws Exception {
+        // TODO. Need implement.
+    }
+
+    /**
+     * <p>
+     * Test video snapshot for each camera by using MediaRecorder.
+     * </p>
+     * <p>
+     * This test covers video snapshot typical use case. The MediaRecorder is
+     * used to record the video for each supported CamcorderProfile
+     * configuration. The largest still capture size is selected to capture the
+     * JPEG image. The still capture images are validated according to the
+     * capture configuration. The preview/recording jitters are evaluated such
+     * that still capture doesn't disrupt the recording session.
+     * </p>
+     */
+    public void testVideoSnapShot() throws Exception {
+        // TODO. Need implement.
+    }
+
+    public void testTimelapseRecording() {
+        // TODO. Need implement.
+    }
+
+    /**
+     * Test camera recording by using each available CamcorderProfile for a
+     * given camera. preview size is set to the video size.
+     */
+    private void basicRecordingTestByCamera() throws Exception {
+        for (int profileId : mCamcorderProfileList) {
+            int cameraId = Integer.valueOf(mCamera.getId());
+            if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+                continue;
+            }
+
+            CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+            Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+            assertTrue("Video size " + videoSz.toString()
+                    + " must be one of the camera device supported video size!",
+                    mSupportedVideoSizes.contains(videoSz));
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
+            }
+
+            // Configure preview and recording surfaces.
+            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            if (DEBUG_DUMP) {
+                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + cameraId + "_"
+                        + videoSz.toString() + ".mp4";
+            }
+
+            prepareRecordingWithProfile(profile);
+
+            // prepare preview surface: preview size is same as video size.
+            preparePreview(videoSz);
+
+            // Start recording
+            startRecording(/* useMediaRecorder */true);
+
+            // Record certain duration.
+            SystemClock.sleep(RECORDING_DURATION_MS);
+
+            // Stop recording and preview
+            stopRecording(/* useMediaRecorder */true);
+
+            // Validation.
+            validateRecording(videoSz, RECORDING_DURATION_MS);
+        }
+    }
+
+    /**
+     * Test camera recording for each supported video size by camera, preview
+     * size is set to the video size.
+     */
+    private void recordingSizeTestByCamera() throws Exception {
+        for (Size sz : mSupportedVideoSizes) {
+            if (!isSupported(sz, VIDEO_FRAME_RATE, VIDEO_FRAME_RATE)) {
+                continue;
+            }
+
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera recording with video size " + sz.toString());
+            }
+
+            // Configure preview and recording surfaces.
+            mOutMediaFileName = VIDEO_FILE_PATH + "/test_video.mp4";
+            if (DEBUG_DUMP) {
+                mOutMediaFileName = VIDEO_FILE_PATH + "/test_video_" + mCamera.getId() + "_"
+                        + sz.toString() + ".mp4";
+            }
+
+            // Use AVC and AAC a/v compression format.
+            prepareRecording(sz, VIDEO_FRAME_RATE);
+
+            // prepare preview surface: preview size is same as video size.
+            preparePreview(sz);
+
+            // Start recording
+            startRecording(/* useMediaRecorder */true);
+
+            // Record certain duration.
+            SystemClock.sleep(RECORDING_DURATION_MS);
+
+            // Stop recording and preview
+            stopRecording(/* useMediaRecorder */true);
+
+            // Validation.
+            validateRecording(sz, RECORDING_DURATION_MS);
+        }
+    }
+
+    /**
+     * Configure MediaRecorder recording session with CamcorderProfile, prepare
+     * the recording surface.
+     */
+    private void prepareRecordingWithProfile(CamcorderProfile profile)
+            throws Exception {
+        // Prepare MediaRecorder.
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+        mMediaRecorder.setProfile(profile);
+        mMediaRecorder.setOutputFile(mOutMediaFileName);
+        mMediaRecorder.prepare();
+        mRecordingSurface = mMediaRecorder.getSurface();
+        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+    }
+
+    /**
+     * Configure MediaRecorder recording session with CamcorderProfile, prepare
+     * the recording surface. Use AVC for video compression, AAC for audio compression.
+     * Both are required for android devices by android CDD.
+     */
+    private void prepareRecording(Size sz, int frameRate) throws Exception {
+        // Prepare MediaRecorder.
+        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
+        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
+        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+        mMediaRecorder.setOutputFile(mOutMediaFileName);
+        mMediaRecorder.setVideoEncodingBitRate(getVideoBitRate(sz));
+        mMediaRecorder.setVideoFrameRate(frameRate);
+        mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
+        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+        mMediaRecorder.prepare();
+        mRecordingSurface = mMediaRecorder.getSurface();
+        assertNotNull("Recording surface must be non-null!", mRecordingSurface);
+    }
+
+    private void startRecording(boolean useMediaRecorder) throws Exception {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(2);
+        assertTrue("Both preview and recording surfaces should be valid",
+                mPreviewSurface.isValid() && mRecordingSurface.isValid());
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mRecordingSurface);
+        mCamera.configureOutputs(outputSurfaces);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+
+        CaptureRequest.Builder recordingRequestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
+        // Make sure camera output frame rate is set to correct value.
+        int[] fpsRange = {VIDEO_FRAME_RATE, VIDEO_FRAME_RATE};
+        recordingRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+        recordingRequestBuilder.addTarget(mRecordingSurface);
+        recordingRequestBuilder.addTarget(mPreviewSurface);
+        mCamera.setRepeatingRequest(recordingRequestBuilder.build(), null, null);
+
+        if (useMediaRecorder) {
+            mMediaRecorder.start();
+        } else {
+            // TODO: need implement MediaCodec path.
+        }
+    }
+
+    /**
+     * Set the preview surface with given size.
+     *
+     * <p>This method shouldn't be called from UI/mail thread.</p>
+     */
+    private void preparePreview(final Size sz) {
+        // Don't need change the preview size if it is same as current one.
+        if (sz.equals(mPreviewSz)) {
+            return;
+        }
+        mPreviewSz = sz;
+
+        Camera2SurfaceViewStubActivity stubActivity = getActivity();
+        final SurfaceHolder holder = stubActivity.getSurfaceView().getHolder();
+        Handler handler = new Handler(Looper.getMainLooper());
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                holder.setFixedSize(sz.getWidth(), sz.getHeight());
+            }
+        });
+
+        boolean res = stubActivity.waitForSurfaceSizeChanged(
+                WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, sz.getWidth(), sz.getHeight());
+        assertTrue("wait for surface change to " + sz.toString() + " timed out", res);
+        mPreviewSurface = holder.getSurface();
+        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
+    }
+
+    private void stopCameraStreaming() throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, "Stopping camera streaming and waiting for idle");
+        }
+        // Stop repeating, wait for captures to complete, and disconnect from
+        // surfaces
+        mCamera.configureOutputs(/* outputs */null);
+        mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+        mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
+    }
+
+    private void stopRecording(boolean useMediaRecorder) throws Exception {
+        if (useMediaRecorder) {
+            stopCameraStreaming();
+
+            mMediaRecorder.stop();
+            // Can reuse the MediaRecorder object after reset.
+            mMediaRecorder.reset();
+        } else {
+            // TODO: need implement MediaCodec path.
+        }
+        if (mRecordingSurface != null) {
+            mRecordingSurface.release();
+            mRecordingSurface = null;
+        }
+    }
+
+    private void releasRecorder() {
+        if (mMediaRecorder != null) {
+            mMediaRecorder.release();
+            mMediaRecorder = null;
+        }
+    }
+
+    private void validateRecording(Size sz, int durationMs) throws Exception {
+        File outFile = new File(mOutMediaFileName);
+        assertTrue("No video is recorded", outFile.exists());
+
+        MediaPlayer mediaPlayer = new MediaPlayer();
+        try {
+            mediaPlayer.setDataSource(mOutMediaFileName);
+            mediaPlayer.prepare();
+            Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
+            assertTrue("Video size doesn't match", videoSz.equals(sz));
+            int duration = mediaPlayer.getDuration();
+            assertTrue(String.format(
+                    "Video duration doesn't match: recorded %dms, expected %dms", duration,
+                    durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+        } finally {
+            mediaPlayer.release();
+            if (!DEBUG_DUMP) {
+                outFile.delete();
+            }
+        }
+    }
+
+    /**
+     * Calculate a video bit rate based on the size. The bit rate is scaled
+     * based on ratio of video size to 1080p size.
+     */
+    private int getVideoBitRate(Size sz) {
+        int rate = BIT_RATE_1080P;
+        float scaleFactor = sz.getHeight() * sz.getWidth() / (float)(1920 * 1080);
+        rate = (int)(rate * scaleFactor);
+
+        // Clamp to the MIN, MAX range.
+        return Math.max(BIT_RATE_MIN, Math.min(BIT_RATE_MAX, rate));
+    }
+
+    /**
+     * Check if the encoder and camera are able to support this size and frame rate.
+     * Assume the video compression format is AVC.
+     */
+    private boolean isSupported(Size sz, int captureRate, int encodingRate) throws Exception {
+        // Check camera capability.
+        if (!isSupportedByCamera(sz, captureRate)) {
+            return false;
+        }
+
+        // Check encode capability.
+        if (!isSupportedByAVCEncoder(sz, encodingRate)){
+            return false;
+        }
+
+        if(VERBOSE) {
+            Log.v(TAG, "Both encoder and camera support " + sz.toString() + "@" + encodingRate + "@"
+                    + getVideoBitRate(sz) / 1000 + "Kbps");
+        }
+
+        return true;
+    }
+
+    private boolean isSupportedByCamera(Size sz, int frameRate) {
+        // Check if camera can support this sz and frame rate combination.
+        // FIXME: enable below code when bug 12957740 is fixed.
+        /*
+        CameraCharacteristics props = mCameraManager.getCameraCharacteristics(mCamera.getId());
+        assertNotNull("CameraCharacteristics shouldn't be null", props);
+        long[] minDurations = props.get(CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS);
+        assertNotNull("android.scaler.availableMinFrameDurations shouldn't be null", minDurations);
+        // Layout of this array: (format, width, height, duration).
+        boolean foundSz = false;
+        int maxFrameRate = 0;
+        for (int i = 0; i < minDurations.length; i += 4) {
+            if (sz.getHeight() == minDurations[i + 1] && sz.getWidth() == minDurations[i + 2]) {
+                assertTrue("Min duration should be a positive number", minDurations[i + 3] > 0);
+                foundSz = true;
+                maxFrameRate = (int)(1e9f / minDurations[i + 3]);
+                break;
+            }
+        }
+        if (!foundSz || maxFrameRate < frameRate) {
+            return false;
+        }
+        */
+
+        return true;
+    }
+
+    /**
+     * Check if encoder can support this size and frame rate combination by querying
+     * MediaCodec capability. Check is based on size and frame rate. Ignore the bit rate
+     * as the bit rates targeted in this test are well below the bit rate max value specified
+     * by AVC specification for certain level.
+     */
+    private static boolean isSupportedByAVCEncoder(Size sz, int frameRate) {
+        String mimeType = "video/avc";
+        MediaCodecInfo codecInfo = getEncoderInfo(mimeType);
+        if (codecInfo == null) {
+            return false;
+        }
+        CodecCapabilities cap = codecInfo.getCapabilitiesForType(mimeType);
+        if (cap == null) {
+            return false;
+        }
+
+        int highestLevel = 0;
+        for (CodecProfileLevel lvl : cap.profileLevels) {
+            if (lvl.level > highestLevel) {
+                highestLevel = lvl.level;
+            }
+        }
+        // Don't support anything meaningful for level 1 or 2.
+        if (highestLevel <= CodecProfileLevel.AVCLevel2) {
+            return false;
+        }
+
+        if(VERBOSE) {
+            Log.v(TAG, "The highest level supported by encoder is: " + highestLevel);
+        }
+
+        // Put bitRate here for future use.
+        int maxW, maxH, bitRate;
+        // Max encoding speed.
+        int maxMacroblocksPerSecond = 0;
+        switch(highestLevel) {
+            case CodecProfileLevel.AVCLevel21:
+                maxW = 352;
+                maxH = 576;
+                bitRate = 4000000;
+                maxMacroblocksPerSecond = 19800;
+                break;
+            case CodecProfileLevel.AVCLevel22:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 4000000;
+                maxMacroblocksPerSecond = 20250;
+                break;
+            case CodecProfileLevel.AVCLevel3:
+                maxW = 720;
+                maxH = 480;
+                bitRate = 10000000;
+                maxMacroblocksPerSecond = 40500;
+                break;
+            case CodecProfileLevel.AVCLevel31:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 14000000;
+                maxMacroblocksPerSecond = 108000;
+                break;
+            case CodecProfileLevel.AVCLevel32:
+                maxW = 1280;
+                maxH = 720;
+                bitRate = 20000000;
+                maxMacroblocksPerSecond = 216000;
+                break;
+            case CodecProfileLevel.AVCLevel4:
+                maxW = 1920;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 20000000;
+                maxMacroblocksPerSecond = 245760;
+                break;
+            case CodecProfileLevel.AVCLevel41:
+                maxW = 1920;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 50000000;
+                maxMacroblocksPerSecond = 245760;
+                break;
+            case CodecProfileLevel.AVCLevel42:
+                maxW = 2048;
+                maxH = 1088; // It should be 1088 in terms of AVC capability.
+                bitRate = 50000000;
+                maxMacroblocksPerSecond = 522240;
+                break;
+            case CodecProfileLevel.AVCLevel5:
+                maxW = 3672;
+                maxH = 1536;
+                bitRate = 135000000;
+                maxMacroblocksPerSecond = 589824;
+                break;
+            case CodecProfileLevel.AVCLevel51:
+            default:
+                maxW = 4096;
+                maxH = 2304;
+                bitRate = 240000000;
+                maxMacroblocksPerSecond = 983040;
+                break;
+        }
+
+        // Check size limit.
+        if (sz.getWidth() > maxW || sz.getHeight() > maxH) {
+            Log.i(TAG, "Requested resolution " + sz.toString() + " exceeds (" +
+                    maxW + "," + maxH + ")");
+            return false;
+        }
+
+        // Check frame rate limit.
+        Size sizeInMb = new Size((sz.getWidth() + 15) / 16, (sz.getHeight() + 15) / 16);
+        int maxFps = maxMacroblocksPerSecond / (sizeInMb.getWidth() * sizeInMb.getHeight());
+        if (frameRate > maxFps) {
+            Log.i(TAG, "Requested frame rate " + frameRate + " exceeds " + maxFps);
+            return false;
+        }
+
+        return true;
+    }
+
+    private static MediaCodecInfo getEncoderInfo(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (!codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
new file mode 100644
index 0000000..656dc41
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -0,0 +1,879 @@
+/*
+ * 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.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import android.hardware.camera2.cts.helpers.Camera2Focuser;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.media.ExifInterface;
+import android.media.Image;
+import android.os.Build;
+import android.os.ConditionVariable;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+public class StillCaptureTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "StillCaptureTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);;
+    private static final String JPEG_FILE_NAME = DEBUG_FILE_NAME_BASE + "/test.jpeg";
+    private static final int NUM_RESULTS_WAIT_TIMEOUT = 100;
+    // 60 second to accommodate the possible long exposure time.
+    private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
+    private static final float EXIF_FOCAL_LENGTH_ERROR_MARGIN = 0.001f;
+    // TODO: exposure time error margin need to be scaled with exposure time.
+    private static final float EXIF_EXPOSURE_TIME_ERROR_MARGIN_SEC = 0.002f;
+    private static final float EXIF_APERTURE_ERROR_MARGIN = 0.001f;
+    // Exif test data vectors.
+    private static final ExifTestData[] EXIF_TEST_DATA = {
+            new ExifTestData(
+                    /* coords */new double[] {
+                            37.736071, -122.441983, 21.0
+                    },
+                    /* procMethod */"GPS NETWORK HYBRID ARE ALL FINE.",
+                    /* timestamp */1199145600L,
+                    /* orientation */90,
+                    /* jpgQuality */(byte) 80,
+                    /* thumbQuality */(byte) 75),
+            new ExifTestData(
+                    /* coords */new double[] {
+                            0.736071, 0.441983, 1.0
+                    },
+                    /* procMethod */"GPS",
+                    /* timestamp */1199145601L,
+                    /* orientation */180,
+                    /* jpgQuality */(byte) 90,
+                    /* thumbQuality */(byte) 85),
+            new ExifTestData(
+                    /* coords */new double[] {
+                            -89.736071, -179.441983, 100000.0
+                    },
+                    /* procMethod */"NETWORK",
+                    /* timestamp */1199145602L,
+                    /* orientation */270,
+                    /* jpgQuality */(byte) 100,
+                    /* thumbQuality */(byte) 95)
+    };
+
+    // Some exif tags that are not defined by ExifInterface but supported.
+    private static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    private static final String TAG_SUBSEC_TIME = "SubSecTime";
+    private static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+    private static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+    private static final int EXIF_DATETIME_LENGTH = 19;
+    private static final int MAX_REGIONS_AE_INDEX = 0;
+    private static final int MAX_REGIONS_AWB_INDEX = 1;
+    private static final int MAX_REGIONS_AF_INDEX = 2;
+    private static final int WAIT_FOR_FOCUS_DONE_TIMEOUT_MS = 3000;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test JPEG capture exif fields for each camera.
+     */
+    public void testJpegExif() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing JPEG exif for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                jpegExifTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test normal still capture sequence.
+     * <p>
+     * Preview and and jpeg output streams are configured. Max still capture
+     * size is used for jpeg capture. The sequnce of still capture being test
+     * is: start preview, auto focus, precapture metering (if AE is not
+     * converged), then capture jpeg. The AWB and AE are in auto modes. AF mode
+     * is CONTINUOUS_PICTURE.
+     * </p>
+     */
+    public void testTakePicture() throws Exception{
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing touch for focus for Camera " + id);
+                openDevice(id);
+
+                takePictureTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test basic Raw capture. Raw buffer avaiablility is checked, but raw buffer data is not.
+     */
+   public void testBasicRawCapture()  throws Exception {
+       for (int i = 0; i < mCameraIds.length; i++) {
+           try {
+               Log.i(TAG, "Testing raw capture for Camera " + mCameraIds[i]);
+               openDevice(mCameraIds[i]);
+
+               rawCaptureTestByCamera();
+           } finally {
+               closeDevice();
+               closeImageReader();
+           }
+       }
+   }
+
+    /**
+     * Test touch for focus.
+     * <p>
+     * AF is in CAF mode when preview is started, test uses several pre-selected
+     * regions to simulate touches. Active scan is triggered to make sure the AF
+     * converges in reasonable time.
+     * </p>
+     */
+    public void testTouchForFocus() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing touch for focus for Camera " + id);
+                openDevice(id);
+                int[] max3ARegions = mStaticInfo.get3aMaxRegionsChecked();
+                if (!(mStaticInfo.hasFocuser() && max3ARegions[MAX_REGIONS_AF_INDEX] > 0)) {
+                    continue;
+                }
+
+                touchForFocusTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    /**
+     * Test all combination of available preview sizes and still sizes.
+     * <p>
+     * For each still capture, Only the jpeg buffer is validated, capture
+     * result validation is covered by {@link #jpegExifTestByCamera} test.
+     * </p>
+     */
+    public void testStillPreviewCombination() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
+                openDevice(id);
+
+                previewStillCombinationTestByCamera();
+            } finally {
+                closeDevice();
+                closeImageReader();
+            }
+        }
+    }
+
+    private void takePictureTestByCamera() throws Exception {
+        boolean hasFocuser = mStaticInfo.hasFocuser();
+
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        CaptureRequest.Builder previewRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillRequest =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        prepareStillCaptureAndStartPreview(previewRequest, stillRequest, maxPreviewSz,
+                maxStillSz, resultListener, imageListener);
+
+        Camera2Focuser focuser = null;
+        if (hasFocuser) {
+            SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
+            focuser = new Camera2Focuser(mCamera, mPreviewSurface, afListener,
+                    mStaticInfo.getCharacteristics(), mHandler);
+
+            // Auto focus.
+            focuser.startAutoFocus(/*afRegions*/null);
+            afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
+        }
+
+        // TODO: Add AE precapture metering sequence here.
+
+        mCamera.capture(stillRequest.build(), /*listener*/null, /*handler*/null);
+        if (hasFocuser) {
+            focuser.cancelAutoFocus();
+        }
+
+        Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+        validateJpegCapture(image, maxStillSz);
+        // stopPreview must be called here to make sure next time a preview stream
+        // is created with new size.
+        stopPreview();
+    }
+
+    /**
+     * Test touch region for focus by camera.
+     */
+    private void touchForFocusTestByCamera() throws Exception {
+        SimpleCaptureListener listener = new SimpleCaptureListener();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        startPreview(requestBuilder, maxPreviewSz, listener);
+
+        SimpleAutoFocusListener afListener = new SimpleAutoFocusListener();
+        Camera2Focuser focuser = new Camera2Focuser(mCamera, mPreviewSurface, afListener,
+                mStaticInfo.getCharacteristics(), mHandler);
+        int[][] testAfRegions = get3ATestRegionsForCamera();
+
+        for (int i = 0; i < testAfRegions.length; i++) {
+            focuser.touchForAutoFocus(testAfRegions[i]);
+            afListener.waitForAutoFocusDone(WAIT_FOR_FOCUS_DONE_TIMEOUT_MS);
+            focuser.cancelAutoFocus();
+        }
+    }
+
+    private void previewStillCombinationTestByCamera() throws Exception {
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+
+        for (Size stillSz : mOrderedStillSizes)
+            for (Size previewSz : mOrderedPreviewSizes) {
+                if (VERBOSE) {
+                    Log.v(TAG, "Testing JPEG capture size " + stillSz.toString()
+                            + " with preview size " + previewSz.toString() + " for camera "
+                            + mCamera.getId());
+                }
+                CaptureRequest.Builder previewRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+                CaptureRequest.Builder stillRequest =
+                        mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+                prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
+                        stillSz, resultListener, imageListener);
+                mCamera.capture(stillRequest.build(), resultListener, mHandler);
+                Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                validateJpegCapture(image, stillSz);
+                // stopPreview must be called here to make sure next time a preview stream
+                // is created with new size.
+                stopPreview();
+            }
+    }
+
+    /**
+     * Basic raw capture test for each camera.
+     */
+    private void rawCaptureTestByCamera() throws Exception {
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size[] rawSizes = mStaticInfo.getRawOutputSizesChecked();
+        for (Size size : rawSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing Raw capture with size " + size.toString()
+                        + ", preview size " + maxPreviewSz);
+            }
+
+            // Prepare raw capture and start preview.
+            CaptureRequest.Builder previewBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            CaptureRequest.Builder rawBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+            SimpleCaptureListener resultListener = new SimpleCaptureListener();
+            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+            prepareRawCaptureAndStartPreview(previewBuilder, rawBuilder, maxPreviewSz, size,
+                    resultListener, imageListener);
+
+            CaptureRequest rawRequest = rawBuilder.build();
+            mCamera.capture(rawRequest, resultListener, mHandler);
+
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            validateRaw16Image(image, size);
+            if (DEBUG) {
+                byte[] rawBuffer = getDataFromImage(image);
+                String rawFileName =
+                        DEBUG_FILE_NAME_BASE + "/test" + "_" + size.toString() +
+                        "_cam" + mCamera.getId() +  ".raw16";
+                Log.d(TAG, "Dump raw file into " + rawFileName);
+                dumpFile(rawFileName, rawBuffer);
+            }
+
+            verifyRawCaptureResult(rawRequest, resultListener);
+            stopPreview();
+        }
+    }
+
+    private void verifyRawCaptureResult(CaptureRequest rawRequest,
+            SimpleCaptureListener resultListener) {
+        // TODO: validate DNG metadata tags.
+    }
+
+    /**
+     * Issue a Jpeg capture and validate the exif information.
+     * <p>
+     * TODO: Differentiate full and limited device, some of the checks rely on
+     * per frame control and synchronization, most of them don't.
+     * </p>
+     */
+    private void jpegExifTestByCamera() throws Exception {
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        Size maxStillSz = mOrderedStillSizes.get(0);
+        if (VERBOSE) {
+            Log.v(TAG, "Testing JPEG exif with jpeg size " + maxStillSz.toString()
+                    + ", preview size " + maxPreviewSz);
+        }
+
+        // prepare capture and start preview.
+        CaptureRequest.Builder previewBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureRequest.Builder stillBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+        prepareStillCaptureAndStartPreview(previewBuilder, stillBuilder, maxPreviewSz, maxStillSz,
+                resultListener, imageListener);
+
+        // Set the jpeg keys, then issue a capture
+        Size[] thumbnailSizes = mStaticInfo.getAvailableThumbnailSizesChecked();
+        Size maxThumbnailSize = thumbnailSizes[thumbnailSizes.length - 1];
+        Size[] testThumbnailSizes = new Size[EXIF_TEST_DATA.length];
+        Arrays.fill(testThumbnailSizes, maxThumbnailSize);
+        // Make sure thumbnail size (0, 0) is covered.
+        testThumbnailSizes[0] = new Size(0, 0);
+
+        for (int i = 0; i < EXIF_TEST_DATA.length; i++) {
+            /**
+             * Capture multiple shots.
+             *
+             * Verify that:
+             * - Capture request get values are same as were set.
+             * - capture result's exif data is the same as was set by
+             *   the capture request.
+             * - new tags in the result set by the camera service are
+             *   present and semantically correct.
+             */
+            stillBuilder.set(CaptureRequest.JPEG_THUMBNAIL_SIZE, testThumbnailSizes[i]);
+            stillBuilder.set(CaptureRequest.JPEG_GPS_COORDINATES, EXIF_TEST_DATA[i].gpsCoordinates);
+            stillBuilder.set(CaptureRequest.JPEG_GPS_PROCESSING_METHOD,
+                    EXIF_TEST_DATA[i].gpsProcessingMethod);
+            stillBuilder.set(CaptureRequest.JPEG_GPS_TIMESTAMP, EXIF_TEST_DATA[i].gpsTimeStamp);
+            stillBuilder.set(CaptureRequest.JPEG_ORIENTATION, EXIF_TEST_DATA[i].jpegOrientation);
+            stillBuilder.set(CaptureRequest.JPEG_QUALITY, EXIF_TEST_DATA[i].jpegQuality);
+            stillBuilder.set(CaptureRequest.JPEG_THUMBNAIL_QUALITY,
+                    EXIF_TEST_DATA[i].thumbnailQuality);
+
+            // Validate request set and get.
+            mCollector.expectEquals("JPEG thumbnail size request set and get should match",
+                    testThumbnailSizes[i],
+                    stillBuilder.get(CaptureRequest.JPEG_THUMBNAIL_SIZE));
+            mCollector.expectEquals("GPS coordinates request set and get should match.",
+                    toObject(EXIF_TEST_DATA[i].gpsCoordinates),
+                    toObject(stillBuilder.get(CaptureRequest.JPEG_GPS_COORDINATES)));
+            mCollector.expectEquals("GPS processing method request set and get should match",
+                    EXIF_TEST_DATA[i].gpsProcessingMethod,
+                    stillBuilder.get(CaptureRequest.JPEG_GPS_PROCESSING_METHOD));
+            mCollector.expectEquals("GPS time stamp request set and get should match",
+                    EXIF_TEST_DATA[i].gpsTimeStamp,
+                    stillBuilder.get(CaptureRequest.JPEG_GPS_TIMESTAMP));
+            mCollector.expectEquals("JPEG orientation request set and get should match",
+                    EXIF_TEST_DATA[i].jpegOrientation,
+                    stillBuilder.get(CaptureRequest.JPEG_ORIENTATION));
+            mCollector.expectEquals("JPEG quality request set and get should match",
+                    EXIF_TEST_DATA[i].jpegQuality, stillBuilder.get(CaptureRequest.JPEG_QUALITY));
+            mCollector.expectEquals("JPEG thumbnail quality request set and get should match",
+                    EXIF_TEST_DATA[i].thumbnailQuality,
+                    stillBuilder.get(CaptureRequest.JPEG_THUMBNAIL_QUALITY));
+
+            // Capture a jpeg image.
+            CaptureRequest request = stillBuilder.build();
+            mCamera.capture(request, resultListener, mHandler);
+            CaptureResult stillResult =
+                    resultListener.getCaptureResultForRequest(request, NUM_RESULTS_WAIT_TIMEOUT);
+            Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+            basicValidateJpegImage(image, maxStillSz);
+
+            byte[] jpegBuffer = getDataFromImage(image);
+            // Have to dump into a file to be able to use ExifInterface
+            dumpFile(JPEG_FILE_NAME, jpegBuffer);
+            ExifInterface exif = new ExifInterface(JPEG_FILE_NAME);
+
+            if (testThumbnailSizes[i].equals(new Size(0,0))) {
+                mCollector.expectTrue(
+                        "Jpeg shouldn't have thumbnail when thumbnail size is (0, 0)",
+                        !exif.hasThumbnail());
+            }
+
+            // Validate capture result vs. request
+            mCollector.expectEquals("JPEG thumbnail size result and request should match",
+                    testThumbnailSizes[i],
+                    stillResult.get(CaptureResult.JPEG_THUMBNAIL_SIZE));
+            Key<double[]> gpsCoordsKey = CaptureResult.JPEG_GPS_COORDINATES;
+            if (mCollector.expectKeyValueNotNull(stillResult, gpsCoordsKey) != null) {
+                mCollector.expectEquals("GPS coordinates result and request should match.",
+                        toObject(EXIF_TEST_DATA[i].gpsCoordinates),
+                        toObject(stillResult.get(gpsCoordsKey)));
+            }
+            mCollector.expectEquals("GPS processing method result and request should match",
+                    EXIF_TEST_DATA[i].gpsProcessingMethod,
+                    stillResult.get(CaptureResult.JPEG_GPS_PROCESSING_METHOD));
+            mCollector.expectEquals("GPS time stamp result and request should match",
+                    EXIF_TEST_DATA[i].gpsTimeStamp,
+                    stillResult.get(CaptureResult.JPEG_GPS_TIMESTAMP));
+            mCollector.expectEquals("JPEG orientation result and request should match",
+                    EXIF_TEST_DATA[i].jpegOrientation,
+                    stillResult.get(CaptureResult.JPEG_ORIENTATION));
+            mCollector.expectEquals("JPEG quality result and request should match",
+                    EXIF_TEST_DATA[i].jpegQuality, stillResult.get(CaptureResult.JPEG_QUALITY));
+            mCollector.expectEquals("JPEG thumbnail quality result and request should match",
+                    EXIF_TEST_DATA[i].thumbnailQuality,
+                    stillResult.get(CaptureResult.JPEG_THUMBNAIL_QUALITY));
+
+            // Validate other exif tags.
+            jpegTestExifExtraTags(exif, maxStillSz, stillResult);
+        }
+    }
+
+    private void jpegTestExifExtraTags(ExifInterface exif, Size jpegSize, CaptureResult result)
+            throws ParseException {
+        /**
+         * TAG_IMAGE_WIDTH and TAG_IMAGE_LENGTH and TAG_ORIENTATION.
+         * Orientation and exif width/height need to be tested carefully, two cases:
+         *
+         * 1. Device rotate the image buffer physically, then exif width/height may not match
+         * the requested still capture size, we need swap them to check.
+         *
+         * 2. Device use the exif tag to record the image orientation, it doesn't rotate
+         * the jpeg image buffer itself. In this case, the exif width/height should always match
+         * the requested still capture size, and the exif orientation should always match the
+         * requested orientation.
+         *
+         */
+        int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, /*defaultValue*/0);
+        int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, /*defaultValue*/0);
+        Size exifSize = new Size(exifWidth, exifHeight);
+        // Orientation could be missing, which is ok, default to 0.
+        int exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,
+                /*defaultValue*/-1);
+        // Get requested orientation from result, because they should be same.
+        if (mCollector.expectKeyValueNotNull(result, CaptureResult.JPEG_ORIENTATION) != null) {
+            int requestedOrientation = result.get(CaptureResult.JPEG_ORIENTATION);
+            final int ORIENTATION_MIN = ExifInterface.ORIENTATION_UNDEFINED;
+            final int ORIENTATION_MAX = ExifInterface.ORIENTATION_ROTATE_270;
+            boolean orientationValid = mCollector.expectTrue(String.format(
+                    "Exif orientation must be in range of [%d, %d]",
+                    ORIENTATION_MIN, ORIENTATION_MAX),
+                    exifOrientation >= ORIENTATION_MIN && exifOrientation <= ORIENTATION_MAX);
+            if (orientationValid) {
+                /**
+                 * Device captured image doesn't respect the requested orientation,
+                 * which means it rotates the image buffer physically. Then we
+                 * should swap the exif width/height accordingly to compare.
+                 */
+                boolean deviceRotatedImage = exifOrientation == ExifInterface.ORIENTATION_UNDEFINED;
+
+                if (deviceRotatedImage) {
+                    // Case 1.
+                    boolean needSwap = (requestedOrientation % 180 == 90);
+                    if (needSwap) {
+                        exifSize = new Size(exifHeight, exifWidth);
+                    }
+                } else {
+                    // Case 2.
+                    mCollector.expectEquals("Exif orientaiton should match requested orientation",
+                            requestedOrientation, getExifOrientationInDegress(exifOrientation));
+                }
+            }
+        }
+
+        /**
+         * Ideally, need check exifSize == jpegSize == actual buffer size. But
+         * jpegSize == jpeg decode bounds size(from jpeg jpeg frame
+         * header, not exif) was validated in ImageReaderTest, no need to
+         * validate again here.
+         */
+        mCollector.expectEquals("Exif size should match jpeg capture size", jpegSize, exifSize);
+
+        // TAG_DATETIME, it should be local time
+        long currentTimeInMs = System.currentTimeMillis();
+        long currentTimeInSecond = currentTimeInMs / 1000;
+        Date date = new Date(currentTimeInMs);
+        String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
+        String dateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+        if (mCollector.expectTrue("Exif TAG_DATETIME shouldn't be null", dateTime != null)) {
+            mCollector.expectTrue("Exif TAG_DATETIME is wrong",
+                    dateTime.length() == EXIF_DATETIME_LENGTH);
+            long exifTimeInSecond =
+                    new SimpleDateFormat("yyyy:MM:dd HH:mm:ss").parse(dateTime).getTime() / 1000;
+            long delta = currentTimeInSecond - exifTimeInSecond;
+            mCollector.expectTrue("Capture time deviates too much from the current time",
+                    Math.abs(delta) < EXIF_DATETIME_ERROR_MARGIN_SEC);
+            // It should be local time.
+            mCollector.expectTrue("Exif date time should be local time",
+                    dateTime.startsWith(localDatetime));
+        }
+
+        // TAG_FOCAL_LENGTH.
+        float[] focalLengths = mStaticInfo.getAvailableFocalLengthsChecked();
+        float exifFocalLength = (float)exif.getAttributeDouble(ExifInterface.TAG_FOCAL_LENGTH, -1);
+        mCollector.expectEquals("Focal length should match",
+                getClosestValueInArray(focalLengths, exifFocalLength),
+                exifFocalLength, EXIF_FOCAL_LENGTH_ERROR_MARGIN);
+        // More checks for focal length.
+        mCollector.expectEquals("Exif focal length should match capture result",
+                validateFocalLength(result), exifFocalLength);
+
+        // TAG_EXPOSURE_TIME
+        // ExifInterface API gives exposure time value in the form of float instead of rational
+        String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
+        mCollector.expectNotNull("Exif TAG_EXPOSURE_TIME shouldn't be null", exposureTime);
+        if (exposureTime != null) {
+            double exposureTimeValue = Double.parseDouble(exposureTime);
+            long  expTimeResult = result.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+            double expected = expTimeResult / 1e9;
+            mCollector.expectEquals("Exif exposure time doesn't match", expected,
+                    exposureTimeValue, EXIF_EXPOSURE_TIME_ERROR_MARGIN_SEC);
+        }
+
+        // TAG_APERTURE
+        // ExifInterface API gives aperture value in the form of float instead of rational
+        String exifAperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
+        float[] apertures = mStaticInfo.getAvailableAperturesChecked();
+        mCollector.expectNotNull("Exif TAG_APERTURE shouldn't be null", exifAperture);
+        if (exifAperture != null) {
+            float apertureValue = Float.parseFloat(exifAperture);
+            mCollector.expectEquals("Aperture value should match",
+                    getClosestValueInArray(apertures, apertureValue),
+                    apertureValue, EXIF_APERTURE_ERROR_MARGIN);
+            // More checks for aperture.
+            mCollector.expectEquals("Exif aperture length should match capture result",
+                    validateAperture(result), apertureValue);
+        }
+
+        /**
+         * TAG_FLASH. TODO: For full devices, can check a lot more info
+         * (http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/EXIF.html#Flash)
+         */
+        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
+        mCollector.expectNotNull("Exif TAG_FLASH shouldn't be null", flash);
+
+        /**
+         * TAG_WHITE_BALANCE. TODO: For full devices, with the DNG tags, we
+         * should be able to cross-check android.sensor.referenceIlluminant.
+         */
+        String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
+        mCollector.expectNotNull("Exif TAG_WHITE_BALANCE shouldn't be null", whiteBalance);
+
+        // TAG_MAKE
+        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
+        mCollector.expectEquals("Exif TAG_MAKE is incorrect", Build.MANUFACTURER, make);
+
+        // TAG_MODEL
+        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
+        mCollector.expectEquals("Exif TAG_MODEL is incorrect", Build.MODEL, model);
+
+
+        // TAG_ISO
+        int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, /*defaultValue*/-1);
+        int expectedIso = result.get(CaptureResult.SENSOR_SENSITIVITY);
+        mCollector.expectEquals("Exif TAG_ISO is incorrect", expectedIso, iso);
+
+        // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
+        String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
+        mCollector.expectNotNull("Exif TAG_DATETIME_DIGITIZED shouldn't be null", digitizedTime);
+        if (digitizedTime != null) {
+            String expectedDateTime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+            mCollector.expectNotNull("Exif TAG_DATETIME shouldn't be null", expectedDateTime);
+            if (expectedDateTime != null) {
+                mCollector.expectEquals("dataTime should match digitizedTime",
+                        expectedDateTime, digitizedTime);
+            }
+        }
+
+        /**
+         * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at
+         * most 9 digits in ExifInterface implementation, use getAttributeInt to
+         * sanitize it. When the default value -1 is returned, it means that
+         * this exif tag either doesn't exist or is a non-numerical invalid
+         * string. Same rule applies to the rest of sub second tags.
+         */
+        int subSecTime = exif.getAttributeInt(TAG_SUBSEC_TIME, /*defaultValue*/-1);
+        mCollector.expectTrue("Exif TAG_SUBSEC_TIME value is null or invalid!", subSecTime > 0);
+
+        // TAG_SUBSEC_TIME_ORIG
+        int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, /*defaultValue*/-1);
+        mCollector.expectTrue("Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!",
+                subSecTimeOrig > 0);
+
+        // TAG_SUBSEC_TIME_DIG
+        int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, /*defaultValue*/-1);
+        mCollector.expectTrue(
+                "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", subSecTimeDig > 0);
+    }
+
+    private int getExifOrientationInDegress(int exifOrientation) {
+        switch (exifOrientation) {
+            case ExifInterface.ORIENTATION_NORMAL:
+                return 0;
+            case ExifInterface.ORIENTATION_ROTATE_90:
+                return 90;
+            case ExifInterface.ORIENTATION_ROTATE_180:
+                return 180;
+            case ExifInterface.ORIENTATION_ROTATE_270:
+                return 270;
+            default:
+                mCollector.addMessage("It is impossible to get non 0, 90, 180, 270 degress exif" +
+                        "info based on the request orientation range");
+                return 0;
+        }
+    }
+    /**
+     * Immutable class wrapping the exif test data.
+     */
+    private static class ExifTestData {
+        public final double[] gpsCoordinates;
+        public final String gpsProcessingMethod;
+        public final long gpsTimeStamp;
+        public final int jpegOrientation;
+        public final byte jpegQuality;
+        public final byte thumbnailQuality;
+
+        public ExifTestData(double[] coords, String procMethod, long timeStamp, int orientation,
+                byte jpgQuality, byte thumbQuality) {
+            gpsCoordinates = coords;
+            gpsProcessingMethod = procMethod;
+            gpsTimeStamp = timeStamp;
+            jpegOrientation = orientation;
+            jpegQuality = jpgQuality;
+            thumbnailQuality = thumbQuality;
+        }
+    }
+
+    //----------------------------------------------------------------
+    //---------Below are common functions for all tests.--------------
+    //----------------------------------------------------------------
+
+    /**
+     * Simple validation of JPEG image size and format.
+     * <p>
+     * Only validate the image object sanity. It is fast, but doesn't actually
+     * check the buffer data. Assert is used here as it make no sense to
+     * continue the test if the jpeg image captured has some serious failures.
+     * </p>
+     *
+     * @param image The captured jpeg image
+     * @param expectedSize Expected capture jpeg size
+     */
+    private static void basicValidateJpegImage(Image image, Size expectedSize) {
+        Size imageSz = new Size(image.getWidth(), image.getHeight());
+        assertTrue(
+                String.format("Image size doesn't match (expected %s, actual %s) ",
+                        expectedSize.toString(), imageSz.toString()), expectedSize.equals(imageSz));
+        assertEquals("Image format should be JPEG", ImageFormat.JPEG, image.getFormat());
+        assertNotNull("Image plane shouldn't be null", image.getPlanes());
+        assertEquals("Image plane number should be 1", 1, image.getPlanes().length);
+
+        // Jpeg decoding validate was done in ImageReaderTest, no need to duplicate the test here.
+    }
+
+    /**
+     * Validate standard raw (RAW16) capture image.
+     *
+     * @param image The raw16 format image captured
+     * @param rawSize The expected raw size
+     */
+    private static void validateRaw16Image(Image image, Size rawSize) {
+        CameraTestUtils.validateImage(image, rawSize.getWidth(), rawSize.getHeight(),
+                ImageFormat.RAW_SENSOR, /*filePath*/null);
+    }
+
+    /**
+     * Validate JPEG capture image object sanity and test.
+     * <p>
+     * In addition to image object sanity, this function also does the decoding
+     * test, which is slower.
+     * </p>
+     *
+     * @param image The JPEG image to be verified.
+     * @param jpegSize The JPEG capture size to be verified against.
+     */
+    private static void validateJpegCapture(Image image, Size jpegSize) {
+        CameraTestUtils.validateImage(image, jpegSize.getWidth(), jpegSize.getHeight(),
+                ImageFormat.JPEG, /*filePath*/null);
+    }
+
+    private static float getClosestValueInArray(float[] values, float target) {
+        int minIdx = 0;
+        float minDistance = Math.abs(values[0] - target);
+        for(int i = 0; i < values.length; i++) {
+            float distance = Math.abs(values[i] - target);
+            if (minDistance > distance) {
+                minDistance = distance;
+                minIdx = i;
+            }
+        }
+
+        return values[minIdx];
+    }
+
+    /**
+     * Validate and return the focal length.
+     *
+     * @param result Capture result to get the focal length
+     * @return Focal length from capture result or -1 if focal length is not available.
+     */
+    private float validateFocalLength(CaptureResult result) {
+        float[] focalLengths = mStaticInfo.getAvailableFocalLengthsChecked();
+        Float resultFocalLength = result.get(CaptureResult.LENS_FOCAL_LENGTH);
+        if (mCollector.expectTrue("Focal length is invalid",
+                resultFocalLength != null && resultFocalLength > 0)) {
+            List<Float> focalLengthList =
+                    Arrays.asList(CameraTestUtils.toObject(focalLengths));
+            mCollector.expectTrue("Focal length should be one of the available focal length",
+                    focalLengthList.contains(resultFocalLength));
+            return resultFocalLength;
+        }
+        return -1;
+    }
+
+    /**
+     * Validate and return the aperture.
+     *
+     * @param result Capture result to get the aperture
+     * @return Aperture from capture result or -1 if aperture is not available.
+     */
+    private float validateAperture(CaptureResult result) {
+        float[] apertures = mStaticInfo.getAvailableAperturesChecked();
+        Float resultAperture = result.get(CaptureResult.LENS_APERTURE);
+        if (mCollector.expectTrue("Capture result aperture is invalid",
+                resultAperture != null && resultAperture > 0)) {
+            List<Float> apertureList =
+                    Arrays.asList(CameraTestUtils.toObject(apertures));
+            mCollector.expectTrue("Aperture should be one of the available apertures",
+                    apertureList.contains(resultAperture));
+            return resultAperture;
+        }
+        return -1;
+    }
+
+    private static class SimpleAutoFocusListener implements Camera2Focuser.AutoFocusListener {
+        final ConditionVariable focusDone = new ConditionVariable();
+        @Override
+        public void onAutoFocusLocked(boolean success) {
+            focusDone.open();
+        }
+
+        public void waitForAutoFocusDone(long timeoutMs) {
+            if (focusDone.block(timeoutMs)) {
+                focusDone.close();
+            } else {
+                throw new TimeoutRuntimeException("Wait for auto focus done timed out after "
+                        + timeoutMs + "ms");
+            }
+        }
+    }
+
+    /**
+     * Get 5 3A test square regions, one is at center, the other four are at corners of
+     * active array rectangle.
+     *
+     * @return array of test 3A regions
+     */
+    private int[][] get3ATestRegionsForCamera() {
+        final int TEST_3A_REGION_NUM = 5;
+        final int NUM_ELEMENT_IN_REGION = 5;
+        final int DEFAULT_REGION_WEIGHT = 30;
+        final int DEFAULT_REGION_SCALE_RATIO = 8;
+        int[][] regions = new int[TEST_3A_REGION_NUM][NUM_ELEMENT_IN_REGION];
+        final Rect activeArraySize = mStaticInfo.getActiveArraySizeChecked();
+        int regionWidth = activeArraySize.width() / DEFAULT_REGION_SCALE_RATIO;
+        int regionHeight = activeArraySize.height() / DEFAULT_REGION_SCALE_RATIO;
+        int centerX = activeArraySize.width() / 2;
+        int centerY = activeArraySize.height() / 2;
+        int bottomRightX = activeArraySize.width() - 1;
+        int bottomRightY = activeArraySize.height() - 1;
+
+        // Center region
+        int i = 0;
+        regions[i][0] = centerX - regionWidth / 2;       // xmin
+        regions[i][1] = centerY - regionHeight / 2;      // ymin
+        regions[i][2] = centerX + regionWidth / 2 - 1;   // xmax
+        regions[i][3] = centerY + regionHeight / 2 - 1;  // ymax
+        regions[i][4] = DEFAULT_REGION_WEIGHT;
+        i++;
+
+        // Upper left corner
+        regions[i][0] = 0;                // xmin
+        regions[i][1] = 0;                // ymin
+        regions[i][2] = regionWidth - 1;  // xmax
+        regions[i][3] = regionHeight - 1; // ymax
+        regions[i][4] = DEFAULT_REGION_WEIGHT;
+        i++;
+
+        // Upper right corner
+        regions[i][0] = activeArraySize.width() - regionWidth; // xmin
+        regions[i][1] = 0;                                     // ymin
+        regions[i][2] = bottomRightX;                          // xmax
+        regions[i][3] = regionHeight - 1;                      // ymax
+        regions[i][4] = DEFAULT_REGION_WEIGHT;
+        i++;
+
+        // Bootom left corner
+        regions[i][0] = 0;                                       // xmin
+        regions[i][1] = activeArraySize.height() - regionHeight; // ymin
+        regions[i][2] = regionWidth - 1;                         // xmax
+        regions[i][3] = bottomRightY;                            // ymax
+        regions[i][4] = DEFAULT_REGION_WEIGHT;
+        i++;
+
+        // Bootom right corner
+        regions[i][0] = activeArraySize.width() - regionWidth;   // xmin
+        regions[i][1] = activeArraySize.height() - regionHeight; // ymin
+        regions[i][2] = bottomRightX;                            // xmax
+        regions[i][3] = bottomRightY;                            // ymax
+        regions[i][4] = DEFAULT_REGION_WEIGHT;
+        i++;
+
+        if (VERBOSE) {
+            Log.v(TAG, "Generated test regions are: " + Arrays.deepToString(regions));
+        }
+
+        return regions;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
new file mode 100644
index 0000000..84ed37f
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -0,0 +1,272 @@
+/*
+ * 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.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureListener;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.util.Log;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatcher;
+
+import static org.mockito.Mockito.*;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * CameraDevice preview test by using SurfaceView.
+ */
+public class SurfaceViewPreviewTest extends Camera2SurfaceViewTestCase {
+    private static final String TAG = "SurfaceViewPreviewTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int FRAME_TIMEOUT_MS = 1000;
+    private static final int NUM_FRAMES_VERIFIED = 30;
+    private static final int NUM_TEST_PATTERN_FRAMES_VERIFIED = 60;
+    private static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    /**
+     * Test all supported preview sizes for each camera device.
+     * <p>
+     * For the first  {@link #NUM_FRAMES_VERIFIED}  of capture results,
+     * the {@link CaptureListener} callback availability and the capture timestamp
+     * (monotonically increasing) ordering are verified.
+     * </p>
+     */
+    public void testCameraPreview() throws Exception {
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                previewTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Basic test pattern mode preview.
+     * <p>
+     * Only test the test pattern preview and capture result, the image buffer
+     * is not validated.
+     * </p>
+     */
+    public void testBasicTestPatternPreview() throws Exception{
+        for (int i = 0; i < mCameraIds.length; i++) {
+            try {
+                Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
+                openDevice(mCameraIds[i]);
+
+                previewTestPatternTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} for preview, validate the preview
+     * frame duration and exposure time.
+     */
+    public void testPreviewFpsRange() throws Exception {
+        for (String id : mCameraIds) {
+            try {
+                openDevice(id);
+
+                previewFpsRangeTestByCamera();
+            } finally {
+                closeDevice();
+            }
+        }
+    }
+
+    /**
+     * Test preview fps range for all supported ranges. The exposure time are frame duration are
+     * validated.
+     */
+    private void previewFpsRangeTestByCamera() throws Exception {
+        final int FPS_RANGE_SIZE = 2;
+        Size maxPreviewSz = mOrderedPreviewSizes.get(0);
+        int[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
+        int[] fpsRange = new int[FPS_RANGE_SIZE];
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        SimpleCaptureListener resultListener = new SimpleCaptureListener();
+        startPreview(requestBuilder, maxPreviewSz, resultListener);
+
+        for (int i = 0; i < fpsRanges.length; i += FPS_RANGE_SIZE) {
+            fpsRange[0] = fpsRanges[i];
+            fpsRange[1] = fpsRanges[i + 1];
+
+            requestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, fpsRange);
+            resultListener = new SimpleCaptureListener();
+            mCamera.setRepeatingRequest(requestBuilder.build(), resultListener, mHandler);
+
+            verifyPreviewTargetFpsRange(resultListener, NUM_FRAMES_VERIFIED, fpsRange,
+                    maxPreviewSz);
+        }
+
+        stopPreview();
+    }
+
+    private void verifyPreviewTargetFpsRange(SimpleCaptureListener resultListener,
+            int numFramesVerified, int[] fpsRange, Size previewSz) {
+        CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+        long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+        long[] frameDurationRange =
+                new long[]{(long) (1e9 / fpsRange[1]), (long) (1e9 / fpsRange[0])};
+        mCollector.expectInRange(
+                "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+                frameDuration, (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
+                (long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
+        long expTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
+        mCollector.expectTrue(String.format("Exposure time %d must be no larger than frame"
+                + "duration %d", expTime, frameDuration), expTime <= frameDuration);
+
+        Long minFrameDuration = mMinPreviewFrameDurationMap.get(previewSz);
+        boolean findDuration = mCollector.expectTrue("Unable to find minFrameDuration for size "
+                + previewSz.toString(), minFrameDuration != null);
+        if (findDuration) {
+            mCollector.expectTrue("Frame duration " + frameDuration + " must be no smaller than"
+                    + " minFrameDuration " + minFrameDuration, frameDuration >= minFrameDuration);
+        }
+    }
+
+    /**
+     * Test all supported preview sizes for a camera device
+     *
+     * @throws Exception
+     */
+    private void previewTestByCamera() throws Exception {
+        List<Size> previewSizes = getSupportedPreviewSizes(
+                mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+
+        for (final Size sz : previewSizes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Testing camera preview size: " + sz.toString());
+            }
+
+            // TODO: vary the different settings like crop region to cover more cases.
+            CaptureRequest.Builder requestBuilder =
+                    mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+            CaptureListener mockCaptureListener =
+                    mock(CameraDevice.CaptureListener.class);
+
+            startPreview(requestBuilder, sz, mockCaptureListener);
+            verifyCaptureResults(mCamera, mockCaptureListener, NUM_FRAMES_VERIFIED,
+                    NUM_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+            stopPreview();
+        }
+    }
+
+    private void previewTestPatternTestByCamera() throws Exception {
+        Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+        int[] testPatternModes = mStaticInfo.getAvailableTestPatternModesChecked();
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        CaptureListener mockCaptureListener;
+
+        final int[] TEST_PATTERN_DATA = {0, 0xFFFFFFFF, 0xFFFFFFFF, 0}; // G:100%, RB:0.
+        for (int mode : testPatternModes) {
+            if (VERBOSE) {
+                Log.v(TAG, "Test pattern mode: " + mode);
+            }
+            requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_MODE, mode);
+            if (mode == CaptureRequest.SENSOR_TEST_PATTERN_MODE_SOLID_COLOR) {
+                // Assign color pattern to SENSOR_TEST_PATTERN_MODE_DATA
+                requestBuilder.set(CaptureRequest.SENSOR_TEST_PATTERN_DATA, TEST_PATTERN_DATA);
+            }
+            mockCaptureListener = mock(CameraDevice.CaptureListener.class);
+            startPreview(requestBuilder, maxPreviewSize, mockCaptureListener);
+            verifyCaptureResults(mCamera, mockCaptureListener, NUM_TEST_PATTERN_FRAMES_VERIFIED,
+                    NUM_TEST_PATTERN_FRAMES_VERIFIED * FRAME_TIMEOUT_MS);
+        }
+
+        stopPreview();
+    }
+
+    private class IsCaptureResultValid extends ArgumentMatcher<CaptureResult> {
+        @Override
+        public boolean matches(Object obj) {
+            CaptureResult result = (CaptureResult)obj;
+            Long timeStamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+            if (timeStamp != null && timeStamp.longValue() > 0L) {
+                return true;
+            }
+            return false;
+        }
+    }
+
+    private void verifyCaptureResults(
+            CameraDevice camera,
+            CameraDevice.CaptureListener mockListener,
+            int expectResultCount,
+            int timeOutMs) {
+        // Should receive expected number of onCaptureStarted callbacks.
+        ArgumentCaptor<Long> timestamps = ArgumentCaptor.forClass(Long.class);
+        verify(mockListener,
+                timeout(timeOutMs).atLeast(expectResultCount))
+                        .onCaptureStarted(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                timestamps.capture());
+
+        // Validate timestamps: all timestamps should be larger than 0 and monotonically increase.
+        long timestamp = 0;
+        for (Long nextTimestamp : timestamps.getAllValues()) {
+            assertNotNull("Next timestamp is null!", nextTimestamp);
+            assertTrue("Captures are out of order", timestamp < nextTimestamp);
+            timestamp = nextTimestamp;
+        }
+
+        // Should receive expected number of capture results.
+        verify(mockListener,
+                timeout(timeOutMs).atLeast(expectResultCount))
+                        .onCaptureCompleted(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                argThat(new IsCaptureResultValid()));
+
+        // Should not receive any capture failed callbacks.
+        verify(mockListener, never())
+                        .onCaptureFailed(
+                                eq(camera),
+                                isA(CaptureRequest.class),
+                                isA(CaptureFailure.class));
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
new file mode 100644
index 0000000..08aa8bf
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/AssertHelpers.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+import static junit.framework.Assert.*;
+
+import java.util.Arrays;
+
+/**
+ * Helper set of methods to add extra useful assert functionality missing in junit.
+ */
+public class AssertHelpers {
+    /**
+     * Assert that at least one of the elements in data is non-zero.
+     *
+     * <p>An empty or a null array always fails.</p>
+     */
+    public static void assertArrayNotAllZeroes(String message, byte[] data) {
+        int size = data.length;
+
+        int i = 0;
+        for (i = 0; i < size; ++i) {
+            if (data[i] != 0) {
+                break;
+            }
+        }
+
+        assertTrue(message, i < size);
+    }
+
+    /**
+     * Assert that every element in left is less than or equals to the corresponding element in
+     * right.
+     *
+     * <p>Array sizes must match.</p>
+     *
+     * @param message Message to use in case the assertion fails
+     * @param left Left array
+     * @param right Right array
+     */
+    public static void assertArrayNotGreater(String message, float[] left, float[] right) {
+        assertEquals("Array lengths did not match", left.length, right.length);
+
+        String leftString = Arrays.toString(left);
+        String rightString = Arrays.toString(right);
+
+        for (int i = 0; i < left.length; ++i) {
+            String msg = String.format(
+                    "%s: (%s should be less than or equals than %s; item index %d; left = %s; " +
+                    "right = %s)",
+                    message, left[i], right[i], i, leftString, rightString);
+
+            assertTrue(msg, left[i] <= right[i]);
+        }
+    }
+
+    /**
+     * Assert that every element in the value array is greater than the lower bound (exclusive).
+     *
+     * @param value an array of items
+     * @param lowerBound the exclusive lower bound
+     */
+    public static void assertArrayWithinLowerBound(String message, float[] value, float lowerBound)
+    {
+        for (int i = 0; i < value.length; ++i) {
+            assertTrue(
+                    String.format("%s: (%s should be greater than than %s; item index %d in %s)",
+                            message, value[i], lowerBound, i, Arrays.toString(value)),
+                    value[i] > lowerBound);
+        }
+    }
+
+    /**
+     * Assert that every element in the value array is less than the upper bound (exclusive).
+     *
+     * @param value an array of items
+     * @param upperBound the exclusive upper bound
+     */
+    public static void assertArrayWithinUpperBound(String message, float[] value, float upperBound)
+    {
+        for (int i = 0; i < value.length; ++i) {
+            assertTrue(
+                    String.format("%s: (%s should be less than than %s; item index %d in %s)",
+                            message, value[i], upperBound, i, Arrays.toString(value)),
+                    value[i] < upperBound);
+        }
+    }
+
+    /**
+     * Assert that {@code low <= value <= high}
+     */
+    public static void assertInRange(float value, float low, float high) {
+        assertTrue(
+                String.format("Value %s must be greater or equal to %s, but was lower", value, low),
+                value >= low);
+        assertTrue(
+                String.format("Value %s must be less than or equal to %s, but was higher",
+                        value, high),
+                value <= high);
+
+        // TODO: generic by using comparators
+    }
+
+    // Suppress default constructor for noninstantiability
+    private AssertHelpers() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
new file mode 100644
index 0000000..e7cc894
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Camera2Focuser.java
@@ -0,0 +1,373 @@
+/*
+ * 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.camera2.cts.helpers;
+
+import android.graphics.Rect;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.os.Handler;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.ex.camera2.pos.AutoFocusStateMachine;
+import com.android.ex.camera2.pos.AutoFocusStateMachine.AutoFocusStateListener;
+
+/**
+ * A focuser utility class to assist camera to do auto focus.
+ * <p>
+ * This class need create repeating request and single request to do auto focus.
+ * The repeating request is used to get the auto focus states; the single
+ * request is used to trigger the auto focus. This class assumes the camera device
+ * supports auto-focus. Don't use this class if the camera device doesn't have focuser
+ * unit.
+ * </p>
+ */
+public class Camera2Focuser implements AutoFocusStateListener {
+    private static final String TAG = "Focuser";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final AutoFocusStateMachine mAutoFocus = new AutoFocusStateMachine(this);
+    private final Handler mHandler;
+    private final AutoFocusListener mAutoFocusListener;
+    private final CameraDevice mCamera;
+    private final Surface mRequestSurface;
+    private final int AF_REGION_NUM_ELEMENTS = 5;
+    private final CameraCharacteristics mStaticInfo;
+
+    private int mAfRun = 0;
+    private int[] mAfRegions; // int x AF_REGION_NUM_ELEMENTS array.
+    private boolean mLocked = false;
+    private boolean mSuccess = false;
+
+    /**
+     * The callback interface to notify auto focus result.
+     */
+    public interface AutoFocusListener {
+        /**
+         * This callback is called when auto focus completes and locked.
+         *
+         * @param success true if focus was successful, false if otherwise
+         */
+        void onAutoFocusLocked(boolean success);
+    }
+
+    /**
+     * Construct a focuser object, with given capture requestSurface, listener
+     * and handler.
+     * <p>
+     * The focuser object will use camera and requestSurface to submit capture
+     * request and receive focus state changes. The {@link AutoFocusListener} is
+     * used to notify the auto focus callback.
+     * </p>
+     *
+     * @param camera The camera device associated with this focuser
+     * @param requestSurface The surface to issue the capture request with
+     * @param listener The auto focus listener to notify AF result
+     * @param staticInfo The CameraCharacteristics of the camera device
+     * @param handler The handler used to post auto focus callbacks
+     * @throws CameraAccessException
+     */
+    public Camera2Focuser(CameraDevice camera, Surface requestSurface, AutoFocusListener listener,
+            CameraCharacteristics staticInfo, Handler handler) throws CameraAccessException {
+        if (camera == null) {
+            throw new IllegalArgumentException("camera must not be null");
+        }
+        if (listener == null) {
+            throw new IllegalArgumentException("listener must not be null");
+        }
+        if (handler == null) {
+            throw new IllegalArgumentException("handler must not be null");
+        }
+        if (requestSurface == null) {
+            throw new IllegalArgumentException("requestSurface must not be null");
+        }
+        if (staticInfo == null) {
+            throw new IllegalArgumentException("staticInfo must not be null");
+        }
+        Float minFocusDist = staticInfo.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+        if (minFocusDist == null || minFocusDist == 0) {
+            throw new IllegalArgumentException("this camera doesn't have a focuser");
+        }
+
+        mCamera = camera;
+        mRequestSurface = requestSurface;
+        mAutoFocusListener = listener;
+        mStaticInfo = staticInfo;
+        mHandler = handler;
+
+        /**
+         * Begin by always being in passive auto focus.
+         */
+        cancelAutoFocus();
+    }
+
+    @Override
+    public synchronized void onAutoFocusSuccess(CaptureResult result, boolean locked) {
+        mSuccess = true;
+        mLocked = locked;
+
+        if (locked) {
+            dispatchAutoFocusStatusLocked(/*success*/true);
+        }
+    }
+
+    @Override
+    public synchronized void onAutoFocusFail(CaptureResult result, boolean locked) {
+        mSuccess = false;
+        mLocked = locked;
+
+        if (locked) {
+            dispatchAutoFocusStatusLocked(/*success*/false);
+        }
+    }
+
+    @Override
+    public synchronized void onAutoFocusScan(CaptureResult result) {
+        mSuccess = false;
+        mLocked = false;
+    }
+
+    @Override
+    public synchronized void onAutoFocusInactive(CaptureResult result) {
+        mSuccess = false;
+        mLocked = false;
+    }
+
+    /**
+     * Start a active auto focus scan based on the given regions.
+     *
+     * <p>This is usually used for touch for focus, it can make the auto-focus converge based
+     * on some particular region aggressively. But it is usually slow as a full active scan
+     * is initiated. After the auto focus is converged, the {@link cancelAutoFocus} must be called
+     * to resume the continuous auto-focus.</p>
+     *
+     * @param afRegions The AF regions used by focuser auto focus, full active
+     * array size is used if afRegions is null.
+     * @throws CameraAccessException
+     */
+    public synchronized void touchForAutoFocus(int[] afRegions) throws CameraAccessException {
+        startAutoFocusLocked(/*active*/true, afRegions);
+    }
+
+    /**
+     * Start auto focus scan.
+     * <p>
+     * Start an auto focus scan if it was not done yet. If AF passively focused,
+     * lock it. If AF is already locked, return. Otherwise, initiate a full
+     * active scan. This is suitable for still capture: focus should need to be
+     * accurate, but the AF latency also need to be as short as possible.
+     * </p>
+     *
+     * @param afRegions The AF regions used by focuser auto focus, full active
+     *            array size is used if afRegions is null.
+     * @throws CameraAccessException
+     */
+    public synchronized void startAutoFocus(int[] afRegions) throws CameraAccessException {
+        startAutoFocusLocked(/*forceActive*/false, afRegions);
+    }
+
+    /**
+     * Cancel ongoing auto focus, unlock the auto-focus if it was locked, and
+     * resume to passive continuous auto focus.
+     *
+     * @throws CameraAccessException
+     */
+    public synchronized void cancelAutoFocus() throws CameraAccessException {
+        mSuccess = false;
+        mLocked = false;
+
+        // reset the AF regions:
+        setAfRegions(null);
+
+        // Create request builders, the af regions are automatically updated.
+        CaptureRequest.Builder repeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        mAutoFocus.setPassiveAutoFocus(/*picture*/true, repeatingBuilder);
+        mAutoFocus.unlockAutoFocus(repeatingBuilder, requestBuilder);
+        CaptureListener listener = createCaptureListener();
+        mCamera.setRepeatingRequest(repeatingBuilder.build(), listener, mHandler);
+        mCamera.capture(requestBuilder.build(), listener, mHandler);
+    }
+
+
+    private void startAutoFocusLocked(boolean forceActive, int[] afRegions) throws CameraAccessException {
+        setAfRegions(afRegions);
+        mAfRun++;
+
+        // Create request builders, the af regions are automatically updated.
+        CaptureRequest.Builder repeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        if (forceActive) {
+            startAutoFocusFullActiveLocked();
+        } else {
+            // Not forcing a full active scan. If AF passively focused, lock it. If AF is already
+            // locked, return. Otherwise, initiate a full active scan.
+            if (mSuccess && mLocked) {
+                dispatchAutoFocusStatusLocked(/*success*/true);
+                return;
+            } else if (mSuccess) {
+                mAutoFocus.lockAutoFocus(repeatingBuilder, requestBuilder);
+                CaptureListener listener = createCaptureListener();
+                mCamera.setRepeatingRequest(repeatingBuilder.build(), listener, mHandler);
+                mCamera.capture(requestBuilder.build(), listener, mHandler);
+            } else {
+                startAutoFocusFullActiveLocked();
+            }
+        }
+    }
+
+    private void startAutoFocusFullActiveLocked() throws CameraAccessException {
+        // Create request builders, the af regions are automatically updated.
+        CaptureRequest.Builder repeatingBuilder = createRequestBuilder();
+        CaptureRequest.Builder requestBuilder = createRequestBuilder();
+        mAutoFocus.setActiveAutoFocus(repeatingBuilder, requestBuilder);
+        if (repeatingBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
+                != CaptureRequest.CONTROL_AF_TRIGGER_IDLE) {
+            throw new AssertionError("Wrong trigger set in repeating request");
+        }
+        if (requestBuilder.get(CaptureRequest.CONTROL_AF_TRIGGER)
+                != CaptureRequest.CONTROL_AF_TRIGGER_START) {
+            throw new AssertionError("Wrong trigger set in queued request");
+        }
+        mAutoFocus.resetState();
+
+        CaptureListener listener = createCaptureListener();
+        mCamera.setRepeatingRequest(repeatingBuilder.build(), listener, mHandler);
+        mCamera.capture(requestBuilder.build(), listener, mHandler);
+    }
+
+    private void dispatchAutoFocusStatusLocked(final boolean success) {
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                mAutoFocusListener.onAutoFocusLocked(success);
+            }
+        });
+    }
+
+    /**
+     * Create request builder, set the af regions.
+     * @throws CameraAccessException
+     */
+    private CaptureRequest.Builder createRequestBuilder() throws CameraAccessException {
+        if (mAfRegions == null) {
+            throw new IllegalStateException("AF regions are not initialized yet");
+        }
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+        requestBuilder.set(CaptureRequest.CONTROL_AF_REGIONS, mAfRegions);
+        requestBuilder.addTarget(mRequestSurface);
+
+        return requestBuilder;
+    }
+
+    /**
+     * Set AF regions, fall back to default region if afRegions is null.
+     *
+     * @param afRegions The AF regions to set
+     * @throws IllegalArgumentException if the region is malformed (length is 0
+     *             or not multiple times of {@value #AF_REGION_NUM_ELEMENTS}).
+     */
+    private void setAfRegions(int[] afRegions) {
+        if (afRegions == null) {
+            setDefaultAfRegions();
+            return;
+        }
+        // Throw IAE if AF regions are malformed.
+        if (afRegions.length % AF_REGION_NUM_ELEMENTS != 0 || afRegions.length == 0) {
+            throw new IllegalArgumentException("afRegions is malformed, length: " + afRegions.length);
+        }
+
+        mAfRegions = afRegions;
+    }
+
+    /**
+     * Set default AF region to full active array size.
+     */
+    private void setDefaultAfRegions() {
+        Rect activeArraySize = mStaticInfo.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+        if (activeArraySize == null) {
+            throw new AssertionError("Active array size shouldn't be null");
+        }
+
+        // Initialize AF regions with all zeros, meaning that it is up to camera device to device
+        // the regions used by AF.
+        mAfRegions = new int[]{0, 0, 0, 0, 0};
+    }
+    private CaptureListener createCaptureListener() {
+
+        int thisAfRun;
+        synchronized (this) {
+            thisAfRun = mAfRun;
+        }
+
+        final int finalAfRun = thisAfRun;
+
+        return new CaptureListener() {
+            private int mLatestFrameCount = -1;
+
+            @Override
+            public void onCapturePartial(CameraDevice camera, CaptureRequest request,
+                    CaptureResult result) {
+                // In case of a partial result, send to focuser if necessary
+                // 3A fields are present
+                if (result.get(CaptureResult.CONTROL_AF_STATE) != null &&
+                        result.get(CaptureResult.CONTROL_AF_MODE) != null) {
+                    if (VERBOSE) {
+                        Log.v(TAG, "Focuser - got early AF state");
+                    }
+
+                    dispatchToFocuser(result);
+                }
+            }
+
+            @Override
+            public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
+                    CaptureResult result) {
+                    dispatchToFocuser(result);
+            }
+
+            private void dispatchToFocuser(CaptureResult result) {
+                int afRun;
+                synchronized (Camera2Focuser.this) {
+                    // In case of partial results, don't send AF update twice
+                    int frameCount = result.get(CaptureResult.REQUEST_FRAME_COUNT);
+                    if (frameCount <= mLatestFrameCount) return;
+                    mLatestFrameCount = frameCount;
+
+                    afRun = mAfRun;
+                }
+
+                if (afRun != finalAfRun) {
+                    if (VERBOSE) {
+                        Log.w(TAG,
+                                "onCaptureCompleted - Ignoring results from previous AF run "
+                                + finalAfRun);
+                    }
+                    return;
+                }
+
+                mAutoFocus.onCaptureCompleted(result);
+            }
+        };
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
new file mode 100644
index 0000000..f3c0171
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureRequest.Builder;
+import android.hardware.camera2.CaptureResult;
+
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.Matcher;
+import org.junit.rules.ErrorCollector;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/**
+ * A camera test ErrorCollector class to gather the test failures during a test,
+ * instead of failing the test immediately for each failure.
+ */
+public class CameraErrorCollector extends ErrorCollector {
+    private String mCameraMsg = "";
+
+    @Override
+    public void verify() throws Throwable {
+        // Do not remove if using JUnit 3 test runners. super.verify() is protected.
+        super.verify();
+    }
+
+    /**
+     * Adds an unconditional error to the table. Execution continues, but test will fail at the end.
+     *
+     * @param message A string containing the failure reason.
+     */
+    public void addMessage(String message) {
+        super.addError(new Throwable(mCameraMsg + message));
+    }
+
+    /**
+     * Adds a Throwable to the table.  Execution continues, but the test will fail at the end.
+     */
+    @Override
+    public void addError(Throwable error) {
+        super.addError(new Throwable(mCameraMsg + error.getMessage(), error));
+    }
+
+    /**
+     * Adds a failure to the table if {@code matcher} does not match {@code value}.
+     * Execution continues, but the test will fail at the end if the match fails.
+     * The camera id is included into the failure log.
+     */
+    @Override
+    public <T> void checkThat(final T value, final Matcher<T> matcher) {
+        super.checkThat(mCameraMsg, value, matcher);
+    }
+
+    /**
+     * Adds a failure with the given {@code reason} to the table if
+     * {@code matcher} does not match {@code value}. Execution continues, but
+     * the test will fail at the end if the match fails. The camera id is
+     * included into the failure log.
+     */
+    @Override
+    public <T> void checkThat(final String reason, final T value, final Matcher<T> matcher) {
+        super.checkThat(mCameraMsg + reason, value, matcher);
+
+    }
+
+    /**
+     * Set the camera id to this error collector object for logging purpose.
+     *
+     * @param id The camera id to be set.
+     */
+    public void setCameraId(String id) {
+        if (id != null) {
+            mCameraMsg = "Test failed for camera " + id + ": ";
+        } else {
+            mCameraMsg = "";
+        }
+    }
+
+    /**
+     * Adds a failure to the table if {@code condition} is not {@code true}.
+     * <p>
+     * Execution continues, but the test will fail at the end if the condition
+     * failed.
+     * </p>
+     *
+     * @param msg Message to be logged when check fails.
+     * @param condition Log the failure if it is not true.
+     */
+    public boolean expectTrue(String msg, boolean condition) {
+        if (!condition) {
+            addMessage(msg);
+        }
+
+        return condition;
+    }
+
+    /**
+     * Check if the two values are equal.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, T expected, T actual) {
+        if (expected == null) {
+            throw new IllegalArgumentException("expected value shouldn't be null");
+        }
+
+        if (!Objects.equals(expected, actual)) {
+            if (actual == null) {
+                addMessage(msg + ", actual value is null");
+                return false;
+            }
+
+            addMessage(String.format("%s (expected = %s, actual = %s) ", msg, expected.toString(),
+                    actual.toString()));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the two arrays of values are deeply equal.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected array of values to be checked against.
+     * @param actual Actual array of values to be checked.
+     * @return {@code true} if the two arrays of values are deeply equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, T[] expected, T[] actual) {
+        if (expected == null) {
+            throw new IllegalArgumentException("expected value shouldn't be null");
+        }
+
+        if (!Arrays.deepEquals(expected, actual)) {
+            if (actual == null) {
+                addMessage(msg + ", actual value is null");
+                return false;
+            }
+
+            addMessage(String.format("%s (expected = %s, actual = %s) ", msg,
+                    Arrays.deepToString(expected), Arrays.deepToString(actual)));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the two float values are equal with given error tolerance.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @param tolerance The error margin for the equality check.
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, float expected, float actual, float tolerance) {
+        if (expected == actual) {
+            return true;
+        }
+
+        if (!(Math.abs(expected - actual) <= tolerance)) {
+            addMessage(String.format("%s (expected = %s, actual = %s, tolerance = %s) ", msg,
+                    expected, actual, tolerance));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Check if the two double values are equal with given error tolerance.
+     *
+     * @param msg Message to be logged when check fails.
+     * @param expected Expected value to be checked against.
+     * @param actual Actual value to be checked.
+     * @param tolerance The error margin for the equality check
+     * @return {@code true} if the two values are equal, {@code false} otherwise.
+     */
+    public <T> boolean expectEquals(String msg, double expected, double actual, double tolerance) {
+        if (expected == actual)
+        {
+            return true;
+        }
+
+        if (!(Math.abs(expected - actual) <= tolerance)) {
+            addMessage(String.format("%s (expected = %s, actual = %s, tolerance = %s) ", msg,
+                    expected, actual, tolerance));
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Expect the list of values are in the range.
+     *
+     * @param msg Message to be logged
+     * @param list The list of values to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     */
+    public <T extends Comparable<? super T>> void expectValuesInRange(String msg, List<T> list,
+            T min, T max) {
+        for (T value : list) {
+            expectTrue(msg + String.format(", array value " + value.toString() +
+                    " is out of range [%s, %s]",
+                    min.toString(), max.toString()),
+                    value.compareTo(max)<= 0 && value.compareTo(min) >= 0);
+        }
+    }
+
+    /**
+     * Expect the array of values are in the range.
+     *
+     * @param msg Message to be logged
+     * @param array The array of values to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     */
+    public <T extends Comparable<? super T>> void expectValuesInRange(String msg, T[] array,
+            T min, T max) {
+        expectValuesInRange(msg, Arrays.asList(array), min, max);
+    }
+
+    /**
+     * Expect the value is in the range.
+     *
+     * @param msg Message to be logged
+     * @param value The value to be checked
+     * @param min The min value of the range
+     * @param max The max value of the range
+     */
+    public <T extends Comparable<? super T>> void expectInRange(String msg, T value,
+            T min, T max) {
+        expectTrue(msg + String.format(", value " + value.toString() + " is out of range [%s, %s]",
+                min.toString(), max.toString()),
+                value.compareTo(max)<= 0 && value.compareTo(min) >= 0);
+    }
+
+    public void expectNotNull(String msg, Object obj) {
+        checkThat(msg, obj, CoreMatchers.notNullValue());
+    }
+
+    /**
+     * Check if the values in the array are monotonically increasing (decreasing) and not all
+     * equal.
+     *
+     * @param array The array of values to be checked
+     * @param ascendingOrder The monotonicity ordering to be checked with
+     */
+    public <T extends Comparable<? super T>>  void checkArrayMonotonicityAndNotAllEqual(T[] array,
+            boolean ascendingOrder) {
+        String orderMsg = ascendingOrder ? ("increasing order") : ("decreasing order");
+        for (int i = 0; i < array.length - 1; i++) {
+            int compareResult = array[i + 1].compareTo(array[i]);
+            boolean condition = compareResult >= 0;
+            if (!ascendingOrder) {
+                condition = compareResult <= 0;
+            }
+
+            expectTrue(String.format("Adjacent values (%s and %s) %s monotonicity is broken",
+                    array[i].toString(), array[i + 1].toString(), orderMsg), condition);
+        }
+
+        expectTrue("All values of this array are equal: " + array[0].toString(),
+                array[0].compareTo(array[array.length - 1]) != 0);
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param request The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(Builder request, Key<T> key) {
+
+        T value = request.get(key);
+        if (value == null) {
+            addMessage("Key " + key.getName() + " shouldn't be null");
+        }
+
+        return value;
+    }
+
+    /**
+     * Check if the key value is not null and return the value.
+     *
+     * @param result The {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @return The value of the key.
+     */
+    public <T> T expectKeyValueNotNull(CaptureResult result, Key<T> key) {
+
+        T value = result.get(key);
+        if (value == null) {
+            addMessage("Key " + key.getName() + " shouldn't be null");
+        }
+
+        return value;
+    }
+
+    /**
+     * Check if the key is non-null and the value is not equal to target.
+     *
+     * @param request The The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @param expected The expected value of the CaptureRequest key.
+     */
+    public <T> void expectKeyValueNotEquals(Builder request, Key<T> key, T expected) {
+        if (request == null || key == null || expected == null) {
+            throw new IllegalArgumentException("request, key and target shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(request, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " shouldn't have value " + value.toString();
+        checkThat(reason, value, CoreMatchers.not(expected));
+    }
+
+    /**
+     * Check if the key is non-null and the value is not equal to target.
+     *
+     * @param result The The {@link CaptureResult} to get the key from.
+     * @param key The {@link CaptureResult} key to be checked.
+     * @param expected The expected value of the CaptureResult key.
+     */
+    public <T> void expectKeyValueNotEquals(CaptureResult result, Key<T> key, T expected) {
+        if (result == null || key == null || expected == null) {
+            throw new IllegalArgumentException("result, key and target shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(result, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " shouldn't have value " + value.toString();
+        checkThat(reason, value, CoreMatchers.not(expected));
+    }
+
+    /**
+     * Check if the key is non-null and the value is equal to target.
+     *
+     * <p>Only check non-null if the target is null.</p>
+     *
+     * @param request The The {@link CaptureRequest#Builder} to get the key from.
+     * @param key The {@link CaptureRequest} key to be checked.
+     * @param expected The expected value of the CaptureRequest key.
+     */
+    public <T> void expectKeyValueEquals(Builder request, Key<T> key, T expected) {
+        if (request == null || key == null || expected == null) {
+            throw new IllegalArgumentException("request, key and target shouldn't be null");
+        }
+
+        T value;
+        if ((value = expectKeyValueNotNull(request, key)) == null) {
+            return;
+        }
+
+        String reason = "Key " + key.getName() + " value " + value.toString()
+                + " doesn't match the expected value " + expected.toString();
+        checkThat(reason, value, CoreMatchers.equalTo(expected));
+    }
+
+    /**
+     * Check if the element inside of the list are unique.
+     *
+     * @param msg The message to be logged
+     * @param list The list of values to be checked
+     */
+    public <T> void expectValuesUnique(String msg, List<T> list) {
+        Set<T> sizeSet = new HashSet<T>(list);
+        expectTrue(msg + " each size must be distinct", sizeSet.size() == list.size());
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java
new file mode 100644
index 0000000..029ab03
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/MaybeNull.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+import java.io.Closeable;
+import java.io.IOException;
+
+/**
+ * Helper set of methods for dealing with objects that are sometimes {@code null}.
+ *
+ * <p>Used to remove common patterns like: <pre>{@code
+ * if (obj != null) {
+ *     obj.doSomething();
+ * }</pre>
+ *
+ * If this is common, consider adding {@code doSomething} to this class so that the code
+ * looks more like <pre>{@code
+ * MaybeNull.doSomething(obj);
+ * }</pre>
+ */
+public class MaybeNull {
+    /**
+     * Close the underlying {@link AutoCloseable}, if it's not {@code null}.
+     *
+     * @param closeable An object which implements {@link AutoCloseable}.
+     * @throws Exception If {@link AutoCloseable#close} fails.
+     */
+    public static <T extends AutoCloseable> void close(T closeable) throws Exception {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    /**
+     * Close the underlying {@link UncheckedCloseable}, if it's not {@code null}.
+     *
+     * <p>No checked exceptions are thrown. An unknown runtime exception might still
+     * be raised.</p>
+     *
+     * @param closeable An object which implements {@link UncheckedCloseable}.
+     */
+    public static <T extends UncheckedCloseable> void close(T closeable) {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    /**
+     * Close the underlying {@link Closeable}, if it's not {@code null}.
+     *
+     * @param closeable An object which implements {@link Closeable}.
+     * @throws Exception If {@link Closeable#close} fails.
+     */
+    public static <T extends Closeable> void close(T closeable) throws IOException {
+        if (closeable != null) {
+            closeable.close();
+        }
+    }
+
+    // Suppress default constructor for noninstantiability
+    private MaybeNull() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java
new file mode 100644
index 0000000..8520a48
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/Preconditions.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+import java.util.Objects;
+
+/**
+ * Helper set of methods to perform precondition checks before starting method execution.
+ *
+ * <p>Typically used to sanity check arguments or the current object state.</p>
+ */
+public final class Preconditions {
+
+    /**
+     * Checks that the value has the expected bitwise flags set.
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @param flagsName Name of the bitwise flags
+     * @param flags Bit flags to check.
+     * @return arg
+     *
+     * @throws IllegalArgumentException if the bitwise flags weren't set
+     */
+    public static int checkBitFlags(String argName, int arg, String flagsName, int flags) {
+        if ((arg & flags) == 0) {
+            throw new IllegalArgumentException(
+                    String.format("Argument '%s' must have flags '%s' set", argName, flagsName));
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the value is {@link Object#equals equal} to the expected value.
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @param expectedName Name of the expected value
+     * @param expectedValue Expected value
+     * @return arg
+     *
+     * @throws IllegalArgumentException if the values were not equal
+     */
+    public static <T> T checkEquals(String argName, T arg,
+            String expectedName, T expectedValue) {
+        if (!Objects.equals(arg, expectedValue)) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Argument '%s' must be equal to '%s' (was '%s', but expected '%s')",
+                            argName, expectedName, arg, expectedValue));
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the value is not {@code null}.
+     *
+     * <p>
+     * Returns the value directly, so you can use {@code checkNotNull("value", value)} inline.
+     * </p>
+     *
+     * @param argName Name of the argument
+     * @param arg Argument to check
+     * @return arg
+     *
+     * @throws NullPointerException if arg was {@code null}
+     */
+    public static <T> T checkNotNull(String argName, T arg) {
+        if (arg == null) {
+            throw new NullPointerException("Argument '" + argName + "' must not be null");
+        }
+
+        return arg;
+    }
+
+    /**
+     * Checks that the state is currently {@link true}.
+     *
+     * @param message Message to raise an exception with if the state checking fails.
+     * @param state State to check
+     *
+     * @throws IllegalStateException if state was {@code false}
+     *
+     * @return The state value (always {@code true}).
+     */
+    public static boolean checkState(String message, boolean state) {
+        if (!state) {
+            throw new IllegalStateException(message);
+        }
+
+        return state;
+    }
+
+    // Suppress default constructor for noninstantiability
+    private Preconditions() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
new file mode 100644
index 0000000..291ec21
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -0,0 +1,1098 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+import android.graphics.Rect;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Helpers to get common static info out of the camera.
+ *
+ * <p>Avoid boiler plate by putting repetitive get/set patterns in this class.</p>
+ *
+ * <p>Attempt to be durable against the camera device having bad or missing metadata
+ * by providing reasonable defaults and logging warnings when that happens.</p>
+ */
+public class StaticMetadata {
+
+    private static final String TAG = "StaticMetadata";
+    private static final int IGNORE_SIZE_CHECK = -1;
+
+    // TODO: don't hardcode, generate from metadata XML
+    private static final int SENSOR_INFO_EXPOSURE_TIME_RANGE_SIZE = 2;
+    private static final int SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN = 0;
+    private static final int SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX = 1;
+    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST = 100000L; // 100us
+    private static final long SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST = 100000000; // 100ms
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_SIZE = 2;
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MIN = 0;
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MAX = 1;
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST = 100;
+    private static final int SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST = 1600;
+    private static final int STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST = 4;
+    private static final int TONEMAP_MAX_CURVE_POINTS_AT_LEAST = 64;
+
+    // TODO: Consider making this work across any metadata object, not just camera characteristics
+    private final CameraCharacteristics mCharacteristics;
+    private final CheckLevel mLevel;
+    private final CameraErrorCollector mCollector;
+
+    public enum CheckLevel {
+        /** Only log warnings for metadata check failures. Execution continues. */
+        WARN,
+        /**
+         * Use ErrorCollector to collect the metadata check failures, Execution
+         * continues.
+         */
+        COLLECT,
+        /** Assert the metadata check failures. Execution aborts. */
+        ASSERT
+    }
+
+    /**
+     * Construct a new StaticMetadata object.
+     *
+     *<p> Default constructor, only log warnings for the static metadata check failures</p>
+     *
+     * @param characteristics static info for a camera
+     * @throws IllegalArgumentException if characteristics was null
+     */
+    public StaticMetadata(CameraCharacteristics characteristics) {
+        this(characteristics, CheckLevel.WARN, /*collector*/null);
+    }
+
+    /**
+     * Construct a new StaticMetadata object with {@link CameraErrorCollector}.
+     * <p>
+     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
+     * ignored, otherwise, it will be used to log the check failures.
+     * </p>
+     *
+     * @param characteristics static info for a camera
+     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
+     * @throws IllegalArgumentException if characteristics or collector was null.
+     */
+    public StaticMetadata(CameraCharacteristics characteristics, CameraErrorCollector collector) {
+        this(characteristics, CheckLevel.COLLECT, collector);
+    }
+
+    /**
+     * Construct a new StaticMetadata object with {@link CheckLevel} and
+     * {@link CameraErrorCollector}.
+     * <p>
+     * When level is not {@link CheckLevel.COLLECT}, the {@link CameraErrorCollector} will be
+     * ignored, otherwise, it will be used to log the check failures.
+     * </p>
+     *
+     * @param characteristics static info for a camera
+     * @param level The {@link CheckLevel} of this StaticMetadata
+     * @param collector The {@link CameraErrorCollector} used by this StaticMetadata
+     * @throws IllegalArgumentException if characteristics was null or level was
+     *         {@link CheckLevel.COLLECT} but collector was null.
+     */
+    public StaticMetadata(CameraCharacteristics characteristics, CheckLevel level,
+            CameraErrorCollector collector) {
+        if (characteristics == null) {
+            throw new IllegalArgumentException("characteristics was null");
+        }
+        if (level == CheckLevel.COLLECT && collector == null) {
+            throw new IllegalArgumentException("collector must valid when COLLECT level is set");
+        }
+
+        mCharacteristics = characteristics;
+        mLevel = level;
+        mCollector = collector;
+    }
+
+    /**
+     * Get the CameraCharacteristics associated with this StaticMetadata.
+     *
+     * @return A non-null CameraCharacteristics object
+     */
+    public CameraCharacteristics getCharacteristics() {
+        return mCharacteristics;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_FULL}.
+     *
+     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
+     * will always return {@code false}.</p>
+     *
+     * @return true if the device is FULL, false otherwise.
+     */
+    public boolean isHardwareLevelFull() {
+        // TODO: Make this key non-optional for all HAL3.2+ devices
+        Integer hwLevel = getValueFromKeyNonNull(
+                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+
+        // Bad. Missing metadata. Warning is logged.
+        if (hwLevel == null) {
+            return false;
+        }
+
+        // Normal. Device could be limited.
+        int hwLevelInt = hwLevel;
+        return hwLevelInt == CameraMetadata.INFO_SUPPORTED_HARDWARE_LEVEL_FULL;
+    }
+
+    /**
+     * Whether or not the per frame control is supported by the camera device.
+     *
+     * @return true if per frame control is supported, false otherwise.
+     */
+    public boolean isPerFrameControlSupported() {
+        Integer perFrameControl = getValueFromKeyNonNull(CameraCharacteristics.SYNC_MAX_LATENCY);
+
+        if (perFrameControl == null) {
+            return false;
+        }
+
+        return perFrameControl == CameraMetadata.SYNC_MAX_LATENCY_PER_FRAME_CONTROL;
+    }
+
+    /**
+     * Whether or not the hardware level reported by android.info.supportedHardwareLevel
+     * is {@value CameraMetadata#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED}.
+     *
+     * <p>If the camera device is incorrectly reporting the hardwareLevel, this
+     * will always return {@code true}.</p>
+     *
+     * @return true if the device is LIMITED, false otherwise.
+     */
+    public boolean isHardwareLevelLimited() {
+        return !isHardwareLevelFull();
+    }
+
+    /**
+     * Get the exposure time value and clamp to the range if needed.
+     *
+     * @param exposure Input exposure time value to check.
+     * @return Exposure value in the legal range.
+     */
+    public long getExposureClampToRange(long exposure) {
+        long minExposure = getExposureMinimumOrDefault(Long.MAX_VALUE);
+        long maxExposure = getExposureMaximumOrDefault(Long.MIN_VALUE);
+        if (minExposure > SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    String.format(
+                    "Min value %d is too large, set to maximal legal value %d",
+                    minExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST));
+            minExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST;
+        }
+        if (maxExposure < SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                    String.format(
+                    "Max value %d is too small, set to minimal legal value %d",
+                    maxExposure, SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST));
+            maxExposure = SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST;
+        }
+
+        return Math.max(minExposure, Math.min(maxExposure, exposure));
+    }
+
+    /**
+     * Check if the camera device support focuser.
+     *
+     * @return true if camera device support focuser, false otherwise.
+     */
+    public boolean hasFocuser() {
+        return (getMinimumFocusDistanceChecked() > 0);
+    }
+
+    /**
+     * Get minimum focus distance.
+     *
+     * @return minimum focus distance, 0 if minimum focus distance is invalid.
+     */
+    public float getMinimumFocusDistanceChecked() {
+        Key<Float> key = CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE;
+        Float minFocusDistance = getValueFromKeyNonNull(key);
+
+        if (minFocusDistance == null) {
+            return 0.0f;
+        }
+
+        checkTrueForKey(key, " minFocusDistance value shouldn't be negative",
+                minFocusDistance >= 0);
+        if (minFocusDistance < 0) {
+            minFocusDistance = 0.0f;
+        }
+
+        return minFocusDistance;
+    }
+
+    /**
+     * Get focusDistanceCalibration.
+     *
+     * @return focusDistanceCalibration, UNCALIBRATED if value is invalid.
+     */
+    public int getFocusDistanceCalibrationChecked() {
+        Key<Integer> key = CameraCharacteristics.LENS_INFO_FOCUS_DISTANCE_CALIBRATION;
+        Integer calibration = getValueFromKeyNonNull(key);
+
+        if (calibration == null) {
+            return CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED;
+        }
+
+        checkTrueForKey(key, " value is out of range" ,
+                calibration >= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED &&
+                calibration <= CameraMetadata.LENS_INFO_FOCUS_DISTANCE_CALIBRATION_CALIBRATED);
+
+        return calibration;
+    }
+
+    /**
+     * Get max 3A regions and do sanity check.
+     *
+     * @return 3A max regions supported by the camera device
+     */
+    public int[] get3aMaxRegionsChecked() {
+        Key<int[]> key = CameraCharacteristics.CONTROL_MAX_REGIONS;
+        int[] regionCounts = getValueFromKeyNonNull(key);
+
+        if (regionCounts == null) {
+            return new int[]{0, 0, 0};
+        }
+
+        checkTrueForKey(key, " value should contain 3 elements", regionCounts.length == 3);
+        return regionCounts;
+    }
+
+    /**
+     * Get the available anti-banding modes.
+     *
+     * @return The array contains available anti-banding modes.
+     */
+    public byte[] getAeAvailableAntiBandingModesChecked() {
+        Key<byte[]> key = CameraCharacteristics.CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
+        byte[] modes = getValueFromKeyNonNull(key);
+
+        boolean foundAuto = false;
+        for (byte mode : modes) {
+            checkTrueForKey(key, "mode value " + mode + " is out if range",
+                    mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
+                    mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
+            if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
+                foundAuto = true;
+                return modes;
+            }
+        }
+        // Must contain AUTO mode.
+        checkTrueForKey(key, "AUTO mode is missing", foundAuto);
+
+        return modes;
+    }
+
+    public Boolean getFlashInfoChecked() {
+        Key<Boolean> key = CameraCharacteristics.FLASH_INFO_AVAILABLE;
+        Boolean hasFlash = getValueFromKeyNonNull(key);
+
+        // In case the failOnKey only gives warning.
+        if (hasFlash == null) {
+            return false;
+        }
+
+        return hasFlash;
+    }
+
+    public int[] getAvailableTestPatternModesChecked() {
+        CameraMetadata.Key<int[]> key =
+                CameraCharacteristics.SENSOR_AVAILABLE_TEST_PATTERN_MODES;
+        int[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new int[0];
+        }
+
+        int expectValue = CameraCharacteristics.SENSOR_TEST_PATTERN_MODE_OFF;
+        Integer[] boxedModes = CameraTestUtils.toObject(modes);
+        checkTrueForKey(key, " value must contain OFF mode",
+                Arrays.asList(boxedModes).contains(expectValue));
+
+        return modes;
+    }
+
+    /**
+     * Get available thumbnail sizes and do the sanity check.
+     *
+     * @return The array of available thumbnail sizes
+     */
+    public Size[] getAvailableThumbnailSizesChecked() {
+        Key<Size[]> key = CameraCharacteristics.JPEG_AVAILABLE_THUMBNAIL_SIZES;
+        Size[] sizes = getValueFromKeyNonNull(key);
+        final List<Size> sizeList = Arrays.asList(sizes);
+
+        // Size must contain (0, 0).
+        checkTrueForKey(key, "size should contain (0, 0)", sizeList.contains(new Size(0, 0)));
+
+        // Each size must be distinct.
+        checkElementDistinct(key, sizeList);
+
+        // Must be sorted in ascending order by area, by width if areas are same.
+        List<Size> orderedSizes =
+                CameraTestUtils.getAscendingOrderSizes(sizeList, /*ascending*/true);
+        checkTrueForKey(key, "Sizes should be in ascending order: Original " + sizeList.toString()
+                + ", Expected " + orderedSizes.toString(), orderedSizes.equals(sizeList));
+
+        // TODO: Aspect ratio match, need wait for android.scaler.availableStreamConfigurations
+        // implementation see b/12958122.
+
+        return sizes;
+    }
+
+    /**
+     * Get available focal lengths and do the sanity check.
+     *
+     * @return The array of available focal lengths
+     */
+    public float[] getAvailableFocalLengthsChecked() {
+        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_FOCAL_LENGTHS;
+        float[] focalLengths = getValueFromKeyNonNull(key);
+
+        checkTrueForKey(key, "Array should contain at least one element", focalLengths.length >= 1);
+
+        for (int i = 0; i < focalLengths.length; i++) {
+            checkTrueForKey(key,
+                    String.format("focalLength[%d] %f should be positive.", i, focalLengths[i]),
+                    focalLengths[i] > 0);
+        }
+        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(focalLengths)));
+
+        return focalLengths;
+    }
+
+    /**
+     * Get available apertures and do the sanity check.
+     *
+     * @return The non-null array of available apertures
+     */
+    public float[] getAvailableAperturesChecked() {
+        Key<float[]> key = CameraCharacteristics.LENS_INFO_AVAILABLE_APERTURES;
+        float[] apertures = getValueFromKeyNonNull(key);
+
+        checkTrueForKey(key, "Array should contain at least one element", apertures.length >= 1);
+
+        for (int i = 0; i < apertures.length; i++) {
+            checkTrueForKey(key,
+                    String.format("apertures[%d] %f should be positive.", i, apertures[i]),
+                    apertures[i] > 0);
+        }
+        checkElementDistinct(key, Arrays.asList(CameraTestUtils.toObject(apertures)));
+
+        return apertures;
+    }
+
+    /**
+     * Get and check available face detection modes.
+     *
+     * @return The non-null array of available face detection modes
+     */
+    public byte[] getAvailableFaceDetectModesChecked() {
+        Key<byte[]> key = CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES;
+        byte[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new byte[0];
+        }
+
+        List<Byte> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, "Array should contain OFF mode",
+                modeList.contains((byte)CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF));
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes, (byte)CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF,
+                (byte)CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL);
+
+        return modes;
+    }
+
+    /**
+     * Get and check max face detected count.
+     *
+     * @return max number of faces that can be detected
+     */
+    public int getMaxFaceCountChecked() {
+        Key<Integer> key = CameraCharacteristics.STATISTICS_INFO_MAX_FACE_COUNT;
+        Integer count = getValueFromKeyNonNull(key);
+
+        if (count == null) {
+            return 0;
+        }
+
+        List<Byte> faceDetectModes =
+                Arrays.asList(CameraTestUtils.toObject(getAvailableFaceDetectModesChecked()));
+        if (faceDetectModes.contains((byte)CameraMetadata.STATISTICS_FACE_DETECT_MODE_OFF) &&
+                faceDetectModes.size() == 1) {
+            checkTrueForKey(key, " value must be 0 if only OFF mode is supported in "
+                    + "availableFaceDetectionModes", count == 0);
+        } else {
+            int maxFaceCountAtLeat = STATISTICS_INFO_MAX_FACE_COUNT_MIN_AT_LEAST;
+            checkTrueForKey(key, " value must be no less than " + maxFaceCountAtLeat + " if SIMPLE"
+                    + "or FULL is also supported in availableFaceDetectionModes",
+                    count >= maxFaceCountAtLeat);
+        }
+
+        return count;
+    }
+
+    /**
+     * Get and check the available tone map modes.
+     *
+     * @return the availalbe tone map modes
+     */
+    public byte[] getAvailableToneMapModesChecked() {
+        Key<byte[]> key = CameraCharacteristics.TONEMAP_AVAILABLE_TONE_MAP_MODES;
+        byte[] modes = getValueFromKeyNonNull(key);
+
+        if (modes == null) {
+            return new byte[0];
+        }
+
+        List<Byte> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
+        checkTrueForKey(key, " Camera devices must always support FAST mode",
+                modeList.contains((byte)CameraMetadata.TONEMAP_MODE_FAST));
+        if (isHardwareLevelFull()) {
+            checkTrueForKey(key, "Full-capability camera devices must support"
+                    + "CONTRAST_CURVE mode",
+                    modeList.contains((byte)CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) &&
+                    modeList.contains((byte)CameraMetadata.TONEMAP_MODE_FAST));
+        }
+        checkElementDistinct(key, modeList);
+        checkArrayValuesInRange(key, modes, (byte)CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE,
+                (byte)CameraMetadata.TONEMAP_MODE_HIGH_QUALITY);
+
+        return modes;
+    }
+
+    /**
+     * Get and check max tonemap curve point.
+     *
+     * @return Max tonemap curve points.
+     */
+    public int getMaxTonemapCurvePointChecked() {
+        Key<Integer> key = CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS;
+        Integer count = getValueFromKeyNonNull(key);
+
+        if (count == null) {
+            return 0;
+        }
+
+        List<Byte> modeList =
+                Arrays.asList(CameraTestUtils.toObject(getAvailableToneMapModesChecked()));
+        if (modeList.contains((byte)CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE)) {
+            checkTrueForKey(key, "Full-capability camera device must support maxCurvePoints "
+                    + ">= " + TONEMAP_MAX_CURVE_POINTS_AT_LEAST,
+                    count >= TONEMAP_MAX_CURVE_POINTS_AT_LEAST);
+        }
+
+        return count;
+    }
+
+    /**
+     * Get and check pixel array size.
+     */
+    public Size getPixelArraySizeChecked() {
+        Key<Size> key = CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE;
+        Size pixelArray = getValueFromKeyNonNull(key);
+        if (pixelArray == null) {
+            return new Size(0, 0);
+        }
+
+        return pixelArray;
+    }
+
+    /**
+     * Get and check active array size.
+     */
+    public Rect getActiveArraySizeChecked() {
+        Key<Rect> key = CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE;
+        Rect activeArray = getValueFromKeyNonNull(key);
+
+        if (activeArray == null) {
+            return new Rect(0, 0, 0, 0);
+        }
+
+        Size pixelArraySize = getPixelArraySizeChecked();
+        checkTrueForKey(key, "values left/top are invalid", activeArray.left >= 0 && activeArray.top >= 0);
+        checkTrueForKey(key, "values width/height are invalid",
+                activeArray.width() <= pixelArraySize.getWidth() &&
+                activeArray.height() <= pixelArraySize.getHeight());
+
+        return activeArray;
+    }
+
+    /**
+     * Get the sensitivity value and clamp to the range if needed.
+     *
+     * @param sensitivity Input sensitivity value to check.
+     * @return Sensitivity value in legal range.
+     */
+    public int getSensitivityClampToRange(int sensitivity) {
+        int minSensitivity = getSensitivityMinimumOrDefault(Integer.MAX_VALUE);
+        int maxSensitivity = getSensitivityMaximumOrDefault(Integer.MIN_VALUE);
+        if (minSensitivity > SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    String.format(
+                    "Min value %d is too large, set to maximal legal value %d",
+                    minSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST));
+            minSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST;
+        }
+        if (maxSensitivity < SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST) {
+            failKeyCheck(CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                    String.format(
+                    "Max value %d is too small, set to minimal legal value %d",
+                    maxSensitivity, SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST));
+            maxSensitivity = SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST;
+        }
+
+        return Math.max(minSensitivity, Math.min(maxSensitivity, sensitivity));
+    }
+
+    /**
+     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the largest minimum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMinimumOrDefault() {
+        return getSensitivityMinimumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MIN_AT_MOST);
+    }
+
+    /**
+     * Get the minimum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMinimumOrDefault(int defaultValue) {
+        return getArrayElementOrDefault(
+                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                defaultValue,
+                "minimum",
+                SENSOR_INFO_SENSITIVITY_RANGE_MIN,
+                SENSOR_INFO_SENSITIVITY_RANGE_SIZE);
+    }
+
+    /**
+     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the smallest maximum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMaximumOrDefault() {
+        return getSensitivityMaximumOrDefault(SENSOR_INFO_SENSITIVITY_RANGE_MAX_AT_LEAST);
+    }
+
+    /**
+     * Get the maximum value for a sensitivity range from android.sensor.info.sensitivityRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public int getSensitivityMaximumOrDefault(int defaultValue) {
+        return getArrayElementOrDefault(
+                CameraCharacteristics.SENSOR_INFO_SENSITIVITY_RANGE,
+                defaultValue,
+                "maximum",
+                SENSOR_INFO_SENSITIVITY_RANGE_MAX,
+                SENSOR_INFO_SENSITIVITY_RANGE_SIZE);
+    }
+
+    /**
+     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMinimumOrDefault(long defaultValue) {
+        return getArrayElementOrDefault(
+                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                defaultValue,
+                "minimum",
+                SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN,
+                SENSOR_INFO_EXPOSURE_TIME_RANGE_SIZE);
+    }
+
+    /**
+     * Get the minimum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the largest minimum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMinimumOrDefault() {
+        return getExposureMinimumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MIN_AT_MOST);
+    }
+
+    /**
+     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param defaultValue Value to return if no legal value is available
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMaximumOrDefault(long defaultValue) {
+        return getArrayElementOrDefault(
+                CameraCharacteristics.SENSOR_INFO_EXPOSURE_TIME_RANGE,
+                defaultValue,
+                "maximum",
+                SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX,
+                SENSOR_INFO_EXPOSURE_TIME_RANGE_SIZE);
+    }
+
+    /**
+     * Get the maximum value for an exposure range from android.sensor.info.exposureTimeRange.
+     *
+     * <p>If the camera is incorrectly reporting values, log a warning and return
+     * the default value instead, which is the smallest maximum value required to be supported
+     * by all camera devices.</p>
+     *
+     * @return The value reported by the camera device or the defaultValue otherwise.
+     */
+    public long getExposureMaximumOrDefault() {
+        return getExposureMaximumOrDefault(SENSOR_INFO_EXPOSURE_TIME_RANGE_MAX_AT_LEAST);
+    }
+
+    /**
+     * Get aeAvailableModes and do the sanity check.
+     *
+     * <p>Depending on the check level this class has, for WAR or COLLECT levels,
+     * If the aeMode list is invalid, return an empty mode array. The the caller doesn't
+     * have to abort the execution even the aeMode list is invalid.</p>
+     * @return AE available modes
+     */
+    public byte[] getAeAvailableModesChecked() {
+        CameraMetadata.Key<byte[]> modesKey = CameraCharacteristics.CONTROL_AE_AVAILABLE_MODES;
+        byte[] modes = getValueFromKeyNonNull(modesKey);
+        if (modes == null) {
+            modes = new byte[0];
+        }
+        List<Integer> modeList = new ArrayList<Integer>();
+        for (byte mode : modes) {
+            modeList.add((int)(mode));
+        }
+        checkTrueForKey(modesKey, "value is empty", !modeList.isEmpty());
+
+        // All camera device must support ON
+        checkTrueForKey(modesKey, "values " + modeList.toString() + " must contain ON mode",
+                modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON));
+
+        // All camera devices with flash units support ON_AUTO_FLASH and ON_ALWAYS_FLASH
+        CameraMetadata.Key<Boolean> flashKey= CameraCharacteristics.FLASH_INFO_AVAILABLE;
+        Boolean hasFlash = getValueFromKeyNonNull(flashKey);
+        if (hasFlash == null) {
+            hasFlash = false;
+        }
+        if (hasFlash) {
+            boolean flashModeConsistentWithFlash =
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) &&
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH);
+            checkTrueForKey(modesKey,
+                    "value must contain ON_AUTO_FLASH and ON_ALWAYS_FLASH and  when flash is" +
+                    "available", flashModeConsistentWithFlash);
+        } else {
+            boolean flashModeConsistentWithoutFlash =
+                    !(modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH) ||
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_ALWAYS_FLASH) ||
+                    modeList.contains(CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE));
+            checkTrueForKey(modesKey,
+                    "value must not contain ON_AUTO_FLASH, ON_ALWAYS_FLASH and" +
+                    "ON_AUTO_FLASH_REDEYE when flash is unavailable",
+                    flashModeConsistentWithoutFlash);
+        }
+
+        // FULL mode camera devices always support OFF mode.
+        boolean condition =
+                !isHardwareLevelFull() || modeList.contains(CameraMetadata.CONTROL_AE_MODE_OFF);
+        checkTrueForKey(modesKey, "Full capability device must have OFF mode", condition);
+
+        // Boundary check.
+        for (byte mode : modes) {
+            checkTrueForKey(modesKey, "Value " + mode + " is out of bound",
+                    mode >= CameraMetadata.CONTROL_AE_MODE_OFF
+                    && mode <= CameraMetadata.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE);
+        }
+
+        return modes;
+    }
+
+    /**
+     * Get supported raw output sizes and do the check.
+     *
+     * @return Empty size array if raw output is not supported
+     */
+    public Size[] getRawOutputSizesChecked() {
+        return getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+    }
+
+    /**
+     * Get supported jpeg output sizes and do the check.
+     *
+     * @return Empty size array if jpeg output is not supported
+     */
+    public Size[] getJpegOutputSizeChecked() {
+        return getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS_OUTPUT);
+    }
+
+    /**
+     * Get available sizes for given format
+     *
+     * @param format The format for the requested size array.
+     * @param direction The stream direction, input or output.
+     * @return The sizes of the given format, empty array if no available size is found.
+     */
+    public Size[] getAvailableSizesForFormatChecked(int format, int direction) {
+        final int NUM_ELEMENTS_IN_STREAM_CONFIG = 4;
+        ArrayList<Size> sizeList = new ArrayList<Size>();
+        CameraMetadata.Key<int[]> key =
+                CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS;
+        int[] config = getValueFromKeyNonNull(key);
+
+        if (config == null) {
+            return new Size[0];
+        }
+
+        checkTrueForKey(key, "array length is invalid", config.length
+                % NUM_ELEMENTS_IN_STREAM_CONFIG == 0);
+        // Round down to 4 boundary if it is not integer times of 4, to avoid array out of bound
+        // in case the above check fails.
+        int configLength = (config.length / NUM_ELEMENTS_IN_STREAM_CONFIG)
+                * NUM_ELEMENTS_IN_STREAM_CONFIG;
+        for (int i = 0; i < configLength; i += NUM_ELEMENTS_IN_STREAM_CONFIG) {
+            if (config[i] == format && config[i+3] == direction) {
+                sizeList.add(new Size(config[i+1], config[i+2]));
+            }
+        }
+
+        Size[] sizes = new Size[sizeList.size()];
+        return sizeList.toArray(sizes);
+    }
+
+    /**
+     * Get available AE target fps ranges.
+     *
+     * @return Empty int array if aeAvailableTargetFpsRanges is invalid.
+     */
+    public int[] getAeAvailableTargetFpsRangesChecked() {
+        final int NUM_ELEMENTS_IN_FPS_RANGE = 2;
+        CameraMetadata.Key<int[]> key =
+                CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES;
+        int[] fpsRanges = getValueFromKeyNonNull(key);
+
+        if (fpsRanges == null) {
+            return new int[0];
+        }
+
+        checkTrueForKey(key, "array length is invalid", fpsRanges.length
+                % NUM_ELEMENTS_IN_FPS_RANGE == 0);
+        // Round down to 2 boundary if it is not integer times of 2, to avoid array out of bound
+        // in case the above check fails.
+        int fpsRangeLength = (fpsRanges.length / NUM_ELEMENTS_IN_FPS_RANGE)
+                * NUM_ELEMENTS_IN_FPS_RANGE;
+        int minFps, maxFps;
+        long maxFrameDuration = getMaxFrameDurationChecked();
+        for (int i = 0; i < fpsRangeLength; i += NUM_ELEMENTS_IN_FPS_RANGE) {
+            minFps = fpsRanges[i];
+            maxFps = fpsRanges[i + 1];
+            checkTrueForKey(key, " min fps must be no larger than max fps!",
+                    minFps > 0 && maxFps >= minFps);
+            long maxDuration = (long) (1e9 / minFps);
+            checkTrueForKey(key, String.format(
+                    " the frame duration %d for min fps %d must smaller than maxFrameDuration %d",
+                    maxDuration, minFps, maxFrameDuration), maxDuration <= maxFrameDuration);
+        }
+
+        return fpsRanges;
+    }
+
+    /**
+     * Get max frame duration.
+     *
+     * @return 0 if maxFrameDuration is null
+     */
+    public long getMaxFrameDurationChecked() {
+        CameraMetadata.Key<Long> key =
+                CameraCharacteristics.SENSOR_INFO_MAX_FRAME_DURATION;
+        Long maxDuration = getValueFromKeyNonNull(key);
+
+        if (maxDuration == null) {
+            return 0;
+        }
+
+        return maxDuration;
+    }
+
+    /**
+     * Get available minimal frame durations for a given format.
+     *
+     * @param format One of the format from {@link ImageFormat}.
+     * @return HashMap of minimal frame durations for different sizes, empty HashMap
+     *         if availableMinFrameDurations is null.
+     */
+    public HashMap<Size, Long> getAvailableMinFrameDurationsForFormatChecked(int format) {
+        final int NUM_ELEMENTS_IN_DURATIONS = 4;
+        CameraMetadata.Key<long[]> key =
+                CameraCharacteristics.SCALER_AVAILABLE_MIN_FRAME_DURATIONS;
+        long[] minDurations = getValueFromKeyNonNull(key);
+        HashMap<Size, Long> minDurationMap = new HashMap<Size, Long>();
+
+        if (minDurations == null) {
+            return minDurationMap;
+        }
+
+        checkTrueForKey(key, "array length is invalid", minDurations.length
+                % NUM_ELEMENTS_IN_DURATIONS == 0);
+        // Round down to 4 boundary if it is not integer times of 4, to avoid array out of bound
+        // in case the above check fails.
+        int durationLength = (minDurations.length / NUM_ELEMENTS_IN_DURATIONS)
+                * NUM_ELEMENTS_IN_DURATIONS;
+        for (int i = 0; i < durationLength; i += NUM_ELEMENTS_IN_DURATIONS) {
+            if (minDurations[i] == format) {
+                Size size = new Size((int)minDurations[i+1], (int)minDurations[i+2]);
+                Long value = minDurations[i + 3];
+                minDurationMap.put(size, value);
+            }
+        }
+
+        return minDurationMap;
+    }
+
+    public byte[] getAvailableEdgeModesChecked() {
+        CameraMetadata.Key<byte[]> key = CameraCharacteristics.EDGE_AVAILABLE_EDGE_MODES;
+        byte[] edgeModes = getValueFromKeyNonNull(key);
+
+        if (edgeModes == null) {
+            return new byte[0];
+        }
+
+        // Full device should always include OFF and FAST
+        if (isHardwareLevelFull()) {
+            List<Byte> modeList = Arrays.asList(CameraTestUtils.toObject(edgeModes));
+            checkTrueForKey(key, "Full device must contain OFF and FAST edge modes",
+                    modeList.contains((byte)CameraMetadata.EDGE_MODE_OFF) &&
+                    modeList.contains((byte)CameraMetadata.EDGE_MODE_FAST));
+        }
+
+        return edgeModes;
+    }
+
+    public byte[] getAvailableNoiseReductionModesChecked() {
+        CameraMetadata.Key<byte[]> key =
+                CameraCharacteristics.NOISE_REDUCTION_AVAILABLE_NOISE_REDUCTION_MODES;
+        byte[] noiseReductionModes = getValueFromKeyNonNull(key);
+
+        if (noiseReductionModes == null) {
+            return new byte[0];
+        }
+
+        // Full device should always include OFF and FAST
+        if (isHardwareLevelFull()) {
+            List<Byte> modeList = Arrays.asList(CameraTestUtils.toObject(noiseReductionModes));
+            checkTrueForKey(key, "Full device must contain OFF and FAST noise reduction modes",
+                    modeList.contains((byte)CameraMetadata.NOISE_REDUCTION_MODE_OFF) &&
+                    modeList.contains((byte)CameraMetadata.NOISE_REDUCTION_MODE_FAST));
+        }
+
+        return noiseReductionModes;
+    }
+
+    /**
+     * Get the value in index for a fixed-size array from a given key.
+     *
+     * <p>If the camera device is incorrectly reporting values, log a warning and return
+     * the default value instead.</p>
+     *
+     * @param key Key to fetch
+     * @param defaultValue Default value to return if camera device uses invalid values
+     * @param name Human-readable name for the array index (logging only)
+     * @param index Array index of the subelement
+     * @param size Expected fixed size of the array
+     *
+     * @return The value reported by the camera device, or the defaultValue otherwise.
+     */
+    private <T> T getArrayElementOrDefault(Key<?> key, T defaultValue, String name, int index,
+            int size) {
+        T elementValue = getArrayElementCheckRangeNonNull(
+                key,
+                index,
+                size);
+
+        if (elementValue == null) {
+            failKeyCheck(key,
+                    "had no valid " + name + " value; using default of " + defaultValue);
+            elementValue = defaultValue;
+        }
+
+        return elementValue;
+    }
+
+    /**
+     * Fetch an array sub-element from an array value given by a key.
+     *
+     * <p>
+     * Prints a warning if the sub-element was null.
+     * </p>
+     *
+     * <p>Use for variable-size arrays since this does not check the array size.</p>
+     *
+     * @param key Metadata key to look up
+     * @param element A non-negative index value.
+     * @return The array sub-element, or null if the checking failed.
+     */
+    private <T> T getArrayElementNonNull(Key<?> key, int element) {
+        return getArrayElementCheckRangeNonNull(key, element, IGNORE_SIZE_CHECK);
+    }
+
+    /**
+     * Fetch an array sub-element from an array value given by a key.
+     *
+     * <p>
+     * Prints a warning if the array size does not match the size, or if the sub-element was null.
+     * </p>
+     *
+     * @param key Metadata key to look up
+     * @param element The index in [0,size)
+     * @param size A positive size value or otherwise {@value #IGNORE_SIZE_CHECK}
+     * @return The array sub-element, or null if the checking failed.
+     */
+    private <T> T getArrayElementCheckRangeNonNull(Key<?> key, int element, int size) {
+        Object array = getValueFromKeyNonNull(key);
+
+        if (array == null) {
+            // Warning already printed
+            return null;
+        }
+
+        if (size != IGNORE_SIZE_CHECK) {
+            int actualLength = Array.getLength(array);
+            if (actualLength != size) {
+                failKeyCheck(key,
+                        String.format("had the wrong number of elements (%d), expected (%d)",
+                                actualLength, size));
+                return null;
+            }
+        }
+
+        @SuppressWarnings("unchecked")
+        T val = (T) Array.get(array, element);
+
+        if (val == null) {
+            failKeyCheck(key, "had a null element at index" + element);
+            return null;
+        }
+
+        return val;
+    }
+
+    /**
+     * Gets the key, logging warnings for null values.
+     */
+    private <T> T getValueFromKeyNonNull(Key<T> key) {
+        if (key == null) {
+            throw new IllegalArgumentException("key was null");
+        }
+
+        T value = mCharacteristics.get(key);
+
+        if (value == null) {
+            failKeyCheck(key, "was null");
+        }
+
+        return value;
+    }
+
+    private void checkArrayValuesInRange(Key<byte[]> key, byte[] array, byte min, byte max) {
+        for (byte value : array) {
+            checkTrueForKey(key, String.format(" value is out of range [%d, %d]", min, max),
+                    value <= max && value >= min);
+        }
+    }
+
+    /**
+     * Check the uniqueness of the values in a list.
+     *
+     * @param key The key to be checked
+     * @param list The list contains the value of the key
+     */
+    private <U, T> void checkElementDistinct(Key<U> key, List<T> list) {
+        // Each size must be distinct.
+        Set<T> sizeSet = new HashSet<T>(list);
+        checkTrueForKey(key, "Each size must be distinct", sizeSet.size() == list.size());
+    }
+
+    private <T> void checkTrueForKey(Key<T> key, String message, boolean condition) {
+        if (!condition) {
+            failKeyCheck(key, message);
+        }
+    }
+
+    private <T> void failKeyCheck(Key<T> key, String message) {
+        // TODO: Consider only warning once per key/message combination if it's too spammy.
+        // TODO: Consider offering other options such as throwing an assertion exception
+        String failureCause = String.format("The static info key '%s' %s", key.getName(), message);
+        switch (mLevel) {
+            case WARN:
+                Log.w(TAG, failureCause);
+                break;
+            case COLLECT:
+                mCollector.addMessage(failureCause);
+                break;
+            case ASSERT:
+                Assert.fail(failureCause);
+            default:
+                throw new UnsupportedOperationException("Unhandled level " + mLevel);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
new file mode 100644
index 0000000..570ef2c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/UncheckedCloseable.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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.camera2.cts.helpers;
+
+/**
+ * Defines an interface for classes that can (or need to) be closed once they
+ * are not used any longer; calling the {@code close} method releases resources
+ * that the object holds.</p>
+ *
+ * <p>This signifies that the implementor will never throw checked exceptions when closing,
+ * allowing for more fine grained exception handling at call sites handling this interface
+ * generically.</p>
+ *
+ * <p>A common pattern for using an {@code UncheckedCloseable} resource:
+ * <pre>   {@code
+ *   // where <Foo extends UncheckedCloseable>
+ *   UncheckedCloseable foo = new Foo();
+ *   try {
+ *      ...;
+ *   } finally {
+ *      foo.close();
+ *   }
+ * }</pre>
+ */
+public interface UncheckedCloseable extends AutoCloseable {
+
+    /**
+     * Closes the object and release any system resources it holds.
+     *
+     * <p>Does not throw any checked exceptions.</p>
+     */
+    @Override
+    void close();
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java
new file mode 100644
index 0000000..e65e819
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationCache.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+/**
+ * Cache {@link Allocation} objects based on their type and usage.
+ *
+ * <p>This avoids expensive re-allocation of objects when they are used over and over again
+ * by different scripts.</p>
+ */
+public class AllocationCache implements UncheckedCloseable {
+
+    private static final String TAG = "AllocationCache";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static int sDebugHits = 0;
+    private static int sDebugMisses = 0;
+
+    private final RenderScript mRS;
+    private final HashMap<AllocationKey, List<Allocation>> mAllocationMap =
+            new HashMap<AllocationKey, List<Allocation>>();
+    private boolean mClosed = false;
+
+    /**
+     * Create a new cache with the specified RenderScript context.
+     *
+     * @param rs A non-{@code null} RenderScript context.
+     *
+     * @throws NullPointerException if rs was null
+     */
+    public AllocationCache(RenderScript rs) {
+        mRS = checkNotNull("rs", rs);
+    }
+
+    /**
+     * Returns the {@link RenderScript} context associated with this AllocationCache.
+     *
+     * @return A non-{@code null} RenderScript value.
+     */
+    public RenderScript getRenderScript() {
+        return mRS;
+    }
+
+    /**
+     * Try to lookup a compatible Allocation from the cache, create one if none exist.
+     *
+     * @param type A non-{@code null} RenderScript Type.
+     * @throws NullPointerException if type was null
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized Allocation getOrCreateTyped(Type type, int usage) {
+        checkNotNull("type", type);
+        checkNotClosed();
+
+        AllocationKey key = new AllocationKey(type, usage);
+        List<Allocation> list = mAllocationMap.get(key);
+
+        Allocation alloc;
+
+        if (list == null || list.isEmpty()) {
+            alloc = Allocation.createTyped(mRS, type, usage);
+
+            if (DEBUG) {
+                sDebugMisses++;
+                Log.d(TAG, String.format(
+                    "Cache MISS (%d): type = '%s', usage = '%x'", sDebugMisses, type, usage));
+            }
+        } else {
+            alloc = list.remove(list.size() - 1);
+
+            if (DEBUG) {
+                sDebugHits++;
+                Log.d(TAG, String.format(
+                    "Cache HIT (%d): type = '%s', usage = '%x'", sDebugHits, type, usage));
+            }
+        }
+
+        return alloc;
+    }
+
+    /**
+     * Return the Allocation to the cache.
+     *
+     * <p>Future calls to getOrCreateTyped with the same type and usage may
+     * return this allocation.</p>
+     *
+     * <p>Allocations that have usage {@link Allocation#USAGE_IO_INPUT} get their
+     * listeners reset. Those that have {@link Allocation#USAGE_IO_OUTPUT} get their
+     * surfaces reset.</p>
+     *
+     * @param allocation A non-{@code null} RenderScript {@link Allocation}
+     * @throws NullPointerException if allocation was null
+     * @throws IllegalArgumentException if the allocation was already returned previously
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized void returnToCache(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+        checkNotClosed();
+
+        int usage = allocation.getUsage();
+        AllocationKey key = new AllocationKey(allocation.getType(), usage);
+        List<Allocation> value = mAllocationMap.get(key);
+
+        if (value != null && value.contains(allocation)) {
+            throw new IllegalArgumentException("allocation was already returned to the cache");
+        }
+
+        if ((usage & Allocation.USAGE_IO_INPUT) != 0) {
+            allocation.setOnBufferAvailableListener(null);
+        }
+        if ((usage & Allocation.USAGE_IO_OUTPUT) != 0) {
+            allocation.setSurface(null);
+        }
+
+        if (value == null) {
+            value = new ArrayList<Allocation>(/*capacity*/1);
+            mAllocationMap.put(key, value);
+        }
+
+        value.add(allocation);
+
+        // TODO: Evict existing allocations from cache when we get too many items in it,
+        // to avoid running out of memory
+
+        // TODO: move to using android.util.LruCache under the hood
+    }
+
+    /**
+     * Return the allocation to the cache, if it wasn't {@code null}.
+     *
+     * <p>Future calls to getOrCreateTyped with the same type and usage may
+     * return this allocation.</p>
+     *
+     * <p>Allocations that have usage {@link Allocation#USAGE_IO_INPUT} get their
+     * listeners reset. Those that have {@link Allocation#USAGE_IO_OUTPUT} get their
+     * surfaces reset.</p>
+     *
+     * <p>{@code null} values are a no-op.</p>
+     *
+     * @param allocation A potentially {@code null} RenderScript {@link Allocation}
+     * @throws IllegalArgumentException if the allocation was already returned previously
+     * @throws IllegalStateException if the cache was closed with {@link #close}
+     */
+    public synchronized void returnToCacheIfNotNull(Allocation allocation) {
+        if (allocation != null) {
+            returnToCache(allocation);
+        }
+    }
+
+    /**
+     * Closes the object and destroys any Allocations still in the cache.
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        for (Map.Entry<AllocationKey, List<Allocation>> entry : mAllocationMap.entrySet()) {
+            List<Allocation> value = entry.getValue();
+
+            for (Allocation alloc : value) {
+                alloc.destroy();
+            }
+
+            value.clear();
+        }
+
+        mAllocationMap.clear();
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    /**
+     * Holder class to check if one allocation is compatible with another.
+     *
+     * <p>An Allocation is considered compatible if both it's Type and usage is equivalent.</p>
+     */
+    private static class AllocationKey {
+        private final Type mType;
+        private final int mUsage;
+
+        public AllocationKey(Type type, int usage) {
+            mType = type;
+            mUsage = usage;
+        }
+
+        @Override
+        public int hashCode() {
+            return mType.hashCode() ^ mUsage;
+        }
+
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof AllocationKey){
+                AllocationKey otherKey = (AllocationKey) other;
+
+                return otherKey.mType.equals(mType) && otherKey.mUsage == otherKey.mUsage;
+            }
+
+            return false;
+        }
+    }
+
+    private void checkNotClosed() {
+        if (mClosed == true) {
+            throw new IllegalStateException("AllocationCache has already been closed");
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java
new file mode 100644
index 0000000..15a5ea6
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/AllocationInfo.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.Size;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+import android.util.Log;
+
+/**
+ * Abstract the information necessary to create new {@link Allocation allocations} with
+ * their size, element, type, and usage.
+ *
+ * <p>This also includes convenience functions for printing to a string, something RenderScript
+ * lacks at the time of writing.</p>
+ *
+ * <p>Note that when creating a new {@link AllocationInfo} the usage flags <b>always</b> get ORd
+ * to {@link Allocation#USAGE_IO_SCRIPT}.</p>
+ */
+public class AllocationInfo {
+
+    private final RenderScript mRS = RenderScriptSingleton.getRS();
+
+    private final Size mSize;
+    private final Element mElement;
+    private final Type mType;
+    private final int mUsage;
+
+    private static final String TAG = "AllocationInfo";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * Create a new {@link AllocationInfo} holding the element, size, and usage
+     * from an existing {@link Allocation}.
+     *
+     * @param allocation {@link Allocation}
+     *
+     * @return A new {@link AllocationInfo}
+     *
+     * @throws NullPointerException if allocation was {@code null}.
+     */
+    public static AllocationInfo newInstance(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+
+        return new AllocationInfo(allocation.getElement(),
+                new Size(allocation.getType().getX(), allocation.getType().getY()),
+                allocation.getUsage());
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
+     * and {@link Allocation#USAGE_SCRIPT usage}.
+     *
+     * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * <p>The closest {@link Element} possible is created from the format.</p>
+     *
+     * @param size {@link Size}
+     * @param format An int format
+     * @param usage Usage flags
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public static AllocationInfo newInstance(Size size, int format, int usage) {
+        RenderScript rs = RenderScriptSingleton.getRS();
+
+        Element element;
+        switch (format) {
+            case ImageFormat.YUV_420_888:
+                element = Element.YUV(rs);
+                break;
+            case PixelFormat.RGBA_8888:
+                element = Element.RGBA_8888(rs);
+                break;
+            // TODO: map more formats here
+            default:
+                throw new UnsupportedOperationException("Unsupported format " + format);
+        }
+
+        return new AllocationInfo(element, size, usage);
+    }
+
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
+     * with the default usage.
+     *
+     * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * <p>The closest {@link Element} possible is created from the format.</p>
+     *
+     * @param size {@link Size}
+     * @param format An int format
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public static AllocationInfo newInstance(Size size, int format) {
+        return newInstance(size, format, Allocation.USAGE_SCRIPT);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
+     * with the default usage.
+     *
+     * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * @param element {@link Element}
+     * @param size {@link Size}
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     * @throws NullPointerException if element was {@code null}.
+     */
+    public static AllocationInfo newInstance(Element element, Size size) {
+        return new AllocationInfo(element, size, Allocation.USAGE_SCRIPT);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
+     * and {@link Allocation#USAGE_SCRIPT usage}.
+     *
+     * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
+     *
+     * @param element {@link Element}
+     * @param size {@link Size}
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} holding the given arguments.
+     *
+     * @throws NullPointerException if size was {@code null}.
+     * @throws NullPointerException if element was {@code null}.
+     */
+    public static AllocationInfo newInstance(Element element, Size size, int usage) {
+        return new AllocationInfo(element, size, usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but appending
+     * the new usage flags to the old usage flags.
+     *
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new usage flags ORd to the old ones.
+     */
+    public AllocationInfo addExtraUsage(int usage) {
+        return new AllocationInfo(mElement, mSize, mUsage | usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
+     * and appending the new usage flags to the old usage flags.
+     *
+     * @param format Format
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeFormatAndUsage(int format, int usage) {
+        return newInstance(getSize(), format, usage);
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but replacing the old
+     * usage with the new usage flags.
+     *
+     * @param usage usage flags
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeElementWithDefaultUsage(Element element) {
+        return newInstance(element, getSize());
+    }
+
+    /**
+     * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
+     * and replacing the old usage flags with default usage flags.
+     *
+     * @param format Format
+     *
+     * @return A new {@link AllocationInfo} with new format/usage.
+     *
+     * @see ImageFormat
+     * @see PixelFormat
+     */
+    public AllocationInfo changeFormatWithDefaultUsage(int format) {
+        return newInstance(getSize(), format, Allocation.USAGE_SCRIPT);
+    }
+
+    private AllocationInfo(Element element, Size size, int usage) {
+        checkNotNull("element", element);
+        checkNotNull("size", size);
+
+        mElement = element;
+        mSize = size;
+        mUsage = usage;
+
+        Type.Builder typeBuilder = typeBuilder(element, size);
+
+        if (element.equals(Element.YUV(mRS))) {
+            typeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
+        }
+
+        mType = typeBuilder.create();
+    }
+
+    /**
+     * Get the {@link Type type} for this info.
+     *
+     * <p>Note that this is the same type that would get used by the {@link Allocation}
+     * created with {@link #createAllocation()}.
+     *
+     * @return The type (never {@code null}).
+     */
+    public Type getType() {
+        return mType;
+    }
+
+    /**
+     * Get the usage.
+     *
+     * <p>The bit for {@link Allocation#USAGE_SCRIPT} will always be set to 1.</p>
+     *
+     * @return usage flags
+     */
+    public int getUsage() {
+        return mUsage;
+    }
+
+    /**
+     * Get the size.
+     *
+     * @return The size (never {@code null}).
+     */
+    public Size getSize() {
+        return mSize;
+    }
+
+    /**
+     * Get the {@link Element}.
+     *
+     * @return The element (never {@code null}).
+     */
+    public Element getElement() {
+        return mElement;
+    }
+
+    /**
+     * Convenience enum to represent commonly-used elements without needing a RenderScript object.
+     */
+    public enum ElementInfo {
+        YUV,
+        RGBA_8888,
+        U8_3,
+        U8_4;
+
+        private static final String TAG = "ElementInfo";
+
+        /**
+         * Create an {@link ElementInfo} by converting it from a {@link Element}.
+         *
+         * @param element The element for which you want to get an enum for.
+         *
+         * @return The element info is a corresponding one exists, or {@code null} otherwise.
+         */
+        public static ElementInfo fromElement(Element element) {
+            checkNotNull("element", element);
+
+            if (element.equals(Element.YUV(RenderScriptSingleton.getRS()))) {
+                return YUV;
+            } else if (element.equals(Element.RGBA_8888(RenderScriptSingleton.getRS()))) {
+                return RGBA_8888;
+            } else if (element.equals(Element.U8_3(RenderScriptSingleton.getRS()))) {
+                return U8_3;
+            } else if (element.equals(Element.U8_4(RenderScriptSingleton.getRS()))) {
+                return U8_4;
+            }
+            // TODO: add more comparisons here as necessary
+
+            Log.w(TAG, "Unknown element of data kind " + element.getDataKind());
+            return null;
+        }
+    }
+
+    /**
+     * Compare the current element against the suggested element (info).
+     *
+     * @param element The other element to compare against.
+     *
+     * @return true if the elements are equal, false otherwise.
+     */
+    public boolean isElementEqualTo(ElementInfo element) {
+        checkNotNull("element", element);
+
+        Element comparison;
+        switch (element) {
+            case YUV:
+                comparison = Element.YUV(mRS);
+                break;
+            case RGBA_8888:
+                comparison = Element.RGBA_8888(mRS);
+                break;
+            case U8_3:
+                comparison = Element.U8_3(mRS);
+                break;
+            case U8_4:
+                comparison = Element.U8_4(mRS);
+                break;
+            default:
+            // TODO: add more comparisons here as necessary
+                comparison = null;
+        }
+
+        return mElement.equals(comparison);
+    }
+
+    /**
+     * Human-readable representation of this info.
+     */
+    @Override
+    public String toString() {
+        return String.format("Size: %s, Element: %s, Usage: %x", mSize,
+                ElementInfo.fromElement(mElement), mUsage);
+    }
+
+    /**
+     * Compare against another object.
+     *
+     * <p>Comparisons against objects that are not instances of {@link AllocationInfo}
+     * always return {@code false}.</p>
+     *
+     * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
+     * sizes, and usage flags are also equal.</p>
+     *
+     * @param other Another info object
+     *
+     * @return true if this is equal to other
+     */
+    @Override
+    public boolean equals(Object other) {
+        if (other instanceof AllocationInfo) {
+            return equals((AllocationInfo)other);
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Compare against another object.
+     *
+     * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
+     * sizes, and usage flags are also equal.</p>
+     *
+     * @param other Another info object
+     *
+     * @return true if this is equal to other
+     */
+    public boolean equals(AllocationInfo other) {
+        if (other == null) {
+            return false;
+        }
+
+        // Element, Size equality is already incorporated into Type equality
+        return mType.equals(other.mType) && mUsage == other.mUsage;
+    }
+
+    /**
+     * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
+     * from this info object.
+     *
+     * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
+     * return it to the cache once done (although this is not necessary).</p>
+     *
+     * @return a new {@link Allocation}
+     */
+    public Allocation createAllocation() {
+        if (VERBOSE) Log.v(TAG, "createAllocation - for info =" + toString());
+        return RenderScriptSingleton.getCache().getOrCreateTyped(mType, mUsage);
+    }
+
+    /**
+     * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
+     * from this info object; immediately wrap inside a new {@link BlockingInputAllocation}.
+     *
+     * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
+     * return it to the cache once done (although this is not necessary).</p>
+     *
+     * @return a new {@link Allocation}
+     *
+     * @throws IllegalArgumentException
+     *            If the usage did not have one of {@code USAGE_IO_INPUT} or {@code USAGE_IO_OUTPUT}
+     */
+    public BlockingInputAllocation createBlockingInputAllocation() {
+        Allocation alloc = createAllocation();
+        return BlockingInputAllocation.wrap(alloc);
+    }
+
+    private static Type.Builder typeBuilder(Element element, Size size) {
+        Type.Builder builder = (new Type.Builder(RenderScriptSingleton.getRS(), element))
+                .setX(size.getWidth())
+                .setY(size.getHeight());
+
+        return builder;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
new file mode 100644
index 0000000..0305540
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BlockingInputAllocation.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.util.Log;
+
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+/**
+ * An {@link Allocation} wrapper that can be used to block until new buffers are available.
+ *
+ * <p>Can only be used only with {@link Allocation#USAGE_IO_INPUT} usage Allocations.</p>
+ *
+ * <p>When used with a {@link android.hardware.camera2.CameraDevice CameraDevice} this
+ * must be used as an output surface.</p>
+ */
+class BlockingInputAllocation implements UncheckedCloseable {
+
+    private static final String TAG = BlockingInputAllocation.class.getSimpleName();
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private final Allocation mAllocation;
+    private final OnBufferAvailableListener mListener;
+    private boolean mClosed;
+
+    /**
+     * Wrap an existing Allocation with this {@link BlockingInputAllocation}.
+     *
+     * <p>Doing this will clear any existing associated buffer listeners and replace
+     * it with a new one.</p>
+     *
+     * @param allocation A non-{@code null} {@link Allocation allocation}
+     * @return a new {@link BlockingInputAllocation} instance
+     *
+     * @throws NullPointerException
+     *           If {@code allocation} was {@code null}
+     * @throws IllegalArgumentException
+     *           If {@code allocation}'s usage did not have one of USAGE_IO_INPUT or USAGE_IO_OUTPUT
+     * @throws IllegalStateException
+     *           If this object has already been {@link #close closed}
+     */
+    public static BlockingInputAllocation wrap(Allocation allocation) {
+        checkNotNull("allocation", allocation);
+        checkBitFlags("usage", allocation.getUsage(), "USAGE_IO_INPUT", Allocation.USAGE_IO_INPUT);
+
+        return new BlockingInputAllocation(allocation);
+    }
+
+    /**
+     * Get the Allocation backing this {@link BlockingInputAllocation}.
+     *
+     * @return Allocation instance (non-{@code null}).
+     *
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public Allocation getAllocation() {
+        checkNotClosed();
+
+        return mAllocation;
+    }
+
+    /**
+     * Waits for a buffer to become available, then immediately
+     * {@link Allocation#ioReceive receives} it.
+     *
+     * <p>After calling this, the next script used with this allocation will use the
+     * newer buffer.</p>
+     *
+     * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public synchronized void waitForBufferAndReceive() {
+        checkNotClosed();
+
+        if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - begin");
+
+        mListener.waitForBuffer();
+        mAllocation.ioReceive();
+
+        if (VERBOSE) Log.v(TAG, "waitForBufferAndReceive - Allocation#ioReceive");
+    }
+
+    /**
+     * If there are multiple pending buffers, {@link Allocation#ioReceive receive} the latest one.
+     *
+     * <p>Does not block if there are no currently pending buffers.</p>
+     *
+     * @return {@code true} only if any buffers were received.
+     *
+     * @throws IllegalStateException If this object has already been {@link #close closed}
+     */
+    public synchronized boolean receiveLatestAvailableBuffers() {
+        checkNotClosed();
+
+        int updatedBuffers = 0;
+        while (mListener.isBufferPending()) {
+            mListener.waitForBuffer();
+            mAllocation.ioReceive();
+            updatedBuffers++;
+        }
+
+        if (VERBOSE) Log.v(TAG, "receiveLatestAvailableBuffers - updated = " + updatedBuffers);
+
+        return updatedBuffers > 0;
+    }
+
+    /**
+     * Closes the object and detaches the listener from the {@link Allocation}.
+     *
+     * <p>This has a side effect of calling {@link #receiveLatestAvailableBuffers}
+     *
+     * <p>Does <i>not</i> destroy the underlying {@link Allocation}.</p>
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        receiveLatestAvailableBuffers();
+        mAllocation.setOnBufferAvailableListener(/*callback*/null);
+        mClosed = true;
+    }
+
+    protected void checkNotClosed() {
+        if (mClosed) {
+            throw new IllegalStateException(TAG + " has been closed");
+        }
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    private BlockingInputAllocation(Allocation allocation) {
+        mAllocation = allocation;
+
+        mListener = new OnBufferAvailableListener();
+        mAllocation.setOnBufferAvailableListener(mListener);
+    }
+
+    // TODO: refactor with the ImageReader Listener code to use a LinkedBlockingQueue
+    private static class OnBufferAvailableListener implements Allocation.OnBufferAvailableListener {
+        private int mPendingBuffers = 0;
+        private final Object mBufferSyncObject = new Object();
+        private static final int TIMEOUT_MS = 5000;
+
+        public boolean isBufferPending() {
+            synchronized (mBufferSyncObject) {
+                return (mPendingBuffers > 0);
+            }
+        }
+
+        /**
+         * Waits for a buffer. Caller must call ioReceive exactly once after calling this.
+         *
+         * @throws TimeoutRuntimeException If waiting for the buffer has timed out.
+         */
+        public void waitForBuffer() {
+            synchronized (mBufferSyncObject) {
+                while (mPendingBuffers == 0) {
+                    try {
+                        if (VERBOSE) Log.v(TAG, "waiting for next buffer");
+                        mBufferSyncObject.wait(TIMEOUT_MS);
+                        if (mPendingBuffers == 0) {
+                            throw new TimeoutRuntimeException("wait for buffer image timed out");
+                        }
+                    } catch (InterruptedException ie) {
+                        throw new AssertionError(ie);
+                    }
+                }
+                mPendingBuffers--;
+            }
+        }
+
+        @Override
+        public void onBufferAvailable(Allocation a) {
+            if (VERBOSE) Log.v(TAG, "new buffer in allocation available");
+            synchronized (mBufferSyncObject) {
+                mPendingBuffers++;
+                mBufferSyncObject.notifyAll();
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
new file mode 100644
index 0000000..8e4c8e9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RenderScriptSingleton.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import android.content.Context;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+// TODO : Replace with dependency injection
+/**
+ * Singleton to hold {@link RenderScript} and {@link AllocationCache} objects.
+ *
+ * <p>The test method must call {@link #setContext} before attempting to retrieve
+ * the underlying objects.</p> *
+ */
+public class RenderScriptSingleton {
+
+    private static final String TAG = "RenderScriptSingleton";
+
+    private static Context sContext;
+    private static RenderScript sRS;
+    private static AllocationCache sCache;
+
+    /**
+     * Initialize the singletons from the given context; the
+     * {@link RenderScript} and {@link AllocationCache} objects are instantiated.
+     *
+     * @param context a non-{@code null} Context.
+     *
+     * @throws IllegalStateException If this was called repeatedly without {@link #clearContext}
+     */
+    public static synchronized void setContext(Context context) {
+        if (context.equals(sContext)) {
+            return;
+        } else if (sContext != null) {
+            Log.v(TAG,
+                    "Trying to set new context " + context +
+                    ", before clearing previous "+ sContext);
+            throw new IllegalStateException(
+                    "Call #clearContext before trying to set a new context");
+        }
+
+        sRS = RenderScript.create(context);
+        sContext = context;
+        sCache = new AllocationCache(sRS);
+    }
+
+    /**
+     * Clean up the singletons from the given context; the
+     * {@link RenderScript} and {@link AllocationCache} objects are destroyed.
+     *
+     * <p>Safe to call multiple times; subsequent invocations have no effect.</p>
+     */
+    public static synchronized void clearContext() {
+        if (sContext != null) {
+            sCache.close();
+            sCache = null;
+
+            sRS.destroy();
+            sRS = null;
+            sContext = null;
+        }
+    }
+
+    /**
+     * Get the current {@link RenderScript} singleton.
+     *
+     * @return A non-{@code null} {@link RenderScript} object.
+     *
+     * @throws IllegalStateException if {@link #setContext} was not called prior to this
+     */
+    public static synchronized RenderScript getRS() {
+        if (sRS == null) {
+            throw new IllegalStateException("Call #setContext before using #get");
+        }
+
+        return sRS;
+    }
+    /**
+     * Get the current {@link AllocationCache} singleton.
+     *
+     * @return A non-{@code null} {@link AllocationCache} object.
+     *
+     * @throws IllegalStateException if {@link #setContext} was not called prior to this
+     */
+    public static synchronized AllocationCache getCache() {
+        if (sCache == null) {
+            throw new IllegalStateException("Call #setContext before using #getCache");
+        }
+
+        return sCache;
+    }
+
+    // Suppress default constructor for noninstantiability
+    private RenderScriptSingleton() { throw new AssertionError(); }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java
new file mode 100644
index 0000000..92ff1cb
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/Script.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.renderscript.Allocation;
+import android.renderscript.RenderScript;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Base class for all renderscript script abstractions.
+ *
+ * <p>Each script has exactly one input and one output allocation, and is able to execute
+ * one {@link android.renderscript.Script} script file.</p>
+ *
+ * <p>Each script owns it's input allocation, but not the output allocation.</p>
+ *
+ * <p>Subclasses of this class must implement exactly one of two constructors:
+ * <ul>
+ * <li>{@code ScriptSubclass(AllocationInfo inputInfo)}
+ *  - if it expects 0 parameters
+ * <li>{@code ScriptSubclass(AllocationInfo inputInfo, ParameterMap<T> parameterMap))}
+ *  - if it expects 1 or more parameters
+ * </ul>
+ *
+ * @param <T> A concrete subclass of {@link android.renderscript.Script}
+ */
+public abstract class Script<T extends android.renderscript.Script> implements UncheckedCloseable {
+
+    /**
+     * A type-safe heterogenous parameter map for script parameters.
+     *
+     * @param <ScriptT> A concrete subclass of {@link Script}.
+     */
+    public static class ParameterMap<ScriptT extends Script<?>> {
+        private final HashMap<Script.ScriptParameter<ScriptT, ?>, Object> mParameterMap =
+                new HashMap<Script.ScriptParameter<ScriptT, ?>, Object>();
+
+        /**
+         * Create a new parameter map with 0 parameters.</p>
+         */
+        public ParameterMap() {}
+
+        /**
+         * Get the value associated with the given parameter key.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         *
+         * @return The value, or {@code null} if none was set.
+         *
+         * @param <T> The type of the value
+         *
+         * @throws NullPointerException if parameter was {@code null}
+         */
+        @SuppressWarnings("unchecked")
+        public <T> T get(Script.ScriptParameter<ScriptT, T> parameter) {
+            checkNotNull("parameter", parameter);
+
+            return (T) mParameterMap.get(parameter);
+        }
+
+        /**
+         * Sets the value associated with the given parameter key.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         * @param value The value
+         *
+         * @param <T> The type of the value
+         *
+         * @throws NullPointerException if parameter was {@code null}
+         * @throws NullPointerException if value was {@code null}
+         */
+        public <T> void set(Script.ScriptParameter<ScriptT, T> parameter, T value) {
+            checkNotNull("parameter", parameter);
+            checkNotNull("value", value);
+
+            if (!parameter.getValueClass().isInstance(value)) {
+                throw new IllegalArgumentException(
+                        "Runtime type mismatch between " + parameter + " and value " + value);
+            }
+
+            mParameterMap.put(parameter, value);
+        }
+
+        /**
+         * Whether or not at least one parameter has been {@link #set}.
+         *
+         * @return true if there is at least one element in the map
+         */
+        public boolean isEmpty() {
+            return mParameterMap.isEmpty();
+        }
+
+        /**
+         * Check if the parameter has been {@link #set} to a value.
+         *
+         * @param parameter A type-safe key corresponding to a parameter.
+         * @return true if there is a value corresponding to this parameter, false otherwise.
+         */
+        public boolean contains(Script.ScriptParameter<ScriptT, ?> parameter) {
+            checkNotNull("parameter", parameter);
+
+            return mParameterMap.containsKey(parameter);
+        }
+    }
+
+    /**
+     * A type-safe parameter key to be used with {@link ParameterMap}.
+     *
+     * @param <J> A concrete subclass of {@link Script}.
+     * @param <K> The type of the value that the parameter holds.
+     */
+    public static class ScriptParameter<J extends Script<?>, K> {
+        private final Class<J> mScriptClass;
+        private final Class<K> mValueClass;
+
+        ScriptParameter(Class<J> jClass, Class<K> kClass) {
+            checkNotNull("jClass", jClass);
+            checkNotNull("kClass", kClass);
+
+            mScriptClass = jClass;
+            mValueClass = kClass;
+        }
+
+        /**
+         * Get the runtime class associated with the value.
+         */
+        public Class<K> getValueClass() {
+            return mValueClass;
+        }
+
+        /**
+         * Compare with another object.
+         *
+         * <p>Two script parameters are considered equal only if their script class and value
+         * class are both equal.</p>
+         */
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean equals(Object other) {
+            if (other instanceof ScriptParameter) {
+                ScriptParameter<J, K> otherParam = (ScriptParameter<J,K>) other;
+
+                return mScriptClass.equals(otherParam.mScriptClass) &&
+                        mValueClass.equals(otherParam.mValueClass);
+            }
+
+            return false;
+        }
+
+        /**
+         * Gets the hash code for this object.
+         */
+        @Override
+        public int hashCode() {
+            return mScriptClass.hashCode() ^ mValueClass.hashCode();
+        }
+    }
+
+    private static final String TAG = "Script";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    protected final AllocationCache mCache = RenderScriptSingleton.getCache();
+    protected final RenderScript mRS = RenderScriptSingleton.getRS();
+
+    protected final AllocationInfo mInputInfo;
+    protected final AllocationInfo mOutputInfo;
+
+    protected Allocation mOutputAllocation;
+    protected Allocation mInputAllocation;
+
+    protected final T mScript;
+    private boolean mClosed = false;
+
+    /**
+     * Gets the {@link AllocationInfo info} associated with this script's input.
+     *
+     * @return A non-{@code null} {@link AllocationInfo} object.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public AllocationInfo getInputInfo() {
+        checkNotClosed();
+
+        return mInputInfo;
+    }
+    /**
+     * Gets the {@link AllocationInfo info} associated with this script's output.
+     *
+     * @return A non-{@code null} {@link AllocationInfo} object.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public AllocationInfo getOutputInfo() {
+        checkNotClosed();
+
+        return mOutputInfo;
+    }
+
+    /**
+     * Set the input.
+     *
+     * <p>Must be called before executing any scripts.</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    void setInput(Allocation allocation) {
+        checkNotClosed();
+        checkNotNull("allocation", allocation);
+        checkEquals("allocation info", AllocationInfo.newInstance(allocation),
+                "input info", mInputInfo);
+
+        // Scripts own the input, so return old input to cache if the input changes
+        if (mInputAllocation != allocation) {
+            mCache.returnToCacheIfNotNull(mInputAllocation);
+        }
+
+        mInputAllocation = allocation;
+        updateScriptInput();
+    }
+
+    protected abstract void updateScriptInput();
+
+    /**
+     * Set the output.
+     *
+     * <p>Must be called before executing any scripts.</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    void setOutput(Allocation allocation) {
+        checkNotClosed();
+        checkNotNull("allocation", allocation);
+        checkEquals("allocation info", AllocationInfo.newInstance(allocation),
+                "output info", mOutputInfo);
+
+        // Scripts do not own the output, simply set a reference to the new one.
+        mOutputAllocation = allocation;
+    }
+
+    protected Script(AllocationInfo inputInfo, AllocationInfo outputInfo, T rsScript) {
+        checkNotNull("inputInfo", inputInfo);
+        checkNotNull("outputInfo", outputInfo);
+        checkNotNull("rsScript", rsScript);
+
+        mInputInfo = inputInfo;
+        mOutputInfo = outputInfo;
+        mScript = rsScript;
+
+        if (VERBOSE) {
+            Log.v(TAG, String.format("%s - inputInfo = %s, outputInfo = %s, rsScript = %s",
+                    getName(), inputInfo, outputInfo, rsScript));
+        }
+    }
+
+    /**
+     * Get the {@link Allocation} associated with this script's input.</p>
+     *
+     * @return The input {@link Allocation}, which is never {@code null}.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public Allocation getInput() {
+        checkNotClosed();
+
+        return mInputAllocation;
+    }
+    /**
+     * Get the {@link Allocation} associated with this script's output.</p>
+     *
+     * @return The output {@link Allocation}, which is never {@code null}.
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public Allocation getOutput() {
+        checkNotClosed();
+
+        return mOutputAllocation;
+    }
+
+    /**
+     * Execute the script's kernel against the input/output {@link Allocation allocations}.
+     *
+     * <p>Once this is complete, the output will have the new data available (for either
+     * the next script, or to read out with a copy).</p>
+     *
+     * @throws IllegalStateException If the script has already been {@link #close closed}.
+     */
+    public void execute() {
+        checkNotClosed();
+
+        if (mInputAllocation == null || mOutputAllocation == null) {
+            throw new IllegalStateException("Both inputs and outputs must have been set");
+        }
+
+        executeUnchecked();
+    }
+
+    /**
+     * Get the name of this script.
+     *
+     * <p>The name is the short hand name of the concrete class backing this script.</p>
+     *
+     * <p>This method works even if the script has already been {@link #close closed}.</p>
+     *
+     * @return A string representing the script name.
+     */
+    public String getName() {
+        return getClass().getSimpleName();
+    }
+
+    protected abstract void executeUnchecked();
+
+    protected void checkNotClosed() {
+        if (mClosed) {
+            throw new IllegalStateException("Script has been closed");
+        }
+    }
+
+    /**
+     * Destroy the underlying script object and return the input allocation back to the
+     * {@link AllocationCache cache}.
+     *
+     * <p>This method has no effect if called more than once.</p>
+     */
+    @Override
+    public void close() {
+        if (mClosed) return;
+
+        // Scripts own the input allocation. They do NOT own outputs.
+        mCache.returnToCacheIfNotNull(mInputAllocation);
+
+        mScript.destroy();
+
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+
+    protected static RenderScript getRS() {
+        return RenderScriptSingleton.getRS();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java
new file mode 100644
index 0000000..9129a6d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptGraph.java
@@ -0,0 +1,656 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+import static junit.framework.Assert.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.helpers.MaybeNull;
+import android.hardware.camera2.cts.helpers.UncheckedCloseable;
+import android.hardware.camera2.cts.rs.Script.ParameterMap;
+import android.renderscript.Allocation;
+import android.util.Log;
+import android.view.Surface;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+/**
+ * An abstraction to simplify chaining together the execution of multiple RenderScript
+ * {@link android.renderscript.Script scripts} and managing their {@link Allocation allocations}.
+ *
+ * <p>Create a new script graph by using {@link #create}, configure the input with
+ * {@link Builder#configureInput}, then configure one or more scripts with
+ * {@link Builder#configureScript} or {@link Builder#chainScript}. Finally, freeze the graph
+ * with {@link Builder#buildGraph}.</p>
+ *
+ * <p>Once a script graph has been built, all underlying scripts and allocations are instantiated.
+ * Each script may be executed with {@link #execute}. Scripts are executed in the order that they
+ * were configured, with each previous script's output used as the input for the next script.
+ * </p>
+ *
+ * <p>In case the input {@link Allocation} is actually backed by a {@link Surface}, convenience
+ * methods ({@link #advanceInputWaiting} and {@link #advanceInputAndDrop} are provided to
+ * automatically update the {@link Allocation allocation} with the latest buffer available.</p>
+ *
+ * <p>All resources are managed by the {@link ScriptGraph} and {@link #close closing} the graph
+ * will release all underlying resources. See {@link #close} for more details.</p>
+ */
+public class ScriptGraph implements UncheckedCloseable {
+
+    private static final String TAG = "ScriptGraph";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static final int INPUT_SCRIPT_LOCATION = 0;
+    private final int OUTPUT_SCRIPT_LOCATION; // calculated in constructor
+
+    private final AllocationCache mCache = RenderScriptSingleton.getCache();
+
+    private final Size mSize;
+    private final int mFormat;
+    private final int mUsage;
+    private final List<Script<?>> mScripts;
+
+    private final BlockingInputAllocation mInputBlocker;
+    private final Allocation mOutputAllocation;
+    private boolean mClosed = false;
+
+    /**
+     * Create a new {@link Builder} that will be used to configure the graph's inputs
+     * and scripts (and parameters).
+     *
+     * <p>Once a graph has been fully built, the configuration is immutable.</p>
+     *
+     * @return a {@link Builder} that will be used to configure the graph settings
+     */
+    public static Builder create() {
+        return new Builder();
+    }
+
+    /**
+     * Wait until another buffer is produced into the input {@link Surface}, then
+     * update the backing input {@link Allocation} with the latest buffer with
+     * {@link Allocation#ioReceive ioReceive}.
+     *
+     * @throws IllegalArgumentException
+     *            if the graph wasn't configured with
+     *            {@link Builder#configureInputWithSurface configureInputWithSurface}
+     * @throws TimeoutRuntimeException
+     *            if waiting for the buffer times out
+     */
+    public void advanceInputWaiting() {
+        checkNotClosed();
+        if (!isInputFromSurface()) {
+            throw new IllegalArgumentException("Graph was not configured with USAGE_IO_INPUT");
+        }
+
+        mInputBlocker.waitForBufferAndReceive();
+    }
+
+    /**
+     * Update the backing input {@link Allocation} with the latest buffer with
+     * {@link Allocation#ioReceive ioReceive} repeatedly until no more buffers are pending.
+     *
+     * <p>Does not wait for new buffers to become available if none are currently available
+     * (i.e. {@code false} is returned immediately).</p>
+     *
+     * @return true if any buffers were pending
+     *
+     * @throws IllegalArgumentException
+     *            if the graph wasn't configured with
+     *            {@link Builder#configureInputWithSurface configureInputWithSurface}
+     * @throws TimeoutRuntimeException
+     *            if waiting for the buffer times out
+     */
+    public boolean advanceInputAndDrop() {
+        checkNotClosed();
+        if (!isInputFromSurface()) {
+            throw new IllegalArgumentException("Graph was not configured with USAGE_IO_INPUT");
+        }
+
+        return mInputBlocker.receiveLatestAvailableBuffers();
+    }
+
+    /**
+     * Execute each script in the graph, with each next script's input using the
+     * previous script's output.
+     *
+     * <p>Scripts are executed in the same order that they were configured by the {@link Builder}.
+     * </p>
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public void execute() {
+        checkNotClosed();
+
+        // TODO: Can we use android.renderscript.ScriptGroup here to make it faster?
+
+        int i = 0;
+        for (Script<?> script : mScripts) {
+            script.execute();
+            i++;
+        }
+
+        if (VERBOSE) Log.v(TAG, "execute - invoked " + i + " scripts");
+    }
+
+    /**
+     * Copies the data from the last script's output {@link Allocation} into a byte array.
+     *
+     * <p>The output allocation must be of an 8 bit integer
+     * {@link android.renderscript.Element Element} type.</p>
+     *
+     * @return A byte[] copy.
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public byte[] getOutputData() {
+        checkNotClosed();
+
+        Allocation outputAllocation = getOutputAllocation();
+
+        byte[] destination = new byte[outputAllocation.getBytesSize()];
+        outputAllocation.copyTo(destination);
+
+        return destination;
+    }
+
+    /**
+     * Copies the data from the first script's input {@link Allocation} into a byte array.
+     *
+     * <p>The input allocation must be of an 8 bit integer
+     * {@link android.renderscript.Element Element} type.</p>
+     *
+     * @return A byte[] copy.
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public byte[] getInputData() {
+        checkNotClosed();
+
+        Allocation inputAllocation = getInputAllocation();
+
+        byte[] destination = new byte[inputAllocation.getBytesSize()];
+        inputAllocation.copyTo(destination);
+
+        return destination;
+    }
+
+    /**
+     * Builds a {@link ScriptGraph} by configuring input size/format/usage,
+     * the script classes in the graph, and the parameters passed to the scripts.
+     *
+     * @see ScriptGraph#create
+     */
+    public static class Builder {
+
+        private Size mSize;
+        private int mFormat;
+        private int mUsage;
+
+        private final List<ScriptBuilder<? extends Script<?>>> mChainedScriptBuilders =
+                new ArrayList<ScriptBuilder<? extends Script<?>>>();
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script, using the default usage.
+         *
+         * <p>Short hand for calling {@link #configureInput(int, int, int, int)} with a
+         * {@code 0} usage.</p>
+         *
+         * @param width Width in pixels
+         * @param height Height in pixels
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         */
+        public Builder configureInput(int width, int height, int format) {
+            return configureInput(new Size(width, height), format, /*usage*/0);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script.
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param width Width in pixels
+         * @param height Height in pixels
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         * @param usage Usage flags such as {@link Allocation#USAGE_IO_INPUT}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         */
+        public Builder configureInput(int width, int height, int format, int usage) {
+            return configureInput(new Size(width, height), format, usage);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script, using the default usage.
+         *
+         * <p>Short hand for calling {@link #configureInput(Size, int, int)} with a
+         * {@code 0} usage.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInput(Size size, int format) {
+            return configureInput(size, format, /*usage*/0);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will use a {@link Surface} to produce input into
+         * the first script.
+         *
+         * <p>Short hand for calling {@link #configureInput(Size, int, int)} with the
+         * {@link Allocation#USAGE_IO_INPUT} usage.</p>
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInputWithSurface(Size size, int format) {
+            return configureInput(size, format, Allocation.USAGE_IO_INPUT);
+        }
+
+        /**
+         * Configure the {@link Allocation} that will be used as the input to the first
+         * script.
+         *
+         * <p>The {@code usage} is always ORd together with {@link Allocation#USAGE_SCRIPT}.</p>
+         *
+         * @param size Size (width, height)
+         * @param format Format from {@link ImageFormat} or {@link PixelFormat}
+         * @param usage Usage flags such as {@link Allocation#USAGE_IO_INPUT}
+         *
+         * @return The current builder ({@code this}). Use for chaining method calls.
+         *
+         * @throws NullPointerException if size was {@code null}
+         */
+        public Builder configureInput(Size size, int format, int usage) {
+            checkNotNull("size", size);
+
+            mSize = size;
+            mFormat = format;
+            mUsage = usage | Allocation.USAGE_SCRIPT;
+
+            return this;
+        }
+
+        /**
+         * Build a {@link Script} by setting parameters it might require for execution.
+         *
+         * <p>Refer to the documentation for {@code T} to see if there are any
+         * {@link Script.ScriptParameter parameters} in it.
+         * </p>
+         *
+         * @param <T> Concrete type subclassing the {@link Script} class.
+         */
+        public class ScriptBuilder<T extends Script<?>> {
+
+            private final Class<T> mScriptClass;
+
+            private ScriptBuilder(Class<T> scriptClass) {
+                mScriptClass = scriptClass;
+            }
+
+            private final ParameterMap<T> mParameterMap = new ParameterMap<T>();
+
+            /**
+             * Set a script parameter to the specified value.
+             *
+             * @param parameter The {@link Script.ScriptParameter parameter key} in {@code T}
+             * @param value A value of type {@code K} that the script expects.
+             * @param <K> The type of the parameter {@code value}.
+             *
+             * @return The current builder ({@code this}). Use to chain method calls.
+             *
+             * @throws NullPointerException if parameter was {@code null}
+             * @throws NullPointerException if value was {@code null}
+             * @throws IllegalStateException if the parameter was already {@link #set}
+             */
+            public <K> ScriptBuilder<T> set(Script.ScriptParameter<T, K> parameter, K value) {
+                checkNotNull("parameter", parameter);
+                checkNotNull("value", value);
+                checkState("Parameter has already been set", !mParameterMap.contains(parameter));
+
+                mParameterMap.set(parameter, value);
+
+                return this;
+            }
+
+            ParameterMap<T> getParameterMap() {
+                return mParameterMap;
+            }
+
+            Class<T> getScriptClass() {
+                return mScriptClass;
+            }
+
+            /**
+             * Build the script and freeze the parameter list to what was {@link #set}.
+             *
+             * @return
+             *            The {@link ScriptGraph#Builder} that was used to configure
+             *            {@link this} script.</p>
+             */
+            public Builder buildScript() {
+                mChainedScriptBuilders.add(this);
+
+                return Builder.this;
+            }
+        }
+
+        /**
+         * Configure the script with no parameters.
+         *
+         * <p>Short hand for invoking {@link #configureScript} immediately followed by
+         * {@link ScriptBuilder#buildScript()}.
+         *
+         * @param scriptClass A concrete class that subclasses {@link Script}
+         * @return The current builder ({@code this}). Use to chain method calls.
+         *
+         * @throws NullPointerException if {@code scriptClass} was {@code null}
+         */
+        public <T extends Script<?>> Builder chainScript(Class<T> scriptClass) {
+            checkNotNull("scriptClass", scriptClass);
+
+            return (new ScriptBuilder<T>(scriptClass)).buildScript();
+        }
+
+        /**
+         * Configure the script with parameters.
+         *
+         * <p>Only useful when the {@code scriptClass} has one or more
+         * {@link Script.ScriptParameter script parameters} defined.</p>
+         *
+         * @param scriptClass A concrete class that subclasses {@link Script}
+         * @return A script configuration {@link ScriptBuilder builder}. Use to chain method calls.
+         *
+         * @throws NullPointerException if {@code scriptClass} was {@code null}
+         */
+        public <T extends Script<?>> ScriptBuilder<T> configureScript(Class<T> scriptClass) {
+            checkNotNull("scriptClass", scriptClass);
+
+            return new ScriptBuilder<T>(scriptClass);
+        }
+
+        /**
+         * Finish configuring the graph and freeze the settings, instantiating all
+         * the {@link Script scripts} and {@link Allocation allocations}.
+         *
+         * @return A constructed {@link ScriptGraph}.
+         */
+        public ScriptGraph buildGraph() {
+            return new ScriptGraph(this);
+        }
+
+        private Builder() {}
+    }
+
+    private ScriptGraph(Builder builder) {
+        mSize = builder.mSize;
+        mFormat = builder.mFormat;
+        mUsage = builder.mUsage;
+        List<Builder.ScriptBuilder<? extends Script<?>>> chainedScriptBuilders =
+                builder.mChainedScriptBuilders;
+        mScripts = new ArrayList<Script<?>>(/*capacity*/chainedScriptBuilders.size());
+        OUTPUT_SCRIPT_LOCATION = chainedScriptBuilders.size() - 1;
+
+        if (mSize == null) {
+            throw new IllegalArgumentException("Inputs were not configured");
+        }
+
+        if (chainedScriptBuilders.isEmpty()) {
+            throw new IllegalArgumentException("At least one script should be chained");
+        }
+
+        /*
+         * The first input is special since it could be USAGE_IO_INPUT.
+         */
+        AllocationInfo inputInfo = AllocationInfo.newInstance(mSize, mFormat, mUsage);
+        Allocation inputAllocation;
+
+        // Create an Allocation with a Surface if the input to the graph requires it
+        if (isInputFromSurface()) {
+            mInputBlocker = inputInfo.createBlockingInputAllocation();
+            inputAllocation = mInputBlocker.getAllocation();
+        } else {
+            mInputBlocker = null;
+            inputAllocation = inputInfo.createAllocation();
+        }
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Instantiating all script classes");
+
+        // Create all scripts.
+        for (Builder.ScriptBuilder<? extends Script<?>> scriptBuilder: chainedScriptBuilders) {
+
+            @SuppressWarnings("unchecked")
+            Class<Script<?>> scriptClass = (Class<Script<?>>) scriptBuilder.getScriptClass();
+
+            @SuppressWarnings("unchecked")
+            ParameterMap<Script<?>> parameters = (ParameterMap<Script<?>>)
+                    scriptBuilder.getParameterMap();
+
+            Script<?> script = instantiateScript(scriptClass, inputInfo, parameters);
+            mScripts.add(script);
+
+            // The next script's input info is the current script's output info
+            inputInfo = script.getOutputInfo();
+        }
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Creating all inputs");
+
+        // Create and wire up all inputs.
+        int i = 0;
+        Script<?> inputScript = mScripts.get(INPUT_SCRIPT_LOCATION);
+        do {
+            if (VERBOSE) {
+                Log.v(TAG, "ScriptGraph() - Setting input for script " + inputScript.getName());
+            }
+
+            inputScript.setInput(inputAllocation);
+
+            i++;
+
+            if (i >= mScripts.size()) {
+                break;
+            }
+
+            // Use the graph input for the first loop iteration
+            inputScript = mScripts.get(i);
+            inputInfo = inputScript.getInputInfo();
+            inputAllocation = inputInfo.createAllocation();
+        } while (true);
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Creating all outputs");
+
+        // Create and wire up all outputs.
+        Allocation lastOutput = null;
+        for (i = 0; i < mScripts.size(); ++i) {
+            Script<?> script = mScripts.get(i);
+            Script<?> nextScript = (i + 1 < mScripts.size()) ? mScripts.get(i + 1) : null;
+
+            // Each script's output uses the next script's input.
+            // -- Since the last script has no next script, we own its output allocation.
+            lastOutput = (nextScript != null) ? nextScript.getInput()
+                                              : script.getOutputInfo().createAllocation();
+
+            if (VERBOSE) {
+                Log.v(TAG, "ScriptGraph() - Setting output for script " + script.getName());
+            }
+
+            script.setOutput(lastOutput);
+        }
+        mOutputAllocation = checkNotNull("lastOutput", lastOutput);
+
+        // Done. Safe to execute the graph now.
+
+        if (VERBOSE) Log.v(TAG, "ScriptGraph() - Graph has been built");
+    }
+
+    /**
+     * Construct the script by instantiating it via reflection.
+     *
+     * <p>The {@link Script scriptClass} should have a {@code Script(AllocationInfo inputInfo)}
+     * constructor if it expects an empty parameter map.</p>
+     *
+     * <p>If it expects a non-empty parameter map, it should have a
+     * {@code Script(AllocationInfo inputInfo, ParameterMap<T> parameterMap)} constructor.</p>
+     */
+    private static <T extends Script<?>> T instantiateScript(
+            Class<T> scriptClass, AllocationInfo inputInfo, ParameterMap<T> parameterMap) {
+
+        Constructor<T> ctor;
+        try {
+            // TODO: Would be better if we looked at the script class to see if it expects params
+            if (parameterMap.isEmpty()) {
+                // Script(AllocationInfo inputInfo)
+                ctor = scriptClass.getConstructor(AllocationInfo.class);
+            } else {
+                // Script(AllocationInfo inputInfo, ParameterMap<T> parameterMap)
+                ctor = scriptClass.getConstructor(AllocationInfo.class, ParameterMap.class);
+            }
+        } catch (NoSuchMethodException e) {
+            throw new UnsupportedOperationException(
+                    "Script class " + scriptClass + " must have a matching constructor", e);
+        }
+
+        try {
+            if (parameterMap.isEmpty()) {
+                return ctor.newInstance(inputInfo);
+            } else {
+                return ctor.newInstance(inputInfo, parameterMap);
+            }
+        } catch (InstantiationException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (IllegalAccessException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (IllegalArgumentException e) {
+            throw new UnsupportedOperationException(e);
+        } catch (InvocationTargetException e) {
+            throw new UnsupportedOperationException(e);
+        }
+    }
+
+    private boolean isInputFromSurface() {
+        return (mUsage & Allocation.USAGE_IO_INPUT) != 0;
+    }
+
+    /**
+     * Get the input {@link Allocation} that is used by the first script as the input.
+     *
+     * @return An {@link Allocation} (never {@code null}).
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public Allocation getInputAllocation() {
+        checkNotClosed();
+
+        return mScripts.get(INPUT_SCRIPT_LOCATION).getInput();
+    }
+
+    /**
+     * Get the output {@link Allocation} that is used by the last script as the output.
+     *
+     * @return An {@link Allocation} (never {@code null}).
+     *
+     * @throws IllegalStateException if the graph was already {@link #close closed}
+     */
+    public Allocation getOutputAllocation() {
+        checkNotClosed();
+        Allocation output = mScripts.get(OUTPUT_SCRIPT_LOCATION).getOutput();
+
+        assertEquals("Graph's output should match last script's output", mOutputAllocation, output);
+
+        return output;
+    }
+
+    /**
+     * Get the {@link Surface} that can be used produce buffers into the
+     * {@link #getInputAllocation input allocation}.
+     *
+     * @throws IllegalStateException
+     *            if input wasn't configured with {@link Allocation#USAGE_IO_INPUT} {@code usage}.
+     * @throws IllegalStateException
+     *            if the graph was already {@link #close closed}
+     *
+     * @return A {@link Surface} (never {@code null}).
+     */
+    public Surface getInputSurface() {
+        checkNotClosed();
+        checkState("This graph was not configured with IO_USAGE_INPUT", isInputFromSurface());
+
+        return getInputAllocation().getSurface();
+    }
+
+    private void checkNotClosed() {
+        checkState("ScriptGraph has been closed", !mClosed);
+    }
+
+    /**
+     * Releases all underlying resources associated with this {@link ScriptGraph}.
+     *
+     * <p>In particular, all underlying {@link Script scripts} and all
+     * {@link Allocation allocations} are also closed.</p>
+     *
+     * <p>All further calls to any other public methods (other than {@link #close}) will throw
+     * an {@link IllegalStateException}.</p>
+     *
+     * <p>This method is idempotent; calling it more than once will
+     * have no further effect.</p>
+     */
+    @Override
+    public synchronized void close() {
+        if (mClosed) return;
+
+        for (Script<?> script : mScripts) {
+            script.close();
+        }
+        mScripts.clear();
+
+        MaybeNull.close(mInputBlocker);
+        mCache.returnToCache(mOutputAllocation);
+
+        mClosed = true;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            close();
+        } finally {
+            super.finalize();
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
new file mode 100644
index 0000000..e1cdf03
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvCrop.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.ScriptC_crop_yuvf_420_to_yuvx_444;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+import android.util.Log;
+
+/**
+ * Crop {@link ImageFormat#YUV_420_888 flexible-YUV} {@link Allocation allocations} into
+ * a {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script must configure it with the
+ * {@link ScriptYuvCrop#CROP_WINDOW crop window} parameter.</p>
+ *
+ */
+public class ScriptYuvCrop extends Script<ScriptC_crop_yuvf_420_to_yuvx_444> {
+    private static final String TAG = "ScriptYuvCrop";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    /**
+     * A rectangle holding the top,left,right,bottom normalized coordinates each within [0,1].
+     *
+     * <p>The output will be a cropped copy of the input to only this crop window.</p>
+     */
+    // TODO: Change this to use Patch
+    public static final Script.ScriptParameter<ScriptYuvCrop, RectF> CROP_WINDOW =
+            new Script.ScriptParameter<ScriptYuvCrop, RectF>(ScriptYuvCrop.class,
+                    RectF.class);
+
+    private final RectF mCropWindow;
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo,
+            ParameterMap<ScriptYuvCrop> parameters) {
+        checkNotNull("inputInfo", inputInfo);
+        checkNotNull("parameters", parameters);
+
+        if (!parameters.contains(CROP_WINDOW)) {
+            throw new IllegalArgumentException("Script's CROP_WINDOW was not set");
+        }
+
+        Size inputSize = inputInfo.getSize();
+        RectF crop = parameters.get(CROP_WINDOW);
+        Size outputSize = new Size(
+                (int)(crop.width() * inputSize.getWidth()),
+                (int)(crop.height() * inputSize.getHeight()));
+
+        if (VERBOSE) Log.v(TAG, String.format("createOutputInfo - outputSize is %s", outputSize));
+
+        /**
+         * Input  YUV  W     x H
+         * Output U8_3 CropW x CropH
+         */
+        return AllocationInfo.newInstance(Element.U8_3(getRS()), outputSize);
+    }
+
+    public ScriptYuvCrop(AllocationInfo inputInfo,
+            ParameterMap<ScriptYuvCrop> parameterMap) {
+        super(inputInfo,
+              createOutputInfo(inputInfo, parameterMap),
+              new ScriptC_crop_yuvf_420_to_yuvx_444(getRS()));
+
+        // YUV_420_888 is the only supported format here
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+
+        mCropWindow = parameterMap.get(CROP_WINDOW);
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach_crop(mOutputAllocation);
+
+        if (VERBOSE) { Log.v(TAG, "executeUnchecked - forEach_crop done"); }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        int x = (int)(mCropWindow.left * mInputInfo.getSize().getWidth());
+        int y = (int)(mCropWindow.top * mInputInfo.getSize().getHeight());
+
+        mScript.set_src_x(x);
+        mScript.set_src_y(y);
+
+        mScript.set_mInput(mInputAllocation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
new file mode 100644
index 0000000..60dca59
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans1d.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.ScriptC_means_yuvx_444_1d_to_single;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+
+/**
+ * Average a {@code Hx1} {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation} into a 1x1
+ * {@code U8x3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script should chain {@link ScriptYuvMeans1d} immediately before this
+ * to average the input down to a 1x1 element.</p>
+ */
+public class ScriptYuvMeans1d extends Script<ScriptC_means_yuvx_444_1d_to_single>{
+    private static final String TAG = "ScriptYuvMeans1d";
+
+    private static final Size UNIT_SQUARE = new Size(/*width*/1, /*height*/1);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo) {
+        checkNotNull("inputInfo", inputInfo);
+        // (input) Hx1 -> 1x1
+        return AllocationInfo.newInstance(Element.U8_3(getRS()), UNIT_SQUARE);
+    }
+
+    public ScriptYuvMeans1d(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              new ScriptC_means_yuvx_444_1d_to_single(getRS()));
+
+        // U8x3 is the only supported element here
+        if (!inputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach_means_yuvx_444(mOutputAllocation);
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.set_mInput(mInputAllocation);
+
+        int width = mInputAllocation.getType().getX();
+        mScript.set_width(width);
+        mScript.set_inv_width(1.0f / width);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
new file mode 100644
index 0000000..335e631
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvMeans2dTo1d.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.RectF;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.ScriptC_means_yuvx_444_2d_to_1d;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.util.Log;
+
+/**
+ * Average pixels from a {@link ImageFormat#YUV_420_888 flexible-YUV} or
+ * {@link ElementInfo#U8_3 U8_3} {@link Allocation allocations} into a 1D Hx1
+ * {@link ElementInfo#U8_3 U8_3} {@link Allocation allocation}.
+ *
+ * <p>Users of this script should chain {@link ScriptYuvMeans1d} immediately afterwards
+ * to average the output down to a 1x1 element.</p>
+ */
+public class ScriptYuvMeans2dTo1d extends Script<ScriptC_means_yuvx_444_2d_to_1d> {
+
+    private static final String TAG = "ScriptYuvMeans2dTo1d";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo inputInfo) {
+        checkNotNull("inputInfo", inputInfo);
+        // (input) WxH -> (output) Hx1
+        return AllocationInfo.newInstance(inputInfo.getElement(),
+                new Size(inputInfo.getSize().getHeight(), /*height*/1));
+    }
+
+    public ScriptYuvMeans2dTo1d(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              new ScriptC_means_yuvx_444_2d_to_1d(getRS()));
+
+        // YUV_420_888 and U8_3 is the only supported format here
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV) &&
+                !inputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        // TODO: replace with switch statement
+        if (mInputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            mScript.forEach_means_yuvf_420(mOutputAllocation);
+
+            if (VERBOSE) Log.v(TAG, "executeUnchecked - forEach_means_yuvf_420");
+        } else if (mInputInfo.isElementEqualTo(ElementInfo.U8_3)) {
+            mScript.forEach_means_yuvx_444(mOutputAllocation);
+
+            if (VERBOSE) Log.v(TAG, "executeUnchecked - forEach_means_yuvx_444");
+        } else {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + mInputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.set_mInput(mInputAllocation);
+
+        int width = mInputAllocation.getType().getX();
+        mScript.set_width(width);
+        mScript.set_inv_width(1.0f / width);
+
+        // Do not crop. Those who want to crop should use ScriptYuvCrop.class
+        mScript.set_src_x(0);
+        mScript.set_src_y(0);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
new file mode 100644
index 0000000..5cdc0a0
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/ScriptYuvToRgb.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 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.camera2.cts.rs;
+
+import static android.hardware.camera2.cts.helpers.Preconditions.*;
+
+import android.graphics.ImageFormat;
+import android.graphics.PixelFormat;
+import android.hardware.camera2.cts.rs.AllocationInfo.ElementInfo;
+import android.renderscript.Element;
+import android.renderscript.ScriptIntrinsicYuvToRGB;
+import android.util.Log;
+
+/**
+ * Convert {@link ImageFormat#YUV_420_888 flexible-YUV} {@link Allocation allocations} into
+ * a {@link ElementInfo#RGBA_8888 RGBA_8888} {@link Allocation allocation}.
+ */
+public class ScriptYuvToRgb extends Script<ScriptIntrinsicYuvToRGB> {
+    private static final String TAG = "ScriptYuvToRgb";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    private static AllocationInfo createOutputInfo(AllocationInfo outputInfo) {
+        checkNotNull("outputInfo", outputInfo);
+        return outputInfo.changeFormatWithDefaultUsage(PixelFormat.RGBA_8888);
+    }
+
+    public ScriptYuvToRgb(AllocationInfo inputInfo) {
+        super(inputInfo,
+              createOutputInfo(inputInfo),
+              ScriptIntrinsicYuvToRGB.create(getRS(), Element.YUV(getRS())));
+
+        // YUV_420_888 is the only supported format here
+        //      XX: Supports any YUV 4:2:0 such as NV21/YV12 or just YUV_420_888 ?
+        if (!inputInfo.isElementEqualTo(ElementInfo.YUV)) {
+            throw new UnsupportedOperationException("Unsupported element "
+                    + inputInfo.getElement());
+        }
+    }
+
+    @Override
+    protected void executeUnchecked() {
+        mScript.forEach(mOutputAllocation);
+
+        if (VERBOSE) { Log.v(TAG, "executeUnchecked - forEach done"); }
+    }
+
+    @Override
+    protected void updateScriptInput() {
+        mScript.setInput(mInputAllocation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
new file mode 100644
index 0000000..1af724b
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -0,0 +1,297 @@
+/*
+ * 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.camera2.cts.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import android.content.Context;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingStateListener;
+
+import java.util.List;
+
+public class Camera2AndroidTestCase extends AndroidTestCase {
+    private static final String TAG = "Camera2AndroidTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    protected static final String DEBUG_FILE_NAME_BASE =
+            Environment.getExternalStorageDirectory().getPath();
+    // Default capture size: VGA size is required by CDD.
+    protected static final Size DEFAULT_CAPTURE_SIZE = new Size(640, 480);
+    protected static final int CAPTURE_WAIT_TIMEOUT_MS = 5000;
+
+    protected CameraManager mCameraManager;
+    protected CameraDevice mCamera;
+    protected BlockingStateListener mCameraListener;
+    protected String[] mCameraIds;
+    protected ImageReader mReader;
+    protected Surface mReaderSurface;
+    protected Handler mHandler;
+    protected HandlerThread mHandlerThread;
+    protected StaticMetadata mStaticInfo;
+    protected CameraErrorCollector mCollector;
+    protected List<Size> mOrderedPreviewSizes; // In descending order.
+    protected List<Size> mOrderedVideoSizes; // In descending order.
+    protected List<Size> mOrderedStillSizes; // In descending order.
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Can't connect to camera manager!", mCameraManager);
+    }
+
+    /**
+     * Set up the camera2 test case required environments, including CameraManager,
+     * HandlerThread, Camera IDs, and CameraStateListener etc.
+     */
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mCameraIds = mCameraManager.getCameraIdList();
+        assertNotNull("Camera ids shouldn't be null", mCameraIds);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+        mCollector = new CameraErrorCollector();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        closeDefaultImageReader();
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Start capture with given {@link #CaptureRequest}.
+     *
+     * @param request The {@link #CaptureRequest} to be captured.
+     * @param repeating If the capture is single capture or repeating.
+     * @param listener The {@link #CaptureListener} camera device used to notify callbacks.
+     * @param handler The handler camera device used to post callbacks.
+     */
+    protected void startCapture(CaptureRequest request, boolean repeating,
+            CaptureListener listener, Handler handler) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Starting capture");
+
+        if (repeating) {
+            mCamera.setRepeatingRequest(request, listener, handler);
+        } else {
+            mCamera.capture(request, listener, handler);
+        }
+    }
+
+    /**
+     * Stop the current active capture.
+     *
+     * @param fast When it is true, {@link CameraDevice#flush} is called, the stop capture
+     * could be faster.
+     */
+    protected void stopCapture(boolean fast) throws Exception {
+        if (VERBOSE) Log.v(TAG, "Stopping capture");
+
+        if (fast) {
+            /**
+             * Flush is useful for canceling long exposure single capture, it also could help
+             * to make the streaming capture stop sooner.
+             */
+            mCamera.flush();
+            mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+        } else {
+            configureCameraOutputs(mCamera, /*outputSurfaces*/null, mCameraListener);
+        }
+    }
+
+    /**
+     * Open a {@link #CameraDevice camera device} and get the StaticMetadata for a given camera id.
+     * The default mCameraListener is used to wait for states.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     */
+    protected void openDevice(String cameraId) throws Exception {
+        openDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Open a {@link #CameraDevice} and get the StaticMetadata for a given camera id and listener.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     * @param listener The {@link #BlockingStateListener} used to wait for states.
+     */
+    protected void openDevice(String cameraId, BlockingStateListener listener) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, listener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(
+                cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+
+        if (VERBOSE) {
+            Log.v(TAG, "Camera " + cameraId + " is opened");
+        }
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id. The default mCameraListener is used to wait for states.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the {@link #CameraDevice camera device} to be closed.
+     */
+    protected void closeDevice(String cameraId) {
+        closeDevice(cameraId, mCameraListener);
+    }
+
+    /**
+     * Close a {@link #CameraDevice camera device} and clear the associated StaticInfo field for a
+     * given camera id and listener.
+     * <p>
+     * This function must be used along with the {@link #openDevice} for the
+     * same camera id.
+     * </p>
+     *
+     * @param cameraId The id of the camera device to be closed.
+     * @param listener The BlockingStateListener used to wait for states.
+     */
+    protected void closeDevice(String cameraId, BlockingStateListener listener) {
+        if (mCamera != null) {
+            if (!cameraId.equals(mCamera.getId())) {
+                throw new IllegalStateException("Try to close a device that is not opened yet");
+            }
+            mCamera.close();
+            listener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+            mStaticInfo = null;
+            mOrderedPreviewSizes = null;
+            mOrderedVideoSizes = null;
+            mOrderedStillSizes = null;
+
+            if (VERBOSE) {
+                Log.v(TAG, "Camera " + cameraId + " is closed");
+            }
+        }
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     * <p>
+     * This function creates {@link ImageReader} object and surface, then assign
+     * to the default {@link mReader} and {@link mReaderSurface}. It closes the
+     * current default active {@link ImageReader} if it exists.
+     * </p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired
+     *            simultaneously.
+     * @param listener The listener used by this ImageReader to notify
+     *            callbacks.
+     */
+    protected void createDefaultImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeDefaultImageReader();
+
+        mReader = createImageReader(size, format, maxNumImages, listener);
+        mReaderSurface = mReader.getSurface();
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+    }
+
+    /**
+     * Create an {@link ImageReader} object.
+     *
+     * <p>This function creates image reader object for given format, maxImages, and size.</p>
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+
+    protected ImageReader createImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+
+        ImageReader reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+                format, maxNumImages);
+        reader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+        return reader;
+    }
+
+    /**
+     * Close the pending images then close current default {@link ImageReader} object.
+     */
+    protected void closeDefaultImageReader() {
+        closeImageReader(mReader);
+        mReader = null;
+        mReaderSurface = null;
+    }
+
+    /**
+     * Close an image reader instance.
+     *
+     * @param reader
+     */
+    protected void closeImageReader(ImageReader reader) {
+        if (reader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = reader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                reader.close();
+                reader = null;
+            }
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
new file mode 100644
index 0000000..89af15d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -0,0 +1,442 @@
+/*
+ * 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.camera2.cts.testcases;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import static com.android.ex.camera2.blocking.BlockingStateListener.STATE_CLOSED;
+
+import android.media.Image;
+import android.media.ImageReader;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata.Key;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import android.hardware.camera2.CameraDevice.CaptureListener;
+import android.hardware.camera2.cts.Camera2SurfaceViewStubActivity;
+import android.hardware.camera2.cts.CameraTestUtils;
+import android.hardware.camera2.cts.helpers.CameraErrorCollector;
+import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.helpers.StaticMetadata.CheckLevel;
+
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import com.android.ex.camera2.exceptions.TimeoutRuntimeException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Camera2 Preview test case base class by using SurfaceView as rendering target.
+ *
+ * <p>This class encapsulates the SurfaceView based preview common functionalities.
+ * The setup and teardown of CameraManager, test HandlerThread, Activity, Camera IDs
+ * and CameraStateListener are handled in this class. Some basic preview related utility
+ * functions are provided to facilitate the derived preview-based test classes.
+ * </p>
+ */
+
+public class Camera2SurfaceViewTestCase extends
+        ActivityInstrumentationTestCase2<Camera2SurfaceViewStubActivity> {
+    private static final String TAG = "SurfaceViewTestCase";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final int WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS = 1000;
+    private static final int MAX_READER_IMAGES = 5;
+
+    private Size mPreviewSize;
+
+    // TODO: Use internal storage for this to make sure the file is only visible to test.
+    protected static final String DEBUG_FILE_NAME_BASE =
+            Environment.getExternalStorageDirectory().getPath();
+    protected static final int WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
+    protected static final float FRAME_DURATION_ERROR_MARGIN = 0.005f; // 0.5 percent error margin.
+
+    protected Context mContext;
+    protected CameraManager mCameraManager;
+    protected String[] mCameraIds;
+    protected HandlerThread mHandlerThread;
+    protected Handler mHandler;
+    protected BlockingStateListener mCameraListener;
+    protected CameraErrorCollector mCollector;
+    // Per device fields:
+    protected StaticMetadata mStaticInfo;
+    protected CameraDevice mCamera;
+    protected ImageReader mReader;
+    protected Surface mReaderSurface;
+    protected Surface mPreviewSurface;
+    protected List<Size> mOrderedPreviewSizes; // In descending order.
+    protected List<Size> mOrderedVideoSizes; // In descending order.
+    protected List<Size> mOrderedStillSizes; // In descending order.
+    protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
+
+    public Camera2SurfaceViewTestCase() {
+        super(Camera2SurfaceViewStubActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        /**
+         * Set up the camera preview required environments, including activity,
+         * CameraManager, HandlerThread, Camera IDs, and CameraStateListener.
+         */
+        super.setUp();
+        mContext = getActivity();
+        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+        assertNotNull("Unable to get CameraManager", mCameraManager);
+        mCameraIds = mCameraManager.getCameraIdList();
+        assertNotNull("Unable to get camera ids", mCameraIds);
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mCameraListener = new BlockingStateListener();
+        mCollector = new CameraErrorCollector();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        // Teardown the camera preview required environments.
+        mHandlerThread.quitSafely();
+        mHandler = null;
+        mCameraListener = null;
+
+        try {
+            mCollector.verify();
+        } catch (Throwable e) {
+            // When new Exception(e) is used, exception info will be printed twice.
+            throw new Exception(e.getMessage());
+        } finally {
+            super.tearDown();
+        }
+    }
+
+    /**
+     * Start camera preview by using the given request, preview size and capture
+     * listener.
+     * <p>
+     * If preview is already started, calling this function will stop the
+     * current preview stream and start a new preview stream with given
+     * parameters. No need to call stopPreview between two startPreview calls.
+     * </p>
+     *
+     * @param request The request builder used to start the preview.
+     * @param previewSz The size of the camera device output preview stream.
+     * @param listener The callbacks the camera device will notify when preview
+     *            capture is available.
+     */
+    protected void startPreview(CaptureRequest.Builder request, Size previewSz,
+            CaptureListener listener) throws Exception {
+        // Update preview size.
+        updatePreviewSurface(previewSz);
+        if (VERBOSE) {
+            Log.v(TAG, "start preview with size " + mPreviewSize.toString());
+        }
+
+        configurePreviewOutput(request);
+
+        mCamera.setRepeatingRequest(request.build(), listener, mHandler);
+    }
+
+    /**
+     * Configure the preview output stream.
+     *
+     * @param request The request to be configured with preview surface
+     */
+    protected void configurePreviewOutput(CaptureRequest.Builder request)
+            throws CameraAccessException {
+        List<Surface> outputSurfaces = new ArrayList<Surface>(/*capacity*/1);
+        outputSurfaces.add(mPreviewSurface);
+        configureCameraOutputs(mCamera, outputSurfaces, mCameraListener);
+
+        request.addTarget(mPreviewSurface);
+    }
+
+    /**
+     * Create a {@link CaptureRequest#Builder} and add the default preview surface.
+     *
+     * @return The {@link CaptureRequest#Builder} to be created
+     * @throws CameraAccessException When create capture request from camera fails
+     */
+    protected CaptureRequest.Builder createRequestForPreview() throws CameraAccessException {
+        if (mPreviewSurface == null) {
+            throw new IllegalStateException(
+                    "Preview surface is not set yet, call updatePreviewSurface or startPreview"
+                    + "first to set the preview surface properly.");
+        }
+        CaptureRequest.Builder requestBuilder =
+                mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+        requestBuilder.addTarget(mPreviewSurface);
+        return requestBuilder;
+    }
+
+    /**
+     * Stop preview for current camera device.
+     */
+    protected void stopPreview() throws Exception {
+        if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
+        // Stop repeat, wait for captures to complete, and disconnect from surfaces
+        configureCameraOutputs(mCamera, /*outputSurfaces*/null, mCameraListener);
+    }
+
+    /**
+     * Setup still (JPEG) capture configuration and start preview.
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param stillRequest The capture request to be used for still capture
+     * @param previewSz Preview size
+     * @param stillSz The still capture size
+     * @param resultListener Capture result listener
+     * @param imageListener The still capture image listener
+     */
+    protected void prepareStillCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder stillRequest, Size previewSz, Size stillSz,
+            CaptureListener resultListener,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        prepareCaptureAndStartPreview(previewRequest, stillRequest, previewSz, stillSz,
+                ImageFormat.JPEG, resultListener, imageListener);
+    }
+
+    /**
+     * Setup raw capture configuration and start preview.
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param rawRequest The capture request to be used for raw capture
+     * @param previewSz Preview size
+     * @param rawSz The raw capture size
+     * @param resultListener Capture result listener
+     * @param imageListener The raw capture image listener
+     */
+    protected void prepareRawCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder rawRequest, Size previewSz, Size rawSz,
+            CaptureListener resultListener,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        prepareCaptureAndStartPreview(previewRequest, rawRequest, previewSz, rawSz,
+                ImageFormat.RAW_SENSOR, resultListener, imageListener);
+    }
+
+    /**
+     * Wait for expected result key value available in a certain number of results.
+     *
+     * <p>
+     * Check the result immediately if numFramesWait is 0.
+     * </p>
+     *
+     * @param listener The capture listener to get capture result
+     * @param resultKey The capture result key associated with the result value
+     * @param expectedValue The result value need to be waited for
+     * @param numResultsWait Number of frame to wait before times out
+     * @throws TimeoutRuntimeException If more than numResultsWait results are
+     * seen before the result matching myRequest arrives, or each individual wait
+     * for result times out after {@value #WAIT_FOR_RESULT_TIMEOUT_MS}ms.
+     */
+    protected <T> void waitForResultValue(SimpleCaptureListener listener, Key<T> resultKey,
+            T expectedValue, int numResultsWait) {
+        if (numResultsWait < 0 || listener == null) {
+            throw new IllegalArgumentException(
+                    "Input must be non-negative number and listener must be non-null");
+        }
+
+        int i = 0;
+        CaptureResult result;
+        do {
+            result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+            T value = result.get(resultKey);
+            if (value.equals(expectedValue)) {
+                return;
+            }
+        } while (i++ < numResultsWait);
+
+        throw new TimeoutRuntimeException(
+                "Unable to get the expected result value " + expectedValue + " for key " +
+                        resultKey.getName() + " after waiting for " + numResultsWait + " results");
+    }
+
+    /**
+     * Create an {@link ImageReader} object and get the surface.
+     *
+     * @param size The size of this ImageReader to be created.
+     * @param format The format of this ImageReader to be created
+     * @param maxNumImages The max number of images that can be acquired simultaneously.
+     * @param listener The listener used by this ImageReader to notify callbacks.
+     */
+    protected void createImageReader(Size size, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeImageReader();
+
+        mReader = ImageReader.newInstance(size.getWidth(), size.getHeight(), format, maxNumImages);
+        mReaderSurface = mReader.getSurface();
+        mReader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
+    }
+
+    /**
+     * Close the pending images then close current active {@link ImageReader} object.
+     */
+    protected void closeImageReader() {
+        if (mReader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = mReader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                mReader.close();
+                mReader = null;
+            }
+        }
+    }
+
+    /**
+     * Open a camera device and get the StaticMetadata for a given camera id.
+     *
+     * @param cameraId The id of the camera device to be opened.
+     */
+    protected void openDevice(String cameraId) throws Exception {
+        mCamera = CameraTestUtils.openCamera(
+                mCameraManager, cameraId, mCameraListener, mHandler);
+        mCollector.setCameraId(cameraId);
+        mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
+                CheckLevel.ASSERT, /*collector*/null);
+        mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+        mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
+        // Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
+        // in public API side.
+        mMinPreviewFrameDurationMap =
+                mStaticInfo.getAvailableMinFrameDurationsForFormatChecked(ImageFormat.YUV_420_888);
+    }
+
+    /**
+     * Close the current actively used camera device.
+     */
+    protected void closeDevice() {
+        if (mCamera != null) {
+            mCamera.close();
+            mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+            mCamera = null;
+            mStaticInfo = null;
+            mOrderedPreviewSizes = null;
+            mOrderedVideoSizes = null;
+            mOrderedStillSizes = null;
+        }
+    }
+
+    protected void updatePreviewSurface(Size size) {
+        if (size.equals(mPreviewSize)) {
+            return;
+        }
+
+        mPreviewSize = size;
+        Camera2SurfaceViewStubActivity stubActivity = getActivity();
+        final SurfaceHolder holder = stubActivity.getSurfaceView().getHolder();
+        Handler handler = new Handler(Looper.getMainLooper());
+        handler.post(new Runnable() {
+                @Override
+            public void run() {
+                holder.setFixedSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
+            }
+        });
+
+        boolean res = stubActivity.waitForSurfaceSizeChanged(
+                WAIT_FOR_SURFACE_CHANGE_TIMEOUT_MS, mPreviewSize.getWidth(),
+                mPreviewSize.getHeight());
+        assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
+        mPreviewSurface = holder.getSurface();
+        assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
+    }
+
+    /**
+     * Setup single capture configuration and start preview.
+     *
+     * @param previewRequest The capture request to be used for preview
+     * @param stillRequest The capture request to be used for still capture
+     * @param previewSz Preview size
+     * @param captureSz Still capture size
+     * @param format The single capture image format
+     * @param resultListener Capture result listener
+     * @param imageListener The single capture capture image listener
+     */
+    private void prepareCaptureAndStartPreview(CaptureRequest.Builder previewRequest,
+            CaptureRequest.Builder stillRequest, Size previewSz, Size captureSz, int format,
+            CaptureListener resultListener,
+            ImageReader.OnImageAvailableListener imageListener) throws Exception {
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Prepare single capture (%s) and preview (%s)",
+                    captureSz.toString(), previewSz.toString()));
+        }
+
+        // Update preview size.
+        updatePreviewSurface(previewSz);
+
+        // Create ImageReader.
+        createImageReader(captureSz, format, MAX_READER_IMAGES, imageListener);
+
+        // Configure output streams with preview and jpeg streams.
+        List<Surface> outputSurfaces = new ArrayList<Surface>();
+        outputSurfaces.add(mPreviewSurface);
+        outputSurfaces.add(mReaderSurface);
+        configureCameraOutputs(mCamera, outputSurfaces, mCameraListener);
+
+        // Configure the requests.
+        previewRequest.addTarget(mPreviewSurface);
+        stillRequest.addTarget(mPreviewSurface);
+        stillRequest.addTarget(mReaderSurface);
+
+        // Start preview.
+        mCamera.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+    }
+
+    /**
+     * Get the max preview size that supports the given fpsRange.
+     *
+     * @param fpsRange The fps range the returned size must support.
+     * @return max size that support the given fps range.
+     */
+    protected Size getMaxPreviewSizeForFpsRange(int[] fpsRange) {
+        if (fpsRange == null || fpsRange[0] <= 0 || fpsRange[1] <= 0) {
+            throw new IllegalArgumentException("Invalid fps range argument");
+        }
+        if (mOrderedPreviewSizes == null || mMinPreviewFrameDurationMap == null) {
+            throw new IllegalStateException("mOrderedPreviewSizes and mMinPreviewFrameDurationMap"
+                    + " must be initialized");
+        }
+
+        long[] frameDurationRange =
+                new long[]{(long) (1e9 / fpsRange[1]), (long) (1e9 / fpsRange[0])};
+        for (Size size : mOrderedPreviewSizes) {
+            long minDuration = mMinPreviewFrameDurationMap.get(size);
+            if (minDuration <= frameDurationRange[0]) {
+                return size;
+            }
+        }
+
+        return null;
+    }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index b198541..b0e6917 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -32,6 +32,7 @@
 import android.media.CamcorderProfile;
 import android.media.ExifInterface;
 import android.media.MediaRecorder;
+import android.os.Build;
 import android.os.ConditionVariable;
 import android.os.Environment;
 import android.os.Looper;
@@ -46,12 +47,14 @@
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.text.ParsePosition;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
+import java.util.TimeZone;
 
 import junit.framework.AssertionFailedError;
 
@@ -62,7 +65,7 @@
 public class CameraTest extends ActivityInstrumentationTestCase2<CameraStubActivity> {
     private static String TAG = "CameraTest";
     private static final String PACKAGE = "com.android.cts.stub";
-    private static final boolean LOGV = false;
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
     private final String JPEG_PATH = Environment.getExternalStorageDirectory().getPath() +
             "/test.jpg";
     private byte[] mJpegData;
@@ -90,6 +93,13 @@
     private static final int AUTOEXPOSURE_LOCK = 0;
     private static final int AUTOWHITEBALANCE_LOCK = 1;
 
+    // Some exif tags that are not defined by ExifInterface but supported.
+    private static final String TAG_DATETIME_DIGITIZED = "DateTimeDigitized";
+    private static final String TAG_SUBSEC_TIME = "SubSecTime";
+    private static final String TAG_SUBSEC_TIME_ORIG = "SubSecTimeOriginal";
+    private static final String TAG_SUBSEC_TIME_DIG = "SubSecTimeDigitized";
+
+
     private PreviewCallback mPreviewCallback = new PreviewCallback();
     private TestShutterCallback mShutterCallback = new TestShutterCallback();
     private RawPictureCallback mRawPictureCallback = new RawPictureCallback();
@@ -107,7 +117,7 @@
 
     public CameraTest() {
         super(PACKAGE, CameraStubActivity.class);
-        if (LOGV) Log.v(TAG, "Camera Constructor");
+        if (VERBOSE) Log.v(TAG, "Camera Constructor");
     }
 
     @Override
@@ -150,7 +160,7 @@
                 Log.v(TAG, "camera is opened");
                 startDone.open();
                 Looper.loop(); // Blocks forever until Looper.quit() is called.
-                if (LOGV) Log.v(TAG, "initializeMessageLooper: quit.");
+                if (VERBOSE) Log.v(TAG, "initializeMessageLooper: quit.");
             }
         }.start();
 
@@ -186,7 +196,7 @@
     private static int calculateBufferSize(int width, int height,
                                            int format, int bpp) {
 
-        if (LOGV) {
+        if (VERBOSE) {
             Log.v(TAG, "calculateBufferSize: w=" + width + ",h=" + height
             + ",f=" + format + ",bpp=" + bpp);
         }
@@ -203,7 +213,7 @@
             int c_size = c_stride * height/2;
             int size = y_size + c_size * 2;
 
-            if (LOGV) {
+            if (VERBOSE) {
                 Log.v(TAG, "calculateBufferSize: YV12 size= " + size);
             }
 
@@ -237,9 +247,9 @@
             }
             mPreviewCallbackResult = PREVIEW_CALLBACK_RECEIVED;
             mCamera.stopPreview();
-            if (LOGV) Log.v(TAG, "notify the preview callback");
+            if (VERBOSE) Log.v(TAG, "notify the preview callback");
             mPreviewDone.open();
-            if (LOGV) Log.v(TAG, "Preview callback stop");
+            if (VERBOSE) Log.v(TAG, "Preview callback stop");
         }
     }
 
@@ -247,7 +257,7 @@
     private final class TestShutterCallback implements ShutterCallback {
         public void onShutter() {
             mShutterCallbackResult = true;
-            if (LOGV) Log.v(TAG, "onShutter called");
+            if (VERBOSE) Log.v(TAG, "onShutter called");
         }
     }
 
@@ -255,7 +265,7 @@
     private final class RawPictureCallback implements PictureCallback {
         public void onPictureTaken(byte [] rawData, Camera camera) {
             mRawPictureCallbackResult = true;
-            if (LOGV) Log.v(TAG, "RawPictureCallback callback");
+            if (VERBOSE) Log.v(TAG, "RawPictureCallback callback");
         }
     }
 
@@ -272,14 +282,14 @@
                     outStream.close();
                     mJpegPictureCallbackResult = true;
 
-                    if (LOGV) {
+                    if (VERBOSE) {
                         Log.v(TAG, "JpegPictureCallback rawDataLength = " + rawData.length);
                     }
                 } else {
                     mJpegPictureCallbackResult = false;
                 }
                 mSnapshotDone.open();
-                if (LOGV) Log.v(TAG, "Jpeg Picture callback");
+                if (VERBOSE) Log.v(TAG, "Jpeg Picture callback");
             } catch (IOException e) {
                 // no need to fail here; callback worked fine
                 Log.w(TAG, "Error writing picture to sd card.");
@@ -312,7 +322,7 @@
     }
 
     private void waitForPreviewDone() {
-        if (LOGV) Log.v(TAG, "Wait for preview callback");
+        if (VERBOSE) Log.v(TAG, "Wait for preview callback");
         if (!mPreviewDone.block(WAIT_FOR_COMMAND_TO_COMPLETE)) {
             // timeout could be expected or unexpected. The caller will decide.
             Log.v(TAG, "waitForPreviewDone: timeout");
@@ -339,7 +349,18 @@
     }
 
     private void checkPreviewCallback() throws Exception {
-        if (LOGV) Log.v(TAG, "check preview callback");
+        if (VERBOSE) Log.v(TAG, "check preview callback");
+        mCamera.startPreview();
+        waitForPreviewDone();
+        mCamera.setPreviewCallback(null);
+    }
+
+    /**
+     * Start preview and wait for the first preview callback, which indicates the
+     * preview becomes active.
+     */
+    private void blockingStartPreview() {
+        mCamera.setPreviewCallback(new SimplePreviewStreamCb(/*Id*/0));
         mCamera.startPreview();
         waitForPreviewDone();
         mCamera.setPreviewCallback(null);
@@ -384,7 +405,7 @@
             assertEquals(pictureSize.height, bmpOptions.outHeight);
         } else {
             int realArea = bmpOptions.outWidth * bmpOptions.outHeight;
-            if (LOGV) Log.v(TAG, "Video snapshot is " +
+            if (VERBOSE) Log.v(TAG, "Video snapshot is " +
                     bmpOptions.outWidth + " x " + bmpOptions.outHeight +
                     ", video size is " + videoWidth + " x " + videoHeight);
             assertTrue ("Video snapshot too small! Expected at least " +
@@ -809,6 +830,21 @@
         assertTrue(mJpegPictureCallbackResult);
         exif = new ExifInterface(JPEG_PATH);
         assertFalse(exif.hasThumbnail());
+        // Primary image should still be valid for no thumbnail capture.
+        BitmapFactory.decodeFile(JPEG_PATH, bmpOptions);
+        if (!recording) {
+            assertTrue("Jpeg primary image size should match requested size",
+                    bmpOptions.outWidth == pictureSize.width &&
+                    bmpOptions.outHeight == pictureSize.height);
+        } else {
+            assertTrue(bmpOptions.outWidth >= recordingWidth ||
+                    bmpOptions.outWidth == size.width);
+            assertTrue(bmpOptions.outHeight >= recordingHeight ||
+                    bmpOptions.outHeight == size.height);
+        }
+
+        assertNotNull("Jpeg primary image data should be decodable",
+                BitmapFactory.decodeFile(JPEG_PATH));
     }
 
     @UiThreadTest
@@ -834,8 +870,10 @@
 
         // Test various exif tags.
         ExifInterface exif = new ExifInterface(JPEG_PATH);
-        assertNotNull(exif.getAttribute(ExifInterface.TAG_MAKE));
-        assertNotNull(exif.getAttribute(ExifInterface.TAG_MODEL));
+        StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
+        boolean extraExiftestPassed = checkExtraExifTagsSucceeds(failedCause, exif);
+
+        if (VERBOSE) Log.v(TAG, "Testing exif tag TAG_DATETIME");
         String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
         assertNotNull(datetime);
         // Datetime should be local time.
@@ -848,6 +886,7 @@
         assertBitmapAndJpegSizeEqual(mJpegData, exif);
 
         // Test gps exif tags.
+        if (VERBOSE) Log.v(TAG, "Testing exif GPS tags");
         testGpsExifValues(parameters, 37.736071, -122.441983, 21, 1199145600,
             "GPS NETWORK HYBRID ARE ALL FINE.");
         testGpsExifValues(parameters, 0.736071, 0.441983, 1, 1199145601, "GPS");
@@ -855,6 +894,7 @@
 
         // Test gps tags do not exist after calling removeGpsData. Also check if
         // image width and height exif match the jpeg when jpeg rotation is set.
+        if (VERBOSE) Log.v(TAG, "Testing exif GPS tag removal");
         if (!recording) mCamera.startPreview();
         parameters.removeGpsData();
         parameters.setRotation(90); // For testing image width and height exif.
@@ -864,11 +904,164 @@
         exif = new ExifInterface(JPEG_PATH);
         checkGpsDataNull(exif);
         assertBitmapAndJpegSizeEqual(mJpegData, exif);
+        assertTrue(failedCause.toString(), extraExiftestPassed);
         // Reset the rotation to prevent from affecting other tests.
         parameters.setRotation(0);
         mCamera.setParameters(parameters);
     }
 
+    /**
+     * Sanity check of some extra exif tags.
+     * <p>
+     * Sanity check some extra exif tags without asserting the check failures
+     * immediately. When a failure is detected, the failure cause is logged,
+     * the rest of the tests are still executed. The caller can assert with the
+     * failure cause based on the returned test status.
+     * </p>
+     *
+     * @param logBuf Log failure cause to this StringBuffer if there is
+     * any failure.
+     * @param exif The exif data associated with a jpeg image being tested.
+     * @return true if no test failure is found, false if there is any failure.
+     */
+    private boolean checkExtraExifTagsSucceeds(StringBuffer logBuf, ExifInterface exif) {
+        if (logBuf == null || exif == null) {
+            throw new IllegalArgumentException("failureCause and exif shouldn't be null");
+        }
+
+        if (VERBOSE) Log.v(TAG, "Testing extra exif tags");
+        boolean allTestsPassed = true;
+        boolean passedSoFar = true;
+        String failureMsg;
+
+        // TAG_EXPOSURE_TIME
+        // ExifInterface API gives exposure time value in the form of float instead of rational
+        String exposureTime = exif.getAttribute(ExifInterface.TAG_EXPOSURE_TIME);
+        passedSoFar = expectNotNull("Exif TAG_EXPOSURE_TIME is null!", logBuf, exposureTime);
+        if (passedSoFar) {
+            double exposureTimeValue = Double.parseDouble(exposureTime);
+            failureMsg = "Exif exposure time " + exposureTime + " should be a positive value";
+            passedSoFar = expectTrue(failureMsg, logBuf, exposureTimeValue > 0);
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_APERTURE
+        // ExifInterface API gives aperture value in the form of float instead of rational
+        String aperture = exif.getAttribute(ExifInterface.TAG_APERTURE);
+        passedSoFar = expectNotNull("Exif TAG_APERTURE is null!", logBuf, aperture);
+        if (passedSoFar) {
+            double apertureValue = Double.parseDouble(aperture);
+            passedSoFar = expectTrue("Exif TAG_APERTURE value " + aperture + " should be positive!",
+                    logBuf, apertureValue > 0);
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_FLASH
+        String flash = exif.getAttribute(ExifInterface.TAG_FLASH);
+        passedSoFar = expectNotNull("Exif TAG_FLASH is null!", logBuf, flash);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_WHITE_BALANCE
+        String whiteBalance = exif.getAttribute(ExifInterface.TAG_WHITE_BALANCE);
+        passedSoFar = expectNotNull("Exif TAG_WHITE_BALANCE is null!", logBuf, whiteBalance);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_MAKE
+        String make = exif.getAttribute(ExifInterface.TAG_MAKE);
+        passedSoFar = expectNotNull("Exif TAG_MAKE is null!", logBuf, make);
+        if (passedSoFar) {
+            passedSoFar = expectTrue("Exif TAG_MODEL value: " + make
+                    + " should match build manufacturer: " + Build.MANUFACTURER, logBuf,
+                    make.equals(Build.MANUFACTURER));
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_MODEL
+        String model = exif.getAttribute(ExifInterface.TAG_MODEL);
+        passedSoFar = expectNotNull("Exif TAG_MODEL is null!", logBuf, model);
+        if (passedSoFar) {
+            passedSoFar = expectTrue("Exif TAG_MODEL value: " + model
+                    + " should match build manufacturer: " + Build.MODEL, logBuf,
+                    model.equals(Build.MODEL));
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_ISO
+        int iso = exif.getAttributeInt(ExifInterface.TAG_ISO, -1);
+        passedSoFar = expectTrue("Exif ISO value " + iso + " is invalid", logBuf, iso > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_DATETIME_DIGITIZED (a.k.a Create time for digital cameras).
+        String digitizedTime = exif.getAttribute(TAG_DATETIME_DIGITIZED);
+        passedSoFar = expectNotNull("Exif TAG_DATETIME_DIGITIZED is null!", logBuf, digitizedTime);
+        if (passedSoFar) {
+            String datetime = exif.getAttribute(ExifInterface.TAG_DATETIME);
+            passedSoFar = expectNotNull("Exif TAG_DATETIME is null!", logBuf, datetime);
+            if (passedSoFar) {
+                passedSoFar = expectTrue("dataTime should match digitizedTime", logBuf,
+                        digitizedTime.equals(datetime));
+            }
+        }
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        /**
+         * TAG_SUBSEC_TIME. Since the sub second tag strings are truncated to at
+         * most 9 digits in ExifInterface implementation, use getAttributeInt to
+         * sanitize it. When the default value -1 is returned, it means that
+         * this exif tag either doesn't exist or is a non-numerical invalid
+         * string. Same rule applies to the rest of sub second tags.
+         */
+        int subSecTime = exif.getAttributeInt(TAG_SUBSEC_TIME, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME value is null or invalid!", logBuf, subSecTime > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_SUBSEC_TIME_ORIG
+        int subSecTimeOrig = exif.getAttributeInt(TAG_SUBSEC_TIME_ORIG, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME_ORIG value is null or invalid!", logBuf, subSecTimeOrig > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        // TAG_SUBSEC_TIME_DIG
+        int subSecTimeDig = exif.getAttributeInt(TAG_SUBSEC_TIME_DIG, -1);
+        passedSoFar = expectTrue(
+                "Exif TAG_SUBSEC_TIME_DIG value is null or invalid!", logBuf, subSecTimeDig > 0);
+        allTestsPassed = allTestsPassed && passedSoFar;
+
+        return allTestsPassed;
+    }
+
+    /**
+     * Check if object is null and log failure msg.
+     *
+     * @param msg Failure msg.
+     * @param logBuffer StringBuffer to log the failure msg.
+     * @param obj Object to test.
+     * @return true if object is not null, otherwise return false.
+     */
+    private boolean expectNotNull(String msg, StringBuffer logBuffer, Object obj)
+    {
+        if (obj == null) {
+            logBuffer.append(msg + "\n");
+        }
+        return (obj != null);
+    }
+
+    /**
+     * Check if condition is false and log failure msg.
+     *
+     * @param msg Failure msg.
+     * @param logBuffer StringBuffer to log the failure msg.
+     * @param condition Condition to test.
+     * @return The value of the condition.
+     */
+    private boolean expectTrue(String msg, StringBuffer logBuffer, boolean condition) {
+        if (!condition) {
+            logBuffer.append(msg + "\n");
+        }
+        return condition;
+    }
+
     private void assertBitmapAndJpegSizeEqual(byte[] jpegData, ExifInterface exif) {
         int exifWidth = exif.getAttributeInt(ExifInterface.TAG_IMAGE_WIDTH, 0);
         int exifHeight = exif.getAttributeInt(ExifInterface.TAG_IMAGE_LENGTH, 0);
@@ -905,7 +1098,26 @@
         assertEquals((float)latitude, latLong[0], 0.0001f);
         assertEquals((float)longitude, latLong[1], 0.0001f);
         assertEquals(altitude, exif.getAltitude(-1), 1);
-        assertEquals(timestamp, exif.getGpsDateTime() / 1000);
+        assertEquals(timestamp, getGpsDateTimeFromJpeg(exif) / 1000);
+    }
+
+    private long getGpsDateTimeFromJpeg(ExifInterface exif) {
+        String date = exif.getAttribute(ExifInterface.TAG_GPS_DATESTAMP);
+        String time = exif.getAttribute(ExifInterface.TAG_GPS_TIMESTAMP);
+        if (date == null || time == null) return -1;
+
+        String dateTimeString = date + ' ' + time;
+        ParsePosition pos = new ParsePosition(0);
+        try {
+            SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
+            formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+
+            Date datetime = formatter.parse(dateTimeString, pos);
+            if (datetime == null) return -1;
+            return datetime.getTime();
+        } catch (IllegalArgumentException ex) {
+            return -1;
+        }
     }
 
     private void checkGpsDataNull(ExifInterface exif) {
@@ -1152,8 +1364,7 @@
             for (int i = 0; i < ratios.size() - 1; i++) {
                 assertTrue(ratios.get(i) < ratios.get(i + 1));
             }
-            mCamera.startPreview();
-            waitForPreviewDone();
+            blockingStartPreview();
 
             // Test each zoom step.
             for (int i = 0; i <= maxZoom; i++) {
@@ -1316,8 +1527,8 @@
 
     private void testFocusDistancesByCamera(int cameraId) throws Exception {
         initializeMessageLooper(cameraId);
-        mCamera.startPreview();
-        waitForPreviewDone();
+        blockingStartPreview();
+
         Parameters parameters = mCamera.getParameters();
 
         // Test every supported focus mode.
@@ -1785,7 +1996,7 @@
                 double intervalMargin = 0.9;
                 long lastArrivalTime = mFrames.get(mFrames.size() - 1);
                 double interval = arrivalTime - lastArrivalTime;
-                if (LOGV) Log.v(TAG, "Frame interval=" + interval);
+                if (VERBOSE) Log.v(TAG, "Frame interval=" + interval);
                 try {
                     assertTrue("Frame interval (" + interval + "ms) is too " +
                             "large. mMaxFrameInterval=" +
@@ -1890,8 +2101,7 @@
 
             // Make sure scene mode settings are consistent before preview and
             // after preview.
-            mCamera.startPreview();
-            waitForPreviewDone();
+            blockingStartPreview();
             for (int i = 0; i < supportedSceneModes.size(); i++) {
                 String sceneMode = supportedSceneModes.get(i);
                 parameters.setSceneMode(sceneMode);
@@ -2064,7 +2274,7 @@
     @UiThreadTest
     public void testMultiCameraRelease() throws Exception {
         // Verify that multiple cameras exist, and that they can be opened at the same time
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Checking pre-conditions.");
         int nCameras = Camera.getNumberOfCameras();
         if (nCameras < 2) {
             Log.i(TAG, "Test multi-camera release: Skipping test because only 1 camera available");
@@ -2086,11 +2296,11 @@
         testCamera1.release();
 
         // Start first camera
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Opening camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 0");
         initializeMessageLooper(0);
         SimplePreviewStreamCb callback0 = new SimplePreviewStreamCb(0);
         mCamera.setPreviewCallback(callback0);
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 0");
         mCamera.startPreview();
         // Run preview for a bit
         for (int f = 0; f < 100; f++) {
@@ -2098,7 +2308,7 @@
             assertTrue("testMultiCameraRelease: First camera preview timed out on frame " + f + "!",
                        mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
         }
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
         mCamera.stopPreview();
         // Save message looper and camera to deterministically release them, instead
         // of letting GC do it at some point.
@@ -2110,11 +2320,11 @@
 
         // Start second camera without releasing the first one (will
         // set mCamera and mLooper to new objects)
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Opening camera 1");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Opening camera 1");
         initializeMessageLooper(1);
         SimplePreviewStreamCb callback1 = new SimplePreviewStreamCb(1);
         mCamera.setPreviewCallback(callback1);
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Starting preview on camera 1");
         mCamera.startPreview();
         // Run preview for a bit - GC of first camera instance should not impact the second's
         // operation.
@@ -2124,11 +2334,11 @@
                        mPreviewDone.block( WAIT_FOR_COMMAND_TO_COMPLETE));
             if (f == 50) {
                 // Release first camera mid-preview, should cause no problems
-                if (LOGV) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
+                if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Releasing camera 0");
                 firstCamera.release();
             }
         }
-        if (LOGV) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
+        if (VERBOSE) Log.v(TAG, "testMultiCameraRelease: Stopping preview on camera 0");
         mCamera.stopPreview();
 
         firstLooper.quit();
@@ -2144,7 +2354,7 @@
             mId = id;
         }
         public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
-            if (LOGV) Log.v(TAG, "Preview frame callback, id " + mId + ".");
+            if (VERBOSE) Log.v(TAG, "Preview frame callback, id " + mId + ".");
             mPreviewDone.open();
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
deleted file mode 100644
index 116ac80..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorEventOrderingTests.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyEventOrderingOperation;
-
-/**
- * Verifies the proper ordering in time of sensor events.
- */
-public class SensorEventOrderingTests extends SensorTestCase {
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createEventOrderingTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * Event ordering test cases.
-     */
-    private int mSensorType;
-    private int mSamplingRateInUs;
-    private int mReportLatencyInUs;
-
-    private static void createEventOrderingTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReportLatencyInUs },
-                { Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_LINEAR_ACCELERATION, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_RELATIVE_HUMIDITY, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_AMBIENT_TEMPERATURE, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST, 0 },
-                { Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,0 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorEventOrderingTests test = new SensorEventOrderingTests();
-            test.mSensorType = definition[0];
-            test.mSamplingRateInUs = definition[1];
-            test.mReportLatencyInUs = definition[2];
-            test.setName("testEventOrdering");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies the ordering of the sampled data reported by the Sensor under test.
-     * This test is used to guarantee that sensor data is reported in the order it occurs, and that
-     * events are always reported in order.
-     *
-     * The test takes a set of samples from the Sensor under test, and then it verifies that each
-     * event's timestamp is in the future compared with the previous event. At the end of the
-     * validation, the full set of events is verified to be ordered by timestamp as they are
-     * generated.
-     *
-     * The test can be susceptible to errors if the sensor sampled data is not timestamped at the
-     * Hardware level. Or events sampled at high rates are added to the FIFO without controlling the
-     * appropriate ordering of the events.
-     *
-     * The assertion associated with the test provides the information of the two consecutive events
-     * that cause the test to fail.
-     */
-    public void testEventOrdering() throws Throwable {
-        VerifyEventOrderingOperation operation = new VerifyEventOrderingOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReportLatencyInUs);
-        operation.execute();
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
deleted file mode 100644
index b35f515..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorFrequencyTests.java
+++ /dev/null
@@ -1,206 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyJitteringOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyMaximumFrequencyOperation;
-
-/**
- * Verifies several properties of the sampling rate of the different sensors in the platform.
- */
-public class SensorFrequencyTests extends SensorTestCase {
-    private int mSensorType;
-    private int mReportLatencyInUs;
-    private int mThresholdPercentageOfNs;
-
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createMaxFrequencyExpectedTestCases(testSuite);
-        // TODO: tests are a unreliable in the lab
-        //createMaxFrequencyTestCases(testSuite);
-        //createJitteringTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * Max frequency test cases.
-     */
-    private static void createMaxFrequencyTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ReportLatencyInUs, ThresholdPercentageOfNs },
-                { Sensor.TYPE_ACCELEROMETER, 0, 10 },
-                { Sensor.TYPE_GYROSCOPE, 0, 10 },
-                { Sensor.TYPE_MAGNETIC_FIELD, 0, 10 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mReportLatencyInUs = definition[1];
-            test.mThresholdPercentageOfNs = definition[2];
-            test.setName("testMaxFrequency");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the Sensor under test can sample and report data at the Maximum
-     * frequency (sampling rate) it advertises.
-     *
-     * The test takes a set of samples from the sensor under test, and calculates the mean of the
-     * frequency at which the events are reported. The frequency between events is calculated by
-     * looking at the delta between the timestamps associated with each event.
-     *
-     * The test is susceptible to errors if the Sensor is not capable to sample data at the maximum
-     * rate it supports, or the sensor events are not timestamped at the Hardware level.
-     *
-     * The assertion associated with the test provides the required data to identify:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expected frequency
-     * - the observed frequency
-     * In addition to that, the device's debug output (adb logcat) dumps the set of timestamp deltas
-     * associated with the set of data gathered from the Sensor under test.
-     */
-    public void testMaxFrequency() throws Throwable {
-        VerifyMaximumFrequencyOperation operation = new VerifyMaximumFrequencyOperation(
-                this.getContext(),
-                mSensorType,
-                mReportLatencyInUs,
-                mThresholdPercentageOfNs);
-        operation.execute();
-    }
-
-    /**
-     * Jittering test cases.
-     */
-    private static void createJitteringTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ReportLatencyInUs, ThresholdPercentageOfNs },
-                { Sensor.TYPE_ACCELEROMETER, 0, 10 },
-                { Sensor.TYPE_GYROSCOPE, 0, 10 },
-                { Sensor.TYPE_MAGNETIC_FIELD, 0, 10 },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mReportLatencyInUs = definition[1];
-            test.mThresholdPercentageOfNs = definition[2];
-            test.setName("testJittering");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the event jittering associated with the sampled data reported by the
-     * Sensor under test, aligns with the requirements imposed in the CDD.
-     * This test characterizes how the sensor behaves while sampling data at a specific rate.
-     *
-     * The test takes a set of samples from the sensor under test, using the maximum sampling rate
-     * advertised by the Sensor under test. It then compares the 95%ile associated with the
-     * jittering of the timestamps, with an expected value.
-     *
-     * The test is susceptible to errors if the sensor events are not timestamped at the Hardware
-     * level.
-     *
-     * The assertion associated with the failure provides the following information:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expectation of the test with respect of the 95%ile
-     * - the calculated 95%ile jittering
-     * Additionally, the device's debug output (adb logcat) dumps the set of jitter values
-     * calculated.
-     */
-    public void testJittering() throws Throwable {
-        VerifyJitteringOperation operation = new VerifyJitteringOperation(
-                this.getContext(),
-                mSensorType,
-                mReportLatencyInUs,
-                mThresholdPercentageOfNs);
-        operation.execute();
-    }
-
-    /**
-     * Max Frequency expected Test Cases.
-     */
-    private int mExpectedSamplingRateInUs;
-
-    private static void createMaxFrequencyExpectedTestCases(TestSuite testSuite) {
-        int testDefinitionMatrix[][] = {
-                // { SensorType, ExpectedSamplingRateInUs },
-                { Sensor.TYPE_ACCELEROMETER, 10000 /* 100 Hz */ },
-                { Sensor.TYPE_GYROSCOPE, 10000 /* 100 Hz */ },
-                { Sensor.TYPE_MAGNETIC_FIELD, 100000 /* 10 Hz */ },
-        };
-
-        for(int definition[] : testDefinitionMatrix) {
-            SensorFrequencyTests test = new SensorFrequencyTests();
-            test.mSensorType = definition[0];
-            test.mExpectedSamplingRateInUs = definition[1];
-            test.setName("testMaxFrequencyExpected");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the sensor's maximum advertised frequency (sampling rate) complies
-     * with the required frequency set in the CDD.
-     * This characterizes that the sensor is able to provide data at the rate the platform requires
-     * it.
-     *
-     * The test simply compares the sampling rate specified in the CDD with the maximum sampling
-     * rate advertised by the Sensor under test.
-     *
-     * The test can fail if the Sensor Hardware does not support the sampling rate required by the
-     * platform.
-     *
-     * The assertion associated with the test failure contains:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expected maximum sampling rate
-     * - the observed maximum sampling rate
-     */
-    public void testMaxFrequencyExpected() {
-        Sensor sensor = SensorCtsHelper.getSensor(this.getContext(), mSensorType);
-        int samplingRateInUs = sensor.getMinDelay();
-        String message = String.format(
-                "samplingRateInUs| expected:%d, actual:%d",
-                mExpectedSamplingRateInUs,
-                samplingRateInUs);
-        assertTrue(message, mExpectedSamplingRateInUs >= samplingRateInUs);
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index a1aa760..d5d7972 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -15,23 +15,19 @@
  */
 package android.hardware.cts;
 
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
 import android.content.Context;
-
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-
 import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorTestCase;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SequentialSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
 
-import android.hardware.cts.helpers.sensorTestOperations.ParallelCompositeSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.RepeatingSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.SequentialCompositeSensorTestOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyEventOrderingOperation;
+import junit.framework.Test;
+import junit.framework.TestSuite;
 
 import java.util.Random;
 
@@ -43,6 +39,8 @@
  *          -w com.android.cts.hardware/android.test.InstrumentationCtsTestRunner
  */
 public class SensorIntegrationTests extends SensorTestCase {
+    private static final String TAG = "SensorIntegrationTests";
+
     /**
      * Builder for the test suite.
      * This is the method that will build dynamically the set of test cases to execute.
@@ -87,7 +85,7 @@
      */
     public void testSensorsWithSeveralClients() throws Throwable {
         final int ITERATIONS = 50;
-        final int BATCHING_RATE_IN_SECONDS = 5;
+        final int MAX_REPORTING_LATENCY_IN_SECONDS = 5;
         final Context context = this.getContext();
 
         int sensorTypes[] = {
@@ -95,23 +93,28 @@
                 Sensor.TYPE_MAGNETIC_FIELD,
                 Sensor.TYPE_GYROSCOPE };
 
-        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
         for(int sensorType : sensorTypes) {
-            SensorTestOperation continuousOperation = new VerifyEventOrderingOperation(
+            TestSensorOperation continuousOperation = new TestSensorOperation(
                     context,
                     sensorType,
                     SensorManager.SENSOR_DELAY_NORMAL,
-                    0 /* reportLatencyInUs */);
-            operation.add(new RepeatingSensorTestOperation(continuousOperation, ITERATIONS));
+                    0 /* reportLatencyInUs */,
+                    100 /* event count */);
+            continuousOperation.addVerification(new EventOrderingVerification());
+            operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
 
-            SensorTestOperation batchingOperation = new VerifyEventOrderingOperation(
+            TestSensorOperation batchingOperation = new TestSensorOperation(
                     context,
                     sensorType,
-                    SensorTestInformation.getMaxSamplingRateInUs(context, sensorType),
-                    SensorCtsHelper.getSecondsAsMicroSeconds(BATCHING_RATE_IN_SECONDS));
-            operation.add(new RepeatingSensorTestOperation(batchingOperation, ITERATIONS));
+                    SensorCtsHelper.getSensor(getContext(), sensorType).getMinDelay(),
+                    SensorCtsHelper.getSecondsAsMicroSeconds(MAX_REPORTING_LATENCY_IN_SECONDS),
+                    100);
+            batchingOperation.addVerification(new EventOrderingVerification());
+            operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
         }
         operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
     }
 
     /**
@@ -140,7 +143,7 @@
         final int INSTANCES_TO_USE = 5;
         final int ITERATIONS_TO_EXECUTE = 100;
 
-        ParallelCompositeSensorTestOperation operation = new ParallelCompositeSensorTestOperation();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
         int sensorTypes[] = {
                 Sensor.TYPE_ACCELEROMETER,
                 Sensor.TYPE_MAGNETIC_FIELD,
@@ -148,14 +151,15 @@
 
         for(int sensorType : sensorTypes) {
             for(int instance = 0; instance < INSTANCES_TO_USE; ++instance) {
-                SequentialCompositeSensorTestOperation sequentialOperation =
-                        new SequentialCompositeSensorTestOperation();
+                SequentialSensorOperation sequentialOperation = new SequentialSensorOperation();
                 for(int iteration = 0; iteration < ITERATIONS_TO_EXECUTE; ++iteration) {
-                    VerifyEventOrderingOperation sensorOperation = new VerifyEventOrderingOperation(
+                    TestSensorOperation sensorOperation = new TestSensorOperation(
                             this.getContext(),
                             sensorType,
                             this.generateSamplingRateInUs(sensorType),
-                            this.generateReportLatencyInUs());
+                            this.generateReportLatencyInUs(),
+                            100);
+                    sensorOperation.addVerification(new EventOrderingVerification());
                     sequentialOperation.add(sensorOperation);
                 }
                 operation.add(sequentialOperation);
@@ -163,6 +167,7 @@
         }
 
         operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
     }
 
     /**
@@ -212,24 +217,30 @@
     public void testSensorStoppingInteraction() throws Throwable {
         Context context = this.getContext();
 
-        SensorTestOperation tester = new VerifyEventOrderingOperation(
+        TestSensorOperation tester = new TestSensorOperation(
                 context,
                 mSensorTypeTester,
                 SensorManager.SENSOR_DELAY_NORMAL,
-                0 /*reportLatencyInUs*/);
-        tester.start();
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        tester.addVerification(new EventOrderingVerification());
 
-        SensorTestOperation testee = new VerifyEventOrderingOperation(
+        TestSensorOperation testee = new TestSensorOperation(
                 context,
                 mSensorTypeTestee,
                 SensorManager.SENSOR_DELAY_UI,
-                0 /*reportLatencyInUs*/);
-        testee.start();
+                0 /*reportLatencyInUs*/,
+                100 /* event count */);
+        testee.addVerification(new EventOrderingVerification());
 
-        testee.waitForCompletion();
-        tester.waitForCompletion();
+        ParallelSensorOperation operation = new ParallelSensorOperation();
+        operation.add(tester, testee);
+        operation.execute();
+        SensorStats.logStats(TAG, operation.getStats());
 
+        testee = testee.clone();
         testee.execute();
+        SensorStats.logStats(TAG, testee.getStats());
     }
 
     /**
@@ -254,9 +265,8 @@
                 break;
             case 4:
             default:
-                int maxSamplingRate = SensorTestInformation.getMaxSamplingRateInUs(
-                        this.getContext(),
-                        sensorType);
+                int maxSamplingRate = SensorCtsHelper.getSensor(getContext(), sensorType)
+                        .getMinDelay();
                 rate = maxSamplingRate * mGenerator.nextInt(10);
         }
         return rate;
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
deleted file mode 100644
index 859b26d..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/SensorMeasurementTests.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts;
-
-import junit.framework.Test;
-import junit.framework.TestSuite;
-
-import android.hardware.Sensor;
-import android.hardware.SensorManager;
-
-import android.hardware.cts.helpers.SensorTestCase;
-
-import android.hardware.cts.helpers.sensorTestOperations.VerifyMagnitudeOperation;
-import android.hardware.cts.helpers.sensorTestOperations.VerifyStandardDeviationOperation;
-
-/**
- * Verifies several properties of the sensor measurements.
- */
-public class SensorMeasurementTests extends SensorTestCase {
-    private int mSensorType;
-    private int mSamplingRateInUs;
-
-    /**
-     * Builder for the test suite.
-     * This is the method that will build dynamically the set of test cases to execute.
-     * Each 'base' test case is composed by three parts:
-     * - the matrix definition
-     * - the test method that will execute the test case
-     * - a static method that will combine both and add test case instances to the test suite
-     */
-    public static Test suite() {
-        TestSuite testSuite = new TestSuite();
-
-        // add test generation routines
-        createEventNormTestCases(testSuite);
-        createStandardDeviationTestCases(testSuite);
-
-        return testSuite;
-    }
-
-    /**
-     * SensorEvent Norm test cases.
-     *
-     * Regress:
-     * - b/9503957
-     * - b/9611609
-     */
-    private float mReferenceValue;
-    private float mThreshold;
-
-    private static void createEventNormTestCases(TestSuite testSuite) {
-        Object testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReferenceValue, Threshold },
-                { Sensor.TYPE_ACCELEROMETER,
-                        SensorManager.SENSOR_DELAY_FASTEST,
-                        SensorManager.STANDARD_GRAVITY,
-                        1.5f /* m / s^2 */},
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0.0f, 2.5f /* dps */ },
-        };
-
-        for(Object definition[] : testDefinitionMatrix)  {
-            SensorMeasurementTests test = new SensorMeasurementTests();
-            test.mSensorType = (Integer)definition[0];
-            test.mSamplingRateInUs = (Integer)definition[1];
-            test.mReferenceValue = (Float)definition[2];
-            test.mThreshold = (Float)definition[3];
-            test.setName("testEventNorm");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the Norm of the sensor data is close to the expected reference value.
-     * The units of the reference value are dependent on the type of sensor.
-     * This test is used to verify that the data reported by the sensor is close to the expected
-     * range and scale.
-     *
-     * The test takes a sample from the sensor under test and calculates the Euclidean Norm of the
-     * vector represented by the sampled data. It then compares it against the test expectations
-     * that are represented by a reference value and a threshold.
-     *
-     * The test is susceptible to errors when the Sensor under test is uncalibrated, or the units in
-     * which the data are reported and the expectations are set are different.
-     *
-     * The assertion associated with the test provides the required data needed to identify any
-     * possible issue. It provides:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the values representing the expectation of the test
-     * - the values sampled from the sensor
-     */
-    public void testEventNorm() throws Throwable {
-        VerifyMagnitudeOperation operation = new VerifyMagnitudeOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReferenceValue,
-                mThreshold);
-        operation.execute();
-    }
-
-    /**
-     * SensorEvent Standard Deviation test cases.
-     */
-    private int mReportLatencyInUs;
-    private float mExpectedStandardDeviation;
-
-    private static void createStandardDeviationTestCases(TestSuite testSuite) {
-        Object testDefinitionMatrix[][] = {
-                // { SensorType, SamplingRateInUs, ReportLatencyInUs, ExpectedStandardDeviation },
-                { Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, 0, 1f /* m/s^2 */ },
-                { Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, 0, 0.5f /* dps */ },
-        };
-
-        for(Object definition[] : testDefinitionMatrix) {
-            SensorMeasurementTests test = new SensorMeasurementTests();
-            test.mSensorType = (Integer)definition[0];
-            test.mSamplingRateInUs = (Integer)definition[1];
-            test.mReportLatencyInUs = (Integer)definition[2];
-            test.mExpectedStandardDeviation = (Float)definition[3];
-            test.setName("testStandardDeviation");
-            testSuite.addTest(test);
-        }
-    }
-
-    /**
-     * This test verifies that the standard deviation of a set of sampled data from a particular
-     * sensor falls into the expectations defined in the CDD. The verification applies to each axis
-     * of the sampled data reported by the Sensor under test.
-     * This test is used to validate the requirement imposed by the CDD to Sensors in Android. And
-     * characterizes how the Sensor behaves while static.
-     *
-     * The test takes a set of samples from the sensor under test, and calculates the Standard
-     * Deviation for each of the axes the Sensor reports data for. The StdDev is compared against
-     * the expected value documented in the CDD.
-     *
-     * The test is susceptible to errors if the device is moving while the test is running, or if
-     * the Sensor's sampled data indeed falls into a large StdDev.
-     *
-     * The assertion associated with the test provides the required data to identify any possible
-     * issue. It provides:
-     * - the thread id on which the failure occurred
-     * - the sensor type and sensor handle that caused the failure
-     * - the expectation of the test
-     * - the std dev calculated and the axis it applies to
-     * Additionally, the device's debug output (adb logcat) dumps the set of values associated with
-     * the failure to help track down the issue.
-     */
-    public void testStandardDeviation() throws Throwable {
-        VerifyStandardDeviationOperation operation = new VerifyStandardDeviationOperation(
-                this.getContext(),
-                mSensorType,
-                mSamplingRateInUs,
-                mReportLatencyInUs,
-                mExpectedStandardDeviation);
-        operation.execute();
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
new file mode 100644
index 0000000..222da56
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts;
+
+import android.app.Instrumentation;
+import android.cts.util.CtsAndroidTestCase;
+import android.cts.util.DeviceReportLog;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorStats;
+import android.util.Log;
+
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+
+/**
+ * Test Case class that handles gracefully sensors that are not available in the device.
+ */
+public abstract class SensorTestCase extends CtsAndroidTestCase {
+    protected final String LOG_TAG = "TestRunner";
+
+    protected SensorTestCase() {}
+
+    @Override
+    public void runTest() throws Throwable {
+        try {
+            super.runTest();
+        } catch (SensorNotSupportedException e) {
+            // the sensor is not supported/available in the device, log a warning and skip the test
+            Log.w(LOG_TAG, e.getMessage());
+        }
+    }
+
+    /**
+     * Utility method to log selected stats to a {@link ReportLog} object.  The stats must be
+     * a number or an array of numbers.
+     */
+    public static void logSelectedStatsToReportLog(Instrumentation instrumentation, int depth,
+            String[] keys, SensorStats stats) {
+        DeviceReportLog reportLog = new DeviceReportLog(depth);
+
+        for (String key : keys) {
+            Object value = stats.getValue(key);
+            if (value instanceof Integer) {
+                reportLog.printValue(key, (Integer) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof Double) {
+                reportLog.printValue(key, (Double) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof Float) {
+                reportLog.printValue(key, (Float) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof double[]) {
+                reportLog.printArray(key, (double[]) value, ResultType.NEUTRAL, ResultUnit.NONE);
+            } else if (value instanceof float[]) {
+                float[] tmpFloat = (float[]) value;
+                double[] tmpDouble = new double[tmpFloat.length];
+                for (int i = 0; i < tmpDouble.length; i++) tmpDouble[i] = tmpFloat[i];
+                reportLog.printArray(key, tmpDouble, ResultType.NEUTRAL, ResultUnit.NONE);
+            }
+        }
+
+        reportLog.printSummary("summary", 0, ResultType.NEUTRAL, ResultUnit.NONE);
+        reportLog.deliverReportToHost(instrumentation);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
new file mode 100644
index 0000000..cd6adb1
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Set of tests to verify that sensors operate correctly when operating alone.
+ * <p>
+ * To execute these test cases, the following command can be used:
+ * </p><pre>
+ * adb shell am instrument -e class android.hardware.cts.SingleSensorTests \
+ *     -w com.android.cts.hardware/android.test.InstrumentationCtsTestRunner
+ * </pre><p>
+ * For each sensor that reports continuously, it takes a set of samples. The test suite verifies
+ * that the event ordering, frequency, and jitter pass for the collected sensor events. It
+ * additionally tests that the mean, standard deviation, and magnitude are correct for the sensor
+ * event values, where applicable for a device in a static environment.
+ * </p><p>
+ * The event ordering test verifies the ordering of the sampled data reported by the Sensor under
+ * test. This test is used to guarantee that sensor data is reported in the order it occurs, and
+ * that events are always reported in order. It verifies that each event's timestamp is in the
+ * future compared with the previous event. At the end of the validation, the full set of events is
+ * verified to be ordered by timestamp as they are generated. The test can be susceptible to errors
+ * if the sensor sampled data is not timestamped at the hardware level. Or events sampled at high
+ * rates are added to the FIFO without controlling the appropriate ordering of the events.
+ * </p><p>
+ * The frequency test verifies that the sensor under test can sample and report data at the maximum
+ * frequency (sampling rate) it advertises. The frequency between events is calculated by looking at
+ * the delta between the timestamps associated with each event to get the period. The test is
+ * susceptible to errors if the sensor is not capable to sample data at the maximum rate it
+ * supports, or the sensor events are not timestamped at the hardware level.
+ * </p><p>
+ * The jitter test verifies that the event jittering associated with the sampled data reported by
+ * the sensor under test aligns with the requirements imposed in the CDD. This test characterizes
+ * how the sensor behaves while sampling data at a specific rate. It compares the 95th percentile of
+ * the jittering with a certain percentage of the minimum period. The test is susceptible to errors
+ * if the sensor events are not timestamped at the hardware level.
+ * </p><p>
+ * The mean test verifies that the mean of a set of sampled data from a particular sensor falls into
+ * the expectations defined in the CDD. The verification applies to each axis of the sampled data
+ * reported by the sensor under test. This test is used to validate the requirement imposed by the
+ * CDD to Sensors in Android and characterizes how the Sensor behaves while static. The test is
+ * susceptible to errors if the device is moving while the test is running, or if the sensor's
+ * sampled data indeed varies from the expected mean.
+ * </p><p>
+ * The magnitude test verifies that the magnitude of the sensor data is close to the expected
+ * reference value. The units of the reference value are dependent on the type of sensor.
+ * This test is used to verify that the data reported by the sensor is close to the expected
+ * range and scale. The test calculates the Euclidean norm of the vector represented by the sampled
+ * data and compares it against the test expectations. The test is susceptible to errors when the
+ * sensor under test is uncalibrated, or the units between the data and expectations are different.
+ * </p><p>
+ * The standard deviation test verifies that the standard deviation of a set of sampled data from a
+ * particular sensor falls into the expectations defined in the CDD. The verification applies to
+ * each axis of the sampled data reported by the sensor under test. This test is used to validate
+ * the requirement imposed by the CDD to Sensors in Android and characterizes how the Sensor behaves
+ * while static. The test is susceptible to errors if the device is moving while the test is
+ * running, or if the sensor's sampled data indeed falls into a large standard deviation.
+ * </p>
+ */
+public class SingleSensorTests extends SensorTestCase {
+    private static final String TAG = "SingleSensorTests";
+
+    private static final int BATCHING_OFF = 0;
+    private static final int BATCHING_5S = 5000000;
+
+    private static final int RATE_100HZ = 10000;
+    private static final int RATE_50HZ = 20000;
+    private static final int RATE_25HZ = 40000;
+    private static final int RATE_15HZ = 66667;
+    private static final int RATE_10HZ = 100000;
+    private static final int RATE_5HZ = 200000;
+    private static final int RATE_1HZ = 1000000;
+
+    private static final String[] STAT_KEYS = {
+        SensorStats.FREQUENCY_KEY,
+        SensorStats.JITTER_95_PERCENTILE_KEY,
+        SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY,
+        SensorStats.MAGNITUDE_KEY,
+        SensorStats.MEAN_KEY,
+        SensorStats.STANDARD_DEVIATION_KEY,
+    };
+
+    /**
+     * This test verifies that the sensor's properties complies with the required properites set in
+     * the CDD.
+     * <p>
+     * It checks that the sampling rate advertised by the sensor under test matches that which is
+     * required by the CDD.
+     * </p>
+     */
+    public void testSensorProperties() {
+        // sensor type: [getMinDelay()]
+        Map<Integer, Object[]> expectedProperties = new HashMap<Integer, Object[]>(3);
+        expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
+        expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+        expectedProperties.put(Sensor.TYPE_MAGNETIC_FIELD, new Object[]{100000});
+
+        for (Entry<Integer, Object[]> entry : expectedProperties.entrySet()) {
+            Sensor sensor = SensorCtsHelper.getSensor(getContext(), entry.getKey());
+            String sensorName = SensorTestInformation.getSensorName(entry.getKey());
+            if (entry.getValue()[0] != null) {
+                int expected = (Integer) entry.getValue()[0];
+                String msg = String.format(
+                        "%s: min delay %dus expected to be less than or equal to %dus",
+                        sensorName, sensor.getMinDelay(), expected);
+                assertTrue(msg, sensor.getMinDelay() <= expected);
+            }
+        }
+    }
+
+    // TODO: Figure out if a better way to enumerate test cases programmatically exists that works
+    // with CTS framework.
+    public void testAccelerometer_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testAccelerometer_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testAccelerometer_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ACCELEROMETER, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testMagneticField_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testMagneticField_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticField_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testMagneticField_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD, RATE_50HZ, BATCHING_5S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_100HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_25HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_15HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_10HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_5HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_1HZ, BATCHING_OFF);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    @SuppressWarnings("deprecation")
+    public void testOrientation_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ORIENTATION, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGyroscope_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testGyroscope_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscope_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testGyroscope_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testPressure_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testPressure_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testPressure_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testPressure_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_PRESSURE, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGravity_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_OFF);
+    }
+
+    public void testGravity_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGravity_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testGravity_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GRAVITY, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+    public void testRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST, BATCHING_5S);
+    }
+
+    public void testRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testMagneticFieldUncalibrated_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testMagneticFieldUncalibrated_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGameRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGameRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGameRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GAME_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void testGyroscopeUncalibrated_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGyroscopeUncalibrated_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGyroscopeUncalibrated_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, RATE_50HZ, BATCHING_5S);
+    }
+
+    public void  testGeomagneticRotationVector_fastest() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_OFF);
+    }
+
+    public void  testGeomagneticRotationVector_100hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_100HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_50hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_25hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_25HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_15hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_15HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_10hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_10HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_5hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_5HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_1hz() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_1HZ, BATCHING_OFF);
+    }
+
+    public void testGeomagneticRotationVector_fastest_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, SensorManager.SENSOR_DELAY_FASTEST,
+                BATCHING_5S);
+    }
+
+    public void testGeomagneticRotationVector_50hz_batching() throws Throwable {
+        runSensorTest(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, RATE_50HZ, BATCHING_5S);
+    }
+
+    private void runSensorTest(int sensorType, int rateUs, int maxBatchReportLatencyUs)
+            throws Throwable {
+        TestSensorOperation op = new TestSensorOperation(this.getContext(), sensorType,
+                rateUs, maxBatchReportLatencyUs, 5, TimeUnit.SECONDS);
+        op.setDefaultVerifications();
+        op.setLogEvents(true);
+        try {
+            op.execute();
+
+            // Only report stats if it passes.
+            logSelectedStatsToReportLog(getInstrumentation(), 2, STAT_KEYS,
+                    op.getStats());
+        } finally {
+            SensorStats.logStats(TAG, op.getStats());
+
+            String sensorName = SensorTestInformation.getSanitizedSensorName(sensorType);
+            String sensorRate;
+            if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
+                sensorRate = "fastest";
+            } else {
+                sensorRate = String.format("%.0fhz",
+                        SensorCtsHelper.getFrequency(rateUs, TimeUnit.MICROSECONDS));
+            }
+            String batching = maxBatchReportLatencyUs > 0 ? "_batching" : "";
+            String fileName = String.format("single_sensor_%s_%s%s.txt",
+                    sensorName, sensorRate, batching);
+            SensorStats.logStatsToFile(fileName, op.getStats());
+
+
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
new file mode 100644
index 0000000..981d74c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.os.SystemClock;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link TestSensorEventListener} which collects events to be processed after the test is run.
+ * This should only be used for short tests.
+ */
+public class CollectingSensorEventListener extends TestSensorEventListener {
+    private final ConcurrentLinkedDeque<TestSensorEvent> mSensorEventsList =
+            new ConcurrentLinkedDeque<TestSensorEvent>();
+
+    /**
+     * Constructs a {@link CollectingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public CollectingSensorEventListener(SensorEventListener2 listener) {
+        super(listener);
+    }
+
+    /**
+     * Constructs a {@link CollectingSensorEventListener}.
+     */
+    public CollectingSensorEventListener() {
+        this(null);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        super.onSensorChanged(event);
+        mSensorEventsList.addLast(new TestSensorEvent(event, SystemClock.elapsedRealtimeNanos()));
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Clears the event queue before starting.
+     * </p>
+     */
+    @Override
+    public void waitForEvents(int eventCount) {
+        clearEvents();
+        super.waitForEvents(eventCount);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Clears the event queue before starting.
+     * </p>
+     */
+    @Override
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        clearEvents();
+        super.waitForEvents(duration, timeUnit);
+    }
+
+    /**
+     * Get the {@link TestSensorEvent} array from the event queue.
+     */
+    public TestSensorEvent[] getEvents() {
+        return mSensorEventsList.toArray(new TestSensorEvent[0]);
+    }
+
+    /**
+     * Clear the event queue.
+     */
+    public void clearEvents() {
+        mSensorEventsList.clear();
+    }
+}
\ No newline at end of file
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java b/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java
new file mode 100644
index 0000000..6075add
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/FrameworkUnitTests.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.cts.helpers.sensoroperations.SensorOperationTest;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerificationTest;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerificationTest;
+import android.hardware.cts.helpers.sensorverification.JitterVerificationTest;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerificationTest;
+import android.hardware.cts.helpers.sensorverification.MeanVerificationTest;
+import android.hardware.cts.helpers.sensorverification.EventGapVerificationTest;
+import android.hardware.cts.helpers.sensorverification.SigNumVerificationTest;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerificationTest;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test suite for the CTS sensor framework.
+ */
+public class FrameworkUnitTests extends TestSuite {
+
+    public FrameworkUnitTests() {
+        super();
+
+        // helpers
+        addTestSuite(SensorCtsHelperTest.class);
+        addTestSuite(SensorStatsTest.class);
+
+        // sensorverification
+        addTestSuite(EventOrderingVerificationTest.class);
+        addTestSuite(FrequencyVerificationTest.class);
+        addTestSuite(JitterVerificationTest.class);
+        addTestSuite(MagnitudeVerificationTest.class);
+        addTestSuite(MeanVerificationTest.class);
+        addTestSuite(EventGapVerificationTest.class);
+        addTestSuite(SigNumVerificationTest.class);
+        addTestSuite(StandardDeviationVerificationTest.class);
+
+        // sensorOperations
+        addTestSuite(SensorOperationTest.class);
+    }
+
+    public static Test suite() {
+        return new FrameworkUnitTests();
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index 9d860cd..ed55b01 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -18,24 +18,21 @@
 import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.os.Environment;
-import android.util.Log;
 
-import java.io.DataOutputStream;
-import java.io.IOException;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 /**
  * Set of static helper methods for CTS tests.
  */
+//TODO: Refactor this class and SensorTestInformation into several more well defined helper classes
 public class SensorCtsHelper {
 
+    private static long NANOS_PER_MILLI = 1000000;
+
     /**
      * Private constructor for static class.
      */
@@ -60,61 +57,6 @@
     }
 
     /**
-     * Calculates the mean for each of the values in the set of TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static double[] getMeans(TestSensorEvent[] events) {
-        if (events.length == 0) {
-            throw new IllegalArgumentException("Events cannot be empty");
-        }
-
-        double[] means = new double[events[0].values.length];
-        for (TestSensorEvent event : events) {
-            for (int i = 0; i < means.length; i++) {
-                means[i] += event.values[i];
-            }
-        }
-        for (int i = 0; i < means.length; i++) {
-            means[i] /= events.length;
-        }
-        return means;
-    }
-
-    /**
-     * Calculates the variance for each of the values in the set of TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static double[] getVariances(TestSensorEvent[] events) {
-        double[] means = getMeans(events);
-        double[] variances = new double[means.length];
-        for (int i = 0; i < means.length; i++) {
-            Collection<Double> squaredDiffs = new ArrayList<Double>(events.length);
-            for (TestSensorEvent event : events) {
-                double diff = event.values[i] - means[i];
-                squaredDiffs.add(diff * diff);
-            }
-            variances[i] = getMean(squaredDiffs);
-        }
-        return variances;
-    }
-
-    /**
-     * Calculates the standard deviation for each of the values in the set of TestSensorEvents.
-     *
-     * @throws IllegalArgumentException if there are no events
-     */
-    public static double[] getStandardDeviations(TestSensorEvent[] events) {
-        double[] variances = getVariances(events);
-        double[] stdDevs = new double[variances.length];
-        for (int i = 0; i < variances.length; i++) {
-            stdDevs[i] = Math.sqrt(variances[i]);
-        }
-        return stdDevs;
-    }
-
-    /**
      * Calculate the mean of a collection.
      *
      * @throws IllegalArgumentException if the collection is null or empty
@@ -130,7 +72,7 @@
     }
 
     /**
-     * Calculate the variance of a collection.
+     * Calculate the bias-corrected sample variance of a collection.
      *
      * @throws IllegalArgumentException if the collection is null or empty
      */
@@ -138,17 +80,21 @@
         validateCollection(collection);
 
         double mean = getMean(collection);
-        ArrayList<Double> squaredDifferences = new ArrayList<Double>();
+        ArrayList<Double> squaredDiffs = new ArrayList<Double>();
         for(TValue value : collection) {
             double difference = mean - value.doubleValue();
-            squaredDifferences.add(Math.pow(difference, 2));
+            squaredDiffs.add(Math.pow(difference, 2));
         }
 
-        return getMean(squaredDifferences);
+        double sum = 0.0;
+        for (Double value : squaredDiffs) {
+            sum += value;
+        }
+        return sum / (squaredDiffs.size() - 1);
     }
 
     /**
-     * Calculate the standard deviation of a collection.
+     * Calculate the bias-corrected standard deviation of a collection.
      *
      * @throws IllegalArgumentException if the collection is null or empty
      */
@@ -158,89 +104,6 @@
     }
 
     /**
-     * Get a list containing the delay between sensor events.
-     *
-     * @param events The array of {@link TestSensorEvent}.
-     * @return A list containing the delay between sensor events in nanoseconds.
-     */
-    public static List<Long> getTimestampDelayValues(TestSensorEvent[] events) {
-        if (events.length < 2) {
-            return new ArrayList<Long>();
-        }
-        List<Long> timestampDelayValues = new ArrayList<Long>(events.length - 1);
-        for (int i = 1; i < events.length; i++) {
-            timestampDelayValues.add(events[i].timestamp - events[i - 1].timestamp);
-        }
-        return timestampDelayValues;
-    }
-
-    /**
-     * Get a list containing the jitter values for a collection of sensor events.
-     *
-     * @param events The array of {@link TestSensorEvent}.
-     * @return A list containing the jitter values between each event.
-     * @throws IllegalArgumentException if the number of events is less that 2.
-     */
-    public static List<Double> getJitterValues(TestSensorEvent[] events) {
-        List<Long> timestampDelayValues = getTimestampDelayValues(events);
-        double averageTimestampDelay = getMean(timestampDelayValues);
-
-        List<Double> jitterValues = new ArrayList<Double>(timestampDelayValues.size());
-        for (long timestampDelay : timestampDelayValues) {
-            jitterValues.add(Math.abs(timestampDelay - averageTimestampDelay));
-        }
-        return jitterValues;
-    }
-
-    /**
-     * NOTE:
-     * - The bug report is usually written to /sdcard/Downloads
-     * - In order for the test Instrumentation to gather useful data the following permissions are
-     *   required:
-     *      . android.permission.READ_LOGS
-     *      . android.permission.DUMP
-     */
-    public static String collectBugreport(String collectorId)
-            throws IOException, InterruptedException {
-        String commands[] = new String[] {
-                "dumpstate",
-                "dumpsys",
-                "logcat -d -v threadtime",
-                "exit"
-        };
-
-        SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
-        String outputFile = String.format(
-                "%s/%s_%s",
-                Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
-                collectorId,
-                dateFormat.format(new Date()));
-
-        DataOutputStream processOutput = null;
-        try {
-            Process process = Runtime.getRuntime().exec("/system/bin/sh -");
-            processOutput = new DataOutputStream(process.getOutputStream());
-
-            for(String command : commands) {
-                processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
-            }
-
-            processOutput.flush();
-            process.waitFor();
-
-            Log.d(collectorId, String.format("Bug-Report collected at: %s", outputFile));
-        } finally {
-            if(processOutput != null) {
-                try {
-                    processOutput.close();
-                } catch(IOException e) {}
-            }
-        }
-
-        return outputFile;
-    }
-
-    /**
      * Get the default sensor for a given type.
      */
     public static Sensor getSensor(Context context, int sensorType) {
@@ -286,57 +149,79 @@
     }
 
     /**
-     * Format an assertion message.
-     *
-     * @param verificationName The verification name
-     * @param sensor The sensor under test
-     * @param format The additional format string, use "" if blank
-     * @param params The additional format params
-     * @return The formatted string.
+     * Convert the sensor delay or rate in microseconds into delay in microseconds.
+     * <p>
+     * The flags SensorManager.SENSOR_DELAY_[GAME|UI|NORMAL] are not supported since the CDD does
+     * not specify values for these flags. The rate is set to the max of
+     * {@link Sensor#getMinDelay()} and the rate given.
+     * </p>
      */
-    public static String formatAssertionMessage(
-            String verificationName,
-            Sensor sensor,
-            String format,
-            Object ... params) {
-        return formatAssertionMessage(verificationName, null, sensor, format, params);
+    public static int getDelay(Sensor sensor, int rateUs) {
+        if (!isDelayRateTestable(rateUs)) {
+            throw new IllegalArgumentException("rateUs cannot be SENSOR_DELAY_[GAME|UI|NORMAL]");
+        }
+        int delay;
+        if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
+            delay = 0;
+        } else {
+            delay = rateUs;
+        }
+        return Math.max(delay, sensor.getMinDelay());
+    }
+
+    /**
+     * Return true if the operation rate is not one of {@link SensorManager#SENSOR_DELAY_GAME},
+     * {@link SensorManager#SENSOR_DELAY_UI}, or {@link SensorManager#SENSOR_DELAY_NORMAL}.
+     */
+    public static boolean isDelayRateTestable(int rateUs) {
+        return (rateUs != SensorManager.SENSOR_DELAY_GAME
+                && rateUs != SensorManager.SENSOR_DELAY_UI
+                && rateUs != SensorManager.SENSOR_DELAY_NORMAL);
+    }
+
+    /**
+     * Helper method to sleep for a given duration.
+     */
+    public static void sleep(long duration, TimeUnit timeUnit) {
+        long durationNs = TimeUnit.NANOSECONDS.convert(duration, timeUnit);
+        try {
+            Thread.sleep(durationNs / NANOS_PER_MILLI, (int) (durationNs % NANOS_PER_MILLI));
+        } catch (InterruptedException e) {
+            // Ignore
+        }
     }
 
     /**
      * Format an assertion message.
      *
-     * @param verificationName The verification name
-     * @param test The test, optional
-     * @param sensor The sensor under test
-     * @param format The additional format string, use "" if blank
-     * @param params The additional format params
-     * @return The formatted string.
+     * @param sensor the {@link Sensor}
+     * @param label the verification name
+     * @param rateUs the rate of the sensor
+     * @param maxBatchReportLatencyUs the max batch report latency of the sensor
+     * @return The formatted string
      */
-    public static String formatAssertionMessage(
-            String verificationName,
-            SensorTestOperation test,
-            Sensor sensor,
-            String format,
-            Object ... params) {
-        StringBuilder builder = new StringBuilder();
+    public static String formatAssertionMessage(Sensor sensor, String label, int rateUs,
+            int maxBatchReportLatencyUs) {
+        return String.format("%s | %s, handle: %d", label,
+                SensorTestInformation.getSensorName(sensor.getType()), sensor.getHandle());
+    }
 
-        // identify the verification
-        builder.append(verificationName);
-        builder.append("| ");
-        // add test context information
-        if(test != null) {
-            builder.append(test.toString());
-            builder.append("| ");
-        }
-        // add context information
-        builder.append(SensorTestInformation.getSensorName(sensor.getType()));
-        builder.append(", handle:");
-        builder.append(sensor.getHandle());
-        builder.append("| ");
-        // add the custom formatting
-        builder.append(String.format(format, params));
-
-        return builder.toString();
+    /**
+     * Format an assertion message with a custom message.
+     *
+     * @param sensor the {@link Sensor}
+     * @param label the verification name
+     * @param rateUs the rate of the sensor
+     * @param maxBatchReportLatencyUs the max batch report latency of the sensor
+     * @param format the additional format string
+     * @param params the additional format params
+     * @return The formatted string
+     */
+    public static String formatAssertionMessage(Sensor sensor, String label, int rateUs,
+            int maxBatchReportLatencyUs, String format, Object ... params) {
+        return String.format("%s | %s, handle: %d, rateUs: %d, maxBatchReportLatencyUs: %d | %s",
+                label, SensorTestInformation.getSensorName(sensor.getType()), sensor.getHandle(),
+                rateUs, maxBatchReportLatencyUs, String.format(format, params));
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
index ccfaf2a..6f99692 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelperTest.java
@@ -53,73 +53,6 @@
     }
 
     /**
-     * Test {@link SensorCtsHelper#getMeans(TestSensorEvent[])}.
-     */
-    public void testGetMeans() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // 2.0
-        double[] means = SensorCtsHelper.getMeans(getSensorEvents(timestamps, values));
-        assertEquals(1, means.length);
-        assertEquals(2.0, means[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // 2.0
-        float[] values2 = {1, 2, 3, 4, 5};  // 3.0
-        float[] values3 = {0, 1, 4, 9, 16};  // 6.0
-        means = SensorCtsHelper.getMeans(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, means.length);
-        assertEquals(2.0, means[0], 0.00001);
-        assertEquals(3.0, means[1], 0.00001);
-        assertEquals(6.0, means[2], 0.00001);
-    }
-
-    /**
-     * Test {@link SensorCtsHelper#getVariances(TestSensorEvent[])}.
-     */
-    public void testGetVariences() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // 2.0
-        double[] variances = SensorCtsHelper.getVariances(getSensorEvents(timestamps, values));
-        assertEquals(1, variances.length);
-        assertEquals(2.0, variances[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // 2.0
-        float[] values2 = {1, 2, 3, 4, 5};  // 2.0
-        float[] values3 = {0, 2, 4, 6, 8};  // 8.0
-        variances = SensorCtsHelper.getVariances(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, variances.length);
-        assertEquals(2.0, variances[0], 0.00001);
-        assertEquals(2.0, variances[1], 0.00001);
-        assertEquals(8.0, variances[2], 0.00001);
-    }
-
-    /**
-     * Test {@link SensorCtsHelper#getStandardDeviations(TestSensorEvent[])}.
-     */
-    public void testGetStandardDeviations() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-
-        float[] values = {0, 1, 2, 3, 4};  // sqrt(2.0)
-        double[] stddev = SensorCtsHelper.getStandardDeviations(
-                getSensorEvents(timestamps, values));
-        assertEquals(1, stddev.length);
-        assertEquals(Math.sqrt(2.0), stddev[0], 0.00001);
-
-        float[] values1 = {0, 1, 2, 3, 4};  // sqrt(2.0)
-        float[] values2 = {1, 2, 3, 4, 5};  // sqrt(2.0)
-        float[] values3 = {0, 2, 4, 6, 8};  // sqrt(8.0)
-        stddev = SensorCtsHelper.getStandardDeviations(
-                getSensorEvents(timestamps, values1, values2, values3));
-        assertEquals(3, stddev.length);
-        assertEquals(Math.sqrt(2.0), stddev[0], 0.00001);
-        assertEquals(Math.sqrt(2.0), stddev[1], 0.00001);
-        assertEquals(Math.sqrt(8.0), stddev[2], 0.00001);
-    }
-
-    /**
      * Test {@link SensorCtsHelper#getMean(Collection)}.
      */
     public void testGetMean() {
@@ -142,15 +75,15 @@
     public void testGetVariance() {
         List<Integer> values = Arrays.asList(0, 1, 2, 3, 4);
         double variance = SensorCtsHelper.getVariance(values);
-        assertEquals(2.0, variance, 0.00001);
+        assertEquals(2.5, variance, 0.00001);
 
         values = Arrays.asList(1, 2, 3, 4, 5);
         variance = SensorCtsHelper.getVariance(values);
-        assertEquals(2.0, variance, 0.00001);
+        assertEquals(2.5, variance, 0.00001);
 
         values = Arrays.asList(0, 2, 4, 6, 8);
         variance = SensorCtsHelper.getVariance(values);
-        assertEquals(8.0, variance, 0.00001);
+        assertEquals(10.0, variance, 0.00001);
     }
 
     /**
@@ -159,31 +92,15 @@
     public void testGetStandardDeviation() {
         List<Integer> values = Arrays.asList(0, 1, 2, 3, 4);
         double stddev = SensorCtsHelper.getStandardDeviation(values);
-        assertEquals(Math.sqrt(2.0), stddev, 0.00001);
+        assertEquals(Math.sqrt(2.5), stddev, 0.00001);
 
         values = Arrays.asList(1, 2, 3, 4, 5);
         stddev = SensorCtsHelper.getStandardDeviation(values);
-        assertEquals(Math.sqrt(2.0), stddev, 0.00001);
+        assertEquals(Math.sqrt(2.5), stddev, 0.00001);
 
         values = Arrays.asList(0, 2, 4, 6, 8);
         stddev = SensorCtsHelper.getStandardDeviation(values);
-        assertEquals(Math.sqrt(8.0), stddev, 0.00001);
-    }
-
-    /**
-     * Test {@link SensorCtsHelper#getTimestampDelayValues(TestSensorEvent[])}.
-     */
-    public void testGetTimestampDelayValues() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps = {0, 0, 1, 3, 100};
-        List<Long> timestampDelayValues = SensorCtsHelper.getTimestampDelayValues(
-                getSensorEvents(timestamps, values));
-        assertEquals(4, timestampDelayValues.size());
-        assertEquals(0, (long) timestampDelayValues.get(0));
-        assertEquals(1, (long) timestampDelayValues.get(1));
-        assertEquals(2, (long) timestampDelayValues.get(2));
-        assertEquals(97, (long) timestampDelayValues.get(3));
+        assertEquals(Math.sqrt(10.0), stddev, 0.00001);
     }
 
     /**
@@ -217,50 +134,4 @@
         assertEquals(100, SensorCtsHelper.getPeriod(10000000, TimeUnit.NANOSECONDS), 0.001);
         assertEquals(1, SensorCtsHelper.getPeriod(1000000000, TimeUnit.NANOSECONDS), 0.001);
     }
-
-    /**
-     * Test {@link SensorCtsHelper#getJitterValues(TestSensorEvent[])}.
-     */
-    public void testGetJitterValues() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps1 = {0, 1, 2, 3, 4};
-        List<Double> jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps1, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(0.0, (double) jitterValues.get(0));
-        assertEquals(0.0, (double) jitterValues.get(1));
-        assertEquals(0.0, (double) jitterValues.get(2));
-        assertEquals(0.0, (double) jitterValues.get(3));
-
-        long[] timestamps2 = {0, 0, 2, 4, 4};
-        jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps2, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(1.0, (double) jitterValues.get(0));
-        assertEquals(1.0, (double) jitterValues.get(1));
-        assertEquals(1.0, (double) jitterValues.get(2));
-        assertEquals(1.0, (double) jitterValues.get(3));
-
-        long[] timestamps3 = {0, 1, 4, 9, 16};
-        jitterValues = SensorCtsHelper.getJitterValues(
-                getSensorEvents(timestamps3, values));
-        assertEquals(4, jitterValues.size());
-        assertEquals(3.0, (double) jitterValues.get(0));
-        assertEquals(1.0, (double) jitterValues.get(1));
-        assertEquals(1.0, (double) jitterValues.get(2));
-        assertEquals(3.0, (double) jitterValues.get(3));
-    }
-
-    private TestSensorEvent[] getSensorEvents(long[] timestamps, float[] ... values) {
-        TestSensorEvent[] events = new TestSensorEvent[timestamps.length];
-        for (int i = 0; i < timestamps.length; i++) {
-            float[] eventValues = new float[values.length];
-            for (int j = 0; j < values.length; j++) {
-                eventValues[j] = values[j][i];
-            }
-            events[i] = new TestSensorEvent(null, timestamps[i], 0, eventValues);
-        }
-        return events;
-    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
deleted file mode 100644
index 85f4f0e..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorManagerTestVerifier.java
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-import android.hardware.SensorManager;
-
-import junit.framework.Assert;
-
-import java.io.Closeable;
-import java.security.InvalidParameterException;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test class to wrap SensorManager with verifications and test checks.
- * This class allows to perform operations in the Sensor Manager and performs all the expected test
- * verification on behalf of the owner.
- * An object can be used to quickly writing tests that focus on the scenario that needs to be
- * verified, and not in the implicit verifications that need to take place at any step.
- */
-public class SensorManagerTestVerifier implements Closeable, SensorEventListener2 {
-    private final int WAIT_TIMEOUT_IN_SECONDS = 30;
-
-    private final SensorManager mSensorManager;
-    private final Sensor mSensorUnderTest;
-    private final int mSamplingRateInUs;
-    private final int mReportLatencyInUs;
-
-    private TestSensorListener mEventListener;
-
-    /**
-     * Construction methods.
-     */
-    public SensorManagerTestVerifier(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs) {
-        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
-        mSensorUnderTest = SensorCtsHelper.getSensor(context, sensorType);
-        mSamplingRateInUs = samplingRateInUs;
-        mReportLatencyInUs = reportLatencyInUs;
-
-        mEventListener = new TestSensorListener(mSensorUnderTest, this);
-    }
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Available for subclasses to implement if they need access to the raw eventing model.
-     * </p>
-     */
-    @Override
-    public void onAccuracyChanged(Sensor sensor, int accuracy) {}
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Available for subclasses to implement if they need access to the raw eventing model.
-     * </p>
-     */
-    @Override
-    public void onSensorChanged(SensorEvent event) {}
-
-    /**
-     * {@inheritDoc}
-     * <p>
-     * Available for subclasses to implement if they need access to the raw eventing model.
-     * </p>
-     */
-    @Override
-    public void onFlushCompleted(Sensor sensor) {}
-
-    /**
-     * Closes the {@link SensorManagerTestVerifier} and unregister the listener.
-     */
-    @Override
-    public void close() {
-        this.unregisterListener();
-        mEventListener = null;
-    }
-
-    /**
-     * Get the sensor under test.
-     */
-    public Sensor getUnderlyingSensor() {
-        return mSensorUnderTest;
-    }
-
-    /**
-     * Register the
-     * @param debugInfo
-     */
-    public void registerListener(String debugInfo) {
-        boolean result = mSensorManager.registerListener(
-                mEventListener,
-                mSensorUnderTest,
-                mSamplingRateInUs,
-                mReportLatencyInUs);
-        String message = SensorCtsHelper.formatAssertionMessage(
-                "registerListener",
-                mSensorUnderTest,
-                debugInfo);
-        Assert.assertTrue(message, result);
-    }
-
-    public void registerListener() {
-        this.registerListener("");
-    }
-
-    public void unregisterListener() {
-        mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-    }
-
-    public TestSensorEvent[] getEvents(int count, String debugInfo) {
-        mEventListener.waitForEvents(count, debugInfo);
-        TestSensorEvent[] events = mEventListener.getAllEvents();
-        mEventListener.clearEvents();
-
-        return events;
-    }
-
-    public TestSensorEvent[] getEvents(int count) {
-        return this.getEvents(count, "");
-    }
-
-    public TestSensorEvent[] getQueuedEvents() {
-        return mEventListener.getAllEvents();
-    }
-
-    public TestSensorEvent[] collectEvents(int eventCount, String debugInfo) {
-        this.registerListener(debugInfo);
-        TestSensorEvent[] events = this.getEvents(eventCount, debugInfo);
-        this.unregisterListener();
-
-        return events;
-    }
-
-    public TestSensorEvent[] collectEvents(int eventCount) {
-        return this.collectEvents(eventCount, "");
-    }
-
-    public void startFlush() {
-        String message = SensorCtsHelper.formatAssertionMessage(
-                "Flush",
-                mSensorUnderTest,
-                "" /* format */);
-        Assert.assertTrue(message, mSensorManager.flush(mEventListener));
-    }
-
-    public void waitForFlush() throws InterruptedException {
-        mEventListener.waitForFlushComplete();
-    }
-
-    public void flush() throws InterruptedException {
-        this.startFlush();
-        this.waitForFlush();
-    }
-
-    /**
-     * Definition of support test classes.
-     */
-    private class TestSensorListener implements SensorEventListener2 {
-        private final Sensor mSensorUnderTest;
-        private final SensorEventListener2 mListener;
-
-        private final ConcurrentLinkedDeque<TestSensorEvent> mSensorEventsList =
-                new ConcurrentLinkedDeque<TestSensorEvent>();
-
-        private volatile CountDownLatch mEventLatch;
-        private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
-
-        public TestSensorListener(Sensor sensor, SensorEventListener2 listener) {
-            if(sensor == null) {
-                throw new InvalidParameterException("sensor cannot be null");
-            }
-            if(listener == null) {
-                throw new InvalidParameterException("listener cannot be null");
-            }
-            mSensorUnderTest = sensor;
-            mListener = listener;
-        }
-
-        @Override
-        public void onSensorChanged(SensorEvent event) {
-            // copy the event because there is no better way to do this in the platform
-            mSensorEventsList.addLast(new TestSensorEvent(event));
-            if(mEventLatch != null) {
-                mEventLatch.countDown();
-            }
-            mListener.onSensorChanged(event);
-        }
-
-        @Override
-        public void onAccuracyChanged(Sensor sensor, int accuracy) {
-            mListener.onAccuracyChanged(sensor, accuracy);
-        }
-
-        @Override
-        public void onFlushCompleted(Sensor sensor) {
-            CountDownLatch latch = mFlushLatch;
-            mFlushLatch = new CountDownLatch(1);
-            if(latch != null) {
-                latch.countDown();
-            }
-            mListener.onFlushCompleted(sensor);
-        }
-
-        public void waitForFlushComplete() throws InterruptedException {
-            CountDownLatch latch = mFlushLatch;
-            if(latch != null) {
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "WaitForFlush",
-                        mSensorUnderTest,
-                        "" /* format */);
-                Assert.assertTrue(message, latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
-            }
-        }
-
-        public void waitForEvents(int eventCount, String timeoutInfo) {
-            mEventLatch = new CountDownLatch(eventCount);
-            this.clearEvents();
-            try {
-                boolean awaitCompleted = mEventLatch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
-                // TODO: can we collect bug reports on error based only if needed? env var?
-
-                String message = SensorCtsHelper.formatAssertionMessage(
-                        "WaitForEvents",
-                        mSensorUnderTest,
-                        "count:%d, available:%d, %s",
-                        eventCount,
-                        mSensorEventsList.size(),
-                        timeoutInfo);
-                Assert.assertTrue(message, awaitCompleted);
-            } catch(InterruptedException e) {
-            } finally {
-                mEventLatch = null;
-            }
-        }
-
-        public TestSensorEvent[] getAllEvents() {
-            return mSensorEventsList.toArray(new TestSensorEvent[0]);
-        }
-
-        public void clearEvents() {
-            mSensorEventsList.clear();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
new file mode 100644
index 0000000..1a500d4
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+/**
+ * Class used to store stats related to {@link ISensorOperation}s.  Sensor stats may be linked
+ * together so that they form a tree.
+ */
+public class SensorStats {
+    public static final String DELIMITER = "__";
+
+    public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
+    public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
+    public static final String ERROR = "error";
+    public static final String EVENT_COUNT_KEY = "event_count";
+    public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
+    public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
+    public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
+    public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+    public static final String FREQUENCY_KEY = "frequency";
+    public static final String JITTER_95_PERCENTILE_KEY = "jitter_95_percentile";
+    public static final String MEAN_KEY = "mean";
+    public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
+    public static final String MAGNITUDE_KEY = "magnitude";
+
+    private final Map<String, Object> mValues = new HashMap<String, Object>();
+    private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
+
+    /**
+     * Add a value.
+     *
+     * @param key the key.
+     * @param value the value as an {@link Object}.
+     */
+    public synchronized void addValue(String key, Object value) {
+        if (value == null) {
+            return;
+        }
+        mValues.put(key, value);
+    }
+
+    /**
+     * Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
+     * {@link ISensorOperation} tree.
+     *
+     * @param key the key
+     * @param stats the sub {@link SensorStats} object.
+     */
+    public synchronized void addSensorStats(String key, SensorStats stats) {
+        if (stats == null) {
+            return;
+        }
+        mSensorStats.put(key, stats);
+    }
+
+    /**
+     * Get the keys from the values table. Will not get the keys from the nested
+     * {@link SensorStats}.
+     */
+    public synchronized Set<String> getKeys() {
+        return mValues.keySet();
+    }
+
+    /**
+     * Get a value from the values table. Will not attempt to get values from nested
+     * {@link SensorStats}.
+     */
+    public synchronized Object getValue(String key) {
+        return mValues.get(key);
+    }
+
+    /**
+     * Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
+     * {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
+     * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
+     * will contain the entry {@code ("key1__key2", "value")}.
+     *
+     * @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
+     */
+    public synchronized Map<String, Object> flatten() {
+        final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+        for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
+            for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
+                String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
+                flattenedMap.put(key, valueEntry.getValue());
+            }
+        }
+        return flattenedMap;
+    }
+
+    /**
+     * Utility method to log the stats to the logcat.
+     */
+    public static void logStats(String tag, SensorStats stats) {
+        final Map<String, Object> flattened = stats.flatten();
+        for (String key : getSortedKeys(flattened)) {
+            Object value = flattened.get(key);
+            Log.v(tag, String.format("%s: %s", key, getValueString(value)));
+        }
+    }
+
+    /**
+     * Utility method to log the stats to a file. Will overwrite the file if it already exists.
+     */
+    public static void logStatsToFile(String fileName, SensorStats stats) throws IOException {
+        final BufferedWriter writer = new BufferedWriter(new FileWriter(
+                new File(Environment.getExternalStorageDirectory(), fileName), false));
+        final Map<String, Object> flattened = stats.flatten();
+        try {
+            for (String key : getSortedKeys(flattened)) {
+                Object value = flattened.get(key);
+                writer.write(String.format("%s: %s\n", key, getValueString(value)));
+            }
+        } finally {
+            writer.flush();
+            writer.close();
+        }
+    }
+
+    private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
+        List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+        Collections.sort(keys);
+        return keys;
+    }
+
+    private static String getValueString(Object value) {
+        if (value == null) {
+            return "";
+        } else if (value instanceof boolean[]) {
+            return Arrays.toString((boolean[]) value);
+        } else if (value instanceof byte[]) {
+            return Arrays.toString((byte[]) value);
+        } else if (value instanceof char[]) {
+            return Arrays.toString((char[]) value);
+        } else if (value instanceof double[]) {
+            return Arrays.toString((double[]) value);
+        } else if (value instanceof float[]) {
+            return Arrays.toString((float[]) value);
+        } else if (value instanceof int[]) {
+            return Arrays.toString((int[]) value);
+        } else if (value instanceof long[]) {
+            return Arrays.toString((long[]) value);
+        } else if (value instanceof short[]) {
+            return Arrays.toString((short[]) value);
+        } else if (value instanceof Object[]) {
+            return Arrays.toString((Object[]) value);
+        } else {
+            return value.toString();
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java
new file mode 100644
index 0000000..8ba0f41
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStatsTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import junit.framework.TestCase;
+
+import java.util.Map;
+
+/**
+ * Unit tests for the {@link SensorStats} class.
+ */
+public class SensorStatsTest extends TestCase {
+
+    /**
+     * Test that {@link SensorStats#flatten()} works correctly.
+     */
+    public void testFlatten() {
+        SensorStats stats = new SensorStats();
+        stats.addValue("value0", 0);
+        stats.addValue("value1", 1);
+
+        SensorStats subStats = new SensorStats();
+        subStats.addValue("value2", 2);
+        subStats.addValue("value3", 3);
+
+        SensorStats subSubStats = new SensorStats();
+        subSubStats.addValue("value4", 4);
+        subSubStats.addValue("value5", 5);
+
+        subStats.addSensorStats("stats1", subSubStats);
+        stats.addSensorStats("stats0", subStats);
+
+        // Add empty stats, expect no value in flattened map
+        stats.addSensorStats("stats2", new SensorStats());
+
+        // Add null values, expect no value in flattened map
+        stats.addSensorStats("stats3", null);
+        stats.addValue("value6", null);
+
+        Map<String, Object> flattened = stats.flatten();
+
+        assertEquals(6, flattened.size());
+        assertEquals(0, (int) (Integer) flattened.get("value0"));
+        assertEquals(1, (int) (Integer) flattened.get("value1"));
+        assertEquals(2, (int) (Integer) flattened.get("stats0__value2"));
+        assertEquals(3, (int) (Integer) flattened.get("stats0__value3"));
+        assertEquals(4, (int) (Integer) flattened.get("stats0__stats1__value4"));
+        assertEquals(5, (int) (Integer) flattened.get("stats0__stats1__value5"));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java
deleted file mode 100644
index 4bd0eed..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestCase.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import android.test.AndroidTestCase;
-
-import android.util.Log;
-
-/**
- * Test Case class that handles gracefully sensors that are not available in the device.
- */
-public abstract class SensorTestCase extends AndroidTestCase {
-    protected final String LOG_TAG = "TestRunner";
-
-    protected SensorTestCase() {}
-
-    @Override
-    public void runTest() throws Throwable {
-        try {
-            super.runTest();
-        } catch (SensorNotSupportedException e) {
-            // the sensor is not supported/available in the device, log a warning and skip the test
-            Log.w(LOG_TAG, e.getMessage());
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
index 90e0706..b220b00 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestInformation.java
@@ -16,137 +16,149 @@
 
 package android.hardware.cts.helpers;
 
-import android.content.Context;
-
 import android.hardware.Sensor;
 
-import java.security.InvalidParameterException;
-
 /**
  * A 'property' bag of sensor information used for testing purposes.
  */
+// TODO: Refactor this class and SensorCtsHelper into several more well defined helper classes
 public class SensorTestInformation {
     private SensorTestInformation() {}
 
-    public static int getAxisCount(int sensorType) {
+    public enum SensorReportingMode {
+        CONTINUOUS,
+        ON_CHANGE,
+        ONE_SHOT,
+    }
+
+    @SuppressWarnings("deprecation")
+    public static SensorReportingMode getReportingMode(int sensorType) {
         switch(sensorType) {
             case Sensor.TYPE_ACCELEROMETER:
-                return 3;
             case Sensor.TYPE_MAGNETIC_FIELD:
-                return 3;
-//            case Sensor.TYPE_ORIENTATION:
-//                return "Orientation";
+            case Sensor.TYPE_ORIENTATION:
             case Sensor.TYPE_GYROSCOPE:
-                return 3;
-//            case Sensor.TYPE_LIGHT:
-//                return "Light";
-//            case Sensor.TYPE_PRESSURE:
-//                return "Pressure";
-//            case Sensor.TYPE_TEMPERATURE:
-//                return "Temperature";
-//            case Sensor.TYPE_PROXIMITY:
-//                return "Proximity";
+            case Sensor.TYPE_PRESSURE:
             case Sensor.TYPE_GRAVITY:
-                return 3;
             case Sensor.TYPE_LINEAR_ACCELERATION:
-                return 3;
-//            case Sensor.TYPE_ROTATION_VECTOR:
-//                return "Rotation Vector";
-//            case Sensor.TYPE_RELATIVE_HUMIDITY:
-//                return "Relative Humidity";
-//            case Sensor.TYPE_AMBIENT_TEMPERATURE:
-//                return "Ambient Temperature";
-//            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-//                return "Magnetic Field Uncalibrated";
-//            case Sensor.TYPE_GAME_ROTATION_VECTOR:
-//                return "Game Rotation Vector";
-//            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
-//                return "Gyroscope Uncalibrated";
-//            case Sensor.TYPE_SIGNIFICANT_MOTION:
-//                return "Significant Motion";
-//            case Sensor.TYPE_STEP_DETECTOR:
-//                return "Step Detector";
-//            case Sensor.TYPE_STEP_COUNTER:
-//                return "Step Counter";
-//            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-//                return "Geomagnetic Rotation Vector";
+            case Sensor.TYPE_ROTATION_VECTOR:
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return SensorReportingMode.CONTINUOUS;
+            case Sensor.TYPE_LIGHT:
+            case Sensor.TYPE_TEMPERATURE:
+            case Sensor.TYPE_PROXIMITY:
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+            case Sensor.TYPE_STEP_DETECTOR:
+            case Sensor.TYPE_STEP_COUNTER:
+                return SensorReportingMode.ON_CHANGE;
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return SensorReportingMode.ONE_SHOT;
             default:
-                throw new InvalidParameterException(
-                        String.format("Invalid sensorType:%d. Unable to find axis count.", sensorType));
+                return null;
         }
     }
 
     public static String getSensorName(int sensorType) {
-        String name;
-        switch(sensorType) {
-            case Sensor.TYPE_ACCELEROMETER:
-                name = "Accelerometer";
-                break;
-            case Sensor.TYPE_MAGNETIC_FIELD:
-                name = "Magnetic Field";
-                break;
-            case Sensor.TYPE_ORIENTATION:
-                name = "Orientation";
-                break;
-            case Sensor.TYPE_GYROSCOPE:
-                name = "Gyroscope";
-                break;
-            case Sensor.TYPE_LIGHT:
-                name = "Light";
-                break;
-            case Sensor.TYPE_PRESSURE:
-                name = "Pressure";
-                break;
-            case Sensor.TYPE_TEMPERATURE:
-                name = "Temperature";
-                break;
-            case Sensor.TYPE_PROXIMITY:
-                name = "Proximity";
-                break;
-            case Sensor.TYPE_GRAVITY:
-                name = "Gravity";
-                break;
-            case Sensor.TYPE_LINEAR_ACCELERATION:
-                name = "Linear Acceleration";
-                break;
-            case Sensor.TYPE_ROTATION_VECTOR:
-                name = "Rotation Vector";
-                break;
-            case Sensor.TYPE_RELATIVE_HUMIDITY:
-                name = "Relative Humidity";
-                break;
-            case Sensor.TYPE_AMBIENT_TEMPERATURE:
-                name = "Ambient Temperature";
-                break;
-            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
-                name = "Magnetic Field Uncalibrated";
-                break;
-            case Sensor.TYPE_GAME_ROTATION_VECTOR:
-                name = "Game Rotation Vector";
-                break;
-            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
-                name = "Gyroscope Uncalibrated";
-                break;
-            case Sensor.TYPE_SIGNIFICANT_MOTION:
-                name = "Significant Motion";
-                break;
-            case Sensor.TYPE_STEP_DETECTOR:
-                name = "Step Detector";
-                break;
-            case Sensor.TYPE_STEP_COUNTER:
-                name = "Step Counter";
-                break;
-            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
-                name = "Geomagnetic Rotation Vector";
-                break;
-            default:
-                name = "<Unknown>";
-        }
-        return String.format("%s (%d)", name, sensorType);
+        return String.format("%s (%d)", getSimpleSensorName(sensorType), sensorType);
     }
 
-    public static int getMaxSamplingRateInUs(Context context, int sensorType) {
-        Sensor sensor = SensorCtsHelper.getSensor(context, sensorType);
-        return sensor.getMinDelay();
+    @SuppressWarnings("deprecation")
+    public static String getSimpleSensorName(int sensorType) {
+        switch(sensorType) {
+            case Sensor.TYPE_ACCELEROMETER:
+                return "Accelerometer";
+            case Sensor.TYPE_MAGNETIC_FIELD:
+                return "Magnetic Field";
+            case Sensor.TYPE_ORIENTATION:
+                return "Orientation";
+            case Sensor.TYPE_GYROSCOPE:
+                return "Gyroscope";
+            case Sensor.TYPE_LIGHT:
+                return "Light";
+            case Sensor.TYPE_PRESSURE:
+                return "Pressure";
+            case Sensor.TYPE_TEMPERATURE:
+                return "Temperature";
+            case Sensor.TYPE_PROXIMITY:
+                return "Proximity";
+            case Sensor.TYPE_GRAVITY:
+                return "Gravity";
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+                return "Linear Acceleration";
+            case Sensor.TYPE_ROTATION_VECTOR:
+                return "Rotation Vector";
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+                return "Relative Humidity";
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+                return "Ambient Temperature";
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+                return "Magnetic Field Uncalibrated";
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+                return "Game Rotation Vector";
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+                return "Gyroscope Uncalibrated";
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return "Significant Motion";
+            case Sensor.TYPE_STEP_DETECTOR:
+                return "Step Detector";
+            case Sensor.TYPE_STEP_COUNTER:
+                return "Step Counter";
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return "Geomagnetic Rotation Vector";
+            default:
+                return "<Unknown>";
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    public static String getSanitizedSensorName(int sensorType) {
+        switch(sensorType) {
+            case Sensor.TYPE_ACCELEROMETER:
+                return "Accelerometer";
+            case Sensor.TYPE_MAGNETIC_FIELD:
+                return "MagneticField";
+            case Sensor.TYPE_ORIENTATION:
+                return "Orientation";
+            case Sensor.TYPE_GYROSCOPE:
+                return "Gyroscope";
+            case Sensor.TYPE_LIGHT:
+                return "Light";
+            case Sensor.TYPE_PRESSURE:
+                return "Pressure";
+            case Sensor.TYPE_TEMPERATURE:
+                return "Temperature";
+            case Sensor.TYPE_PROXIMITY:
+                return "Proximity";
+            case Sensor.TYPE_GRAVITY:
+                return "Gravity";
+            case Sensor.TYPE_LINEAR_ACCELERATION:
+                return "LinearAcceleration";
+            case Sensor.TYPE_ROTATION_VECTOR:
+                return "RotationVector";
+            case Sensor.TYPE_RELATIVE_HUMIDITY:
+                return "RelativeHumidity";
+            case Sensor.TYPE_AMBIENT_TEMPERATURE:
+                return "AmbientTemperature";
+            case Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED:
+                return "MagneticFieldUncalibrated";
+            case Sensor.TYPE_GAME_ROTATION_VECTOR:
+                return "GameRotationVector";
+            case Sensor.TYPE_GYROSCOPE_UNCALIBRATED:
+                return "GyroscopeUncalibrated";
+            case Sensor.TYPE_SIGNIFICANT_MOTION:
+                return "SignificantMotion";
+            case Sensor.TYPE_STEP_DETECTOR:
+                return "StepDetector";
+            case Sensor.TYPE_STEP_COUNTER:
+                return "StepCounter";
+            case Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR:
+                return "GeomagneticRotationVector";
+            default:
+                return String.format("UnknownSensorType%d", sensorType);
+        }
     }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
deleted file mode 100644
index 902c802..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestOperation.java
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import junit.framework.Assert;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * Base test class that supports a basic test operation performed in a sensor.
- * The class follows a command patter as a base for its work.
- *
- * Remarks:
- * - The class wraps verifications and test checks that are needed to verify the operation.
- * - The operation runs in a background thread where it performs the bulk of its work.
- */
-public abstract class SensorTestOperation {
-    private final SensorTestExceptionHandler mExceptionHandler = new SensorTestExceptionHandler();
-
-    protected final String LOG_TAG = "TestRunner";
-    protected final long WAIT_TIMEOUT_IN_MILLISECONDS =
-            TimeUnit.MILLISECONDS.convert(5, TimeUnit.MINUTES);
-
-    private Thread mThread;
-
-    protected int mIterationCount;
-
-    /**
-     * Public API definition.
-     */
-    public synchronized void start() throws Throwable {
-        if(mThread != null) {
-            throw new IllegalStateException("The operation has already been started.");
-        }
-
-        mThread = new Thread() {
-            @Override
-            public void run() {
-                try {
-                    doWork();
-                } catch (Throwable e) {
-                    // log the exception so it can be sent back to the appropriate test thread
-                    this.getUncaughtExceptionHandler().uncaughtException(this, e);
-                }
-            }
-        };
-
-        ++mIterationCount;
-        mThread.setUncaughtExceptionHandler(mExceptionHandler);
-        mThread.start();
-    }
-
-    public synchronized void waitForCompletion() throws Throwable {
-        if(mThread == null) {
-            // let a wait on a stopped operation to be no-op
-            return;
-        }
-        mThread.join(WAIT_TIMEOUT_IN_MILLISECONDS);
-        if(mThread.isAlive()) {
-            // the test is hung so collect the state of the system and fail
-            String operationName = this.getClass().getSimpleName();
-            String message = String.format(
-                    "%s hung. %s. BugReport collected at: %s",
-                    operationName,
-                    this.toString(),
-                    SensorCtsHelper.collectBugreport(operationName));
-            Assert.fail(message);
-        }
-        mThread = null;
-        mExceptionHandler.rethrow();
-    }
-
-    public void execute() throws Throwable {
-        this.start();
-        this.waitForCompletion();
-    }
-
-    @Override
-    public String toString() {
-        return String.format("ThreadId:%d, Iteration:%d", mThread.getId(), mIterationCount);
-    }
-
-    /**
-     * Subclasses implement this method to perform the work associated with the operation they
-     * represent.
-     */
-    protected abstract void doWork() throws Throwable;
-
-    /**
-     * Private helpers.
-     */
-    private class SensorTestExceptionHandler implements Thread.UncaughtExceptionHandler {
-        private final Object mLock = new Object();
-
-        private Throwable mThrowable;
-
-        @Override
-        public void uncaughtException(Thread thread, Throwable throwable) {
-            synchronized(mLock) {
-                // the fist exception is in general the one that is more interesting
-                if(mThrowable != null) {
-                    return;
-                }
-                mThrowable = throwable;
-            }
-        }
-
-        public void rethrow() throws Throwable {
-            Throwable throwable;
-            synchronized(mLock) {
-                throwable = mThrowable;
-                mThrowable = null;
-            }
-            if(throwable != null) {
-                throw throwable;
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java
deleted file mode 100644
index d1013e0..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelper.java
+++ /dev/null
@@ -1,352 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.hardware.cts.helpers;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Set of static helper methods to verify sensor CTS tests.
- */
-public class SensorVerificationHelper {
-
-    private static final int MESSAGE_LENGTH = 3;
-
-    /**
-     * Class which holds results from the verification.
-     */
-    public static class VerificationResult {
-        private boolean mFailed = false;
-        private String mMessage = null;
-        private Map<String, Object> mValueMap = new HashMap<String, Object>();
-
-        public void fail(String messageFormat, Object ... args) {
-            mFailed = true;
-            mMessage = String.format(messageFormat, args);
-        }
-
-        public boolean isFailed() {
-            return mFailed;
-        }
-
-        public String getFailureMessage() {
-            return mMessage;
-        }
-
-        public void putValue(String key, Object value) {
-            mValueMap.put(key, value);
-        }
-
-        public Object getValue(String key) {
-            return mValueMap.get(key);
-        }
-    }
-
-    /**
-     * Private constructor for static class.
-     */
-    private SensorVerificationHelper() {}
-
-    /**
-     * Verify that the events are in the correct order.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @return a {@link VerificationResult} containing the verification info including the keys
-     *     "count" which is the number of events out of order and "positions" which contains an
-     *     array of indexes that were out of order.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyEventOrdering(TestSensorEvent[] events) {
-        VerificationResult result = new VerificationResult();
-        List<Integer> indices = new ArrayList<Integer>();
-        long maxTimestamp = events[0].timestamp;
-        for (int i = 1; i < events.length; i++) {
-            long currentTimestamp = events[i].timestamp;
-            if (currentTimestamp < maxTimestamp) {
-                indices.add(i);
-            } else if (currentTimestamp > maxTimestamp) {
-                maxTimestamp = currentTimestamp;
-            }
-        }
-
-        result.putValue("count", indices.size());
-        result.putValue("positions", indices);
-
-        if (indices.size() > 0) {
-            StringBuilder sb = new StringBuilder();
-            sb.append(indices.size()).append(" events out of order: ");
-            for (int i = 0; i < Math.min(indices.size(), MESSAGE_LENGTH); i++) {
-                int index = indices.get(i);
-                sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", index,
-                        events[index - 1].timestamp, events[index].timestamp));
-            }
-            if (indices.size() > MESSAGE_LENGTH) {
-                sb.append(indices.size() - MESSAGE_LENGTH).append(" more");
-            } else {
-                // Delete the "; "
-                sb.delete(sb.length() - 2, sb.length());
-            }
-
-            result.fail(sb.toString());
-        }
-
-        return result;
-    }
-
-    /**
-     * Verify that the sensor frequency matches the expected frequency.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The expected frequency in Hz
-     * @param threshold The acceptable margin of error in Hz
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "frequency" which is the computed frequency of the events in Hz.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyFrequency(TestSensorEvent[] events, double expected,
-            double threshold) {
-        VerificationResult result = new VerificationResult();
-        List<Long> timestampDelayValues = SensorCtsHelper.getTimestampDelayValues(events);
-        double frequency = SensorCtsHelper.getFrequency(
-                SensorCtsHelper.getMean(timestampDelayValues), TimeUnit.NANOSECONDS);
-        result.putValue("frequency", frequency);
-
-        if (Math.abs(frequency - expected) > threshold) {
-            result.fail("Frequency out of range: frequency=%.2fHz, expected=%.2f+/-%.2fHz",
-                    frequency, expected, threshold);
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the jitter is in an acceptable range
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param threshold The acceptable margin of error in nanoseconds
-     * @return a {@link VerificationResult} containing the verification info including the keys
-     *     "jitter" which is the list of computed jitter values and "jitter95Percentile" which is
-     *     95th percentile of the jitter values.
-     * @throws IllegalStateException if number of events less than 2.
-     */
-    public static VerificationResult verifyJitter(TestSensorEvent[] events, double threshold) {
-        VerificationResult result = new VerificationResult();
-        List<Double> jitterValues = SensorCtsHelper.getJitterValues(events);
-        double jitter95Percentile = SensorCtsHelper.get95PercentileValue(jitterValues);
-        result.putValue("jitter", jitterValues);
-        result.putValue("jitter95Percentile", jitter95Percentile);
-
-        if (jitter95Percentile > threshold) {
-            result.fail("Jitter out of range: jitter at 95th percentile=%.0fns, expected=<%.0fns",
-                    jitter95Percentile, threshold);
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the means matches the expected measurement.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The array of expected values
-     * @param threshold The array of thresholds
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "mean" which is the computed means for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyMean(TestSensorEvent[] events, double[] expected,
-            double[] threshold) {
-        VerificationResult result = new VerificationResult();
-        double[] means = SensorCtsHelper.getMeans(events);
-        result.putValue("means", means);
-
-        boolean failed = false;
-        StringBuilder meanSb = new StringBuilder();
-        StringBuilder expectedSb = new StringBuilder();
-
-        if (means.length > 1) {
-            meanSb.append("(");
-            expectedSb.append("(");
-        }
-        for (int i = 0; i < means.length && !failed; i++) {
-            if (Math.abs(means[i] - expected[i]) > threshold[i]) {
-                failed = true;
-            }
-            meanSb.append(String.format("%.2f", means[i]));
-            if (i != means.length - 1) meanSb.append(", ");
-            expectedSb.append(String.format("%.2f+/-%.2f", expected[i], threshold[i]));
-            if (i != means.length - 1) expectedSb.append(", ");
-        }
-        if (means.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Mean out of range: mean=%s, expected=%s",
-                    meanSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the mean of the magnitude of the sensors vector is within the expected range.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param expected The expected value
-     * @param threshold The threshold
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "magnitude" which is the mean of the computed magnitude of the sensor values.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyMagnitude(TestSensorEvent[] events, double expected,
-            double threshold) {
-        VerificationResult result = new VerificationResult();
-        Collection<Double> magnitudes = new ArrayList<Double>(events.length);
-
-        for (TestSensorEvent event : events) {
-            double norm = 0;
-            for (int i = 0; i < event.values.length; i++) {
-                norm += event.values[i] * event.values[i];
-            }
-            magnitudes.add(Math.sqrt(norm));
-        }
-
-        double mean = SensorCtsHelper.getMean(magnitudes);
-        result.putValue("magnitude", mean);
-
-        if (Math.abs(mean - expected) > threshold) {
-            result.fail(String.format("Magnitude mean out of range: mean=%s, expected=%s+/-%s",
-                    mean, expected, threshold));
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the sign of each of the sensor values is correct.
-     * <p>
-     * If the value of the measurement is in [-threshold, threshold], the sign is considered 0. If
-     * it is less than -threshold, it is considered -1. If it is greater than threshold, it is
-     * considered 1.
-     * </p>
-     *
-     * @param events
-     * @param threshold The threshold that needs to be crossed to consider a measurement nonzero
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "mean" which is the computed means for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifySignum(TestSensorEvent[] events, int[] expected,
-            double[] threshold) {
-        VerificationResult result = new VerificationResult();
-        for (int i = 0; i < expected.length; i++) {
-            if (!(expected[i] == -1 || expected[i] == 0 || expected[i] == 1)) {
-                throw new IllegalArgumentException("Expected value must be -1, 0, or 1");
-            }
-        }
-        double[] means = SensorCtsHelper.getMeans(events);
-        result.putValue("means", means);
-
-        boolean failed = false;
-        StringBuilder meanSb = new StringBuilder();
-        StringBuilder expectedSb = new StringBuilder();
-
-        if (means.length > 1) {
-            meanSb.append("(");
-            expectedSb.append("(");
-        }
-        for (int i = 0; i < means.length; i++) {
-            meanSb.append(String.format("%.2f", means[i]));
-            if (i != means.length - 1) meanSb.append(", ");
-
-            if (expected[i] == 0) {
-                if (Math.abs(means[i]) > threshold[i]) {
-                    failed = true;
-                }
-                expectedSb.append(String.format("[%.2f, %.2f]", -threshold[i], threshold[i]));
-            } else {
-                if (expected[i] > 0) {
-                    if (means[i] <= threshold[i]) {
-                        failed = true;
-                    }
-                    expectedSb.append(String.format("(%.2f, inf)", threshold[i]));
-                } else {
-                    if (means[i] >= -1 * threshold[i]) {
-                        failed = true;
-                    }
-                    expectedSb.append(String.format("(-inf, %.2f)", -1 * threshold[i]));
-                }
-            }
-            if (i != means.length - 1) expectedSb.append(", ");
-        }
-        if (means.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Signum out of range: mean=%s, expected=%s",
-                    meanSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-
-    /**
-     * Verify that the standard deviations is within the expected range.
-     *
-     * @param events The array of {@link TestSensorEvent}
-     * @param threshold The array of thresholds
-     * @return a {@link VerificationResult} containing the verification info including the key
-     *     "stddevs" which is the computed standard deviations for each value of the sensor.
-     * @throws IllegalStateException if number of events less than 1.
-     */
-    public static VerificationResult verifyStandardDeviation(TestSensorEvent[] events,
-            double[] threshold) {
-        VerificationResult result = new VerificationResult();
-        double[] standardDeviations = SensorCtsHelper.getStandardDeviations(events);
-        result.putValue("stddevs", standardDeviations);
-
-        boolean failed = false;
-        StringBuilder meanSb = new StringBuilder();
-        StringBuilder expectedSb = new StringBuilder();
-
-        if (standardDeviations.length > 1) {
-            meanSb.append("(");
-            expectedSb.append("(");
-        }
-        for (int i = 0; i < standardDeviations.length && !failed; i++) {
-            if (standardDeviations[i] > threshold[i]) {
-                failed = true;
-            }
-            meanSb.append(String.format("%.2f", standardDeviations[i]));
-            if (i != standardDeviations.length - 1) meanSb.append(", ");
-            expectedSb.append(String.format("0+/-%.2f", threshold[i]));
-            if (i != standardDeviations.length - 1) expectedSb.append(", ");
-        }
-        if (standardDeviations.length > 1) {
-            meanSb.append(")");
-            expectedSb.append(")");
-        }
-
-        if (failed) {
-            result.fail("Standard deviation out of range: mean=%s, expected=%s",
-                    meanSb.toString(), expectedSb.toString());
-        }
-        return result;
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java
deleted file mode 100644
index 875fa7f..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorVerificationHelperTest.java
+++ /dev/null
@@ -1,284 +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;
-
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-
-import junit.framework.TestCase;
-
-import java.util.List;
-
-/**
- * Unit tests for the {@link SensorVerificationHelper} class.
- */
-public class SensorVerificationHelperTest extends TestCase {
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyEventOrdering(TestSensorEvent[])}.
-     */
-    @SuppressWarnings("unchecked")
-    public void testVerifyEventOrdering() {
-        float[] values = {0, 1, 2, 3, 4};
-
-        long[] timestamps1 = {0, 0, 0, 0, 0};
-        TestSensorEvent[] events1 = getSensorEvents(timestamps1, values);
-        VerificationResult result = SensorVerificationHelper.verifyEventOrdering(events1);
-        assertFalse(result.isFailed());
-        assertEquals(0, result.getValue("count"));
-
-        long[] timestamps2 = {0, 1, 2, 3, 4};
-        TestSensorEvent[] events2 = getSensorEvents(timestamps2, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events2);
-        assertFalse(result.isFailed());
-        assertEquals(0, result.getValue("count"));
-
-        long[] timestamps3 = {0, 2, 1, 3, 4};
-        TestSensorEvent[] events3 = getSensorEvents(timestamps3, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events3);
-        assertTrue(result.isFailed());
-        assertEquals(1, result.getValue("count"));
-        List<Integer> indices = (List<Integer>) result.getValue("positions");
-        assertTrue(indices.contains(2));
-
-        long[] timestamps4 = {4, 0, 1, 2, 3};
-        TestSensorEvent[] events4 = getSensorEvents(timestamps4, values);
-        result = SensorVerificationHelper.verifyEventOrdering(events4);
-        assertTrue(result.isFailed());
-        assertEquals(4, result.getValue("count"));
-        indices = (List<Integer>) result.getValue("positions");
-        assertTrue(indices.contains(1));
-        assertTrue(indices.contains(2));
-        assertTrue(indices.contains(3));
-        assertTrue(indices.contains(4));
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyFrequency(TestSensorEvent[], double, double)}.
-     */
-    public void testVerifyFrequency() {
-        float[] values = {0, 1, 2, 3, 4};
-        long[] timestamps = {0, 1000000, 2000000, 3000000, 4000000};  // 1000Hz
-        TestSensorEvent[] events = getSensorEvents(timestamps, values);
-
-        VerificationResult result = SensorVerificationHelper.verifyFrequency(events, 1000.0, 1.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 950.0, 100.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 1050.0, 100.0);
-        assertFalse(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-
-        result = SensorVerificationHelper.verifyFrequency(events, 950.0, 25.0);
-        assertTrue(result.isFailed());
-        assertEquals(1000.0, (Double) result.getValue("frequency"), 0.01);
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyJitter(TestSensorEvent[], double)}.
-     */
-    public void testVerifyJitter() {
-        final int SAMPLE_SIZE = 100;
-        float[] values = new float[SAMPLE_SIZE];
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            values[i] = i;
-        }
-
-        long[] timestamps1 = new long[SAMPLE_SIZE];  // 100 samples at 1000Hz
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            timestamps1[i] = i * 100000;
-        }
-        TestSensorEvent[] events1 = getSensorEvents(timestamps1, values);
-        VerificationResult result = SensorVerificationHelper.verifyJitter(events1, 100000);
-        assertFalse(result.isFailed());
-        assertEquals(0.0, (Double) result.getValue("jitter95Percentile"), 0.01);
-
-        long[] timestamps2 = new long[SAMPLE_SIZE];  // 90 samples at 1000Hz, 10 samples at 2000Hz
-        long timestamp = 0;
-        for (int i = 0; i < SAMPLE_SIZE; i++) {
-            timestamps2[i] = timestamp;
-            timestamp += (i % 10 == 0) ? 500000 : 1000000;
-        }
-        TestSensorEvent[] events2 = getSensorEvents(timestamps2, values);
-        result = SensorVerificationHelper.verifyJitter(events2, 100000);
-        assertTrue(result.isFailed());
-        assertNotNull(result.getValue("jitter"));
-        assertNotNull(result.getValue("jitter95Percentile"));
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyMean(TestSensorEvent[], double[], double[])}.
-     */
-    public void testVerifyMean() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 1, 2, 3, 4};
-        float[] values2 = {1, 2, 3, 4, 5};
-        float[] values3 = {0, 1, 4, 9, 16};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        double[] expected1 = {2.0, 3.0, 6.0};
-        double[] threshold1 = {0.1, 0.1, 0.1};
-        VerificationResult result = SensorVerificationHelper.verifyMean(events, expected1,
-                threshold1);
-        assertFalse(result.isFailed());
-        double[] means = (double[]) result.getValue("means");
-        assertEquals(2.0, means[0], 0.01);
-        assertEquals(3.0, means[1], 0.01);
-        assertEquals(6.0, means[2], 0.01);
-
-        double[] expected2 = {2.5, 2.5, 5.5};
-        double[] threshold2 = {0.6, 0.6, 0.6};
-        result = SensorVerificationHelper.verifyMean(events, expected2, threshold2);
-        assertFalse(result.isFailed());
-
-        double[] expected3 = {2.5, 2.5, 5.5};
-        double[] threshold3 = {0.1, 0.6, 0.6};
-        result = SensorVerificationHelper.verifyMean(events, expected3, threshold3);
-        assertTrue(result.isFailed());
-
-        double[] expected4 = {2.5, 2.5, 5.5};
-        double[] threshold4 = {0.6, 0.1, 0.6};
-        result = SensorVerificationHelper.verifyMean(events, expected4, threshold4);
-        assertTrue(result.isFailed());
-
-        double[] expected5 = {2.5, 2.5, 5.5};
-        double[] threshold5 = {0.6, 0.6, 0.1};
-        result = SensorVerificationHelper.verifyMean(events, expected5, threshold5);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyMagnitude(TestSensorEvent[], double, double)}.
-     */
-    public void testVerifyMagnitude() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 4, 3, 0, 6};
-        float[] values2 = {3, 0, 4, 0, 0};
-        float[] values3 = {4, 3, 0, 4, 0};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        double expected = 5.0;
-        double threshold = 0.1;
-        VerificationResult result = SensorVerificationHelper.verifyMagnitude(events, expected,
-                threshold);
-        assertFalse(result.isFailed());
-        assertEquals(5.0, (Double) result.getValue("magnitude"), 0.01);
-
-        expected = 4.5;
-        threshold = 0.6;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = 5.5;
-        threshold = 0.6;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertFalse(result.isFailed());
-
-        expected = 4.5;
-        threshold = 0.1;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertTrue(result.isFailed());
-
-        expected = 5.5;
-        threshold = 0.1;
-        result = SensorVerificationHelper.verifyMagnitude(events, expected, threshold);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifySignum(TestSensorEvent[], int[], double[])}.
-     */
-    public void testVerifySignum() {
-        long[] timestamps = {0};
-        float[][] values = {{1}, {0.2f}, {0}, {-0.2f}, {-1}};
-        TestSensorEvent[] events = getSensorEvents(timestamps, values);
-
-        int[] expected1 = {1, 1, 0, -1, -1};
-        double[] threshold1 = {0.1, 0.1, 0.1, 0.1, 0.1};
-        VerificationResult result = SensorVerificationHelper.verifySignum(events, expected1,
-                threshold1);
-        assertFalse(result.isFailed());
-        assertNotNull(result.getValue("means"));
-
-        int[] expected2 = {1, 0, 0, 0, -1};
-        double[] threshold2 = {0.5, 0.5, 0.5, 0.5, 0.5};
-        result = SensorVerificationHelper.verifySignum(events, expected2, threshold2);
-        assertFalse(result.isFailed());
-
-        int[] expected3 = {0, 1, 0, -1, 0};
-        double[] threshold3 = {1.5, 0.1, 0.1, 0.1, 1.5};
-        result = SensorVerificationHelper.verifySignum(events, expected3, threshold3);
-        assertFalse(result.isFailed());
-
-        int[] expected4 = {1, 0, 0, 0, 1};
-        double[] threshold4 = {0.5, 0.5, 0.5, 0.5, 0.5};
-        result = SensorVerificationHelper.verifySignum(events, expected4, threshold4);
-        assertTrue(result.isFailed());
-
-        int[] expected5 = {-1, 0, 0, 0, -1};
-        double[] threshold5 = {0.5, 0.5, 0.5, 0.5, 0.5};
-        result = SensorVerificationHelper.verifySignum(events, expected5, threshold5);
-        assertTrue(result.isFailed());
-    }
-
-    /**
-     * Test {@link SensorVerificationHelper#verifyStandardDeviation(TestSensorEvent[], double[])}.
-     */
-    public void testVerifyStandardDeviation() {
-        long[] timestamps = {0, 1, 2, 3, 4};
-        float[] values1 = {0, 1, 2, 3, 4};  // sqrt(2.0)
-        float[] values2 = {1, 2, 3, 4, 5};  // sqrt(2.0)
-        float[] values3 = {0, 2, 4, 6, 8};  // sqrt(8.0)
-        TestSensorEvent[] events = getSensorEvents(timestamps, values1, values2, values3);
-
-        double[] threshold1 = {2, 2, 3};
-        VerificationResult result = SensorVerificationHelper.verifyStandardDeviation(events,
-                threshold1);
-        assertFalse(result.isFailed());
-        double[] means = (double[]) result.getValue("stddevs");
-        assertEquals(Math.sqrt(2.0), means[0], 0.01);
-        assertEquals(Math.sqrt(2.0), means[1], 0.01);
-        assertEquals(Math.sqrt(8.0), means[2], 0.01);
-
-        double[] threshold2 = {1, 2, 3};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold2);
-        assertTrue(result.isFailed());
-
-        double[] threshold3 = {2, 1, 3};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold3);
-        assertTrue(result.isFailed());
-
-        double[] threshold4 = {2, 2, 2};
-        result = SensorVerificationHelper.verifyStandardDeviation(events, threshold4);
-        assertTrue(result.isFailed());
-    }
-
-    private TestSensorEvent[] getSensorEvents(long[] timestamps, float[] ... values) {
-        TestSensorEvent[] events = new TestSensorEvent[timestamps.length];
-        for (int i = 0; i < timestamps.length; i++) {
-            float[] eventValues = new float[values.length];
-            for (int j = 0; j < values.length; j++) {
-                eventValues[j] = values[j][i];
-            }
-            events[i] = new TestSensorEvent(null, timestamps[i], 0, eventValues);
-        }
-        return events;
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
index 48bc1d3..b349e1b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -18,33 +18,51 @@
 
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
 
 /**
- * Test class to wrap SensorEvent.
- * It currently only provides a way to clone SensorEvent data, but in the future it can contain
- * verifications and test checks.
+ * Class for holding information about individual {@link SensorEvent}s.
  */
 public class TestSensorEvent {
     public final Sensor sensor;
     public final long timestamp;
+    public final long receivedTimestamp;
     public final int accuracy;
     public final float values[];
 
-    public TestSensorEvent(SensorEvent event) {
+    /**
+     * Construct a TestSensorEvent from {@link SensorEvent} data and a received timestamp.
+     *
+     * @param event the {@link SensorEvent} to be cloned
+     * @param receivedTimestamp the timestamp when
+     * {@link SensorEventListener2#onSensorChanged(SensorEvent)} was called, in nanoseconds.
+     */
+    public TestSensorEvent(SensorEvent event, long receivedTimestamp) {
         values = new float[event.values.length];
-        System.arraycopy(event.values, 0, values, 0, event.values.length);
+        System.arraycopy(event.values, 0, values, 0, values.length);
 
         sensor = event.sensor;
         timestamp = event.timestamp;
         accuracy = event.accuracy;
+
+        this.receivedTimestamp = receivedTimestamp;
     }
 
     /**
      * Constructor for TestSensorEvent. Exposed for unit testing.
      */
-    protected TestSensorEvent(Sensor sensor, long timestamp, int accuracy, float[] values) {
+    public TestSensorEvent(Sensor sensor, long timestamp, int accuracy, float[] values) {
+        this(sensor, timestamp, timestamp, accuracy, values);
+    }
+
+    /**
+     * Constructor for TestSensorEvent. Exposed for unit testing.
+     */
+    public TestSensorEvent(Sensor sensor, long timestamp, long receivedTimestamp, int accuracy,
+            float[] values) {
         this.sensor = sensor;
         this.timestamp = timestamp;
+        this.receivedTimestamp = receivedTimestamp;
         this.accuracy = accuracy;
         this.values = values;
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
new file mode 100644
index 0000000..ddbc8c2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link SensorEventListener2} which performs operations such as waiting for a specific number of
+ * events or for a specific time, or waiting for a flush to complete. This class performs
+ * verifications and will throw {@link AssertionError}s if there are any errors. It may also wrap
+ * another {@link SensorEventListener2}.
+ */
+public class TestSensorEventListener implements SensorEventListener2 {
+    public static final String LOG_TAG = "TestSensorEventListener";
+    private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+    private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+
+    private final SensorEventListener2 mListener;
+
+    private volatile CountDownLatch mEventLatch = null;
+    private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
+
+    private Sensor mSensor = null;
+    private int mRateUs = 0;
+    private int mMaxBatchReportLatencyUs = 0;
+    private boolean mLogEvents = false;
+
+    /**
+     * Construct a {@link TestSensorEventListener}.
+     */
+    public TestSensorEventListener() {
+        this(null);
+    }
+
+    /**
+     * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
+     */
+    public TestSensorEventListener(SensorEventListener2 listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Set the sensor, rate, and batch report latency used for the assertions.
+     */
+    public void setSensorInfo(Sensor sensor, int rateUs, int maxBatchReportLatencyUs) {
+        mSensor = sensor;
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+    }
+
+    /**
+     * Set whether or not to log events
+     */
+    public void setLogEvents(boolean log) {
+        mLogEvents = log;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        if(mEventLatch != null) {
+            mEventLatch.countDown();
+        }
+        if (mListener != null) {
+            mListener.onSensorChanged(event);
+        }
+        if (mLogEvents) {
+            StringBuilder valuesSb = new StringBuilder();
+            if (event.values.length == 1) {
+                valuesSb.append(String.format("%.2f", event.values[0]));
+            } else {
+                valuesSb.append("[").append(String.format("%.2f", event.values[0]));
+                for (int i = 1; i < event.values.length; i++) {
+                    valuesSb.append(String.format(", %.2f", event.values[i]));
+                }
+                valuesSb.append("]");
+            }
+
+            Log.v(LOG_TAG, String.format(
+                    "Sensor %d: sensor_timestamp=%d, received_timestamp=%d, values=%s",
+                    mSensor.getType(), event.timestamp, System.nanoTime(),
+                    Arrays.toString(event.values)));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onAccuracyChanged(Sensor sensor, int accuracy) {
+        if (mListener != null) {
+            mListener.onAccuracyChanged(sensor, accuracy);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onFlushCompleted(Sensor sensor) {
+        CountDownLatch latch = mFlushLatch;
+        mFlushLatch = new CountDownLatch(1);
+        if(latch != null) {
+            latch.countDown();
+        }
+        if (mListener != null) {
+            mListener.onFlushCompleted(sensor);
+        }
+    }
+
+    /**
+     * Wait for {@link #onFlushCompleted(Sensor)} to be called.
+     *
+     * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
+     */
+    public void waitForFlushComplete() {
+        CountDownLatch latch = mFlushLatch;
+        try {
+            if(latch != null) {
+                String message = SensorCtsHelper.formatAssertionMessage(mSensor, "WaitForFlush",
+                        mRateUs, mMaxBatchReportLatencyUs);
+                Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+            }
+        } catch(InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Collect a specific number of {@link TestSensorEvent}s.
+     *
+     * @throws AssertionError if there was a timeout after {@value #FLUSH_TIMEOUT_US} &micro;s
+     */
+    public void waitForEvents(int eventCount) {
+        mEventLatch = new CountDownLatch(eventCount);
+        try {
+            int rateUs = SensorCtsHelper.getDelay(mSensor, mRateUs);
+            // Timeout is 2 * event count * expected period + batch timeout + default wait
+            long timeoutUs = ((2 * eventCount * rateUs)
+                    + mMaxBatchReportLatencyUs + EVENT_TIMEOUT_US);
+
+            String message = SensorCtsHelper.formatAssertionMessage(mSensor, "WaitForEvents",
+                    mRateUs, mMaxBatchReportLatencyUs, "count:%d, available:%d", eventCount,
+                    mEventLatch.getCount());
+            Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+        } catch(InterruptedException e) {
+            // Ignore
+        } finally {
+            mEventLatch = null;
+        }
+    }
+
+    /**
+     * Collect {@link TestSensorEvent} for a specific duration.
+     */
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        SensorCtsHelper.sleep(duration, timeUnit);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
new file mode 100644
index 0000000..a45ad70
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorEventListener2;
+import android.hardware.SensorManager;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A test class that performs the actions of {@link SensorManager} on a single sensor. This
+ * class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events. This class also manages
+ * performing the test verifications for the sensor manager.
+ * <p>
+ * This class requires that operations are performed in the following order:
+ * <p><ul>
+ * <li>{@link #registerListener(TestSensorEventListener)}</li>
+ * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
+ * <li>{@link #unregisterListener()}</li>
+ * </ul><p>Or:</p><ul>
+ * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
+ * </ul><p>Or:</p><ul>
+ * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
+ * </ul><p>
+ * If methods are called outside of this order, they will print a warning to the log and then
+ * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
+ * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
+ * set up and tear down.
+ * <p>
+ */
+public class TestSensorManager {
+    private static final String LOG_TAG = "TestSensorManager";
+
+    private final SensorManager mSensorManager;
+    private final Sensor mSensor;
+    private final int mRateUs;
+    private final int mMaxBatchReportLatencyUs;
+
+    private TestSensorEventListener mTestSensorEventListener = null;
+
+    /**
+     * Construct a {@link TestSensorManager}.
+     */
+    public TestSensorManager(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs) {
+        mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+        mSensor = SensorCtsHelper.getSensor(context, sensorType);
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+    }
+
+    /**
+     * Register the listener. This method will perform a no-op if the sensor is already registered.
+     *
+     * @throws AssertionError if there was an error registering the listener with the
+     * {@link SensorManager}
+     */
+    public void registerListener(TestSensorEventListener listener) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
+        mTestSensorEventListener.setSensorInfo(mSensor, mRateUs, mMaxBatchReportLatencyUs);
+
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "registerListener",
+                mRateUs, mMaxBatchReportLatencyUs);
+        boolean result = mSensorManager.registerListener(mTestSensorEventListener, mSensor, mRateUs,
+                mMaxBatchReportLatencyUs);
+        Assert.assertTrue(message, result);
+    }
+
+    /**
+     * Unregister the listener. This method will perform a no-op if the sensor is not registered.
+     */
+    public void unregisterListener() {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        mSensorManager.unregisterListener(mTestSensorEventListener, mSensor);
+        mTestSensorEventListener = null;
+    }
+
+    /**
+     * Wait for a specific number of events.
+     */
+    public void waitForEvents(int eventCount) {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        mTestSensorEventListener.waitForEvents(eventCount);
+    }
+
+    /**
+     * Wait for a specific duration.
+     */
+    public void waitForEvents(long duration, TimeUnit timeUnit) {
+        if (mTestSensorEventListener == null) {
+            Log.w(LOG_TAG, "No listener registered, returning.");
+            return;
+        }
+
+        mTestSensorEventListener.waitForEvents(duration, timeUnit);
+    }
+
+    /**
+     * Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
+     * the sensor is not registered.
+     *
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+     */
+    public void startFlush() {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        String message = SensorCtsHelper.formatAssertionMessage(mSensor, "Flush", mRateUs,
+                mMaxBatchReportLatencyUs);
+        Assert.assertTrue(message, mSensorManager.flush(mTestSensorEventListener));
+    }
+
+    /**
+     * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
+     * perform a no-op if the sensor is not registered.
+     *
+     * @throws AssertionError if there is a time out
+     * @throws InterruptedException if the thread was interrupted
+     */
+    public void waitForFlushCompleted() throws InterruptedException {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        mTestSensorEventListener.waitForFlushComplete();
+    }
+
+    /**
+     * Call {@link SensorManager#flush(SensorEventListener)} and wait for
+     * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
+     * a no-op if the sensor is not registered.
+     *
+     * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
+     * if there is a time out
+     * @throws InterruptedException if the thread was interrupted
+     */
+    public void flush() throws InterruptedException {
+        if (mTestSensorEventListener == null) {
+            return;
+        }
+
+        startFlush();
+        waitForFlushCompleted();
+    }
+
+    /**
+     * Register a listener, wait for a specific number of events, and then unregister the listener.
+     */
+    public void runSensor(TestSensorEventListener listener, int eventCount) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        try {
+            registerListener(listener);
+            waitForEvents(eventCount);
+        } finally {
+            unregisterListener();
+        }
+    }
+
+    /**
+     * Register a listener, wait for a specific duration, and then unregister the listener.
+     */
+    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit) {
+        if (mTestSensorEventListener != null) {
+            Log.w(LOG_TAG, "Listener already registered, returning.");
+            return;
+        }
+
+        try {
+            registerListener(listener);
+            waitForEvents(duration, timeUnit);
+        } finally {
+            unregisterListener();
+        }
+    }
+
+    /**
+     * Get the sensor under test.
+     */
+    public Sensor getSensor() {
+        return mSensor;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
new file mode 100644
index 0000000..ae7ea04
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers;
+
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.os.SystemClock;
+
+import java.util.Collection;
+import java.util.LinkedList;
+
+/**
+ * A {@link TestSensorEventListener} which performs validations on the received events on the fly.
+ * This class is useful for long running tests where it is not practical to store all the events to
+ * be processed after.
+ */
+public class ValidatingSensorEventListener extends TestSensorEventListener {
+
+    private final Collection<ISensorVerification> mVerifications =
+            new LinkedList<ISensorVerification>();
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public ValidatingSensorEventListener(SensorEventListener2 listener,
+            ISensorVerification ... verifications) {
+        super(listener);
+        for (ISensorVerification verification : verifications) {
+            mVerifications.add(verification);
+        }
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener} with an additional
+     * {@link SensorEventListener2}.
+     */
+    public ValidatingSensorEventListener(SensorEventListener2 listener,
+            Collection<ISensorVerification> verifications) {
+        this(listener, verifications.toArray(new ISensorVerification[0]));
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener}.
+     */
+    public ValidatingSensorEventListener(ISensorVerification ... verifications) {
+        this(null, verifications);
+    }
+
+    /**
+     * Construct a {@link ValidatingSensorEventListener}.
+     */
+    public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
+        this(null, verifications);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void onSensorChanged(SensorEvent event) {
+        TestSensorEvent testEvent = new TestSensorEvent(event, SystemClock.elapsedRealtimeNanos());
+        for (ISensorVerification verification : mVerifications) {
+            verification.addSensorEvent(testEvent);
+        }
+        super.onSensorChanged(event);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
deleted file mode 100644
index 3730f4b..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/ParallelCompositeSensorTestOperation.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-import java.util.ArrayList;
-
-/**
- * A test operation that groups a set of SensorTestOperations and allows to execute them all in
- * parallel.
- * This class can be combined to compose other primitive SensorTestOperations.
- */
-public class ParallelCompositeSensorTestOperation extends SensorTestOperation {
-    private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
-
-    /**
-     * There is no synchronization
-     * @param operations
-     */
-    public void add(SensorTestOperation ... operations) {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : operations) {
-                mOperations.add(operation);
-            }
-        }
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : mOperations) {
-                operation.start();
-            }
-            for(SensorTestOperation operation : mOperations) {
-                operation.waitForCompletion();
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
deleted file mode 100644
index 7a3c450..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/RepeatingSensorTestOperation.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-/**
- * High level SensorTestOperation that executes the inner operation in a loop.
- */
-public class RepeatingSensorTestOperation extends SensorTestOperation {
-    private final SensorTestOperation mSensorTestOperation;
-    private final int mRepetitionCount;
-
-    public RepeatingSensorTestOperation(SensorTestOperation operation, int repetitionCount) {
-        mSensorTestOperation = operation;
-        mRepetitionCount = repetitionCount;
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        for(int i = 0; i < mRepetitionCount; ++i) {
-            mSensorTestOperation.execute();
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
deleted file mode 100644
index 4b92168..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/SequentialCompositeSensorTestOperation.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.hardware.cts.helpers.SensorTestOperation;
-
-import java.util.ArrayList;
-
-/**
- * A test operation that groups a set of SensorTestOperations and allows to execute them in a
- * sequence, each operation executes in the order they are added to the composite container.
- * This class can be combined to compose other primitive SensorTestOperations.
- */
-public class SequentialCompositeSensorTestOperation extends SensorTestOperation {
-    private final ArrayList<SensorTestOperation> mOperations = new ArrayList<SensorTestOperation>();
-
-    /**
-     * There is no synchronization
-     * @param operations
-     */
-    public void add(SensorTestOperation ... operations) {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : operations) {
-                mOperations.add(operation);
-            }
-        }
-    }
-
-    @Override
-    protected void doWork() throws Throwable {
-        synchronized (mOperations) {
-            for(SensorTestOperation operation : mOperations) {
-                operation.execute();
-            }
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
deleted file mode 100644
index 03d0f9a..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyEventOrderingOperation.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-/**
- * Test Operation class that validates the ordering of sensor events.
- */
-public class VerifyEventOrderingOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-
-    public VerifyEventOrderingOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifyEventOrdering(events);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Ordering",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
deleted file mode 100644
index 303dc9b..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyJitteringOperation.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-import java.security.InvalidParameterException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test Operation class that validates the sampling rate jittering of a given sensor.
- *
- * Remarks:
- * - In order to guarantee proper results in any environment, the maximum sampling rate supported by
- *   the Sensor is used, this guarantees the frequency reference for the test.
- */
-public class VerifyJitteringOperation extends SensorTestOperation {
-    protected SensorManagerTestVerifier mSensor;
-    protected long mExpectedtimestampInNs;
-    protected long mThresholdPercentage;
-    protected long mThresholdInNs;
-
-    public VerifyJitteringOperation(
-            Context context,
-            int sensorType,
-            int reportLatencyInUs,
-            int thresholdPercentageOfNs) throws InvalidParameterException {
-        if(thresholdPercentageOfNs < 0) {
-            throw new InvalidParameterException("thresholdPercentageOfNs needs to be >= 0");
-        }
-        // use the max sampling frequency the sensor reports to guarantee the results
-        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                maxSamplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mExpectedtimestampInNs = TimeUnit.NANOSECONDS.convert(
-                maxSamplingRateInUs,
-                TimeUnit.MICROSECONDS);
-        mThresholdPercentage = thresholdPercentageOfNs;
-        mThresholdInNs = mExpectedtimestampInNs / mThresholdPercentage;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifyJitter(events, mThresholdInNs);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Jitter(95%ile)",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMagnitudeOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMagnitudeOperation.java
deleted file mode 100644
index cbcff6a..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMagnitudeOperation.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-/**
- * Test Operation class that validates the norm of a given sensor.
- * The operation relies in the number of axes each sensor type reports.
- */
-public class VerifyMagnitudeOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-    private int mAxisCount;
-    private double mReferenceValue;
-    private double mThreshold;
-
-    public VerifyMagnitudeOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            float referenceValue,
-            float threshold) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                0 /*reportLatencyInUs*/);
-        // set expectations
-        mAxisCount = SensorTestInformation.getAxisCount(mSensor.getUnderlyingSensor().getType());
-        mReferenceValue = referenceValue;
-        mThreshold = threshold;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(1);
-        VerificationResult result = SensorVerificationHelper.verifyMagnitude(events, mReferenceValue,
-                mThreshold);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Norm",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
deleted file mode 100644
index 0af15a2..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMaximumFrequencyOperation.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-import java.security.InvalidParameterException;
-import java.util.concurrent.TimeUnit;
-
-/**
- * Test Operation class that validates the max sampling rate of a given sensor.
- *
- * Remarks:
- * - In order to guarantee proper results in any environment, the maximum sampling rate supported by
- *   the Sensor is used, this guarantees the frequency reference for the test.
- */
-public class VerifyMaximumFrequencyOperation extends SensorTestOperation {
-    protected SensorManagerTestVerifier mSensor;
-    protected long mExpectedTimestampInNs;
-    protected long mThresholdPercentage;
-    protected long mThresholdInNs;
-
-    public VerifyMaximumFrequencyOperation(
-            Context context,
-            int sensorType,
-            int reportLatencyInUs,
-            int thresholdPercentageOfNs) throws InvalidParameterException {
-        if(thresholdPercentageOfNs < 0) {
-            throw new InvalidParameterException("thresholdPercentageOfNs needs to be >= 0");
-        }
-        // use the max sampling frequency the sensor reports to guarantee the results
-        int maxSamplingRateInUs = SensorTestInformation.getMaxSamplingRateInUs(context, sensorType);
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                maxSamplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mExpectedTimestampInNs = TimeUnit.NANOSECONDS.convert(
-                maxSamplingRateInUs,
-                TimeUnit.MICROSECONDS);
-        mThresholdPercentage = thresholdPercentageOfNs;
-        mThresholdInNs = mExpectedTimestampInNs / mThresholdPercentage;
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifyFrequency(events,
-                mExpectedTimestampInNs, mThresholdInNs);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Frequency",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java
deleted file mode 100644
index 2368eb4..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyMeasurementsOperation.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-import java.security.InvalidParameterException;
-
-/**
- * Test Operation class that validates the measurements of a a given sensor.
- * The operation relies on the number of axes each sensor type reports.
- * The verification calculates the mean for each axis on the measurements, and verifies that they
- * fall into the expected intervals.
- */
-public class VerifyMeasurementsOperation extends SensorTestOperation {
-    private final SensorManagerTestVerifier mSensor;
-    private final int mAxisCount;
-    private final double[] mReferenceValues;
-    private final double[] mThreshold;
-
-    public VerifyMeasurementsOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs,
-            double referenceValues[],
-            float threshold) {
-        mAxisCount = SensorTestInformation.getAxisCount(sensorType);
-        if(mAxisCount != referenceValues.length) {
-            throw new InvalidParameterException(
-                    String.format("%d reference values are expected.", mAxisCount));
-        }
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mReferenceValues = referenceValues;
-        mThreshold = new double[mAxisCount];
-        for (int i = 0; i < mThreshold.length; i++) {
-            mThreshold[i] = threshold;
-        }
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifyMean(events, mReferenceValues,
-                mThreshold);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Measurement",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java
deleted file mode 100644
index f58baa1..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifySignumOperation.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-import java.security.InvalidParameterException;
-
-/**
- * Test Operation class that validates the sign of measurements of a a given sensor.
- * The operation relies in the number of axes each sensor type reports.
- */
-public class VerifySignumOperation extends SensorTestOperation {
-    private final SensorManagerTestVerifier mSensor;
-    private final int mAxisCount;
-    private final int mReferenceValues[];
-    private final double mNoiseThresholds[];
-
-    /**
-     * @param noiseThreshold Defines the threshold that needs to be crossed to consider a
-     *                       measurement different from zero
-     */
-    public VerifySignumOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int referenceValues[],
-            double noiseThreshold) {
-        mAxisCount = SensorTestInformation.getAxisCount(sensorType);
-        if(mAxisCount != referenceValues.length) {
-            throw new InvalidParameterException(
-                    String.format("%d reference values are expected.", mAxisCount));
-        }
-        for(int i = 0; i < referenceValues.length; ++i) {
-            int value = referenceValues[i];
-            if(value != 0 && value != -1 && value != +1) {
-                throw new InvalidParameterException(
-                        "A ReferenceValue can only be one of the following: -1, 0, +1");
-            }
-        }
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                0 /*reportLatencyInUs*/);
-        // set expectations
-        mReferenceValues = referenceValues;
-        mNoiseThresholds = new double[mReferenceValues.length];
-        for (int i = 0; i < mNoiseThresholds.length; i++) {
-            mNoiseThresholds[i] = noiseThreshold;
-        }
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifySignum(events, mReferenceValues,
-                mNoiseThresholds);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "Measurement",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
deleted file mode 100644
index 05b92e0..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorTestOperations/VerifyStandardDeviationOperation.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensorTestOperations;
-
-import android.content.Context;
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorManagerTestVerifier;
-import android.hardware.cts.helpers.SensorTestInformation;
-import android.hardware.cts.helpers.SensorTestOperation;
-import android.hardware.cts.helpers.SensorVerificationHelper;
-import android.hardware.cts.helpers.SensorVerificationHelper.VerificationResult;
-import android.hardware.cts.helpers.TestSensorEvent;
-
-import junit.framework.Assert;
-
-/**
- * Test Operation class that validates the standard deviation of a given sensor.
- */
-public class VerifyStandardDeviationOperation extends SensorTestOperation {
-    private SensorManagerTestVerifier mSensor;
-    private int mAxisCount;
-    private double[] mExpectedStandardDeviation;
-
-    public VerifyStandardDeviationOperation(
-            Context context,
-            int sensorType,
-            int samplingRateInUs,
-            int reportLatencyInUs,
-            float expectedStandardDeviation) {
-        mSensor = new SensorManagerTestVerifier(
-                context,
-                sensorType,
-                samplingRateInUs,
-                reportLatencyInUs);
-        // set expectations
-        mAxisCount = SensorTestInformation.getAxisCount(mSensor.getUnderlyingSensor().getType());
-        mExpectedStandardDeviation = new double[mAxisCount];
-        for (int i = 0; i < mExpectedStandardDeviation.length; i++) {
-            mExpectedStandardDeviation[i] = expectedStandardDeviation;
-        }
-    }
-
-    @Override
-    public void doWork() {
-        TestSensorEvent[] events = mSensor.collectEvents(100);
-        VerificationResult result = SensorVerificationHelper.verifyStandardDeviation(events,
-                mExpectedStandardDeviation);
-        if (result.isFailed()) {
-            Assert.fail(SensorCtsHelper.formatAssertionMessage(
-                    "StandardDeviation",
-                    this,
-                    mSensor.getUnderlyingSensor(),
-                    result.getFailureMessage()));
-        }
-    }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
new file mode 100644
index 0000000..5b969f2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * A {@link ISensorOperation} which contains a common implementation for gathering
+ * {@link SensorStats}.
+ */
+public abstract class AbstractSensorOperation implements ISensorOperation {
+
+    private final SensorStats mStats = new SensorStats();
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+     */
+    protected void addSensorStats(String key, SensorStats stats) {
+        mStats.addSensorStats(key, stats);
+    }
+
+    /**
+     * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+     * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
+     * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+     * padded.
+     */
+    protected void addSensorStats(String key, int index, SensorStats stats) {
+        addSensorStats(String.format("%s_%03d", key, index), stats);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mStats;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public abstract ISensorOperation clone();
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
new file mode 100644
index 0000000..95f1248
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.cts.helpers.SensorStats;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * after a specified period of time and waits for the child operation to complete.
+ * <p>
+ * This operation can be used to allow the device to go to sleep and wake it up after a specified
+ * period of time. After the device wakes up, this operation will hold a wake lock until the child
+ * operation finishes. This operation will not force the device into suspend, so if another
+ * operation is holding a wake lock, the device will stay awake.  Also, if the child operation
+ * finishes before the specified period, this operation return when the child operation finishes
+ * but wake the device one time at the specified period.
+ * </p>
+ */
+public class AlarmOperation extends AbstractSensorOperation {
+    private static final String ACTION = "AlarmOperationAction";
+    private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
+
+    private final ISensorOperation mOperation;
+    private final Context mContext;
+    private final long mSleepDuration;
+    private final TimeUnit mTimeUnit;
+
+    private boolean mCompleted = false;
+    private WakeLock mWakeLock = null;
+
+    /**
+     * Constructor for {@link DelaySensorOperation}
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the alarm manager
+     * @param sleepDuration the amount of time to sleep
+     * @param timeUnit the unit of the duration
+     */
+    public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+            TimeUnit timeUnit) {
+        mOperation = operation;
+        mContext = context;
+        mSleepDuration = sleepDuration;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        // Start alarm
+        IntentFilter intentFilter = new IntentFilter(ACTION);
+        BroadcastReceiver receiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                acquireWakeLock();
+            }
+        };
+        mContext.registerReceiver(receiver, intentFilter);
+
+        AlarmManager am = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
+        long wakeupTimeMs = (System.currentTimeMillis()
+                + TimeUnit.MILLISECONDS.convert(mSleepDuration, mTimeUnit));
+        Intent intent = new Intent(ACTION);
+        PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+        am.setExact(AlarmManager.RTC_WAKEUP, wakeupTimeMs, pendingIntent);
+
+        // Execute operation
+        try {
+            mOperation.execute();
+        } finally {
+            releaseWakeLock();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public AlarmOperation clone() {
+        return new AlarmOperation(mOperation, mContext, mSleepDuration, mTimeUnit);
+    }
+
+    /**
+     * Method that acquires a wake lock if a wake lock has not already been acquired and if the
+     * operation has not yet completed.
+     */
+    private synchronized void acquireWakeLock() {
+        // Don't acquire wake lock if the operation has already completed.
+        if (mCompleted == true || mWakeLock != null) {
+            return;
+        }
+        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKE_LOCK_TAG);
+    }
+
+    /**
+     * Method that releases the wake lock if it has been acquired.
+     */
+    private synchronized void releaseWakeLock() {
+        mCompleted = true;
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+        mWakeLock = null;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
new file mode 100644
index 0000000..bf43189
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * An {@link ISensorOperation} which delays for a specified period of time before performing another
+ * {@link ISensorOperation}.
+ */
+public class DelaySensorOperation implements ISensorOperation {
+    private final ISensorOperation mOperation;
+    private final long mDelay;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for {@link DelaySensorOperation}
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param delay the amount of time to delay
+     * @param timeUnit the unit of the delay
+     */
+    public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
+        if (operation == null || timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mOperation = operation;
+        mDelay = delay;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        sleep(mDelay, mTimeUnit);
+        mOperation.execute();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public DelaySensorOperation clone() {
+        return new DelaySensorOperation(mOperation.clone(), mDelay, mTimeUnit);
+    }
+
+    /**
+     * Helper method to sleep for a given number of ns. Exposed for unit testing.
+     */
+    void sleep(long delay, TimeUnit timeUnit) {
+        SensorCtsHelper.sleep(delay, timeUnit);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
new file mode 100644
index 0000000..bb64dfa
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
+ * debugging the framework.
+ */
+public class FakeSensorOperation extends AbstractSensorOperation {
+    private static final int NANOS_PER_MILLI = 1000000;
+
+    private final boolean mFail;
+    private final long mDelay;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for {@link FakeSensorOperation} that passes
+     */
+    public FakeSensorOperation(long delay, TimeUnit timeUnit) {
+        this(false, delay, timeUnit);
+    }
+
+    /**
+     * Constructor for {@link FakeSensorOperation}
+     */
+    public FakeSensorOperation(boolean fail, long delay, TimeUnit timeUnit) {
+        if (timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mFail = fail;
+        mDelay = delay;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
+        try {
+            Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
+            getStats().addValue("executed", true);
+            if (mFail) {
+                doFail();
+            }
+        }catch (InterruptedException e) {
+            // Ignore
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FakeSensorOperation clone() {
+        return new FakeSensorOperation(mFail, mDelay, mTimeUnit);
+    }
+
+    /**
+     * Fails the operation.
+     */
+    protected void doFail() {
+        String msg = "FakeSensorOperation failed";
+        getStats().addValue(SensorStats.ERROR, msg);
+        Assert.fail(msg);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
new file mode 100644
index 0000000..4ae56ea
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * Interface used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link ISensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
+ * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
+ * synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
+ * the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public interface ISensorOperation {
+
+    /**
+     * Executes the sensor operation.  This may throw {@link RuntimeException}s such as
+     * {@link AssertionError}s.
+     */
+    public void execute();
+
+    /**
+     * Get the stats for the operation.
+     *
+     * @return The {@link SensorStats} for the operation.
+     */
+    public SensorStats getStats();
+
+    /**
+     * Clones the {@link ISensorOperation}. The implementation should also clone all child
+     * operations, so that a cloned operation will run with the exact same parameters as the
+     * original. The stats should not be cloned.
+     *
+     * @return The cloned {@link ISensorOperation}
+     */
+    public ISensorOperation clone();
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
new file mode 100644
index 0000000..4cca428
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * The children are run in parallel but are given an index label in the order they are added. This
+ * class can be combined to compose complex {@link ISensorOperation}s.
+ */
+public class ParallelSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "parallel";
+
+    private static final String TAG = "ParallelSensorOperation";
+    private static final int NANOS_PER_MILLI = 1000000;
+
+    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+    private final Long mTimeout;
+    private final TimeUnit mTimeUnit;
+
+    /**
+     * Constructor for the {@link ParallelSensorOperation} without a timeout.
+     */
+    public ParallelSensorOperation() {
+        mTimeout = null;
+        mTimeUnit = null;
+    }
+
+    /**
+     * Constructor for the {@link ParallelSensorOperation} with a timeout.
+     */
+    public ParallelSensorOperation(long timeout, TimeUnit timeUnit) {
+        if (timeUnit == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mTimeout = timeout;
+        mTimeUnit = timeUnit;
+    }
+
+    /**
+     * Add a set of {@link ISensorOperation}s.
+     */
+    public void add(ISensorOperation ... operations) {
+        for (ISensorOperation operation : operations) {
+            if (operation == null) {
+                throw new IllegalArgumentException("Arguments cannot be null");
+            }
+            mOperations.add(operation);
+        }
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+     * operations, the first exception will be thrown once all operations are completed.
+     */
+    @Override
+    public void execute() {
+        Long timeoutTimeNs = null;
+        if (mTimeout != null && mTimeUnit != null) {
+            timeoutTimeNs = System.nanoTime() + TimeUnit.NANOSECONDS.convert(mTimeout, mTimeUnit);
+        }
+
+        List<OperationThread> threadPool = new ArrayList<OperationThread>(mOperations.size());
+        for (final ISensorOperation operation : mOperations) {
+            OperationThread thread = new OperationThread(operation);
+            thread.start();
+            threadPool.add(thread);
+        }
+
+        List<Integer> timeoutIndices = new ArrayList<Integer>();
+        List<OperationExceptionInfo> exceptions = new ArrayList<OperationExceptionInfo>();
+        Throwable earliestException = null;
+        Long earliestExceptionTime = null;
+
+        for (int i = 0; i < threadPool.size(); i++) {
+            OperationThread thread = threadPool.get(i);
+            join(thread, timeoutTimeNs);
+            if (thread.isAlive()) {
+                timeoutIndices.add(i);
+                thread.interrupt();
+            }
+
+            Throwable exception = thread.getException();
+            Long exceptionTime = thread.getExceptionTime();
+            if (exception != null && exceptionTime != null) {
+                if (exception instanceof AssertionError) {
+                    exceptions.add(new OperationExceptionInfo(i, (AssertionError) exception));
+                }
+                if (earliestExceptionTime == null || exceptionTime < earliestExceptionTime) {
+                    earliestException = exception;
+                    earliestExceptionTime = exceptionTime;
+                }
+            }
+
+            addSensorStats(STATS_TAG, i, thread.getSensorOperation().getStats());
+        }
+
+        if (earliestException == null) {
+            if (timeoutIndices.size() > 0) {
+                Assert.fail(getTimeoutMessage(timeoutIndices));
+            }
+        } else if (earliestException instanceof AssertionError) {
+            String msg = getExceptionMessage(exceptions, timeoutIndices);
+            getStats().addValue(SensorStats.ERROR, msg);
+            throw new AssertionError(msg, earliestException);
+        } else if (earliestException instanceof RuntimeException) {
+            throw (RuntimeException) earliestException;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ParallelSensorOperation clone() {
+        ParallelSensorOperation operation = new ParallelSensorOperation();
+        for (ISensorOperation subOperation : mOperations) {
+            operation.add(subOperation.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Helper method that joins a thread at a given time in the future.
+     */
+    private void join(Thread thread, Long timeoutTimeNs) {
+        try {
+            if (timeoutTimeNs == null) {
+                thread.join();
+            } else {
+                // Cap wait time to 1ns so that join doesn't block indefinitely.
+                long waitTimeNs = Math.max(timeoutTimeNs - System.nanoTime(), 1);
+                thread.join(waitTimeNs / NANOS_PER_MILLI, (int) waitTimeNs % NANOS_PER_MILLI);
+            }
+        } catch (InterruptedException e) {
+            // Log and ignore
+            Log.w(TAG, "Thread interrupted during join, operations may timeout before expected"
+                    + " time");
+        }
+    }
+
+    /**
+     * Helper method for joining the exception messages used in assertions.
+     */
+    private String getExceptionMessage(List<OperationExceptionInfo> exceptions,
+            List<Integer> timeoutIndices) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(exceptions.get(0).toString());
+        for (int i = 1; i < exceptions.size(); i++) {
+            sb.append(", ").append(exceptions.get(i).toString());
+        }
+        if (timeoutIndices.size() > 0) {
+            sb.append(", ").append(getTimeoutMessage(timeoutIndices));
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Helper method for formatting the operation timed out message used in assertions
+     */
+    private String getTimeoutMessage(List<Integer> indices) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("Operation");
+        if (indices.size() != 1) {
+            sb.append("s");
+        }
+        sb.append(" ").append(indices.get(0));
+        for (int i = 1; i < indices.size(); i++) {
+            sb.append(", ").append(indices.get(i));
+        }
+        sb.append(" timed out");
+        return sb.toString();
+    }
+
+    /**
+     * Helper class for holding operation index and exception
+     */
+    private class OperationExceptionInfo {
+        private final int mIndex;
+        private final AssertionError mException;
+
+        public OperationExceptionInfo(int index, AssertionError exception) {
+            mIndex = index;
+            mException = exception;
+        }
+
+        @Override
+        public String toString() {
+            return String.format("Operation %d failed: \"%s\"", mIndex, mException.getMessage());
+        }
+    }
+
+    /**
+     * Helper class to run the {@link ISensorOperation} in its own thread.
+     */
+    private class OperationThread extends Thread {
+        final private ISensorOperation mOperation;
+        private Throwable mException = null;
+        private Long mExceptionTime = null;
+
+        public OperationThread(ISensorOperation operation) {
+            mOperation = operation;
+        }
+
+        /**
+         * Run the thread catching {@link RuntimeException}s and {@link AssertionError}s and
+         * the time it happened.
+         */
+        @Override
+        public void run() {
+            try {
+                mOperation.execute();
+            } catch (AssertionError e) {
+                mExceptionTime = System.nanoTime();
+                mException = e;
+            } catch (RuntimeException e) {
+                mExceptionTime = System.nanoTime();
+                mException = e;
+            }
+        }
+
+        public ISensorOperation getSensorOperation() {
+            return mOperation;
+        }
+
+        public Throwable getException() {
+            return mException;
+        }
+
+        public Long getExceptionTime() {
+            return mExceptionTime;
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
new file mode 100644
index 0000000..5e023e5
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ */
+public class RepeatingSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "repeating";
+
+    private final ISensorOperation mOperation;
+    private final int mIterations;
+
+    /**
+     * Constructor for {@link RepeatingSensorOperation}.
+     *
+     * @param operation the {@link ISensorOperation} to run.
+     * @param iterations the number of iterations to run the operation for.
+     */
+    public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+        if (operation == null) {
+            throw new IllegalArgumentException("Arguments cannot be null");
+        }
+        mOperation = operation;
+        mIterations = iterations;
+
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
+     * in one iterations, it is thrown and all subsequent iterations will not run.
+     */
+    @Override
+    public void execute() {
+        for(int i = 0; i < mIterations; ++i) {
+            ISensorOperation operation = mOperation.clone();
+            try {
+                operation.execute();
+            } catch (AssertionError e) {
+                String msg = String.format("Iteration %d failed: \"%s\"", i, e.getMessage());
+                getStats().addValue(SensorStats.ERROR, msg);
+                throw new AssertionError(msg, e);
+            } finally {
+                addSensorStats(STATS_TAG, i, operation.getStats());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public RepeatingSensorOperation clone() {
+        return new RepeatingSensorOperation(mOperation.clone(), mIterations);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
new file mode 100644
index 0000000..7148454
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.TestCase;
+
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
+ * {@link SequentialSensorOperation}.
+ */
+public class SensorOperationTest extends TestCase {
+    private static final int THRESHOLD_MS = 50;
+
+    /**
+     * Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
+     * rely on this operation.
+     */
+    public void testFakeSensorOperation() {
+        final int opDurationMs = 100;
+
+        ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+
+        assertFalse(op.getStats().flatten().containsKey("executed"));
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(opDurationMs - duration) < THRESHOLD_MS);
+        assertTrue(op.getStats().flatten().containsKey("executed"));
+
+        op = new FakeSensorOperation(true, 0, TimeUnit.MILLISECONDS);
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+        }
+        assertTrue(op.getStats().flatten().keySet().contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link DelaySensorOperation} functions correctly.
+     */
+    public void testDelaySensorOperation() {
+        final int opDurationMs = 500;
+        final int subOpDurationMs = 100;
+
+        FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(opDurationMs + subOpDurationMs - duration) < THRESHOLD_MS);
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly.
+     */
+    public void testParallelSensorOperation() {
+        final int subOpCount = 100;
+        final int subOpDurationMs = 500;
+
+        ParallelSensorOperation op = new ParallelSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testParallelSensorOperation_fail() {
+        final int subOpCount = 100;
+
+        ParallelSensorOperation op = new ParallelSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
+            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+            // TODO: Verify that the exception rethrown was at t=5ms.
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount + 3, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+            if (i % 50 == 5) {
+                assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                        ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER,
+                        SensorStats.ERROR)));
+            }
+
+        }
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link ParallelSensorOperation} functions correctly if a child exceeds the
+     * timeout.
+     */
+    public void testParallelSensorOperation_timeout() {
+        final int subOpCount = 100;
+
+        ParallelSensorOperation op = new ParallelSensorOperation(100, TimeUnit.MILLISECONDS);
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger timeouts in the 5th, 55th operations (5 seconds vs 0 seconds)
+            ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+            // TODO: Verify that the exception rethrown was at t=5ms.
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount - 2, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            if (i % 50 != 5) {
+                assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                        ParallelSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+            }
+        }
+    }
+
+    /**
+     * Test that the {@link RepeatingSensorOperation} functions correctly.
+     */
+    public void testRepeatingSensorOperation() {
+        final int iterations = 10;
+        final int subOpDurationMs = 100;
+
+        ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs * iterations - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(iterations, statsKeys.size());
+        for (int i = 0; i < iterations; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    RepeatingSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link RepeatingSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testRepeatingSensorOperation_fail() {
+        final int iterations = 100;
+        final int failCount = 75;
+
+        ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+            private int mExecutedCount = 0;
+            private SensorStats mFakeStats = new SensorStats();
+
+            @Override
+            public void execute() {
+                super.execute();
+                mExecutedCount++;
+
+                if (failCount == mExecutedCount) {
+                    doFail();
+                }
+            }
+
+            @Override
+            public FakeSensorOperation clone() {
+                // Don't clone
+                mFakeStats = new SensorStats();
+                return this;
+            }
+
+            @Override
+            public SensorStats getStats() {
+                return mFakeStats;
+            }
+        };
+        ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(failCount + 2, statsKeys.size());
+        for (int i = 0; i < failCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    RepeatingSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+        assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                RepeatingSensorOperation.STATS_TAG, failCount - 1, SensorStats.DELIMITER,
+                SensorStats.ERROR)));
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+
+    /**
+     * Test that the {@link SequentialSensorOperation} functions correctly.
+     */
+    public void testSequentialSensorOperation() {
+        final int subOpCount = 10;
+        final int subOpDurationMs = 100;
+
+        SequentialSensorOperation op = new SequentialSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        long start = System.currentTimeMillis();
+        op.execute();
+        long duration = System.currentTimeMillis() - start;
+        assertTrue(Math.abs(subOpDurationMs * subOpCount - duration) < THRESHOLD_MS);
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(subOpCount, statsKeys.size());
+        for (int i = 0; i < subOpCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    SequentialSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+    }
+
+    /**
+     * Test that the {@link SequentialSensorOperation} functions correctly if there is a failure in
+     * a child operation.
+     */
+    public void testSequentialSensorOperation_fail() {
+        final int subOpCount = 100;
+        final int failCount = 75;
+
+        SequentialSensorOperation op = new SequentialSensorOperation();
+        for (int i = 0; i < subOpCount; i++) {
+            // Trigger a failure in the 75th operation only
+            ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+                    TimeUnit.MILLISECONDS);
+            op.add(subOp);
+        }
+
+        Set<String> statsKeys = op.getStats().flatten().keySet();
+        assertEquals(0, statsKeys.size());
+
+        try {
+            op.execute();
+            fail("AssertionError expected");
+        } catch (AssertionError e) {
+            // Expected
+            System.out.println(e.getMessage());
+        }
+
+        statsKeys = op.getStats().flatten().keySet();
+        assertEquals(failCount + 2, statsKeys.size());
+        for (int i = 0; i < failCount; i++) {
+            assertTrue(statsKeys.contains(String.format("%s_%03d%sexecuted",
+                    SequentialSensorOperation.STATS_TAG, i, SensorStats.DELIMITER)));
+        }
+        assertTrue(statsKeys.contains(String.format("%s_%03d%s%s",
+                SequentialSensorOperation.STATS_TAG, failCount - 1, SensorStats.DELIMITER,
+                SensorStats.ERROR)));
+        assertTrue(statsKeys.contains(SensorStats.ERROR));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
new file mode 100644
index 0000000..050a8f6
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * sequence. The children are executed in the order they are added. This class can be combined to
+ * compose complex {@link ISensorOperation}s.
+ */
+public class SequentialSensorOperation extends AbstractSensorOperation {
+    public static final String STATS_TAG = "sequential";
+
+    private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+
+    /**
+     * Add a set of {@link ISensorOperation}s.
+     */
+    public void add(ISensorOperation ... operations) {
+        for (ISensorOperation operation : operations) {
+            if (operation == null) {
+                throw new IllegalArgumentException("Arguments cannot be null");
+            }
+            mOperations.add(operation);
+        }
+    }
+
+    /**
+     * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+     * in one operation, it is thrown and all subsequent operations will not run.
+     */
+    @Override
+    public void execute() {
+        for (int i = 0; i < mOperations.size(); i++) {
+            ISensorOperation operation = mOperations.get(i);
+            try {
+                operation.execute();
+            } catch (AssertionError e) {
+                String msg = String.format("Operation %d failed: \"%s\"", i, e.getMessage());
+                getStats().addValue(SensorStats.ERROR, msg);
+                throw new AssertionError(msg, e);
+            } finally {
+                addSensorStats(STATS_TAG, i, operation.getStats());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SequentialSensorOperation clone() {
+        SequentialSensorOperation operation = new SequentialSensorOperation();
+        for (ISensorOperation subOperation : mOperations) {
+            operation.add(subOperation.clone());
+        }
+        return operation;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
new file mode 100644
index 0000000..1be0ba2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.ValidatingSensorEventListener;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.hardware.cts.helpers.sensorverification.JitterVerification;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
+
+import junit.framework.Assert;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * <p>
+ * Provides methods to set test expectations as well as providing a set of default expectations
+ * depending on sensor type.  When {{@link #execute()} is called, the sensor will collect the
+ * events and then run all the tests.
+ * </p>
+ */
+public class TestSensorOperation extends AbstractSensorOperation {
+    private final TestSensorManager mSensorManager;
+    private final Context mContext;
+    private final int mSensorType;
+    private final int mRateUs;
+    private final int mMaxBatchReportLatencyUs;
+    private final Integer mEventCount;
+    private final Long mDuration;
+    private final TimeUnit mTimeUnit;
+
+    private final Collection<ISensorVerification> mVerifications =
+            new HashSet<ISensorVerification>();
+
+    private boolean mLogEvents = false;
+
+    /**
+     * Create a {@link TestSensorOperation}.
+     *
+     * @param context the {@link Context}.
+     * @param sensorType the sensor type
+     * @param rateUs the rate that
+     * @param maxBatchReportLatencyUs the max batch report latency
+     * @param eventCount the number of events to gather
+     */
+    public TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, int eventCount) {
+        this(context, sensorType, rateUs, maxBatchReportLatencyUs, eventCount, null, null);
+    }
+
+    /**
+     * Create a {@link TestSensorOperation}.
+     *
+     * @param context the {@link Context}.
+     * @param sensorType the sensor type
+     * @param rateUs the rate that
+     * @param maxBatchReportLatencyUs the max batch report latency
+     * @param duration the duration to gather events for
+     * @param timeUnit the time unit of the duration
+     */
+    public TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, long duration, TimeUnit timeUnit) {
+        this(context, sensorType, rateUs, maxBatchReportLatencyUs, null, duration, timeUnit);
+    }
+
+    /**
+     * Private helper constructor.
+     */
+    private TestSensorOperation(Context context, int sensorType, int rateUs,
+            int maxBatchReportLatencyUs, Integer eventCount, Long duration, TimeUnit timeUnit) {
+        mContext = context;
+        mSensorType = sensorType;
+        mRateUs = rateUs;
+        mMaxBatchReportLatencyUs = maxBatchReportLatencyUs;
+        mEventCount = eventCount;
+        mDuration = duration;
+        mTimeUnit = timeUnit;
+        mSensorManager = new TestSensorManager(mContext, mSensorType, mRateUs,
+                mMaxBatchReportLatencyUs);
+    }
+
+    /**
+     * Set whether to log events.
+     */
+    public void setLogEvents(boolean logEvents) {
+        mLogEvents = logEvents;
+    }
+
+    /**
+     * Set all of the default test expectations.
+     */
+    public void setDefaultVerifications() {
+        Sensor sensor = mSensorManager.getSensor();
+        addVerification(EventGapVerification.getDefault(sensor, mRateUs));
+        addVerification(EventOrderingVerification.getDefault(sensor));
+        addVerification(FrequencyVerification.getDefault(sensor, mRateUs));
+        addVerification(JitterVerification.getDefault(sensor, mRateUs));
+        addVerification(MagnitudeVerification.getDefault(sensor));
+        addVerification(MeanVerification.getDefault(sensor));
+        // Skip SigNumVerification since it has no default
+        addVerification(StandardDeviationVerification.getDefault(sensor));
+    }
+
+    public void addVerification(ISensorVerification verification) {
+        if (verification != null) {
+            mVerifications.add(verification);
+        }
+    }
+
+    /**
+     * Collect the specified number of events from the sensor and run all enabled verifications.
+     */
+    @Override
+    public void execute() {
+        getStats().addValue("sensor_name", SensorTestInformation.getSensorName(mSensorType));
+        getStats().addValue("sensor_handle", mSensorManager.getSensor().getHandle());
+
+        ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
+        listener.setLogEvents(mLogEvents);
+
+        if (mEventCount != null) {
+            mSensorManager.runSensor(listener, mEventCount);
+        } else {
+            mSensorManager.runSensor(listener, mDuration, mTimeUnit);
+        }
+
+        boolean failed = false;
+        StringBuilder sb = new StringBuilder();
+
+        for (ISensorVerification verification : mVerifications) {
+            failed |= evaluateResults(verification, sb);
+        }
+
+        if (failed) {
+            String msg = SensorCtsHelper.formatAssertionMessage(mSensorManager.getSensor(),
+                    "VerifySensorOperation", mRateUs, mMaxBatchReportLatencyUs, sb.toString());
+            getStats().addValue(SensorStats.ERROR, msg);
+            Assert.fail(msg);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public TestSensorOperation clone() {
+        TestSensorOperation operation;
+        if (mEventCount != null) {
+            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+                    mMaxBatchReportLatencyUs, mEventCount);
+        } else {
+            operation = new TestSensorOperation(mContext, mSensorType, mRateUs,
+                    mMaxBatchReportLatencyUs, mDuration, mTimeUnit);
+        }
+
+        for (ISensorVerification verification : mVerifications) {
+            operation.addVerification(verification.clone());
+        }
+        return operation;
+    }
+
+    /**
+     * Evaluate the results of a test, aggregate the stats, and build the error message.
+     */
+    private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
+        try {
+            verification.verify(getStats());
+        } catch (AssertionError e) {
+            if (sb.length() > 0) {
+                sb.append(", ");
+            }
+            sb.append(e.getMessage());
+            return true;
+        }
+        return false;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
new file mode 100644
index 0000000..73da9c9
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.content.Context;
+import android.hardware.cts.helpers.SensorStats;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+/**
+ * An {@link ISensorOperation} which holds a wakelock while performing another
+ * {@link ISensorOperation}.
+ */
+public class WakeLockOperation extends AbstractSensorOperation {
+    private static final String TAG = "WakeLockOperation";
+
+    private final ISensorOperation mOperation;
+    private final Context mContext;
+    private final int mWakelockFlags;
+
+    /**
+     * Constructor for {@link WakeLockOperation}.
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the power manager
+     * @param wakelockFlags the flags used when acquiring the wakelock
+     */
+    public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+        mOperation = operation;
+        mContext = context;
+        mWakelockFlags = wakelockFlags;
+    }
+
+    /**
+     * Constructor for {@link WakeLockOperation}.
+     *
+     * @param operation the child {@link ISensorOperation} to perform after the delay
+     * @param context the context used to access the power manager
+     */
+    public WakeLockOperation(ISensorOperation operation, Context context) {
+        this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void execute() {
+        PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+        WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
+
+        wakeLock.acquire();
+        try {
+            mOperation.execute();
+        } finally {
+            wakeLock.release();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SensorStats getStats() {
+        return mOperation.getStats();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ISensorOperation clone() {
+        return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java
new file mode 100644
index 0000000..8d132a3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractMeanVerification.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+/**
+ * Abstract class that calculates of the mean event values.
+ */
+public abstract class AbstractMeanVerification extends AbstractSensorVerification {
+    private float[] mSums = null;
+    private int mCount = 0;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mSums == null) {
+            mSums = new float[event.values.length];
+        }
+        Assert.assertEquals(mSums.length, event.values.length);
+        for (int i = 0; i < mSums.length; i++) {
+            mSums[i] += event.values[i];
+        }
+        mCount++;
+    }
+
+    /**
+     * Return the number of events.
+     */
+    protected int getCount() {
+        return mCount;
+    }
+
+    /**
+     * Return the means of the event values.
+     */
+    protected float[] getMeans() {
+        if (mCount < 0) {
+            return null;
+        }
+
+        float[] means = new float[mSums.length];
+        for (int i = 0; i < mSums.length; i++) {
+            means[i] = mSums[i] / mCount;
+        }
+        return means;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
new file mode 100644
index 0000000..911ae3a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.TestSensorEvent;
+
+/**
+ * Abstract class that deals with the synchronization of the sensor verifications.
+ */
+public abstract class AbstractSensorVerification implements ISensorVerification {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void addSensorEvents(TestSensorEvent ... events) {
+        for (TestSensorEvent event : events) {
+            addSensorEventInternal(event);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public synchronized void addSensorEvent(TestSensorEvent event) {
+        addSensorEventInternal(event);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public abstract ISensorVerification clone();
+
+    /**
+     * Used by implementing classes to add a sensor event.
+     */
+    protected abstract void addSensorEventInternal(TestSensorEvent event);
+
+    /**
+     * Helper class to store the index, previous event, and current event.
+     */
+    protected class IndexedEventPair {
+        public final int index;
+        public final TestSensorEvent event;
+        public final TestSensorEvent previousEvent;
+
+        public IndexedEventPair(int index, TestSensorEvent event,
+                TestSensorEvent previousEvent) {
+            this.index = index;
+            this.event = event;
+            this.previousEvent = previousEvent;
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
new file mode 100644
index 0000000..251ef3c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -0,0 +1,117 @@
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that there are no missing events. This is done by
+ * checking the last received sensor timestamp and checking that it is within 1.8 * the expected
+ * period.
+ */
+public class EventGapVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "missing_event_passed";
+
+    // Fail if no events are delivered within 1.8 times the expected interval
+    private static final double THRESHOLD = 1.8;
+
+    // Number of indices to print in assert message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private final int mExpectedDelayUs;
+
+    private final List<IndexedEventPair> mEventGaps = new LinkedList<IndexedEventPair>();
+    private TestSensorEvent mPreviousEvent = null;
+    private int mIndex = 0;
+
+    /**
+     * Construct a {@link EventGapVerification}
+     *
+     * @param expectedDelayUs the expected period in us.
+     */
+    public EventGapVerification(int expectedDelayUs) {
+        mExpectedDelayUs = expectedDelayUs;
+    }
+
+    /**
+     * Get the default {@link EventGapVerification}.
+     *
+     * @param sensor the {@link Sensor}
+     * @param rateUs the requested rate in us
+     * @return the verification or null if the verification is not a continuous mode sensor.
+     */
+    public static EventGapVerification getDefault(Sensor sensor, int rateUs) {
+        if (!SensorReportingMode.CONTINUOUS.equals(SensorTestInformation.getReportingMode(
+                sensor.getType()))) {
+            return null;
+        }
+        return new EventGapVerification(SensorCtsHelper.getDelay(sensor, rateUs));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        final int count = mEventGaps.size();
+        stats.addValue(PASSED_KEY, count == 0);
+        stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
+
+        final int[] indices = new int[count];
+        for (int i = 0; i < indices.length; i++) {
+            indices[i] = mEventGaps.get(i).index;
+        }
+        stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+
+        if (count > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(count).append(" events gaps: ");
+            for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+                IndexedEventPair info = mEventGaps.get(i);
+                sb.append(String.format("position=%d, delta_time=%dns; ", info.index,
+                        info.event.timestamp - info.previousEvent.timestamp));
+            }
+            if (count > TRUNCATE_MESSAGE_LENGTH) {
+                sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more; ");
+            }
+            sb.append(String.format("(expected <%dns)",
+                    TimeUnit.NANOSECONDS.convert((int) (THRESHOLD * mExpectedDelayUs),
+                            TimeUnit.MICROSECONDS)));
+            Assert.fail(sb.toString());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventGapVerification clone() {
+        return new EventGapVerification(mExpectedDelayUs);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mPreviousEvent != null) {
+            long deltaNs = event.timestamp - mPreviousEvent.timestamp;
+            long deltaUs = TimeUnit.MICROSECONDS.convert(deltaNs, TimeUnit.NANOSECONDS);
+            if (deltaUs > mExpectedDelayUs * THRESHOLD) {
+                mEventGaps.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
+            }
+        }
+
+        mPreviousEvent = event;
+        mIndex++;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
new file mode 100644
index 0000000..b7861b2
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link EventGapVerification}.
+ */
+public class EventGapVerificationTest extends TestCase {
+
+    /**
+     * Test that the verification passes when there are no results.
+     */
+    public void testVerify_no_events() {
+        // Timestamps in ns, expected in us
+        runVerification(1000, new long[]{}, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification passes when there are not missing events.
+     */
+    public void testVerify_correct() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1000000, 2000000, 3000000, 4000000, 5000000};
+        runVerification(1000, timestamps, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification passes when there are not missing events but some jitter.
+     */
+    public void testVerify_jitter() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1100000, 2050000, 2990000, 4000000, 4950000};
+        runVerification(1000, timestamps, true, new int[]{});
+    }
+
+    /**
+     * Test that the verification fails when there are missing events.
+     */
+    public void testVerify_missing_events() {
+        // Timestamps in ns, expected in us
+        long[] timestamps = {1000000, 2000000, 3000000, 5000000, 6000000};
+        runVerification(1000, timestamps, false, new int[]{3});
+    }
+
+    private void runVerification(int expected, long[] timestamps, boolean pass,
+            int[] indices) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, timestamps);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            boolean failed = false;
+            try {
+                verification.verify(stats);
+            } catch (AssertionError e) {
+                // Expected;
+                failed = true;
+            }
+            assertTrue("Expected an AssertionError", failed);
+        }
+        assertEquals(pass, stats.getValue(EventGapVerification.PASSED_KEY));
+        assertEquals(indices.length, stats.getValue(SensorStats.EVENT_GAP_COUNT_KEY));
+        assertNotNull(stats.getValue(SensorStats.EVENT_GAP_POSITIONS_KEY));
+        int[] actualIndices = (int[]) stats.getValue(SensorStats.EVENT_GAP_POSITIONS_KEY);
+        assertEquals(indices.length, actualIndices.length);
+
+        for (int i = 0; i < indices.length; i++) {
+            assertEquals(indices[i], actualIndices[i]);
+        }
+    }
+
+    private ISensorVerification getVerification(int expected, long ... timestamps) {
+        ISensorVerification verification = new EventGapVerification(expected);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
new file mode 100644
index 0000000..c74c826
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A {@link ISensorVerification} which verifies that all events are received in the correct order.
+ */
+public class EventOrderingVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "event_out_of_order_passed";
+
+    // Number of indices to print in assert message before truncating
+    private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+    private Long mMaxTimestamp = null;
+    private final List<IndexedEventPair> mOutOfOrderEvents = new LinkedList<IndexedEventPair>();
+    private TestSensorEvent mPreviousEvent = null;
+    private int mIndex = 0;
+
+    /**
+     * Get the default {@link EventOrderingVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    @SuppressWarnings("deprecation")
+    public static EventOrderingVerification getDefault(Sensor sensor) {
+        SensorReportingMode mode = SensorTestInformation.getReportingMode(sensor.getType());
+        if (!SensorReportingMode.CONTINUOUS.equals(mode)
+                && !SensorReportingMode.ON_CHANGE.equals(mode)) {
+            return null;
+        }
+        return new EventOrderingVerification();
+    }
+
+    /**
+     * Verify that the events are in the correct order.  Add {@value #PASSED_KEY},
+     * {@value SensorStats#EVENT_OUT_OF_ORDER_COUNT_KEY}, and
+     * {@value SensorStats#EVENT_OUT_OF_ORDER_POSITIONS_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        final int count = mOutOfOrderEvents.size();
+        stats.addValue(PASSED_KEY, count == 0);
+        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
+
+        final int[] indices = new int[count];
+        for (int i = 0; i < indices.length; i++) {
+            indices[i] = mOutOfOrderEvents.get(i).index;
+        }
+        stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+
+        if (count > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(count).append(" events out of order: ");
+            for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+                IndexedEventPair info = mOutOfOrderEvents.get(i);
+                sb.append(String.format("position=%d, previous=%d, timestamp=%d; ", info.index,
+                        info.previousEvent.timestamp, info.event.timestamp));
+            }
+            if (count > TRUNCATE_MESSAGE_LENGTH) {
+                sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more");
+            } else {
+                // Delete the trailing "; "
+                sb.delete(sb.length() - 2, sb.length());
+            }
+
+            Assert.fail(sb.toString());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public EventOrderingVerification clone() {
+        return new EventOrderingVerification();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mPreviousEvent == null) {
+            mMaxTimestamp = event.timestamp;
+        } else {
+            if (event.timestamp < mMaxTimestamp) {
+                mOutOfOrderEvents.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
+            } else if (event.timestamp > mMaxTimestamp) {
+                mMaxTimestamp = event.timestamp;
+            }
+        }
+
+        mPreviousEvent = event;
+        mIndex++;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
new file mode 100644
index 0000000..28cbd01
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class EventOrderingVerificationTest extends TestCase {
+
+    /**
+     * Test that the verification passes when there are no results.
+     */
+    public void testNoEvents() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification();
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification passes when the timestamps are the same.
+     */
+    public void testSameTimestamp() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 0, 0, 0, 0);
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification passes when the timestamps are increasing.
+     */
+    public void testSequentialTimestamp() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 1, 2, 3, 4);
+        verification.verify(stats);
+        verifyStats(stats, true, 0);
+    }
+
+    /**
+     * Test that the verification fails when there is one event out of order.
+     */
+    public void testSingleOutofOrder() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(0, 2, 1, 3, 4);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1);
+        List<Integer> indices = getIndices(stats);
+        assertTrue(indices.contains(2));
+    }
+
+    /**
+     * Test that the verification fails when there are multiple events out of order.
+     */
+    public void testMultipleOutOfOrder() {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(4, 0, 1, 2, 3);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 4);
+        List<Integer> indices = getIndices(stats);
+        assertTrue(indices.contains(1));
+        assertTrue(indices.contains(2));
+        assertTrue(indices.contains(3));
+        assertTrue(indices.contains(4));
+    }
+
+    private ISensorVerification getVerification(long ... timestamps) {
+        ISensorVerification verification = new EventOrderingVerification();
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, int count) {
+        assertEquals(passed, stats.getValue(EventOrderingVerification.PASSED_KEY));
+        assertEquals(count, stats.getValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY));
+        assertNotNull(stats.getValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY));
+    }
+
+    private List<Integer> getIndices(SensorStats stats) {
+        int[] primitiveIndices = (int[]) stats.getValue(
+                SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY);
+        List<Integer> indices = new ArrayList<Integer>(primitiveIndices.length);
+        for (int index : primitiveIndices) {
+            indices.add(index);
+        }
+        return indices;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
new file mode 100644
index 0000000..4815688
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerification.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestInformation;
+import android.hardware.cts.helpers.SensorTestInformation.SensorReportingMode;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sensor frequency are within the expected
+ * range.
+ */
+public class FrequencyVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "frequency_passed";
+
+    // lower threshold is (100 - 10)% expected
+    private static final int DEFAULT_LOWER_THRESHOLD = 10;
+    // upper threshold is (100 + 110)% expected
+    private static final int DEFAULT_UPPER_THRESHOLD = 110;
+
+    private final double mExpected;
+    private final double mLowerThreshold;
+    private final double mUpperThreshold;
+
+    private long mMinTimestamp = 0;
+    private long mMaxTimestamp = 0;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link FrequencyVerification}.
+     *
+     * @param expected the expected frequency in Hz.
+     * @param lowerTheshold the lower threshold in Hz. {@code expected - lower} should be the
+     * slowest acceptable frequency of the sensor.
+     * @param upperThreshold the upper threshold in Hz. {@code expected + upper} should be the
+     * fastest acceptable frequency of the sensor.
+     */
+    public FrequencyVerification(double expected, double lowerTheshold, double upperThreshold) {
+        mExpected = expected;
+        mLowerThreshold = lowerTheshold;
+        mUpperThreshold = upperThreshold;
+    }
+
+    /**
+     * Get the default {@link FrequencyVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @param rateUs the desired rate of the sensor
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static FrequencyVerification getDefault(Sensor sensor, int rateUs) {
+        if (!SensorReportingMode.CONTINUOUS.equals(
+                SensorTestInformation.getReportingMode(sensor.getType()))) {
+            return null;
+        }
+
+        // Expected frequency in Hz
+        double expected = SensorCtsHelper.getFrequency(SensorCtsHelper.getDelay(sensor, rateUs),
+                TimeUnit.MICROSECONDS);
+        // Expected frequency * threshold percentage
+        double lowerThreshold = expected * DEFAULT_LOWER_THRESHOLD / 100;
+        double upperThreshold = expected * DEFAULT_UPPER_THRESHOLD / 100;
+        return new FrequencyVerification(expected, lowerThreshold, upperThreshold);
+    }
+
+    /**
+     * Verify that the frequency is correct. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#FREQUENCY_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        double frequency = SensorCtsHelper.getFrequency(
+                ((double) (mMaxTimestamp - mMinTimestamp)) / (mCount - 1), TimeUnit.NANOSECONDS);
+        boolean failed = (frequency <= mExpected - mLowerThreshold
+                || frequency >= mExpected + mUpperThreshold);
+
+        stats.addValue(SensorStats.FREQUENCY_KEY, frequency);
+        stats.addValue(PASSED_KEY, !failed);
+
+        if (failed) {
+            Assert.fail(String.format("Frequency out of range: frequency=%.2fHz "
+                    + "(expected (%.2f-%.2fHz, %.2f+%.2fHz))", frequency, mExpected,
+                    mLowerThreshold, mExpected, mUpperThreshold));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public FrequencyVerification clone() {
+        return new FrequencyVerification(mExpected, mLowerThreshold, mUpperThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mCount == 0) {
+            mMinTimestamp = event.timestamp;
+            mMaxTimestamp = event.timestamp;
+        } else {
+            if (mMinTimestamp > event.timestamp) {
+                mMinTimestamp = event.timestamp;
+            }
+            if (mMaxTimestamp < event.timestamp) {
+                mMaxTimestamp = event.timestamp;
+            }
+        }
+        mCount++;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
new file mode 100644
index 0000000..cec09a5
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class FrequencyVerificationTest extends TestCase {
+
+    /**
+     * Test that the verifications passes/fails based on threshold given.
+     */
+    public void testVerifification() {
+        long[] timestamps = {0, 1000000, 2000000, 3000000, 4000000};  // 1000Hz
+
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(1000.0, 1.0, 1.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(950.0, 100.0, 100.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(1050.0, 100.0, 100.0, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(950.0, 100.0, 25.0, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1000.0);
+
+        stats = new SensorStats();
+        verification = getVerification(1050.0, 25.0, 100.0, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 1000.0);
+    }
+
+    private ISensorVerification getVerification(double expected, double lowerThreshold,
+            double upperThreshold, long ... timestamps) {
+        ISensorVerification verification = new FrequencyVerification(expected, lowerThreshold,
+                upperThreshold);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, double frequency) {
+        assertEquals(passed, stats.getValue(FrequencyVerification.PASSED_KEY));
+        assertEquals(frequency, stats.getValue(SensorStats.FREQUENCY_KEY));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
new file mode 100644
index 0000000..07af392
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+/**
+ * Interface describing the sensor verification. This class was designed for to handle streaming
+ * events. The methods {@link #addSensorEvent(TestSensorEvent)} and
+ * {@link #addSensorEvents(TestSensorEvent...)} should be called in the order that the events are
+ * received. The method {@link #verify(SensorStats)} should be called after all events are added.
+ */
+public interface ISensorVerification {
+
+    /**
+     * Add a single {@link TestSensorEvent} to be evaluated.
+     */
+    public void addSensorEvent(TestSensorEvent event);
+
+    /**
+     * Add multiple {@link TestSensorEvent}s to be evaluated.
+     */
+    public void addSensorEvents(TestSensorEvent ... events);
+
+    /**
+     * Evaluate all added {@link TestSensorEvent}s and update stats.
+     *
+     * @param stats a {@link SensorStats} object used to keep track of the stats.
+     * @throws AssertionError if the verification fails.
+     */
+    public void verify(SensorStats stats);
+
+    /**
+     * Clones the {@link ISensorVerification}
+     */
+    public ISensorVerification clone();
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
new file mode 100644
index 0000000..6feceb8
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerification.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sensor jitter is in an acceptable range.
+ */
+public class JitterVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "jitter_passed";
+
+    // sensorType: threshold (% of expected period)
+    private static final Map<Integer, Integer> DEFAULTS = new HashMap<Integer, Integer>(12);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final int mExpected;
+    private final int mThreshold;
+
+    private List<Long> mTimestamps = new LinkedList<Long>();
+
+    /**
+     * Construct a {@link JitterVerification}
+     *
+     * @param expected the expected period in ns
+     * @param threshold the acceptable margin of error as a percentage
+     */
+    public JitterVerification(int expected, int threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link JitterVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @param rateUs the desired rate of the sensor
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static JitterVerification getDefault(Sensor sensor, int rateUs) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+
+        int expected = (int) TimeUnit.NANOSECONDS.convert(SensorCtsHelper.getDelay(sensor, rateUs),
+                TimeUnit.MICROSECONDS);
+        return new JitterVerification(expected, DEFAULTS.get(sensor.getType()));
+    }
+
+    /**
+     * Verify that the 95th percentile of the jitter is in the acceptable range. Add
+     * {@value #PASSED_KEY} and {@value SensorStats#JITTER_95_PERCENTILE_KEY} keys to
+     * {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mTimestamps.size() < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        List<Double> jitters = getJitterValues();
+        double jitter95Percentile = SensorCtsHelper.get95PercentileValue(jitters);
+        boolean failed = (jitter95Percentile > mExpected * (mThreshold / 100.0));
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.JITTER_95_PERCENTILE_KEY, jitter95Percentile);
+
+        if (failed) {
+            Assert.fail(String.format("Jitter out of range: jitter at 95th percentile=%.0fns "
+                    + "(expected <%.0fns)", jitter95Percentile, mExpected * (mThreshold / 100.0)));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public JitterVerification clone() {
+        return new JitterVerification(mExpected, mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        mTimestamps.add(event.timestamp);
+    }
+
+    /**
+     * Get the list of all jitter values. Exposed for unit testing.
+     */
+    List<Double> getJitterValues() {
+        List<Long> deltas = new ArrayList<Long>(mTimestamps.size() - 1);
+        for (int i = 1; i < mTimestamps.size(); i++) {
+            deltas.add(mTimestamps.get(i) - mTimestamps.get(i -1));
+        }
+        double deltaMean = SensorCtsHelper.getMean(deltas);
+        List<Double> jitters = new ArrayList<Double>(deltas.size());
+        for (long delta : deltas) {
+            jitters.add(Math.abs(delta - deltaMean));
+        }
+        return jitters;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_PRESSURE, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GRAVITY, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, Integer.MAX_VALUE);
+        DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR, Integer.MAX_VALUE);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
new file mode 100644
index 0000000..a9e872a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+import java.util.List;
+
+/**
+ * Tests for {@link EventOrderingVerification}.
+ */
+public class JitterVerificationTest extends TestCase {
+
+
+    public void testVerify() {
+        final int SAMPLE_SIZE = 100;
+
+        // 100 samples at 1000Hz
+        long[] timestamps = new long[SAMPLE_SIZE];
+        for (int i = 0; i < SAMPLE_SIZE; i++) {
+            timestamps[i] = i * 100000;
+        }
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(1000, 1, timestamps);
+        verification.verify(stats);
+        verifyStats(stats, true, 0.0);
+
+        // 90 samples at 1000Hz, 10 samples at 2000Hz
+        long timestamp = 0;
+        for (int i = 0; i < SAMPLE_SIZE; i++) {
+            timestamps[i] = timestamp;
+            timestamp += (i % 10 == 0) ? 500000 : 1000000;
+        }
+        stats = new SensorStats();
+        verification = getVerification(1000, 1, timestamps);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, 449494.9494);
+    }
+
+    public void testCalculateJitter() {
+        long[] timestamps = new long[]{0, 1, 2, 3, 4};
+        JitterVerification verification = getVerification(1000, 1, timestamps);
+        List<Double> jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(0.0, (double) jitterValues.get(0));
+        assertEquals(0.0, (double) jitterValues.get(1));
+        assertEquals(0.0, (double) jitterValues.get(2));
+        assertEquals(0.0, (double) jitterValues.get(3));
+
+        timestamps = new long[]{0, 0, 2, 4, 4};
+        verification = getVerification(1000, 1, timestamps);
+        jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(1.0, (double) jitterValues.get(0));
+        assertEquals(1.0, (double) jitterValues.get(1));
+        assertEquals(1.0, (double) jitterValues.get(2));
+        assertEquals(1.0, (double) jitterValues.get(3));
+
+        timestamps = new long[]{0, 1, 4, 9, 16};
+        verification = getVerification(1000, 1, timestamps);
+        jitterValues = verification.getJitterValues();
+        assertEquals(4, jitterValues.size());
+        assertEquals(4, jitterValues.size());
+        assertEquals(3.0, (double) jitterValues.get(0));
+        assertEquals(1.0, (double) jitterValues.get(1));
+        assertEquals(1.0, (double) jitterValues.get(2));
+        assertEquals(3.0, (double) jitterValues.get(3));
+    }
+
+    private JitterVerification getVerification(int expected, int threshold, long ... timestamps) {
+        JitterVerification verification = new JitterVerification(expected, threshold);
+        for (long timestamp : timestamps) {
+            verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, double jitter95) {
+        assertEquals(passed, stats.getValue(JitterVerification.PASSED_KEY));
+        assertEquals(jitter95, (Double) stats.getValue(SensorStats.JITTER_95_PERCENTILE_KEY), 0.1);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java
new file mode 100644
index 0000000..5e44273
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerification.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the mean of the magnitude of the sensors vector
+ * is within the expected range.
+ */
+public class MagnitudeVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "magnitude_passed";
+
+    // sensorType: {expected, threshold}
+    private static Map<Integer, Float[]> DEFAULTS = new HashMap<Integer, Float[]>(3);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final float mExpected;
+    private final float mThreshold;
+
+    private float mSum = 0.0f;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link MagnitudeVerification}
+     *
+     * @param expected the expected value
+     * @param threshold the threshold
+     */
+    public MagnitudeVerification(float expected, float threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link MagnitudeVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static MagnitudeVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+        Float expected = DEFAULTS.get(sensor.getType())[0];
+        Float threshold = DEFAULTS.get(sensor.getType())[1];
+        return new MagnitudeVerification(expected, threshold);
+    }
+
+    /**
+     * Verify that the magnitude is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MAGNITUDE_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float mean = mSum / mCount;
+        boolean failed = Math.abs(mean - mExpected) > mThreshold;
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.MAGNITUDE_KEY, mean);
+
+        if (failed) {
+            Assert.fail(String.format("Magnitude mean out of range: mean=%s (expected %s+/-%s)",
+                    mean, mExpected, mThreshold));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public MagnitudeVerification clone() {
+        return new MagnitudeVerification(mExpected, mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        float sumOfSquares = 0.0f;
+        for (float value : event.values) {
+            sumOfSquares += value * value;
+        }
+        mSum += (float) Math.sqrt(sumOfSquares);
+        mCount++;
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new Float[]{SensorManager.STANDARD_GRAVITY, 1.5f});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new Float[]{0.0f, 1.5f});
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_GRAVITY,
+                new Float[]{SensorManager.STANDARD_GRAVITY, Float.MAX_VALUE});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
new file mode 100644
index 0000000..9a50753
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link MagnitudeVerification}.
+ */
+public class MagnitudeVerificationTest extends TestCase {
+
+    /**
+     * Test {@link MagnitudeVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {
+                {0, 3, 4},
+                {4, 0, 3},
+                {3, 4, 0},
+                {0, 0, 4},
+                {6, 0, 0},
+        };
+
+        runStats(5.0f, 0.1f, values, true, 5.0f);
+        runStats(4.5f, 0.6f, values, true, 5.0f);
+        runStats(5.5f, 0.6f, values, true, 5.0f);
+        runStats(4.5f, 0.1f, values, false, 5.0f);
+        runStats(5.5f, 0.1f, values, false, 5.0f);
+    }
+
+    private void runStats(float expected, float threshold, float[][] values, boolean pass, float magnitude) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            try {
+                verification.verify(stats);
+                fail("Expected an AssertionError");
+            } catch (AssertionError e) {
+                // Expected;
+            }
+        }
+        assertEquals(pass, stats.getValue(MagnitudeVerification.PASSED_KEY));
+        assertEquals(magnitude, (Float) stats.getValue(SensorStats.MAGNITUDE_KEY), 0.01);
+    }
+
+    private ISensorVerification getVerification(float expected, float threshold,
+            float[] ... values) {
+        ISensorVerification verification = new MagnitudeVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
new file mode 100644
index 0000000..d6769d0
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerification.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the means matches the expected measurement.
+ */
+public class MeanVerification extends AbstractMeanVerification {
+    public static final String PASSED_KEY = "mean_passed";
+
+    // sensorType: {expected, threshold}
+    private static final Map<Integer, Object[]> DEFAULTS = new HashMap<Integer, Object[]>(5);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final float[] mExpected;
+    private final float[] mThreshold;
+
+    /**
+     * Construct a {@link MeanVerification}
+     *
+     * @param expected the expected values
+     * @param threshold the thresholds
+     */
+    public MeanVerification(float[] expected, float[] threshold) {
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link MeanVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static MeanVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+        float[] expected = (float[]) DEFAULTS.get(sensor.getType())[0];
+        float[] threshold = (float[]) DEFAULTS.get(sensor.getType())[1];
+        return new MeanVerification(expected, threshold);
+    }
+
+    /**
+     * Verify that the mean is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MEAN_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (getCount() < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float[] means = getMeans();
+
+        boolean failed = false;
+        StringBuilder meanSb = new StringBuilder();
+        StringBuilder expectedSb = new StringBuilder();
+
+        if (means.length > 1) {
+            meanSb.append("(");
+            expectedSb.append("(");
+        }
+        for (int i = 0; i < means.length; i++) {
+            if (Math.abs(means[i] - mExpected[i]) > mThreshold[i]) {
+                failed = true;
+            }
+            meanSb.append(String.format("%.2f", means[i]));
+            if (i != means.length - 1) meanSb.append(", ");
+            expectedSb.append(String.format("%.2f+/-%.2f", mExpected[i], mThreshold[i]));
+            if (i != means.length - 1) expectedSb.append(", ");
+        }
+        if (means.length > 1) {
+            meanSb.append(")");
+            expectedSb.append(")");
+        }
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.MEAN_KEY, means);
+
+        if (failed) {
+            Assert.fail(String.format("Mean out of range: mean=%s (expected %s)", meanSb.toString(),
+                    expectedSb.toString()));
+        }
+    }
+
+    @Override
+    public MeanVerification clone() {
+        return new MeanVerification(mExpected, mThreshold);
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        // Sensors that we don't want to test at this time but still want to record the values.
+        // Gyroscope should be 0 for a static device
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new Object[]{
+                new float[]{0.0f, 0.0f, 0.0f},
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        // Pressure will not be exact in a controlled environment but should be relatively close to
+        // sea level. Second values should always be 0.
+        DEFAULTS.put(Sensor.TYPE_PRESSURE, new Object[]{
+                new float[]{SensorManager.PRESSURE_STANDARD_ATMOSPHERE, 0.0f, 0.0f},
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        // Linear acceleration should be 0 in all directions for a static device
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION, new Object[]{
+                new float[]{0.0f, 0.0f, 0.0f},
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE}});
+        // Game rotation vector should be (0, 0, 0, 1, 0) for a static device
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR, new Object[]{
+                new float[]{0.0f, 0.0f, 0.0f, 1.0f, 0.0f},
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                        Float.MAX_VALUE}});
+        // Uncalibrated gyroscope should be 0 for a static device but allow a bigger threshold
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED, new Object[]{
+                new float[]{0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f},
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                        Float.MAX_VALUE, Float.MAX_VALUE}});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
new file mode 100644
index 0000000..94b6362
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link MeanVerification}.
+ */
+public class MeanVerificationTest extends TestCase {
+
+    /**
+     * Test {@link MeanVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {
+                {0, 1, 0},
+                {1, 2, 1},
+                {2, 3, 4},
+                {3, 4, 9},
+                {4, 5, 16},
+        };
+
+        float[] expected = {2.0f, 3.0f, 6.0f};
+        float[] threshold = {0.1f, 0.1f, 0.1f};
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        verification.verify(stats);
+        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.6f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        verification.verify(stats);
+        verifyStats(stats, true, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.1f, 0.6f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+
+        expected = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.1f, 0.6f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+
+        threshold = new float[]{2.5f, 2.5f, 5.5f};
+        threshold = new float[]{0.6f, 0.6f, 0.1f};
+        stats = new SensorStats();
+        verification = getVerification(expected, threshold, values);
+        try {
+            verification.verify(stats);
+            fail("Expected an AssertionError");
+        } catch (AssertionError e) {
+            // Expected;
+        }
+        verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
+    }
+
+    private ISensorVerification getVerification(float[] expected, float[] threshold,
+            float[] ... values) {
+        ISensorVerification verification = new MeanVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+
+    private void verifyStats(SensorStats stats, boolean passed, float[] means) {
+        assertEquals(passed, stats.getValue(MeanVerification.PASSED_KEY));
+        float[] actual = (float[]) stats.getValue(SensorStats.MEAN_KEY);
+        for (int i = 0; i < means.length; i++) {
+            assertEquals(means[i], actual[i], 0.1);
+        }
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java
new file mode 100644
index 0000000..9428d1d
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerification.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link ISensorVerification} which verifies that the sign of each of the sensor values is
+ * correct.
+ * <p>
+ * If the value of the measurement is in [-threshold, threshold], the sign is considered 0. If
+ * it is less than -threshold, it is considered -1. If it is greater than threshold, it is
+ * considered 1.
+ * </p>
+ */
+public class SigNumVerification extends AbstractMeanVerification {
+    public static final String PASSED_KEY = "sig_num_passed";
+
+    private final int[] mExpected;
+    private final float[] mThreshold;
+
+    /**
+     * Construct a {@link SigNumVerification}
+     *
+     * @param expected the expected values
+     * @param threshold the threshold that needs to be crossed to consider a measurement nonzero
+     * @throws IllegalStateException if the expected values are not 0, -1, or 1.
+     */
+    public SigNumVerification(int[] expected, float[] threshold) {
+        for (int i = 0; i < expected.length; i++) {
+            if (!(expected[i] == -1 || expected[i] == 0 || expected[i] == 1)) {
+                throw new IllegalArgumentException("Expected value must be -1, 0, or 1");
+            }
+        }
+
+        mExpected = expected;
+        mThreshold = threshold;
+    }
+
+    /**
+     * Verify that the sign of each of the sensor values is correct. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#MEAN_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (getCount() < 1) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float[] means = getMeans();
+
+        boolean failed = false;
+        StringBuilder meanSb = new StringBuilder();
+        StringBuilder expectedSb = new StringBuilder();
+
+        if (means.length > 1) {
+            meanSb.append("(");
+            expectedSb.append("(");
+        }
+        for (int i = 0; i < means.length; i++) {
+            meanSb.append(String.format("%.2f", means[i]));
+            if (i != means.length - 1) meanSb.append(", ");
+
+            if (mExpected[i] == 0) {
+                if (Math.abs(means[i]) > mThreshold[i]) {
+                    failed = true;
+                }
+                expectedSb.append(String.format("[%.2f, %.2f]", -mThreshold[i], mThreshold[i]));
+            } else {
+                if (mExpected[i] > 0) {
+                    if (means[i] <= mThreshold[i]) {
+                        failed = true;
+                    }
+                    expectedSb.append(String.format("(%.2f, inf)", mThreshold[i]));
+                } else {
+                    if (means[i] >= -1 * mThreshold[i]) {
+                        failed = true;
+                    }
+                    expectedSb.append(String.format("(-inf, %.2f)", -1 * mThreshold[i]));
+                }
+            }
+            if (i != means.length - 1) expectedSb.append(", ");
+        }
+        if (means.length > 1) {
+            meanSb.append(")");
+            expectedSb.append(")");
+        }
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.MEAN_KEY, means);
+
+        if (failed) {
+            Assert.fail(String.format("Signum out of range: mean=%s (expected %s)",
+                    meanSb.toString(), expectedSb.toString()));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public SigNumVerification clone() {
+        return new SigNumVerification(mExpected, mThreshold);
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java
new file mode 100644
index 0000000..009ab65
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/SigNumVerificationTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link SigNumVerification}.
+ */
+public class SigNumVerificationTest extends TestCase {
+
+    /**
+     * Test {@link SigNumVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        float[][] values = {{1.0f, 0.2f, 0.0f, -0.2f, -1.0f}};
+
+        int[] expected = {1, 1, 0, -1, -1};
+        float[] threshold = {0.1f, 0.1f, 0.1f, 0.1f, 0.1f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{1, 0, 0, 0, -1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{0, 1, 0, -1, 0};
+        threshold = new float[]{1.5f, 0.1f, 0.1f, 0.1f, 1.5f};
+        runVerification(true, expected, threshold, values);
+
+        expected = new int[]{1, 0, 0, 0, 1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(false, expected, threshold, values);
+
+        expected = new int[]{-1, 0, 0, 0, -1};
+        threshold = new float[]{0.5f, 0.5f, 0.5f, 0.5f, 0.5f};
+        runVerification(false, expected, threshold, values);
+    }
+
+    private SigNumVerification getVerification(int[] expected, float[] threshold,
+            float[] ... values) {
+        SigNumVerification verification = new SigNumVerification(expected, threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+
+    private void runVerification(boolean passed, int[] expected, float[] threshold,
+            float[][] values) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(expected, threshold, values);
+        if (passed) {
+            verification.verify(stats);
+        } else {
+            try {
+                verification.verify(stats);
+                fail("Expected an AssertionError");
+            } catch (AssertionError e) {
+                // Expected;
+            }
+        }
+        assertEquals(passed, stats.getValue(SigNumVerification.PASSED_KEY));
+        assertNotNull(stats.getValue(SensorStats.MEAN_KEY));
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
new file mode 100644
index 0000000..57b34b0
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerification.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link ISensorVerification} which verifies that the standard deviations is within the expected
+ * range.
+ */
+public class StandardDeviationVerification extends AbstractSensorVerification {
+    public static final String PASSED_KEY = "standard_deviation_passed";
+
+    // sensorType: threshold
+    private static final Map<Integer, float[]> DEFAULTS = new HashMap<Integer, float[]>(12);
+    static {
+        // Use a method so that the @deprecation warning can be set for that method only
+        setDefaults();
+    }
+
+    private final float[] mThreshold;
+
+    private float[] mMeans = null;
+    private float[] mM2s = null;
+    private int mCount = 0;
+
+    /**
+     * Construct a {@link StandardDeviationVerification}
+     *
+     * @param threshold the thresholds
+     */
+    public StandardDeviationVerification(float[] threshold) {
+        mThreshold = threshold;
+    }
+
+    /**
+     * Get the default {@link StandardDeviationVerification} for a sensor.
+     *
+     * @param sensor a {@link Sensor}
+     * @return the verification or null if the verification does not apply to the sensor.
+     */
+    public static StandardDeviationVerification getDefault(Sensor sensor) {
+        if (!DEFAULTS.containsKey(sensor.getType())) {
+            return null;
+        }
+
+        return new StandardDeviationVerification(DEFAULTS.get(sensor.getType()));
+    }
+
+    /**
+     * Verify that the standard deviation is in the acceptable range. Add {@value #PASSED_KEY} and
+     * {@value SensorStats#STANDARD_DEVIATION_KEY} keys to {@link SensorStats}.
+     *
+     * @throws AssertionError if the verification failed.
+     */
+    @Override
+    public void verify(SensorStats stats) {
+        if (mCount < 2) {
+            stats.addValue(PASSED_KEY, true);
+            return;
+        }
+
+        float[] stdDevs = new float[mM2s.length];
+        for (int i = 0; i < mM2s.length; i++) {
+            stdDevs[i] = (float) Math.sqrt(mM2s[i] / (mCount - 1));
+        }
+
+        boolean failed = false;
+        StringBuilder stddevSb = new StringBuilder();
+        StringBuilder expectedSb = new StringBuilder();
+
+        if (stdDevs.length > 1) {
+            stddevSb.append("(");
+            expectedSb.append("(");
+        }
+        for (int i = 0; i < stdDevs.length; i++) {
+            if (stdDevs[i] > mThreshold[i]) {
+                failed = true;
+            }
+            stddevSb.append(String.format("%.2f", stdDevs[i]));
+            if (i != stdDevs.length - 1) stddevSb.append(", ");
+            expectedSb.append(String.format("<%.2f", mThreshold[i]));
+            if (i != stdDevs.length - 1) expectedSb.append(", ");
+        }
+        if (stdDevs.length > 1) {
+            stddevSb.append(")");
+            expectedSb.append(")");
+        }
+
+        stats.addValue(PASSED_KEY, !failed);
+        stats.addValue(SensorStats.STANDARD_DEVIATION_KEY, stdDevs);
+
+        if (failed) {
+            Assert.fail(String.format("Standard deviation out of range: stddev=%s (expected %s)",
+                    stddevSb.toString(), expectedSb.toString()));
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public StandardDeviationVerification clone() {
+        return new StandardDeviationVerification(mThreshold);
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     * Computes the standard deviation using
+     * <a href="http://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#On-line_algorithm">
+     * Welford's algorith</a>.
+     * </p>
+     */
+    @Override
+    protected void addSensorEventInternal(TestSensorEvent event) {
+        if (mMeans == null || mM2s == null) {
+            mMeans = new float[event.values.length];
+            mM2s = new float[event.values.length];
+        }
+
+        Assert.assertEquals(mMeans.length, event.values.length);
+        Assert.assertEquals(mM2s.length, event.values.length);
+
+        mCount++;
+
+        for (int i = 0; i < event.values.length; i++) {
+            float delta = event.values[i] - mMeans[i];
+            mMeans[i] += delta / mCount;
+            mM2s[i] += delta * (event.values[i] - mMeans[i]);
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    private static void setDefaults() {
+        DEFAULTS.put(Sensor.TYPE_ACCELEROMETER, new float[]{1.0f, 1.0f, 1.0f});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE, new float[]{0.5f, 0.5f, 0.5f});
+        // Sensors that we don't want to test at this time but still want to record the values.
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_ORIENTATION,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_PRESSURE,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GRAVITY,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_LINEAR_ACCELERATION,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_MAGNETIC_FIELD_UNCALIBRATED,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GAME_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE, Float.MAX_VALUE});
+        DEFAULTS.put(Sensor.TYPE_GEOMAGNETIC_ROTATION_VECTOR,
+                new float[]{Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE,
+                Float.MAX_VALUE});
+    }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
new file mode 100644
index 0000000..308f114
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEvent;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for {@link StandardDeviationVerification}.
+ */
+public class StandardDeviationVerificationTest extends TestCase {
+
+    /**
+     * Test {@link StandardDeviationVerification#verify(SensorStats)}.
+     */
+    public void testVerify() {
+        // Stddev should be {sqrt(2.5), sqrt(2.5), sqrt(2.5)}
+        float[][] values = {
+                {0, 1, 0},
+                {1, 2, 2},
+                {2, 3, 4},
+                {3, 4, 6},
+                {4, 5, 8},
+        };
+        float[] standardDeviations = {
+                (float) Math.sqrt(2.5), (float) Math.sqrt(2.5), (float) Math.sqrt(10.0)
+        };
+
+        float[] threshold = {2, 2, 4};
+        runVerification(threshold, values, true, standardDeviations);
+
+        threshold = new float[]{1, 2, 4};
+        runVerification(threshold, values, false, standardDeviations);
+
+        threshold = new float[]{2, 1, 4};
+        runVerification(threshold, values, false, standardDeviations);
+
+        threshold = new float[]{2, 2, 3};
+        runVerification(threshold, values, false, standardDeviations);
+    }
+
+    private void runVerification(float[] threshold, float[][] values, boolean pass,
+            float[] standardDeviations) {
+        SensorStats stats = new SensorStats();
+        ISensorVerification verification = getVerification(threshold, values);
+        if (pass) {
+            verification.verify(stats);
+        } else {
+            boolean failed = false;
+            try {
+                verification.verify(stats);
+            } catch (AssertionError e) {
+                // Expected;
+                failed = true;
+            }
+            assertTrue("Expected an AssertionError", failed);
+        }
+        assertEquals(pass, stats.getValue(StandardDeviationVerification.PASSED_KEY));
+        float[] actual = (float[]) stats.getValue(SensorStats.STANDARD_DEVIATION_KEY);
+        for (int i = 0; i < standardDeviations.length; i++) {
+            assertEquals(standardDeviations[i], actual[i], 0.1);
+        }
+    }
+
+    private ISensorVerification getVerification(float[] threshold, float[] ... values) {
+        ISensorVerification verification = new StandardDeviationVerification(threshold);
+        for (float[] value : values) {
+            verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+        }
+        return verification;
+    }
+}
diff --git a/tests/tests/holo/Android.mk b/tests/tests/holo/Android.mk
index afc4e17..e21b7bd 100644
--- a/tests/tests/holo/Android.mk
+++ b/tests/tests/holo/Android.mk
@@ -20,8 +20,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/holo/AndroidManifest.xml b/tests/tests/holo/AndroidManifest.xml
index 1c59630..34f8e72 100644
--- a/tests/tests/holo/AndroidManifest.xml
+++ b/tests/tests/holo/AndroidManifest.xml
@@ -59,8 +59,11 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.holo"
-            android:label="CTS tests for the Holo theme" />
+            android:label="CTS tests for the Holo theme" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/jni/Android.mk b/tests/tests/jni/Android.mk
index 4f44e15..8b3edd2 100644
--- a/tests/tests/jni/Android.mk
+++ b/tests/tests/jni/Android.mk
@@ -18,16 +18,12 @@
 
 LOCAL_PACKAGE_NAME := CtsJniTestCases
 
-
 # Don't include this package in any target.
 LOCAL_MODULE_TAGS := optional
 
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_JNI_SHARED_LIBRARIES := libjnitest
diff --git a/tests/tests/jni/AndroidManifest.xml b/tests/tests/jni/AndroidManifest.xml
index c3407d1..843b322 100644
--- a/tests/tests/jni/AndroidManifest.xml
+++ b/tests/tests/jni/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.jni"
-                     android:label="CTS tests of calling native code via JNI"/>
+                     android:label="CTS tests of calling native code via JNI">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/keystore/Android.mk b/tests/tests/keystore/Android.mk
index f2dae38..0f2cd03 100644
--- a/tests/tests/keystore/Android.mk
+++ b/tests/tests/keystore/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_MODULE_TAGS := optional
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner core-tests-support
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/keystore/AndroidManifest.xml b/tests/tests/keystore/AndroidManifest.xml
index 0ce9f09..106a0dc 100644
--- a/tests/tests/keystore/AndroidManifest.xml
+++ b/tests/tests/keystore/AndroidManifest.xml
@@ -24,9 +24,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.keystore"
-                     android:label="CTS tests of com.android.cts.keystore"/>
+                     android:label="CTS tests of com.android.cts.keystore">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/location/Android.mk b/tests/tests/location/Android.mk
index b76672c..2503fc7 100644
--- a/tests/tests/location/Android.mk
+++ b/tests/tests/location/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,7 +29,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/location/AndroidManifest.xml b/tests/tests/location/AndroidManifest.xml
index 147f0ba..5016f49 100644
--- a/tests/tests/location/AndroidManifest.xml
+++ b/tests/tests/location/AndroidManifest.xml
@@ -27,8 +27,11 @@
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.location"
-                     android:label="CTS tests of android.location"/>
+                     android:label="CTS tests of android.location">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index 8028422..34bc0e4 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -81,7 +81,7 @@
         // remove test provider if left over from an aborted run
         LocationProvider lp = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         if (lp != null) {
-            removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+            mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         }
 
         addTestProvider(TEST_MOCK_PROVIDER_NAME);
@@ -107,7 +107,7 @@
     protected void tearDown() throws Exception {
         LocationProvider provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         if (provider != null) {
-            removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+            mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         }
         if (mPendingIntent != null) {
             mManager.removeProximityAlert(mPendingIntent);
@@ -138,12 +138,12 @@
             // expected
         }
 
-        removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+        mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         provider = mManager.getProvider(TEST_MOCK_PROVIDER_NAME);
         assertNull(provider);
 
         try {
-            removeTestProvider(UNKNOWN_PROVIDER_NAME);
+            mManager.removeTestProvider(UNKNOWN_PROVIDER_NAME);
             fail("Should throw IllegalArgumentException when no provider exists!");
         } catch (IllegalArgumentException e) {
             // expected
@@ -177,7 +177,7 @@
         assertEquals(oldSizeAllProviders, providers.size());
         assertTrue(hasTestProvider(providers));
 
-        removeTestProvider(TEST_MOCK_PROVIDER_NAME);
+        mManager.removeTestProvider(TEST_MOCK_PROVIDER_NAME);
         providers = mManager.getAllProviders();
         assertEquals(oldSizeAllProviders - 1, providers.size());
         assertFalse(hasTestProvider(providers));
@@ -275,8 +275,8 @@
 
         // Assert that if there are no test providers enabled, LocationManager just returns the
         // values from Settings.Secure.
-        forceClearTestProvider(LocationManager.GPS_PROVIDER);
-        forceClearTestProvider(LocationManager.NETWORK_PROVIDER);
+        forceRemoveTestProvider(LocationManager.GPS_PROVIDER);
+        forceRemoveTestProvider(LocationManager.NETWORK_PROVIDER);
         boolean lmGps = mManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
         boolean lmNlp = mManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
         assertEquals("Inconsistent GPS values", gps, lmGps);
@@ -304,20 +304,12 @@
     }
 
     /**
-     * Clears the test provider. Works around b/11446702 by temporarily adding the test provider
-     * so we are allowed to clear it.
+     * Ensures the test provider is removed. {@link LocationManager#removeTestProvider(String)}
+     * throws an {@link java.lang.IllegalArgumentException} if there is no such test provider,
+     * so we have to add it before we clear it.
      */
-    private void forceClearTestProvider(String provider) {
+    private void forceRemoveTestProvider(String provider) {
         addTestProvider(provider);
-        mManager.clearTestProviderEnabled(provider);
-        removeTestProvider(provider);
-    }
-
-    /**
-     * Work around b/11446702 by clearing the test provider before removing it
-     */
-    private void removeTestProvider(String provider) {
-        mManager.clearTestProviderEnabled(provider);
         mManager.removeTestProvider(provider);
     }
 
@@ -413,7 +405,7 @@
                     // run the update location test logic to ensure location updates can be injected
                     doLocationUpdatesWithLocationListener(providerName);
                 } finally {
-                    removeTestProvider(providerName);
+                    mManager.removeTestProvider(providerName);
                 }
             }
         }
@@ -825,7 +817,7 @@
     }
 
     private void unmockFusedLocation() {
-        removeTestProvider(FUSED_PROVIDER_NAME);
+        mManager.removeTestProvider(FUSED_PROVIDER_NAME);
     }
 
     /**
diff --git a/tests/tests/location2/Android.mk b/tests/tests/location2/Android.mk
index e89204d..6daca72 100644
--- a/tests/tests/location2/Android.mk
+++ b/tests/tests/location2/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,7 +29,7 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-#LOCAL_SDK_VERSION := current
+# uncomment when Location.EXTRA_NO_GPS_LOCATION is removed
+#LOCAL_SDK_VERSION := curren
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/location2/AndroidManifest.xml b/tests/tests/location2/AndroidManifest.xml
index 118278b..ad77b7e 100644
--- a/tests/tests/location2/AndroidManifest.xml
+++ b/tests/tests/location2/AndroidManifest.xml
@@ -27,8 +27,11 @@
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.location2"
-                     android:label="CTS tests of android.location"/>
+                     android:label="CTS tests of android.location">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 4474b4a..53983a8 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -21,14 +21,14 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestserver ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsMediaTestCases
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13249737 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index 90024c4..d53b2c6 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -66,9 +66,12 @@
         </service>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.media"
-                     android:label="CTS tests of android.media"/>
+                     android:label="CTS tests of android.media">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/media/res/raw/big5_1.mp3 b/tests/tests/media/res/raw/big5_1.mp3
new file mode 100644
index 0000000..faa3eb4
--- /dev/null
+++ b/tests/tests/media/res/raw/big5_1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/big5_2.mp3 b/tests/tests/media/res/raw/big5_2.mp3
new file mode 100644
index 0000000..a69da4f
--- /dev/null
+++ b/tests/tests/media/res/raw/big5_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_3.mp3 b/tests/tests/media/res/raw/cp1251_3.mp3
new file mode 100644
index 0000000..179a1a5
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_4.mp3 b/tests/tests/media/res/raw/cp1251_4.mp3
new file mode 100644
index 0000000..3df1d32
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_5.mp3 b/tests/tests/media/res/raw/cp1251_5.mp3
new file mode 100644
index 0000000..46df442
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_6.mp3 b/tests/tests/media/res/raw/cp1251_6.mp3
new file mode 100644
index 0000000..545834d
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_7.mp3 b/tests/tests/media/res/raw/cp1251_7.mp3
new file mode 100644
index 0000000..d1c492b
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_8.mp3 b/tests/tests/media/res/raw/cp1251_8.mp3
new file mode 100644
index 0000000..17f7e31
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_8.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_v1.mp3 b/tests/tests/media/res/raw/cp1251_v1.mp3
new file mode 100644
index 0000000..173d970
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_v1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/cp1251_v1v2.mp3 b/tests/tests/media/res/raw/cp1251_v1v2.mp3
new file mode 100644
index 0000000..abffa92
--- /dev/null
+++ b/tests/tests/media/res/raw/cp1251_v1v2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/football_qvga.yuv b/tests/tests/media/res/raw/football_qvga.yuv
new file mode 100644
index 0000000..f18f676
--- /dev/null
+++ b/tests/tests/media/res/raw/football_qvga.yuv
Binary files differ
diff --git a/tests/tests/media/res/raw/football_qvga_desc.txt b/tests/tests/media/res/raw/football_qvga_desc.txt
new file mode 100644
index 0000000..f6b44b2
--- /dev/null
+++ b/tests/tests/media/res/raw/football_qvga_desc.txt
@@ -0,0 +1,2 @@
+Football_qvga.yuv contains 3 seconds of raw 320x240 yuv420 video @ 30 fps.
+Extracted from http://media.xiph.org/video/derf/y4m/football_cif.y4m.
\ No newline at end of file
diff --git a/tests/tests/media/res/raw/gb18030_1.mp3 b/tests/tests/media/res/raw/gb18030_1.mp3
new file mode 100644
index 0000000..dc63de5
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_2.mp3 b/tests/tests/media/res/raw/gb18030_2.mp3
new file mode 100644
index 0000000..6109c97
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_3.mp3 b/tests/tests/media/res/raw/gb18030_3.mp3
new file mode 100644
index 0000000..4fcb22f
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_4.mp3 b/tests/tests/media/res/raw/gb18030_4.mp3
new file mode 100644
index 0000000..fedffd7
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_5.mp3 b/tests/tests/media/res/raw/gb18030_5.mp3
new file mode 100644
index 0000000..70f76ce
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_6.mp3 b/tests/tests/media/res/raw/gb18030_6.mp3
new file mode 100644
index 0000000..b4817b2
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_7.mp3 b/tests/tests/media/res/raw/gb18030_7.mp3
new file mode 100644
index 0000000..7932596
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/gb18030_8.mp3 b/tests/tests/media/res/raw/gb18030_8.mp3
new file mode 100644
index 0000000..f5f54de
--- /dev/null
+++ b/tests/tests/media/res/raw/gb18030_8.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/hebrew.mp3 b/tests/tests/media/res/raw/hebrew.mp3
new file mode 100644
index 0000000..59d76d8
--- /dev/null
+++ b/tests/tests/media/res/raw/hebrew.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/hebrew2.mp3 b/tests/tests/media/res/raw/hebrew2.mp3
new file mode 100644
index 0000000..d48cad2
--- /dev/null
+++ b/tests/tests/media/res/raw/hebrew2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/iso88591_1.ogg b/tests/tests/media/res/raw/iso88591_1.ogg
new file mode 100644
index 0000000..c20bf34
--- /dev/null
+++ b/tests/tests/media/res/raw/iso88591_1.ogg
Binary files differ
diff --git a/tests/tests/media/res/raw/iso88591_2.mp3 b/tests/tests/media/res/raw/iso88591_2.mp3
new file mode 100644
index 0000000..bcfdaad
--- /dev/null
+++ b/tests/tests/media/res/raw/iso88591_2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis1.mp3 b/tests/tests/media/res/raw/shiftjis1.mp3
new file mode 100644
index 0000000..1c50c76
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis1.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis2.mp3 b/tests/tests/media/res/raw/shiftjis2.mp3
new file mode 100644
index 0000000..808c597
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis2.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis3.mp3 b/tests/tests/media/res/raw/shiftjis3.mp3
new file mode 100644
index 0000000..820631b
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis3.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis4.mp3 b/tests/tests/media/res/raw/shiftjis4.mp3
new file mode 100644
index 0000000..3fbc25e
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis4.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis5.mp3 b/tests/tests/media/res/raw/shiftjis5.mp3
new file mode 100644
index 0000000..90520f8
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis5.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis6.mp3 b/tests/tests/media/res/raw/shiftjis6.mp3
new file mode 100644
index 0000000..5310936
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis6.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis7.mp3 b/tests/tests/media/res/raw/shiftjis7.mp3
new file mode 100644
index 0000000..6143126
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis7.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/shiftjis8.mp3 b/tests/tests/media/res/raw/shiftjis8.mp3
new file mode 100644
index 0000000..c45c130
--- /dev/null
+++ b/tests/tests/media/res/raw/shiftjis8.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/testmp3_3.raw b/tests/tests/media/res/raw/testmp3_3.raw
new file mode 100644
index 0000000..1d5ee19
--- /dev/null
+++ b/tests/tests/media/res/raw/testmp3_3.raw
@@ -0,0 +1 @@
+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
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4
new file mode 100644
index 0000000..19c4e06
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4
new file mode 100644
index 0000000..c321586
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 090cde8..b076e6b 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -447,10 +447,9 @@
     // Playback properties
     // ----------------------------------
 
-    // Test case 1: setStereoVolume() with max volume returns SUCCESS
-    public void testSetStereoVolumeMax() throws Exception {
+    // Common code for the testSetStereoVolume* and testSetVolume* tests
+    private void testSetVolumeCommon(String testName, float vol, boolean isStereo) throws Exception {
         // constants for test
-        final String TEST_NAME = "testSetStereoVolumeMax";
         final int TEST_SR = 22050;
         final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
         final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
@@ -466,61 +465,35 @@
         track.write(data, OFFSET_DEFAULT, data.length);
         track.write(data, OFFSET_DEFAULT, data.length);
         track.play();
-        float maxVol = AudioTrack.getMaxVolume();
-        assertTrue(TEST_NAME, track.setStereoVolume(maxVol, maxVol) == AudioTrack.SUCCESS);
+        if (isStereo) {
+            // TODO to really test this, do a pan instead of using same value for left and right
+            assertTrue(testName, track.setStereoVolume(vol, vol) == AudioTrack.SUCCESS);
+        } else {
+            assertTrue(testName, track.setVolume(vol) == AudioTrack.SUCCESS);
+        }
         // -------- tear down --------------
         track.release();
     }
 
+    // Test case 1: setStereoVolume() with max volume returns SUCCESS
+    public void testSetStereoVolumeMax() throws Exception {
+        final String TEST_NAME = "testSetStereoVolumeMax";
+        float maxVol = AudioTrack.getMaxVolume();
+        testSetVolumeCommon(TEST_NAME, maxVol, true /*isStereo*/);
+    }
+
     // Test case 2: setStereoVolume() with min volume returns SUCCESS
     public void testSetStereoVolumeMin() throws Exception {
-        // constants for test
         final String TEST_NAME = "testSetStereoVolumeMin";
-        final int TEST_SR = 22050;
-        final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
-        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
-        final int TEST_MODE = AudioTrack.MODE_STREAM;
-        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
-        // -------- initialization --------------
-        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
-                2 * minBuffSize, TEST_MODE);
-        byte data[] = new byte[minBuffSize];
-        // -------- test --------------
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.play();
         float minVol = AudioTrack.getMinVolume();
-        assertTrue(TEST_NAME, track.setStereoVolume(minVol, minVol) == AudioTrack.SUCCESS);
-        // -------- tear down --------------
-        track.release();
+        testSetVolumeCommon(TEST_NAME, minVol, true /*isStereo*/);
     }
 
     // Test case 3: setStereoVolume() with mid volume returns SUCCESS
     public void testSetStereoVolumeMid() throws Exception {
-        // constants for test
         final String TEST_NAME = "testSetStereoVolumeMid";
-        final int TEST_SR = 22050;
-        final int TEST_CONF = AudioFormat.CHANNEL_CONFIGURATION_STEREO;
-        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
-        final int TEST_MODE = AudioTrack.MODE_STREAM;
-        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-
-        // -------- initialization --------------
-        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
-        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
-                2 * minBuffSize, TEST_MODE);
-        byte data[] = new byte[minBuffSize];
-        // -------- test --------------
-
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.write(data, OFFSET_DEFAULT, data.length);
-        track.play();
         float midVol = (AudioTrack.getMaxVolume() - AudioTrack.getMinVolume()) / 2;
-        assertTrue(TEST_NAME, track.setStereoVolume(midVol, midVol) == AudioTrack.SUCCESS);
-        // -------- tear down --------------
-        track.release();
+        testSetVolumeCommon(TEST_NAME, midVol, true /*isStereo*/);
     }
 
     // Test case 4: setPlaybackRate() with half the content rate returns SUCCESS
@@ -645,6 +618,27 @@
         track.release();
     }
 
+    // Test case 9: setVolume() with max volume returns SUCCESS
+    public void testSetVolumeMax() throws Exception {
+        final String TEST_NAME = "testSetVolumeMax";
+        float maxVol = AudioTrack.getMaxVolume();
+        testSetVolumeCommon(TEST_NAME, maxVol, false /*isStereo*/);
+    }
+
+    // Test case 10: setVolume() with min volume returns SUCCESS
+    public void testSetVolumeMin() throws Exception {
+        final String TEST_NAME = "testSetVolumeMin";
+        float minVol = AudioTrack.getMinVolume();
+        testSetVolumeCommon(TEST_NAME, minVol, false /*isStereo*/);
+    }
+
+    // Test case 11: setVolume() with mid volume returns SUCCESS
+    public void testSetVolumeMid() throws Exception {
+        final String TEST_NAME = "testSetVolumeMid";
+        float midVol = (AudioTrack.getMaxVolume() - AudioTrack.getMinVolume()) / 2;
+        testSetVolumeCommon(TEST_NAME, midVol, false /*isStereo*/);
+    }
+
     // -----------------------------------------------------------------
     // Playback progress
     // ----------------------------------
diff --git a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
index 0d83647..bb430e3 100644
--- a/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
+++ b/tests/tests/media/src/android/media/cts/DecodeEditEncodeTest.java
@@ -149,7 +149,8 @@
     /**
      * Tests editing of a video file with GL.
      */
-    private void videoEditTest() {
+    private void videoEditTest()
+            throws IOException {
         VideoChunks sourceChunks = new VideoChunks();
 
         if (!generateVideoFile(sourceChunks)) {
@@ -182,7 +183,8 @@
      *
      * @return true on success, false on "soft" failure
      */
-    private boolean generateVideoFile(VideoChunks output) {
+    private boolean generateVideoFile(VideoChunks output)
+            throws IOException {
         if (VERBOSE) Log.d(TAG, "generateVideoFile " + mWidth + "x" + mHeight);
         MediaCodec encoder = null;
         InputSurface inputSurface = null;
@@ -315,7 +317,7 @@
                     encoderOutputBuffers = encoder.getOutputBuffers();
                     if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                    // not expected for an encoder
+                    // expected on API 18+
                     MediaFormat newFormat = encoder.getOutputFormat();
                     if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                 } else if (encoderStatus < 0) {
@@ -393,7 +395,8 @@
      * for output and a Surface for input, we can avoid issues with obscure formats and can
      * use a fragment shader to do transformations.
      */
-    private VideoChunks editVideoFile(VideoChunks inputData) {
+    private VideoChunks editVideoFile(VideoChunks inputData)
+            throws IOException {
         if (VERBOSE) Log.d(TAG, "editVideoFile " + mWidth + "x" + mHeight);
         VideoChunks outputData = new VideoChunks();
         MediaCodec decoder = null;
@@ -613,7 +616,8 @@
      * Checks the video file to see if the contents match our expectations.  We decode the
      * video to a Surface and check the pixels with GL.
      */
-    private void checkVideoFile(VideoChunks inputData) {
+    private void checkVideoFile(VideoChunks inputData)
+            throws IOException {
         OutputSurface surface = null;
         MediaCodec decoder = null;
 
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index f250c3d..0b6e0d7 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -33,6 +33,7 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.List;
 import java.util.zip.CRC32;
 
 public class DecoderTest extends MediaPlayerTestBase {
@@ -41,6 +42,12 @@
     private static final int RESET_MODE_NONE = 0;
     private static final int RESET_MODE_RECONFIGURE = 1;
     private static final int RESET_MODE_FLUSH = 2;
+    private static final int RESET_MODE_EOS_FLUSH = 3;
+
+    private static final String[] CSD_KEYS = new String[] { "csd-0", "csd-1" };
+
+    private static final int CONFIG_MODE_NONE = 0;
+    private static final int CONFIG_MODE_QUEUE = 1;
 
     private Resources mResources;
     short[] mMasterBuffer;
@@ -69,6 +76,20 @@
         masterFd.close();
     }
 
+    // TODO: add similar tests for other audio and video formats
+    public void testBug11696552() throws Exception {
+        MediaCodec mMediaCodec = MediaCodec.createDecoderByType("audio/mp4a-latm");
+        MediaFormat mFormat = MediaFormat.createAudioFormat("audio/mp4a-latm", 48000, 2);
+        mFormat.setByteBuffer("csd-0", ByteBuffer.wrap( new byte [] {0x13, 0x10} ));
+        mFormat.setInteger(MediaFormat.KEY_IS_ADTS, 1);
+        mMediaCodec.configure(mFormat, null, null, 0);
+        mMediaCodec.start();
+        int index = mMediaCodec.dequeueInputBuffer(250000);
+        mMediaCodec.queueInputBuffer(index, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM );
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        mMediaCodec.dequeueOutputBuffer(info, 250000);
+    }
+
     // The allowed errors in the following tests are the actual maximum measured
     // errors with the standard decoders, plus 10%.
     // This should allow for some variation in decoders, while still detecting
@@ -104,8 +125,257 @@
         monoTest(R.raw.monotestogg);
     }
 
+    public void testTrackSelection() throws Exception {
+        testTrackSelection(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz);
+        testTrackSelection(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
+        testTrackSelection(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
+    }
+
+    private void testTrackSelection(int resid) throws Exception {
+        AssetFileDescriptor fd1 = null;
+        try {
+            fd1 = mResources.openRawResourceFd(resid);
+            MediaExtractor ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+
+            ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
+            ArrayList<Integer> vid = new ArrayList<Integer>();
+            ArrayList<Integer> aud = new ArrayList<Integer>();
+
+            // scan the file once and build lists of audio and video samples
+            ex1.selectTrack(0);
+            ex1.selectTrack(1);
+            while(true) {
+                int n1 = ex1.readSampleData(buf1, 0);
+                if (n1 < 0) {
+                    break;
+                }
+                int idx = ex1.getSampleTrackIndex();
+                if (idx == 0) {
+                    vid.add(n1);
+                } else if (idx == 1) {
+                    aud.add(n1);
+                } else {
+                    fail("unexpected track index: " + idx);
+                }
+                ex1.advance();
+            }
+
+            // read the video track once, then rewind and do it again, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            ex1.selectTrack(0);
+            for (int i = 0; i < 2; i++) {
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        assertEquals(vid.size(), idx);
+                        break;
+                    }
+                    assertEquals(vid.get(idx++).intValue(), n1);
+                    ex1.advance();
+                }
+            }
+
+            // read the audio track once, then rewind and do it again, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            ex1.selectTrack(1);
+            for (int i = 0; i < 2; i++) {
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        assertEquals(aud.size(), idx);
+                        break;
+                    }
+                    assertEquals(aud.get(idx++).intValue(), n1);
+                    ex1.advance();
+                }
+            }
+
+            // read the video track first, then rewind and get the audio track instead, and
+            // verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(i);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int idx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (i == 0) {
+                        if (n1 < 0) {
+                            assertEquals(vid.size(), idx);
+                            break;
+                        }
+                        assertEquals(vid.get(idx++).intValue(), n1);
+                    } else if (i == 1) {
+                        if (n1 < 0) {
+                            assertEquals(aud.size(), idx);
+                            break;
+                        }
+                        assertEquals(aud.get(idx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + idx);
+                    }
+                    ex1.advance();
+                }
+                ex1.unselectTrack(i);
+            }
+
+            // read the video track first, then rewind, enable the audio track in addition
+            // to the video track, and verify we get the right samples
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(i);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int vididx = 0;
+                int audidx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        // we should have read all audio and all video samples at this point
+                        assertEquals(vid.size(), vididx);
+                        if (i == 1) {
+                            assertEquals(aud.size(), audidx);
+                        }
+                        break;
+                    }
+                    int trackidx = ex1.getSampleTrackIndex();
+                    if (trackidx == 0) {
+                        assertEquals(vid.get(vididx++).intValue(), n1);
+                    } else if (trackidx == 1) {
+                        assertEquals(aud.get(audidx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + trackidx);
+                    }
+                    ex1.advance();
+                }
+            }
+
+            // read both tracks from the start, then rewind and verify we get the right
+            // samples both times
+            ex1.release();
+            ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+            for (int i = 0; i < 2; i++) {
+                ex1.selectTrack(0);
+                ex1.selectTrack(1);
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                int vididx = 0;
+                int audidx = 0;
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    if (n1 < 0) {
+                        // we should have read all audio and all video samples at this point
+                        assertEquals(vid.size(), vididx);
+                        assertEquals(aud.size(), audidx);
+                        break;
+                    }
+                    int trackidx = ex1.getSampleTrackIndex();
+                    if (trackidx == 0) {
+                        assertEquals(vid.get(vididx++).intValue(), n1);
+                    } else if (trackidx == 1) {
+                        assertEquals(aud.get(audidx++).intValue(), n1);
+                    } else {
+                        fail("unexpected track index: " + trackidx);
+                    }
+                    ex1.advance();
+                }
+            }
+
+        } finally {
+            if (fd1 != null) {
+                fd1.close();
+            }
+        }
+    }
+
+    public void testDecodeFragmented() throws Exception {
+        testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented);
+        testDecodeFragmented(R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz,
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_dash);
+    }
+
+    private void testDecodeFragmented(int reference, int teststream) throws Exception {
+        AssetFileDescriptor fd1 = null;
+        AssetFileDescriptor fd2 = null;
+        try {
+            fd1 = mResources.openRawResourceFd(reference);
+            MediaExtractor ex1 = new MediaExtractor();
+            ex1.setDataSource(fd1.getFileDescriptor(), fd1.getStartOffset(), fd1.getLength());
+
+            fd2 = mResources.openRawResourceFd(teststream);
+            MediaExtractor ex2 = new MediaExtractor();
+            ex2.setDataSource(fd2.getFileDescriptor(), fd2.getStartOffset(), fd2.getLength());
+
+            assertEquals("different track count", ex1.getTrackCount(), ex2.getTrackCount());
+
+            ByteBuffer buf1 = ByteBuffer.allocate(1024*1024);
+            ByteBuffer buf2 = ByteBuffer.allocate(1024*1024);
+
+            for (int i = 0; i < ex1.getTrackCount(); i++) {
+                // note: this assumes the tracks are reported in the order in which they appear
+                // in the file.
+                ex1.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                ex1.selectTrack(i);
+                ex2.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                ex2.selectTrack(i);
+
+                while(true) {
+                    int n1 = ex1.readSampleData(buf1, 0);
+                    int n2 = ex2.readSampleData(buf2, 0);
+                    assertEquals("different buffer size on track " + i, n1, n2);
+
+                    if (n1 < 0) {
+                        break;
+                    }
+                    // see bug 13008204
+                    buf1.limit(n1);
+                    buf2.limit(n2);
+                    buf1.rewind();
+                    buf2.rewind();
+
+                    assertEquals("limit does not match return value on track " + i,
+                            n1, buf1.limit());
+                    assertEquals("limit does not match return value on track " + i,
+                            n2, buf2.limit());
+
+                    assertEquals("buffer data did not match on track " + i, buf1, buf2);
+
+                    ex1.advance();
+                    ex2.advance();
+                }
+                ex1.unselectTrack(i);
+                ex2.unselectTrack(i);
+            }
+        } finally {
+            if (fd1 != null) {
+                fd1.close();
+            }
+            if (fd2 != null) {
+                fd2.close();
+            }
+        }
+    }
+
+
     private void monoTest(int res) throws Exception {
-        short [] mono = decodeToMemory(res, RESET_MODE_NONE);
+        short [] mono = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
         if (mono.length == 44100) {
             // expected
         } else if (mono.length == 88200) {
@@ -118,14 +388,11 @@
             fail("wrong number of samples: " + mono.length);
         }
 
-        // we should get the same data when reconfiguring the codec
-        short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE);
+        short [] mono2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, null);
         assertTrue(Arrays.equals(mono, mono2));
 
-        // NOTE: coming soon
-        // and when flushing it
-//        short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH);
-//        assertTrue(Arrays.equals(mono, mono3));
+        short [] mono3 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, null);
+        assertTrue(Arrays.equals(mono, mono3));
     }
 
     /**
@@ -135,7 +402,7 @@
      */
     private void decode(int testinput, float maxerror) throws IOException {
 
-        short [] decoded = decodeToMemory(testinput, RESET_MODE_NONE);
+        short[] decoded = decodeToMemory(testinput, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, null);
 
         assertEquals("wrong data size", mMasterBuffer.length, decoded.length);
 
@@ -152,22 +419,54 @@
         double rmse = Math.sqrt(avgErrorSquared);
         assertTrue("decoding error too big: " + rmse, rmse <= maxerror);
 
-        short [] decoded2 = decodeToMemory(testinput, RESET_MODE_RECONFIGURE);
-        assertEquals("count different with reconfigure", decoded.length, decoded2.length);
-        for (int i = 0; i < decoded.length; i++) {
-            assertEquals("samples don't match", decoded[i], decoded2[i]);
-        }
+        int[] resetModes = new int[] { RESET_MODE_NONE, RESET_MODE_RECONFIGURE,
+                RESET_MODE_FLUSH, RESET_MODE_EOS_FLUSH };
+        int[] configModes = new int[] { CONFIG_MODE_NONE, CONFIG_MODE_QUEUE };
 
-        // NOTE: coming soon
-//        short [] decoded3 = decodeToMemory(testinput, RESET_MODE_FLUSH);
-//        assertEquals("count different with flush", decoded.length, decoded3.length);
-//        for (int i = 0; i < decoded.length; i++) {
-//            assertEquals("samples don't match", decoded[i], decoded3[i]);
-//        }
+        for (int conf : configModes) {
+            for (int reset : resetModes) {
+                if (conf == CONFIG_MODE_NONE && reset == RESET_MODE_NONE) {
+                    // default case done outside of loop
+                    continue;
+                }
+                if (conf == CONFIG_MODE_QUEUE && !hasAudioCsd(testinput)) {
+                    continue;
+                }
+
+                String params = String.format("(using reset: %d, config: %s)", reset, conf);
+                short[] decoded2 = decodeToMemory(testinput, reset, conf, -1, null);
+                assertEquals("count different with reconfigure" + params,
+                        decoded.length, decoded2.length);
+                for (int i = 0; i < decoded.length; i++) {
+                    assertEquals("samples don't match" + params, decoded[i], decoded2[i]);
+                }
+            }
+        }
     }
 
-    private short[] decodeToMemory(int testinput, int resetMode) throws IOException {
+    private boolean hasAudioCsd(int testinput) throws IOException {
+        AssetFileDescriptor fd = null;
+        try {
 
+            fd = mResources.openRawResourceFd(testinput);
+            MediaExtractor extractor = new MediaExtractor();
+            extractor.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
+            MediaFormat format = extractor.getTrackFormat(0);
+
+            return format.containsKey(CSD_KEYS[0]);
+
+        } finally {
+            if (fd != null) {
+                fd.close();
+            }
+        }
+    }
+
+    private short[] decodeToMemory(int testinput, int resetMode, int configMode,
+            int eossample, List<Long> timestamps) throws IOException {
+
+        String localTag = TAG + "#decodeToMemory";
+        Log.v(localTag, String.format("reset = %d; config: %s", resetMode, configMode));
         short [] decoded = new short[0];
         int decodedIdx = 0;
 
@@ -188,15 +487,32 @@
         String mime = format.getString(MediaFormat.KEY_MIME);
         assertTrue("not an audio file", mime.startsWith("audio/"));
 
+        MediaFormat configFormat = format;
         codec = MediaCodec.createDecoderByType(mime);
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+        if (configMode == CONFIG_MODE_QUEUE && format.containsKey(CSD_KEYS[0])) {
+            configFormat = MediaFormat.createAudioFormat(mime,
+                    format.getInteger(MediaFormat.KEY_SAMPLE_RATE),
+                    format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
+
+            configFormat.setLong(MediaFormat.KEY_DURATION,
+                    format.getLong(MediaFormat.KEY_DURATION));
+            String[] keys = new String[] { "max-input-size", "encoder-delay", "encoder-padding" };
+            for (String k : keys) {
+                if (format.containsKey(k)) {
+                    configFormat.setInteger(k, format.getInteger(k));
+                }
+            }
+        }
+        Log.v(localTag, "configuring with " + configFormat);
+        codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
+
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
         codecOutputBuffers = codec.getOutputBuffers();
 
         if (resetMode == RESET_MODE_RECONFIGURE) {
             codec.stop();
-            codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
+            codec.configure(configFormat, null /* surface */, null /* crypto */, 0 /* flags */);
             codec.start();
             codecInputBuffers = codec.getInputBuffers();
             codecOutputBuffers = codec.getOutputBuffers();
@@ -206,12 +522,17 @@
 
         extractor.selectTrack(0);
 
+        if (configMode == CONFIG_MODE_QUEUE) {
+            queueConfig(codec, format);
+        }
+
         // start decoding
         final long kTimeOutUs = 5000;
         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
         boolean sawInputEOS = false;
         boolean sawOutputEOS = false;
         int noOutputCounter = 0;
+        int samplecounter = 0;
         while (!sawOutputEOS && noOutputCounter < 50) {
             noOutputCounter++;
             if (!sawInputEOS) {
@@ -225,14 +546,20 @@
 
                     long presentationTimeUs = 0;
 
+                    if (sampleSize < 0 && eossample > 0) {
+                        fail("test is broken: never reached eos sample");
+                    }
                     if (sampleSize < 0) {
                         Log.d(TAG, "saw input EOS.");
                         sawInputEOS = true;
                         sampleSize = 0;
                     } else {
+                        if (samplecounter == eossample) {
+                            sawInputEOS = true;
+                        }
+                        samplecounter++;
                         presentationTimeUs = extractor.getSampleTime();
                     }
-
                     codec.queueInputBuffer(
                             inputBufIndex,
                             0 /* offset */,
@@ -253,22 +580,33 @@
 
                 if (info.size > 0) {
                     noOutputCounter = 0;
+                    if (timestamps != null) {
+                        timestamps.add(info.presentationTimeUs);
+                    }
                 }
-                if (info.size > 0 && resetMode != RESET_MODE_NONE) {
+                if (info.size > 0 &&
+                        resetMode != RESET_MODE_NONE && resetMode != RESET_MODE_EOS_FLUSH) {
                     // once we've gotten some data out of the decoder, reset and start again
                     if (resetMode == RESET_MODE_RECONFIGURE) {
                         codec.stop();
-                        codec.configure(format, null /* surface */, null /* crypto */,
+                        codec.configure(configFormat, null /* surface */, null /* crypto */,
                                 0 /* flags */);
                         codec.start();
                         codecInputBuffers = codec.getInputBuffers();
                         codecOutputBuffers = codec.getOutputBuffers();
+                        if (configMode == CONFIG_MODE_QUEUE) {
+                            queueConfig(codec, format);
+                        }
                     } else /* resetMode == RESET_MODE_FLUSH */ {
                         codec.flush();
                     }
                     resetMode = RESET_MODE_NONE;
                     extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
                     sawInputEOS = false;
+                    samplecounter = 0;
+                    if (timestamps != null) {
+                        timestamps.clear();
+                    }
                     continue;
                 }
 
@@ -279,15 +617,29 @@
                     decoded = Arrays.copyOf(decoded, decodedIdx + (info.size / 2));
                 }
 
+                buf.position(info.offset);
                 for (int i = 0; i < info.size; i += 2) {
-                    decoded[decodedIdx++] = buf.getShort(i);
+                    decoded[decodedIdx++] = buf.getShort();
                 }
 
                 codec.releaseOutputBuffer(outputBufIndex, false /* render */);
 
                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                     Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
+                    if (resetMode == RESET_MODE_EOS_FLUSH) {
+                        resetMode = RESET_MODE_NONE;
+                        codec.flush();
+                        extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+                        sawInputEOS = false;
+                        samplecounter = 0;
+                        decoded = new short[0];
+                        decodedIdx = 0;
+                        if (timestamps != null) {
+                            timestamps.clear();
+                        }
+                    } else {
+                        sawOutputEOS = true;
+                    }
                 }
             } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                 codecOutputBuffers = codec.getOutputBuffers();
@@ -301,12 +653,106 @@
                 Log.d(TAG, "dequeueOutputBuffer returned " + res);
             }
         }
+        if (noOutputCounter >= 50) {
+            fail("decoder stopped outputing data");
+        }
 
         codec.stop();
         codec.release();
         return decoded;
     }
 
+    private void queueConfig(MediaCodec codec, MediaFormat format) {
+        for (String csdKey : CSD_KEYS) {
+            if (!format.containsKey(csdKey)) {
+                continue;
+            }
+            ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
+            int inputBufIndex = codec.dequeueInputBuffer(-1);
+            if (inputBufIndex < 0) {
+                fail("failed to queue configuration buffer " + csdKey);
+            } else {
+                ByteBuffer csd = (ByteBuffer) format.getByteBuffer(csdKey).rewind();
+                Log.v(TAG + "#queueConfig", String.format("queueing %s:%s", csdKey, csd));
+                codecInputBuffers[inputBufIndex].put(csd);
+                codec.queueInputBuffer(
+                        inputBufIndex,
+                        0 /* offset */,
+                        csd.limit(),
+                        0 /* presentation time (us) */,
+                        MediaCodec.BUFFER_FLAG_CODEC_CONFIG);
+            }
+        }
+    }
+
+    public void testDecodeWithEOSOnLastBuffer() throws Exception {
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepm4a);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3lame);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepmp3smpb);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepwav);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepflac);
+        testDecodeWithEOSOnLastBuffer(R.raw.sinesweepogg);
+    }
+
+    /* setting EOS on the last full input buffer should be equivalent to setting EOS on an empty
+     * input buffer after all the full ones. */
+    private void testDecodeWithEOSOnLastBuffer(int res) throws Exception {
+        int numsamples = countSamples(res);
+        assertTrue(numsamples != 0);
+
+        List<Long> timestamps1 = new ArrayList<Long>();
+        short[] decode1 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, -1, timestamps1);
+
+        List<Long> timestamps2 = new ArrayList<Long>();
+        short[] decode2 = decodeToMemory(res, RESET_MODE_NONE, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+
+        // check that the data and the timestamps are the same for EOS-on-last and EOS-after-last
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertEquals(timestamps1.size(), timestamps2.size());
+        assertTrue(timestamps1.equals(timestamps2));
+
+        // ... and that this is also true when reconfiguring the codec
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, -1, timestamps2);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_RECONFIGURE, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+
+        // ... and that this is also true when flushing the codec
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, -1, timestamps2);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+        timestamps2.clear();
+        decode2 = decodeToMemory(res, RESET_MODE_FLUSH, CONFIG_MODE_NONE, numsamples - 1,
+                timestamps2);
+        assertEquals(decode1.length, decode2.length);
+        assertTrue(Arrays.equals(decode1, decode2));
+        assertTrue(timestamps1.equals(timestamps2));
+    }
+
+    private int countSamples(int res) throws IOException {
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(res);
+
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        testFd.close();
+        extractor.selectTrack(0);
+        int numsamples = 0;
+        while (extractor.advance()) {
+            numsamples++;
+        }
+        return numsamples;
+    }
+
     public void testCodecBasicH264() throws Exception {
         Surface s = getActivity().getSurfaceHolder().getSurface();
         int frames1 = countFrames(
@@ -472,21 +918,21 @@
 //    }
 
     public void testCodecResetsMp3() throws Exception {
-        testCodecReconfig(R.raw.sinesweepmp3lame, null);
+        testCodecReconfig(R.raw.sinesweepmp3lame);
         // NOTE: replacing testCodecReconfig call soon
 //        testCodecResets(R.raw.sinesweepmp3lame, null);
     }
 
     public void testCodecResetsM4a() throws Exception {
-        testCodecReconfig(R.raw.sinesweepm4a, null);
+        testCodecReconfig(R.raw.sinesweepm4a);
         // NOTE: replacing testCodecReconfig call soon
 //        testCodecResets(R.raw.sinesweepm4a, null);
     }
 
-    private void testCodecReconfig(int video, Surface s) throws Exception {
-        int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s);
-        int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s);
-        assertEquals("different number of frames when using reconfigured codec", frames1, frames2);
+    private void testCodecReconfig(int audio) throws Exception {
+        int size1 = countSize(audio, RESET_MODE_NONE, -1 /* eosframe */);
+        int size2 = countSize(audio, RESET_MODE_RECONFIGURE, -1 /* eosframe */);
+        assertEquals("different output size when using reconfigured codec", size1, size2);
     }
 
     private void testCodecResets(int video, Surface s) throws Exception {
@@ -497,55 +943,146 @@
         assertEquals("different number of frames when using flushed codec", frames1, frames3);
     }
 
-    private MediaCodec createDecoder(String mime) {
-        if (false) {
-            // change to force testing software codecs
-            if (mime.contains("avc")) {
-                return MediaCodec.createByCodecName("OMX.google.h264.decoder");
-            } else if (mime.contains("3gpp")) {
-                return MediaCodec.createByCodecName("OMX.google.h263.decoder");
-            } else if (mime.contains("mp4v")) {
-                return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
-            } else if (mime.contains("vp8")) {
-                return MediaCodec.createByCodecName("OMX.google.vp8.decoder");
-            } else if (mime.contains("vp9")) {
-                return MediaCodec.createByCodecName("OMX.google.vp9.decoder");
+    private static MediaCodec createDecoder(String mime) {
+        try {
+            if (false) {
+                // change to force testing software codecs
+                if (mime.contains("avc")) {
+                    return MediaCodec.createByCodecName("OMX.google.h264.decoder");
+                } else if (mime.contains("3gpp")) {
+                    return MediaCodec.createByCodecName("OMX.google.h263.decoder");
+                } else if (mime.contains("mp4v")) {
+                    return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
+                } else if (mime.contains("vp8")) {
+                    return MediaCodec.createByCodecName("OMX.google.vp8.decoder");
+                } else if (mime.contains("vp9")) {
+                    return MediaCodec.createByCodecName("OMX.google.vp9.decoder");
+                }
             }
+            return MediaCodec.createDecoderByType(mime);
+        } catch (Exception e) {
+            return null;
         }
-        return MediaCodec.createDecoderByType(mime);
     }
 
+    // for video
     private int countFrames(int video, int resetMode, int eosframe, Surface s)
             throws Exception {
-        int numframes = 0;
-
         AssetFileDescriptor testFd = mResources.openRawResourceFd(video);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0);
 
-        MediaExtractor extractor;
-        MediaCodec codec = null;
+        int numframes = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTFRAMES
+                | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH, resetMode, s,
+                eosframe, null, null);
+
+        extractor.release();
+        testFd.close();
+        return numframes;
+    }
+
+    // for audio
+    private int countSize(int audio, int resetMode, int eosframe)
+            throws Exception {
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(audio);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0);
+
+        // fails CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH
+        int outputSize = decodeWithChecks(extractor, CHECKFLAG_RETURN_OUTPUTSIZE, resetMode, null,
+                eosframe, null, null);
+
+        extractor.release();
+        testFd.close();
+        return outputSize;
+    }
+
+    private void testEOSBehavior(int movie, int stopatsample) throws Exception {
+        testEOSBehavior(movie, new int[] {stopatsample});
+    }
+
+    private void testEOSBehavior(int movie, int[] stopAtSample) throws Exception {
+        Surface s = null;
+        AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
+        MediaExtractor extractor = new MediaExtractor();
+        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
+                testFd.getLength());
+        extractor.selectTrack(0); // consider variable looping on track
+        List<Long> outputChecksums = new ArrayList<Long>();
+        List<Long> outputTimestamps = new ArrayList<Long>();
+        Arrays.sort(stopAtSample);
+        int last = stopAtSample.length - 1;
+
+        // decode reference (longest sequence to stop at + 100) and
+        // store checksums/pts in outputChecksums and outputTimestamps
+        // (will fail CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH)
+        decodeWithChecks(extractor,
+                CHECKFLAG_SETCHECKSUM | CHECKFLAG_SETPTS | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
+                RESET_MODE_NONE, s,
+                stopAtSample[last] + 100, outputChecksums, outputTimestamps);
+
+        // decode stopAtSample requests in reverse order (longest to
+        // shortest) and compare to reference checksums/pts in
+        // outputChecksums and outputTimestamps
+        for (int i = last; i >= 0; --i) {
+            if (true) { // reposition extractor
+                extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
+            } else { // create new extractor
+                extractor.release();
+                extractor = new MediaExtractor();
+                extractor.setDataSource(testFd.getFileDescriptor(),
+                        testFd.getStartOffset(), testFd.getLength());
+                extractor.selectTrack(0); // consider variable looping on track
+            }
+            decodeWithChecks(extractor,
+                    CHECKFLAG_COMPARECHECKSUM | CHECKFLAG_COMPAREPTS
+                    | CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH
+                    | CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH,
+                    RESET_MODE_NONE, s,
+                    stopAtSample[i], outputChecksums, outputTimestamps);
+        }
+        extractor.release();
+        testFd.close();
+    }
+
+    private static final int CHECKFLAG_SETCHECKSUM = 1 << 0;
+    private static final int CHECKFLAG_COMPARECHECKSUM = 1 << 1;
+    private static final int CHECKFLAG_SETPTS = 1 << 2;
+    private static final int CHECKFLAG_COMPAREPTS = 1 << 3;
+    private static final int CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH = 1 << 4;
+    private static final int CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH = 1 << 5;
+    private static final int CHECKFLAG_RETURN_OUTPUTFRAMES = 1 << 6;
+    private static final int CHECKFLAG_RETURN_OUTPUTSIZE = 1 << 7;
+
+    /**
+     * Decodes frames with parameterized checks and return values.
+     * The integer return can be selected through the checkFlags variable.
+     */
+    private static int decodeWithChecks(MediaExtractor extractor, int checkFlags, int resetMode,
+            Surface surface, int stopAtSample,
+            List<Long> outputChecksums, List<Long> outputTimestamps)
+            throws Exception {
+        int trackIndex = extractor.getSampleTrackIndex();
+        MediaFormat format = extractor.getTrackFormat(trackIndex);
+        String mime = format.getString(MediaFormat.KEY_MIME);
+        boolean isAudio = mime.startsWith("audio/");
         ByteBuffer[] codecInputBuffers;
         ByteBuffer[] codecOutputBuffers;
 
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        MediaFormat format = extractor.getTrackFormat(0);
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        boolean isAudio = mime.startsWith("audio/");
-
-        codec = createDecoder(mime);
-
-        assertNotNull("couldn't find codec", codec);
+        MediaCodec codec = createDecoder(mime);
         Log.i("@@@@", "using codec: " + codec.getName());
-        codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+        codec.configure(format, surface, null /* crypto */, 0 /* flags */);
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
         codecOutputBuffers = codec.getOutputBuffers();
 
         if (resetMode == RESET_MODE_RECONFIGURE) {
             codec.stop();
-            codec.configure(format, s /* surface */, null /* crypto */, 0 /* flags */);
+            codec.configure(format, surface, null /* crypto */, 0 /* flags */);
             codec.start();
             codecInputBuffers = codec.getInputBuffers();
             codecOutputBuffers = codec.getOutputBuffers();
@@ -553,19 +1090,28 @@
             codec.flush();
         }
 
-        Log.i("@@@@", "format: " + format);
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        final long kTimeOutUs = 5000;
+        // start decode loop
         MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+
+        final long kTimeOutUs = 5000; // 5ms timeout
         boolean sawInputEOS = false;
         boolean sawOutputEOS = false;
         int deadDecoderCounter = 0;
-        int samplecounter = 0;
+        int samplenum = 0;
+        int numframes = 0;
+        int outputSize = 0;
+        int width = 0;
+        int height = 0;
+        boolean dochecksum = false;
         ArrayList<Long> timestamps = new ArrayList<Long>();
+        if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+            outputTimestamps.clear();
+        }
+        if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+            outputChecksums.clear();
+        }
         while (!sawOutputEOS && deadDecoderCounter < 100) {
+            // handle input
             if (!sawInputEOS) {
                 int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
 
@@ -573,418 +1119,286 @@
                     ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
 
                     int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-
-                    long presentationTimeUs = 0;
+                            extractor.readSampleData(dstBuf, 0 /* offset */);
+                    long presentationTimeUs = extractor.getSampleTime();
+                    boolean advanceDone = extractor.advance();
+                    // int flags = extractor.getSampleFlags();
+                    // Log.i("@@@@", "read sample " + samplenum + ":" +
+                    // extractor.getSampleFlags()
+                    // + " @ " + extractor.getSampleTime() + " size " +
+                    // sampleSize);
+                    assertEquals("extractor.advance() should match end of stream", sampleSize >= 0,
+                            advanceDone);
 
                     if (sampleSize < 0) {
                         Log.d(TAG, "saw input EOS.");
                         sawInputEOS = true;
-                        sampleSize = 0;
+                        assertEquals("extractor.readSampleData() must return -1 at end of stream",
+                                -1, sampleSize);
+                        assertEquals("extractor.getSampleTime() must return -1 at end of stream",
+                                -1, presentationTimeUs);
+                        sampleSize = 0; // required otherwise queueInputBuffer
+                                        // returns invalid.
                     } else {
-                        presentationTimeUs = extractor.getSampleTime();
-                        samplecounter++;
-                        if (samplecounter == eosframe) {
-                            sawInputEOS = true;
+                        timestamps.add(presentationTimeUs);
+                        samplenum++; // increment before comparing with stopAtSample
+                        if (samplenum == stopAtSample) {
+                            Log.d(TAG, "saw input EOS (stop at sample).");
+                            sawInputEOS = true; // tag this sample as EOS
                         }
                     }
-
-                    timestamps.add(presentationTimeUs);
-
-                    int flags = extractor.getSampleFlags();
-
                     codec.queueInputBuffer(
                             inputBufIndex,
                             0 /* offset */,
                             sampleSize,
                             presentationTimeUs,
                             sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
+                } else {
+                    assertEquals(
+                            "codec.dequeueInputBuffer() unrecognized return value: " + inputBufIndex,
+                            MediaCodec.INFO_TRY_AGAIN_LATER, inputBufIndex);
                 }
             }
 
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
+            // handle output
+            int outputBufIndex = codec.dequeueOutputBuffer(info, kTimeOutUs);
 
             deadDecoderCounter++;
-            if (res >= 0) {
-                //Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs);
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
+            if (outputBufIndex >= 0) {
+                if (info.size > 0) { // Disregard 0-sized buffers at the end.
                     deadDecoderCounter = 0;
                     if (resetMode != RESET_MODE_NONE) {
-                        // once we've gotten some data out of the decoder, reset and start again
+                        // once we've gotten some data out of the decoder, reset
+                        // and start again
                         if (resetMode == RESET_MODE_RECONFIGURE) {
                             codec.stop();
-                            codec.configure(format, s /* surface */, null /* crypto */,
+                            codec.configure(format, surface /* surface */, null /* crypto */,
                                     0 /* flags */);
                             codec.start();
                             codecInputBuffers = codec.getInputBuffers();
                             codecOutputBuffers = codec.getOutputBuffers();
-                        } else /* resetMode == RESET_MODE_FLUSH */ {
+                        } else if (resetMode == RESET_MODE_FLUSH) {
                             codec.flush();
+                        } else {
+                            fail("unknown resetMode: " + resetMode);
                         }
+                        // restart at beginning, clear resetMode
                         resetMode = RESET_MODE_NONE;
                         extractor.seekTo(0, MediaExtractor.SEEK_TO_NEXT_SYNC);
                         sawInputEOS = false;
                         numframes = 0;
                         timestamps.clear();
+                        if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+                            outputTimestamps.clear();
+                        }
+                        if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+                            outputChecksums.clear();
+                        }
                         continue;
                     }
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames and check the timestamp
-                        numframes++;
-                        assertTrue("invalid timestamp", timestamps.remove(info.presentationTimeUs));
+                    if ((checkFlags & CHECKFLAG_COMPAREPTS) != 0) {
+                        assertTrue("number of frames (" + numframes
+                                + ") exceeds number of reference timestamps",
+                                numframes < outputTimestamps.size());
+                        assertEquals("frame ts mismatch at frame " + numframes,
+                                (long) outputTimestamps.get(numframes), info.presentationTimeUs);
+                    } else if ((checkFlags & CHECKFLAG_SETPTS) != 0) {
+                        outputTimestamps.add(info.presentationTimeUs);
                     }
+                    if ((checkFlags & (CHECKFLAG_SETCHECKSUM | CHECKFLAG_COMPARECHECKSUM)) != 0) {
+                        long sum = 0;   // note: checksum is 0 if buffer format unrecognized
+                        if (dochecksum) {
+                            // TODO: add stride - right now just use info.size (as before)
+                            //sum = checksum(codecOutputBuffers[outputBufIndex], width, height,
+                            //        stride);
+                            ByteBuffer outputBuffer = codecOutputBuffers[outputBufIndex];
+                            outputBuffer.position(info.offset);
+                            sum = checksum(outputBuffer, info.size);
+                        }
+                        if ((checkFlags & CHECKFLAG_COMPARECHECKSUM) != 0) {
+                            assertTrue("number of frames (" + numframes
+                                    + ") exceeds number of reference checksums",
+                                    numframes < outputChecksums.size());
+                            Log.d(TAG, "orig checksum: " + outputChecksums.get(numframes)
+                                    + " new checksum: " + sum);
+                            assertEquals("frame data mismatch at frame " + numframes,
+                                    (long) outputChecksums.get(numframes), sum);
+                        } else if ((checkFlags & CHECKFLAG_SETCHECKSUM) != 0) {
+                            outputChecksums.add(sum);
+                        }
+                    }
+                    if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTPTSMATCH) != 0) {
+                        assertTrue("output timestamp " + info.presentationTimeUs
+                                + " without corresponding input timestamp"
+                                , timestamps.remove(info.presentationTimeUs));
+                    }
+                    outputSize += info.size;
+                    numframes++;
                 }
-                int outputBufIndex = res;
+                // Log.d(TAG, "got frame, size " + info.size + "/" +
+                // info.presentationTimeUs +
+                // "/" + numframes + "/" + info.flags);
                 codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
                 if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                     Log.d(TAG, "saw output EOS.");
                     sawOutputEOS = true;
                 }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                 codecOutputBuffers = codec.getOutputBuffers();
-
                 Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+            } else if (outputBufIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                 MediaFormat oformat = codec.getOutputFormat();
-
-                Log.d(TAG, "output format has changed to " + oformat);
+                if (oformat.containsKey(MediaFormat.KEY_COLOR_FORMAT) &&
+                        oformat.containsKey(MediaFormat.KEY_WIDTH) &&
+                        oformat.containsKey(MediaFormat.KEY_HEIGHT)) {
+                    int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                    width = oformat.getInteger(MediaFormat.KEY_WIDTH);
+                    height = oformat.getInteger(MediaFormat.KEY_HEIGHT);
+                    dochecksum = isRecognizedFormat(colorFormat); // only checksum known raw
+                                                                  // buf formats
+                    Log.d(TAG, "checksum fmt: " + colorFormat + " dim " + width + "x" + height);
+                } else {
+                    dochecksum = false; // check with audio later
+                    width = height = 0;
+                    Log.d(TAG, "output format has changed to (unknown video) " + oformat);
+                }
             } else {
-                Log.d(TAG, "no output");
+                assertEquals(
+                        "codec.dequeueOutputBuffer() unrecognized return index: "
+                                + outputBufIndex,
+                        MediaCodec.INFO_TRY_AGAIN_LATER, outputBufIndex);
             }
         }
-
         codec.stop();
         codec.release();
-        testFd.close();
-        return numframes;
+
+        assertTrue("last frame didn't have EOS", sawOutputEOS);
+        if ((checkFlags & CHECKFLAG_COMPAREINPUTOUTPUTSAMPLEMATCH) != 0) {
+            assertEquals("I!=O", samplenum, numframes);
+            if (stopAtSample != 0) {
+                assertEquals("did not stop with right number of frames", stopAtSample, numframes);
+            }
+        }
+        return (checkFlags & CHECKFLAG_RETURN_OUTPUTSIZE) != 0 ? outputSize :
+                (checkFlags & CHECKFLAG_RETURN_OUTPUTFRAMES) != 0 ? numframes :
+                        0;
     }
 
     public void testEOSBehaviorH264() throws Exception {
         // this video has an I frame at 44
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 44);
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 45);
-        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 55);
+        testEOSBehavior(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                new int[] {44, 45, 55});
     }
 
     public void testEOSBehaviorH263() throws Exception {
         // this video has an I frame every 12 frames.
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 24);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 25);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 48);
-        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 50);
+        testEOSBehavior(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
+                new int[] {24, 25, 48, 50});
     }
 
     public void testEOSBehaviorMpeg4() throws Exception {
         // this video has an I frame every 12 frames
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 24);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 25);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 48);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 50);
-        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 2);
+        testEOSBehavior(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
+                new int[] {24, 25, 48, 50, 2});
     }
 
     public void testEOSBehaviorVP8() throws Exception {
         // this video has an I frame at 46
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 46);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 47);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 57);
-        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45);
+        testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+                new int[] {46, 47, 57, 45});
     }
 
     public void testEOSBehaviorVP9() throws Exception {
         // this video has an I frame at 44
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 44);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 45);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 55);
-        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 43);
-    }
-
-    private void testEOSBehavior(int movie, int stopatsample) throws Exception {
-
-        int numframes = 0;
-
-        long [] checksums = new long[stopatsample];
-
-        AssetFileDescriptor testFd = mResources.openRawResourceFd(movie);
-
-        MediaExtractor extractor;
-        MediaCodec codec = null;
-        ByteBuffer[] codecInputBuffers;
-        ByteBuffer[] codecOutputBuffers;
-
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        MediaFormat format = extractor.getTrackFormat(0);
-        String mime = format.getString(MediaFormat.KEY_MIME);
-        boolean isAudio = mime.startsWith("audio/");
-
-        codec = createDecoder(mime);
-
-        assertNotNull("couldn't find codec", codec);
-        Log.i("@@@@", "using codec: " + codec.getName());
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-        codec.start();
-        codecInputBuffers = codec.getInputBuffers();
-        codecOutputBuffers = codec.getOutputBuffers();
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        final long kTimeOutUs = 5000;
-        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
-        boolean sawInputEOS = false;
-        boolean sawOutputEOS = false;
-        int deadDecoderCounter = 0;
-        int samplenum = 0;
-        boolean dochecksum = false;
-        while (!sawOutputEOS && deadDecoderCounter < 100) {
-            if (!sawInputEOS) {
-                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
-
-                if (inputBufIndex >= 0) {
-                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
-
-                    int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
-//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
-                    samplenum++;
-
-                    long presentationTimeUs = 0;
-
-                    if (sampleSize < 0 || samplenum >= (stopatsample + 100)) {
-                        Log.d(TAG, "saw input EOS.");
-                        sawInputEOS = true;
-                        sampleSize = 0;
-                    } else {
-                        presentationTimeUs = extractor.getSampleTime();
-                    }
-
-                    int flags = extractor.getSampleFlags();
-
-                    codec.queueInputBuffer(
-                            inputBufIndex,
-                            0 /* offset */,
-                            sampleSize,
-                            presentationTimeUs,
-                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
-                }
-            }
-
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
-
-            deadDecoderCounter++;
-            if (res >= 0) {
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
-                    deadDecoderCounter = 0;
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames
-                        long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0;
-                        if (numframes < checksums.length) {
-                            checksums[numframes] = sum;
-                        }
-                        numframes++;
-                    }
-                }
-//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
-//                        "/" + numframes + "/" + info.flags);
-
-                int outputBufIndex = res;
-                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
-                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                    Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
-                }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                codecOutputBuffers = codec.getOutputBuffers();
-
-                Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                MediaFormat oformat = codec.getOutputFormat();
-                int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
-                dochecksum = isRecognizedFormat(colorFormat);
-                Log.d(TAG, "output format has changed to " + oformat);
-            } else {
-                Log.d(TAG, "no output");
-            }
-        }
-
-        codec.stop();
-        codec.release();
-        extractor.release();
-
-
-        // We now have checksums for every frame.
-        // Now decode again, but signal EOS right before an index frame, and ensure the frames
-        // prior to that are the same.
-
-        extractor = new MediaExtractor();
-        extractor.setDataSource(testFd.getFileDescriptor(), testFd.getStartOffset(),
-                testFd.getLength());
-
-        codec = createDecoder(mime);
-        codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
-        codec.start();
-        codecInputBuffers = codec.getInputBuffers();
-        codecOutputBuffers = codec.getOutputBuffers();
-
-        extractor.selectTrack(0);
-
-        // start decoding
-        info = new MediaCodec.BufferInfo();
-        sawInputEOS = false;
-        sawOutputEOS = false;
-        deadDecoderCounter = 0;
-        samplenum = 0;
-        numframes = 0;
-        dochecksum = false;
-        while (!sawOutputEOS && deadDecoderCounter < 100) {
-            if (!sawInputEOS) {
-                int inputBufIndex = codec.dequeueInputBuffer(kTimeOutUs);
-
-                if (inputBufIndex >= 0) {
-                    ByteBuffer dstBuf = codecInputBuffers[inputBufIndex];
-
-                    int sampleSize =
-                        extractor.readSampleData(dstBuf, 0 /* offset */);
-//                    Log.i("@@@@", "read sample " + samplenum + ":" + extractor.getSampleFlags()
-//                            + " @ " + extractor.getSampleTime() + " size " + sampleSize);
-                    samplenum++;
-
-                    long presentationTimeUs = extractor.getSampleTime();
-
-                    if (sampleSize < 0 || samplenum >= stopatsample) {
-                        Log.d(TAG, "saw input EOS.");
-                        sawInputEOS = true;
-                        if (sampleSize < 0) {
-                            sampleSize = 0;
-                        }
-                    }
-
-                    int flags = extractor.getSampleFlags();
-
-                    codec.queueInputBuffer(
-                            inputBufIndex,
-                            0 /* offset */,
-                            sampleSize,
-                            presentationTimeUs,
-                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                    if (!sawInputEOS) {
-                        extractor.advance();
-                    }
-                }
-            }
-
-            int res = codec.dequeueOutputBuffer(info, kTimeOutUs);
-
-            deadDecoderCounter++;
-            if (res >= 0) {
-
-                // Some decoders output a 0-sized buffer at the end. Disregard those.
-                if (info.size > 0) {
-                    deadDecoderCounter = 0;
-
-                    if (isAudio) {
-                        // for audio, count the number of bytes that were decoded, not the number
-                        // of access units
-                        numframes += info.size;
-                    } else {
-                        // for video, count the number of video frames
-                        long sum = dochecksum ? checksum(codecOutputBuffers[res], info.size) : 0;
-                        if (numframes < checksums.length) {
-                            assertEquals("frame data mismatch at frame " + numframes,
-                                    checksums[numframes], sum);
-                        }
-                        numframes++;
-                    }
-                }
-//                Log.d(TAG, "got frame, size " + info.size + "/" + info.presentationTimeUs +
-//                        "/" + numframes + "/" + info.flags);
-
-                int outputBufIndex = res;
-                codec.releaseOutputBuffer(outputBufIndex, true /* render */);
-
-                if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                    Log.d(TAG, "saw output EOS.");
-                    sawOutputEOS = true;
-                }
-            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                codecOutputBuffers = codec.getOutputBuffers();
-
-                Log.d(TAG, "output buffers have changed.");
-            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                MediaFormat oformat = codec.getOutputFormat();
-                int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
-                dochecksum = isRecognizedFormat(colorFormat);
-                Log.d(TAG, "output format has changed to " + oformat);
-            } else {
-                Log.d(TAG, "no output");
-            }
-        }
-
-        codec.stop();
-        codec.release();
-        extractor.release();
-
-        assertEquals("I!=O", samplenum, numframes);
-        assertTrue("last frame didn't have EOS", sawOutputEOS);
-        assertEquals(stopatsample, numframes);
-
-        testFd.close();
+        testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+                new int[] {44, 45, 55, 43});
     }
 
     /* from EncodeDecodeTest */
     private static boolean isRecognizedFormat(int colorFormat) {
+        // Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
         switch (colorFormat) {
-            // these are the formats we know how to handle for this test
+        // these are the formats we know how to handle for this test
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
             case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
+                /*
+                 * TODO: Check newer formats or ignore.
+                 * OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
+                 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar64x32Tile2m8ka = 0x7FA30C03: N4/N7_2
+                 * OMX_QCOM_COLOR_FormatYUV420PackedSemiPlanar32m = 0x7FA30C04: N5
+                 */
                 return true;
             default:
                 return false;
         }
     }
 
-    private long checksum(ByteBuffer buf, int size) {
-        assertTrue(size != 0);
-        assertTrue(size <= buf.capacity());
+    private static long checksum(ByteBuffer buf, int size) {
+        int cap = buf.capacity();
+        assertTrue("checksum() params are invalid: size = " + size + " cap = " + cap,
+                size > 0 && size <= cap);
         CRC32 crc = new CRC32();
-        int pos = buf.position();
-        buf.rewind();
-        for (int i = 0; i < size; i++) {
-            crc.update(buf.get());
+        if (buf.hasArray()) {
+            crc.update(buf.array(), buf.position() + buf.arrayOffset(), size);
+        } else {
+            int pos = buf.position();
+            final int rdsize = Math.min(4096, size);
+            byte bb[] = new byte[rdsize];
+            int chk;
+            for (int i = 0; i < size; i += chk) {
+                chk = Math.min(rdsize, size - i);
+                buf.get(bb, 0, chk);
+                crc.update(bb, 0, chk);
+            }
+            buf.position(pos);
         }
-        buf.position(pos);
+        return crc.getValue();
+    }
+
+    private static long checksum(ByteBuffer buf, int width, int height, int stride) {
+        int cap = buf.capacity();
+        assertTrue("checksum() params are invalid: w x h , s = "
+                + width + " x " + height + " , " + stride + " cap = " + cap,
+                width > 0 && width <= stride && height > 0 && height * stride <= cap);
+        // YUV 4:2:0 should generally have a data storage height 1.5x greater
+        // than the declared image height, representing the UV planes.
+        //
+        // We only check Y frame for now. Somewhat unknown with tiling effects.
+        //
+        //long tm = System.nanoTime();
+        final int lineinterval = 1; // line sampling frequency
+        CRC32 crc = new CRC32();
+        if (buf.hasArray()) {
+            byte b[] = buf.array();
+            int offs = buf.arrayOffset();
+            for (int i = 0; i < height; i += lineinterval) {
+                crc.update(b, i * stride + offs, width);
+            }
+        } else { // almost always ends up here due to direct buffers
+            int pos = buf.position();
+            if (true) { // this {} is 80x times faster than else {} below.
+                byte[] bb = new byte[width]; // local line buffer
+                for (int i = 0; i < height; i += lineinterval) {
+                    buf.position(i * stride);
+                    buf.get(bb, 0, width);
+                    crc.update(bb, 0, width);
+                }
+            } else {
+                for (int i = 0; i < height; i += lineinterval) {
+                    buf.position(i * stride);
+                    for (int j = 0; j < width; ++j) {
+                        crc.update(buf.get());
+                    }
+                }
+            }
+            buf.position(pos);
+        }
+        //tm = System.nanoTime() - tm;
+        //Log.d(TAG, "checksum time " + tm);
         return crc.getValue();
     }
 
@@ -1017,6 +1431,8 @@
         assertTrue("not an audio file", mime.startsWith("audio/"));
 
         codec = MediaCodec.createDecoderByType(mime);
+        assertNotNull("couldn't find codec " + mime, codec);
+
         codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
         codec.start();
         codecInputBuffers = codec.getInputBuffers();
@@ -1073,8 +1489,9 @@
                 int outputBufIndex = res;
                 ByteBuffer buf = codecOutputBuffers[outputBufIndex];
 
+                buf.position(info.offset);
                 for (int i = 0; i < info.size; i += 2) {
-                    short sample = buf.getShort(i);
+                    short sample = buf.getShort();
                     if (maxvalue < sample) {
                         maxvalue = sample;
                     }
diff --git a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
index 027e00d..5e967eb 100644
--- a/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeDecodeTest.java
@@ -593,7 +593,7 @@
                     encoderOutputBuffers = encoder.getOutputBuffers();
                     if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                 } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                    // not expected for an encoder
+                    // expected on API 18+
                     MediaFormat newFormat = encoder.getOutputFormat();
                     if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                 } else if (encoderStatus < 0) {
@@ -882,7 +882,7 @@
                         encoderOutputBuffers = encoder.getOutputBuffers();
                         if (VERBOSE) Log.d(TAG, "encoder output buffers changed");
                     } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
-                        // not expected for an encoder
+                        // expected on API 18+
                         MediaFormat newFormat = encoder.getOutputFormat();
                         if (VERBOSE) Log.d(TAG, "encoder output format changed: " + newFormat);
                     } else if (encoderStatus < 0) {
@@ -1061,16 +1061,17 @@
             x += cropLeft;
 
             int testY, testU, testV;
+            int off = frameData.position();
             if (semiPlanar) {
                 // Galaxy Nexus uses OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
-                testY = frameData.get(y * width + x) & 0xff;
-                testU = frameData.get(width*height + 2*(y/2) * halfWidth + 2*(x/2)) & 0xff;
-                testV = frameData.get(width*height + 2*(y/2) * halfWidth + 2*(x/2) + 1) & 0xff;
+                testY = frameData.get(off + y * width + x) & 0xff;
+                testU = frameData.get(off + width*height + 2*(y/2) * halfWidth + 2*(x/2)) & 0xff;
+                testV = frameData.get(off + width*height + 2*(y/2) * halfWidth + 2*(x/2) + 1) & 0xff;
             } else {
                 // Nexus 10, Nexus 7 use COLOR_FormatYUV420Planar
-                testY = frameData.get(y * width + x) & 0xff;
-                testU = frameData.get(width*height + (y/2) * halfWidth + (x/2)) & 0xff;
-                testV = frameData.get(width*height + halfWidth * (height / 2) +
+                testY = frameData.get(off + y * width + x) & 0xff;
+                testU = frameData.get(off + width*height + (y/2) * halfWidth + (x/2)) & 0xff;
+                testV = frameData.get(off + width*height + halfWidth * (height / 2) +
                         (y/2) * halfWidth + (x/2)) & 0xff;
             }
 
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
index 872b905..d9ef023 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -220,7 +220,7 @@
     /**
      * Prepares the encoder, decoder, and virtual display.
      */
-    private void encodeVirtualDisplayTest() {
+    private void encodeVirtualDisplayTest() throws IOException {
         MediaCodec encoder = null;
         MediaCodec decoder = null;
         OutputSurface outputSurface = null;
diff --git a/tests/tests/media/src/android/media/cts/EncoderTest.java b/tests/tests/media/src/android/media/cts/EncoderTest.java
index e9d0b5f..c2e59d4 100644
--- a/tests/tests/media/src/android/media/cts/EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/EncoderTest.java
@@ -187,8 +187,13 @@
     }
 
     private void testEncoder(String componentName, MediaFormat format) {
-        MediaCodec codec = MediaCodec.createByCodecName(componentName);
-
+        MediaCodec codec;
+        try {
+            codec = MediaCodec.createByCodecName(componentName);
+        } catch (Exception e) {
+            fail("codec '" + componentName + "' failed construction.");
+            return; /* does not get here, but avoids warning */
+        }
         try {
             codec.configure(
                     format,
@@ -196,9 +201,7 @@
                     null /* crypto */,
                     MediaCodec.CONFIGURE_FLAG_ENCODE);
         } catch (IllegalStateException e) {
-            Log.e(TAG, "codec '" + componentName + "' failed configuration.");
-
-            assertTrue("codec '" + componentName + "' failed configuration.", false);
+            fail("codec '" + componentName + "' failed configuration.");
         }
 
         codec.start();
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 40b4949..43b769a 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -517,7 +517,8 @@
      * @param inputFormat the format of the stream to decode
      * @param surface into which to decode the frames
      */
-    private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) {
+    private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface)
+            throws IOException {
         MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
         decoder.configure(inputFormat, surface, null, 0);
         decoder.start();
@@ -537,7 +538,8 @@
     private MediaCodec createVideoEncoder(
             MediaCodecInfo codecInfo,
             MediaFormat format,
-            AtomicReference<Surface> surfaceReference) {
+            AtomicReference<Surface> surfaceReference)
+            throws IOException {
         MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
         encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
         // Must be called before start() is.
@@ -551,7 +553,8 @@
      *
      * @param inputFormat the format of the stream to decode
      */
-    private MediaCodec createAudioDecoder(MediaFormat inputFormat) {
+    private MediaCodec createAudioDecoder(MediaFormat inputFormat)
+            throws IOException {
         MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
         decoder.configure(inputFormat, null, null, 0);
         decoder.start();
@@ -564,7 +567,8 @@
      * @param codecInfo of the codec to use
      * @param format of the stream to be produced
      */
-    private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) {
+    private MediaCodec createAudioEncoder(MediaCodecInfo codecInfo, MediaFormat format) 
+            throws IOException {
         MediaCodec encoder = MediaCodec.createByCodecName(codecInfo.getName());
         encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
         encoder.start();
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
new file mode 100644
index 0000000..f115b63
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -0,0 +1,572 @@
+/*
+ * Copyright 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.content.res.AssetFileDescriptor;
+import android.content.res.Resources;
+import android.graphics.ImageFormat;
+import android.media.Image;
+import android.media.Image.Plane;
+import android.media.ImageReader;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Basic test for ImageReader APIs.
+ * <p>
+ * It uses MediaCodec to decode a short video stream, send the video frames to
+ * the surface provided by ImageReader. Then compare if output buffers of the
+ * ImageReader matches the output buffers of the MediaCodec. The video format
+ * used here is AVC although the compression format doesn't matter for this
+ * test. For decoder test, hw and sw decoders are tested,
+ * </p>
+ */
+public class ImageReaderDecoderTest extends AndroidTestCase {
+    private static final String TAG = "ImageReaderDecoderTest";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+    private static final long DEFAULT_TIMEOUT_US = 10000;
+    private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
+    private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
+    private static final int NUM_FRAME_DECODED = 100;
+    private static final int MAX_NUM_IMAGES = 3;
+
+    private Resources mResources;
+    private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
+    private ByteBuffer[] mInputBuffers;
+    private ByteBuffer[] mOutputBuffers;
+    private ImageReader mReader;
+    private Surface mReaderSurface;
+    private HandlerThread mHandlerThread;
+    private Handler mHandler;
+    private ImageListener mImageListener;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mResources = mContext.getResources();
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHandlerThread = new HandlerThread(TAG);
+        mHandlerThread.start();
+        mHandler = new Handler(mHandlerThread.getLooper());
+        mImageListener = new ImageListener();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mHandlerThread.quitSafely();
+        mHandler = null;
+    }
+
+    /**
+     * Test ImageReader with 480x360 hw AVC decoding for flexible yuv format, which is mandatory
+     * to be supported by hw decoder.
+     */
+    public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
+        try {
+            int format = ImageFormat.YUV_420_888;
+            videoDecodeToSurface(
+                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                    /* width */480, /* height */ 360, format, /* useHw */ true);
+        } finally {
+            closeImageReader();
+        }
+    }
+
+    /**
+     * Test ImageReader with 480x360 sw AVC decoding for flexible yuv format, which is mandatory
+     * to be supported by sw decoder.
+     */
+    public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
+        try {
+            int format = ImageFormat.YUV_420_888;
+            videoDecodeToSurface(
+                    R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+                    /* width */ 480, /* height */ 360, format, /* useHw */ false);
+        } finally {
+            closeImageReader();
+        }
+    }
+
+    private static class ImageListener implements ImageReader.OnImageAvailableListener {
+        private final LinkedBlockingQueue<Image> mQueue =
+                new LinkedBlockingQueue<Image>();
+
+        @Override
+        public void onImageAvailable(ImageReader reader) {
+            try {
+                mQueue.put(reader.acquireNextImage());
+            } catch (InterruptedException e) {
+                throw new UnsupportedOperationException(
+                        "Can't handle InterruptedException in onImageAvailable");
+            }
+        }
+
+        /**
+         * Get an image from the image reader.
+         *
+         * @param timeout Timeout value for the wait.
+         * @return The image from the image reader.
+         */
+        public Image getImage(long timeout) throws InterruptedException {
+            Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+            assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
+            return image;
+        }
+    }
+
+    private void videoDecodeToSurface(int video, int width,
+            int height, int imageFormat, boolean useHw) throws Exception {
+        MediaCodec decoder = null;
+        MediaExtractor extractor;
+        int outputBufferIndex;
+        ByteBuffer[] decoderInputBuffers;
+        ByteBuffer[] decoderOutputBuffers;
+
+        AssetFileDescriptor vidFD = mResources.openRawResourceFd(video);
+
+        extractor = new MediaExtractor();
+        extractor.setDataSource(vidFD.getFileDescriptor(), vidFD.getStartOffset(),
+                vidFD.getLength());
+
+        MediaFormat mediaFmt = extractor.getTrackFormat(0);
+        String mime = mediaFmt.getString(MediaFormat.KEY_MIME);
+        try {
+            // Create decoder
+            decoder = createDecoder(mime, useHw);
+            assertNotNull("couldn't find decoder", decoder);
+            if (VERBOSE) Log.v(TAG, "using decoder: " + decoder.getName());
+
+            decodeFramesToImageReader(width, height, imageFormat, decoder,
+                    extractor, mediaFmt, mime);
+
+            decoder.stop();
+        } finally {
+            decoder.release();
+        }
+
+    }
+
+    /**
+     * Decode video frames to image reader.
+     */
+    private void decodeFramesToImageReader(int width, int height, int imageFormat,
+            MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFmt, String mime)
+            throws Exception, InterruptedException {
+        ByteBuffer[] decoderInputBuffers;
+        ByteBuffer[] decoderOutputBuffers;
+        // Get decoder output ImageFormat, will be used to create ImageReader
+        int codecImageFormat = getImageFormatFromCodecType(mime);
+        assertEquals("Codec image format should match image reader format",
+                imageFormat, codecImageFormat);
+        createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
+
+        // Configure decoder.
+        if (VERBOSE) Log.v(TAG, "stream format: " + mediaFmt);
+        decoder.configure(mediaFmt, mReaderSurface, /*crypto*/null, /*flags*/0);
+        decoder.start();
+        decoderInputBuffers = decoder.getInputBuffers();
+        decoderOutputBuffers = decoder.getOutputBuffers();
+        extractor.selectTrack(0);
+
+        // Start decoding and get Image, only test the first NUM_FRAME_DECODED frames.
+        MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+        boolean sawInputEOS = false;
+        boolean sawOutputEOS = false;
+        int outputFrameCount = 0;
+        while (!sawOutputEOS && outputFrameCount < NUM_FRAME_DECODED) {
+            if (VERBOSE) Log.v(TAG, "loop:" + outputFrameCount);
+            // Feed input frame.
+            if (!sawInputEOS) {
+                int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    ByteBuffer dstBuf = decoderInputBuffers[inputBufIndex];
+                    int sampleSize =
+                        extractor.readSampleData(dstBuf, 0 /* offset */);
+
+                    if (VERBOSE) Log.v(TAG, "queue a input buffer, idx/size: "
+                        + inputBufIndex + "/" + sampleSize);
+
+                    long presentationTimeUs = 0;
+
+                    if (sampleSize < 0) {
+                        if (VERBOSE) Log.v(TAG, "saw input EOS.");
+                        sawInputEOS = true;
+                        sampleSize = 0;
+                    } else {
+                        presentationTimeUs = extractor.getSampleTime();
+                    }
+
+                    decoder.queueInputBuffer(
+                            inputBufIndex,
+                            0 /* offset */,
+                            sampleSize,
+                            presentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    if (!sawInputEOS) {
+                        extractor.advance();
+                    }
+                }
+            }
+
+            // Get output frame
+            int res = decoder.dequeueOutputBuffer(info, DEFAULT_TIMEOUT_US);
+            if (VERBOSE) Log.v(TAG, "got a buffer: " + info.size + "/" + res);
+            if (res == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                // no output available yet
+                if (VERBOSE) Log.v(TAG, "no output frame available");
+            } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                // decoder output buffers changed, need update.
+                if (VERBOSE) Log.v(TAG, "decoder output buffers changed");
+                decoderOutputBuffers = decoder.getOutputBuffers();
+            } else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                // this happens before the first frame is returned.
+                MediaFormat outFormat = decoder.getOutputFormat();
+                if (VERBOSE) Log.v(TAG, "decoder output format changed: " + outFormat);
+            } else if (res < 0) {
+                // Should be decoding error.
+                fail("unexpected result from deocder.dequeueOutputBuffer: " + res);
+            } else {
+                // res >= 0: normal decoding case, copy the output buffer.
+                // Will use it as reference to valid the ImageReader output
+                // Some decoders output a 0-sized buffer at the end. Ignore those.
+                outputFrameCount++;
+                boolean doRender = (info.size != 0);
+
+                decoder.releaseOutputBuffer(res, doRender);
+                if (doRender) {
+                    // Read image and verify
+                    Image image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
+                    Plane[] imagePlanes = image.getPlanes();
+
+                    //Verify
+                    String fileName = DEBUG_FILE_NAME_BASE + width + "x" + height + "_"
+                            + outputFrameCount + ".yuv";
+                    validateImage(image, width, height, imageFormat, fileName);
+
+                    if (VERBOSE) {
+                        Log.v(TAG, "Image " + outputFrameCount + " Info:");
+                        Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
+                        Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
+                        Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+                    }
+                    image.close();
+                }
+            }
+        }
+    }
+
+    private int getImageFormatFromCodecType(String mimeType) {
+        // TODO: Need pick a codec first, then get the codec info, will revisit for future.
+        MediaCodecInfo codecInfo = getCodecInfoByType(mimeType);
+        if (VERBOSE) Log.v(TAG, "found decoder: " + codecInfo.getName());
+
+        int colorFormat = selectDecoderOutputColorFormat(codecInfo, mimeType);
+        if (VERBOSE) Log.v(TAG, "found decoder output color format: " + colorFormat);
+        switch (colorFormat) {
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+                // TODO: This is fishy, OMX YUV420P is not identical as YV12, U and V planes are
+                // swapped actually. It should give YV12 if producer is setup first, that is, after
+                // Configuring the Surface (provided by ImageReader object) into codec, but this
+                // is Chicken-egg issue, do the translation on behalf of driver here:)
+                return ImageFormat.YV12;
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+                // same as above.
+                return ImageFormat.NV21;
+            default:
+                return colorFormat;
+        }
+    }
+
+    private static MediaCodecInfo getCodecInfoByType(String mimeType) {
+        int numCodecs = MediaCodecList.getCodecCount();
+        for (int i = 0; i < numCodecs; i++) {
+            MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+
+            if (codecInfo.isEncoder()) {
+                continue;
+            }
+
+            String[] types = codecInfo.getSupportedTypes();
+            for (int j = 0; j < types.length; j++) {
+                if (types[j].equalsIgnoreCase(mimeType)) {
+                    return codecInfo;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static int selectDecoderOutputColorFormat(MediaCodecInfo codecInfo, String mimeType) {
+        MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+        for (int i = 0; i < capabilities.colorFormats.length; i++) {
+            int colorFormat = capabilities.colorFormats[i];
+            if (isRecognizedFormat(colorFormat)) {
+                return colorFormat;
+            }
+        }
+        fail("couldn't find a good color format for " + codecInfo.getName() + " / " + mimeType);
+        return 0;   // not reached
+    }
+
+    // Need make this function simple, may be merge into above functions.
+    private static boolean isRecognizedFormat(int colorFormat) {
+        if (VERBOSE) Log.v(TAG, "colorformat: " + colorFormat);
+        switch (colorFormat) {
+            // these are the formats we know how to handle for this test
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+            case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+            case ImageFormat.YUV_420_888:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private MediaCodec createDecoder(String mime, boolean useHw) throws Exception {
+        if (!useHw) {
+            if (mime.contains("avc")) {
+                return MediaCodec.createByCodecName("OMX.google.h264.decoder");
+            } else if (mime.contains("3gpp")) {
+                return MediaCodec.createByCodecName("OMX.google.h263.decoder");
+            } else if (mime.contains("mp4v")) {
+                return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
+            } else if (mime.contains("vp8")) {
+                return MediaCodec.createByCodecName("OMX.google.vpx.decoder");
+            }
+        }
+        return MediaCodec.createDecoderByType(mime);
+    }
+
+    /**
+     * Validate image based on format and size.
+     *
+     * @param image The image to be validated.
+     * @param width The image width.
+     * @param height The image height.
+     * @param format The image format.
+     * @param filePath The debug dump file path, null if don't want to dump to file.
+     */
+    public static void validateImage(Image image, int width, int height, int format,
+            String filePath) {
+        assertNotNull("Input image is invalid", image);
+        assertEquals("Format doesn't match", format, image.getFormat());
+        assertEquals("Width doesn't match", width, image.getWidth());
+        assertEquals("Height doesn't match", height, image.getHeight());
+
+        if(VERBOSE) Log.v(TAG, "validating Image");
+        byte[] data = getDataFromImage(image);
+        assertTrue("Invalid image data", data != null && data.length > 0);
+
+        validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+    }
+
+    private static void validateYuvData(byte[] yuvData, int width, int height, int format,
+            long ts, String fileName) {
+
+        assertTrue("YUV format must be one of the YUV420_888, NV21, or YV12",
+                format == ImageFormat.YUV_420_888 ||
+                format == ImageFormat.NV21 ||
+                format == ImageFormat.YV12);
+
+        if (VERBOSE) Log.v(TAG, "Validating YUV data");
+        int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
+        assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
+
+        if (DEBUG && fileName != null) {
+            dumpFile(fileName, yuvData);
+        }
+    }
+
+    private static void checkYuvFormat(int format) {
+        if ((format != ImageFormat.YUV_420_888) &&
+                (format != ImageFormat.NV21) &&
+                (format != ImageFormat.YV12)) {
+            fail("Wrong formats: " + format);
+        }
+    }
+    /**
+     * <p>Check android image format validity for an image, only support below formats:</p>
+     *
+     * <p>Valid formats are YUV_420_888/NV21/YV12 for video decoder</p>
+     */
+    private static void checkAndroidImageFormat(Image image) {
+        int format = image.getFormat();
+        Plane[] planes = image.getPlanes();
+        switch (format) {
+            case ImageFormat.YUV_420_888:
+            case ImageFormat.NV21:
+            case ImageFormat.YV12:
+                assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
+                break;
+            default:
+                fail("Unsupported Image Format: " + format);
+        }
+    }
+
+    /**
+     * Get a byte array image data from an Image object.
+     * <p>
+     * Read data from all planes of an Image into a contiguous unpadded,
+     * unpacked 1-D linear byte array, such that it can be write into disk, or
+     * accessed by software conveniently. It supports YUV_420_888/NV21/YV12
+     * input Image format.
+     * </p>
+     * <p>
+     * For YUV_420_888/NV21/YV12/Y8/Y16, it returns a byte array that contains
+     * the Y plane data first, followed by U(Cb), V(Cr) planes if there is any
+     * (xstride = width, ystride = height for chroma and luma components).
+     * </p>
+     */
+    private static byte[] getDataFromImage(Image image) {
+        assertNotNull("Invalid image:", image);
+        int format = image.getFormat();
+        int width = image.getWidth();
+        int height = image.getHeight();
+        int rowStride, pixelStride;
+        byte[] data = null;
+
+        // Read image data
+        Plane[] planes = image.getPlanes();
+        assertTrue("Fail to get image planes", planes != null && planes.length > 0);
+
+        // Check image validity
+        checkAndroidImageFormat(image);
+
+        ByteBuffer buffer = null;
+
+        int offset = 0;
+        data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];
+        byte[] rowData = new byte[planes[0].getRowStride()];
+        if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
+        for (int i = 0; i < planes.length; i++) {
+            buffer = planes[i].getBuffer();
+            assertNotNull("Fail to get bytebuffer from plane", buffer);
+            rowStride = planes[i].getRowStride();
+            pixelStride = planes[i].getPixelStride();
+            assertTrue("pixel stride " + pixelStride + " is invalid", pixelStride > 0);
+            if (VERBOSE) {
+                Log.v(TAG, "pixelStride " + pixelStride);
+                Log.v(TAG, "rowStride " + rowStride);
+                Log.v(TAG, "width " + width);
+                Log.v(TAG, "height " + height);
+            }
+            // For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
+            int w = (i == 0) ? width : width / 2;
+            int h = (i == 0) ? height : height / 2;
+            assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
+            for (int row = 0; row < h; row++) {
+                int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+                if (pixelStride == bytesPerPixel) {
+                    // Special case: optimized read of the entire row
+                    int length = w * bytesPerPixel;
+                    buffer.get(data, offset, length);
+                    // Advance buffer the remainder of the row stride
+                    buffer.position(buffer.position() + rowStride - length);
+                    offset += length;
+                } else {
+                    // Generic case: should work for any pixelStride but slower.
+                    // Use intermediate buffer to avoid read byte-by-byte from
+                    // DirectByteBuffer, which is very bad for performance
+                    buffer.get(rowData, 0, rowStride);
+                    for (int col = 0; col < w; col++) {
+                        data[offset++] = rowData[col * pixelStride];
+                    }
+                }
+            }
+            if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
+        }
+        return data;
+    }
+
+    private static void dumpFile(String fileName, byte[] data) {
+        assertNotNull("fileName must not be null", fileName);
+        assertNotNull("data must not be null", data);
+
+        FileOutputStream outStream;
+        try {
+            Log.v(TAG, "output will be saved as " + fileName);
+            outStream = new FileOutputStream(fileName);
+        } catch (IOException ioe) {
+            throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
+        }
+
+        try {
+            outStream.write(data);
+            outStream.close();
+        } catch (IOException ioe) {
+            throw new RuntimeException("failed writing data to file " + fileName, ioe);
+        }
+    }
+
+    private void createImageReader(int width, int height, int format, int maxNumImages,
+            ImageReader.OnImageAvailableListener listener) throws Exception {
+        closeImageReader();
+
+        mReader = ImageReader.newInstance(width, height, format, maxNumImages);
+        mReaderSurface = mReader.getSurface();
+        mReader.setOnImageAvailableListener(listener, mHandler);
+        if (VERBOSE) {
+            Log.v(TAG, String.format("Created ImageReader size (%dx%d), format %d", width, height,
+                    format));
+        }
+    }
+
+    /**
+     * Close the pending images then close current active {@link ImageReader} object.
+     */
+    private void closeImageReader() {
+        if (mReader != null) {
+            try {
+                // Close all possible pending images first.
+                Image image = mReader.acquireLatestImage();
+                if (image != null) {
+                    image.close();
+                }
+            } finally {
+                mReader.close();
+                mReader = null;
+            }
+        }
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/IvfReader.java b/tests/tests/media/src/android/media/cts/IvfReader.java
index 508ae25..2f679ae 100644
--- a/tests/tests/media/src/android/media/cts/IvfReader.java
+++ b/tests/tests/media/src/android/media/cts/IvfReader.java
@@ -22,18 +22,19 @@
 /**
  * A simple reader for an IVF file.
  *
- * IVF format is a simple container format for VP8 encoded frames.
+ * IVF format is a simple container format for VP8 encoded frames defined at
+ * http://wiki.multimedia.cx/index.php?title=IVF.
  * This reader is capable of getting frame count, width and height
  * from the header, and access individual frames randomly by
  * frame number.
  */
 
 public class IvfReader {
-    private static final byte HEADER_END = 32;
-    private static final byte FOURCC_HEAD = 8;
-    private static final byte WIDTH_HEAD = 12;
-    private static final byte HEIGHT_HEAD = 14;
-    private static final byte FRAMECOUNT_HEAD = 24;
+    private static final byte HEADER_SIZE = 32;
+    private static final byte FOURCC_OFFSET = 8;
+    private static final byte WIDTH_OFFSET = 12;
+    private static final byte HEIGHT_OFFSET = 14;
+    private static final byte FRAMECOUNT_OFFSET = 24;
     private static final byte FRAME_HEADER_SIZE = 12;
 
     private RandomAccessFile mIvfFile;
@@ -101,7 +102,7 @@
      * than 0 and less than frameCount.
      */
     public byte[] readFrame(int frameIndex) throws IOException {
-        if (frameIndex > mFrameCount | frameIndex < 0){
+        if (frameIndex > mFrameCount || frameIndex < 0){
             return null;
         }
         int frameSize = mFrameSizes[frameIndex];
@@ -124,7 +125,7 @@
     private boolean verifyHeader() throws IOException{
         mIvfFile.seek(0);
 
-        if (mIvfFile.length() < HEADER_END){
+        if (mIvfFile.length() < HEADER_SIZE){
             return false;
         }
 
@@ -135,7 +136,7 @@
                 (mIvfFile.readByte() == (byte)'F'));
 
         // Fourcc
-        mIvfFile.seek(FOURCC_HEAD);
+        mIvfFile.seek(FOURCC_OFFSET);
         boolean fourccMatch = ((mIvfFile.readByte() == (byte)'V') &&
                 (mIvfFile.readByte() == (byte)'P') &&
                 (mIvfFile.readByte() == (byte)'8') &&
@@ -146,15 +147,15 @@
 
     private void readHeaderData() throws IOException{
         // width
-        mIvfFile.seek(WIDTH_HEAD);
+        mIvfFile.seek(WIDTH_OFFSET);
         mWidth = (int) changeEndianness(mIvfFile.readShort());
 
         // height
-        mIvfFile.seek(HEIGHT_HEAD);
+        mIvfFile.seek(HEIGHT_OFFSET);
         mHeight = (int) changeEndianness(mIvfFile.readShort());
 
         // frame count
-        mIvfFile.seek(FRAMECOUNT_HEAD);
+        mIvfFile.seek(FRAMECOUNT_OFFSET);
         mFrameCount = changeEndianness(mIvfFile.readInt());
 
         // allocate frame metadata
@@ -163,7 +164,7 @@
     }
 
     private void readFrameMetadata() throws IOException{
-        int frameHead = HEADER_END;
+        int frameHead = HEADER_SIZE;
         for(int i = 0; i < mFrameCount; i++){
             mIvfFile.seek(frameHead);
             int frameSize = changeEndianness(mIvfFile.readInt());
diff --git a/tests/tests/media/src/android/media/cts/IvfWriter.java b/tests/tests/media/src/android/media/cts/IvfWriter.java
index ccc0ac5..075f73c 100644
--- a/tests/tests/media/src/android/media/cts/IvfWriter.java
+++ b/tests/tests/media/src/android/media/cts/IvfWriter.java
@@ -22,7 +22,8 @@
 /**
  * Writes an IVF file.
  *
- * IVF format is a simple container format for VP8 encoded frames.
+ * IVF format is a simple container format for VP8 encoded frames defined at
+ * http://wiki.multimedia.cx/index.php?title=IVF.
  */
 
 public class IvfWriter {
@@ -56,13 +57,13 @@
         mScale = scale;
         mRate = rate;
         mFrameCount = 0;
+        mOutputFile.setLength(0);
         mOutputFile.seek(HEADER_END);  // Skip the header for now, as framecount is unknown
     }
 
     /**
      * Initializes the IVF file writer with a microsecond timebase.
      *
-     *
      * Microsecond timebase is default for OMX thus stagefright.
      *
      * @param filename   name of the IVF file
@@ -87,7 +88,7 @@
      * Writes a single encoded VP8 frame with its frame header.
      *
      * @param frame     actual contents of the encoded frame data
-     * @param width     timestamp of the frame (in accordance to specified timebase)
+     * @param timeStamp timestamp of the frame (in accordance to specified timebase)
      */
     public void writeFrame(byte[] frame, long timeStamp) throws IOException {
         mOutputFile.write(makeIvfFrameHeader(frame.length, timeStamp));
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 3428e86..9c07cf1 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 
 import java.io.File;
+import java.io.IOException;
 import java.util.List;
 import java.util.ArrayList;
 
@@ -56,7 +57,7 @@
 
     // Each component advertised by MediaCodecList should at least be
     // instantiate-able.
-    public void testComponentInstantiation() {
+    public void testComponentInstantiation() throws IOException {
         Log.d(TAG, "testComponentInstantiation");
 
         int codecCount = MediaCodecList.getCodecCount();
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 4b8abcd..ae14e6f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -87,7 +87,11 @@
         format.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
 
         try {
-            encoder = MediaCodec.createByCodecName(codecInfo.getName());
+            try {
+                encoder = MediaCodec.createByCodecName(codecInfo.getName());
+            } catch (IOException e) {
+                fail("failed to create codec " + codecInfo.getName());
+            }
             try {
                 surface = encoder.createInputSurface();
                 fail("createInputSurface should not work pre-configure");
@@ -120,7 +124,6 @@
         assertNull(surface);
     }
 
-
     /**
      * Tests:
      * <br> signaling end-of-stream before any data is sent works
@@ -133,7 +136,11 @@
         InputSurface inputSurface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             inputSurface = new InputSurface(encoder.createInputSurface());
             inputSurface.makeCurrent();
@@ -173,6 +180,61 @@
 
     /**
      * Tests:
+     * <br> stopping with buffers in flight doesn't crash or hang
+     */
+    public void testAbruptStop() {
+        // There appears to be a race, so run it several times with a short delay between runs
+        // to allow any previous activity to shut down.
+        for (int i = 0; i < 50; i++) {
+            Log.d(TAG, "testAbruptStop " + i);
+            doTestAbruptStop();
+            try { Thread.sleep(400); } catch (InterruptedException ignored) {}
+        }
+    }
+    private void doTestAbruptStop() {
+        MediaFormat format = createMediaFormat();
+        MediaCodec encoder = null;
+        InputSurface inputSurface = null;
+
+        try {
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
+            encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            inputSurface = new InputSurface(encoder.createInputSurface());
+            inputSurface.makeCurrent();
+            encoder.start();
+
+            int totalBuffers = encoder.getInputBuffers().length +
+                    encoder.getOutputBuffers().length;
+            if (VERBOSE) Log.d(TAG, "Total buffers: " + totalBuffers);
+
+            // Submit several frames quickly, without draining the encoder output, to try to
+            // ensure that we've got some queued up when we call stop().  If we do too many
+            // we'll block in swapBuffers().
+            for (int i = 0; i < totalBuffers; i++) {
+                GLES20.glClearColor(0.0f, (i % 8) / 8.0f, 0.0f, 1.0f);
+                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
+                inputSurface.swapBuffers();
+            }
+            Log.d(TAG, "stopping");
+            encoder.stop();
+            Log.d(TAG, "stopped");
+        } finally {
+            if (encoder != null) {
+                encoder.stop();
+                encoder.release();
+            }
+            if (inputSurface != null) {
+                inputSurface.release();
+            }
+        }
+    }
+
+    /**
+     * Tests:
      * <br> dequeueInputBuffer() fails when encoder configured with an input Surface
      */
     public void testDequeueSurface() {
@@ -181,7 +243,11 @@
         Surface surface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             surface = encoder.createInputSurface();
             encoder.start();
@@ -215,7 +281,11 @@
         Surface surface = null;
 
         try {
-            encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            try {
+                encoder = MediaCodec.createEncoderByType(MIME_TYPE);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE + " encoder");
+            }
             encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             surface = encoder.createInputSurface();
             encoder.start();
@@ -365,11 +435,19 @@
         MediaCodec audioDecoderA = null;
         MediaCodec audioDecoderB = null;
         try {
-            audioDecoderA = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoderA = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create first " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoderA.configure(format, null, null, 0);
             audioDecoderA.start();
 
-            audioDecoderB = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoderB = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create second " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoderB.configure(format, null, null, 0);
             audioDecoderB.start();
         } finally {
@@ -407,11 +485,19 @@
         MediaCodec audioEncoder = null;
         MediaCodec audioDecoder = null;
         try {
-            audioEncoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioEncoder = MediaCodec.createEncoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE_AUDIO + " encoder");
+            }
             audioEncoder.configure(encoderFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
             audioEncoder.start();
 
-            audioDecoder = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            try {
+                audioDecoder = MediaCodec.createDecoderByType(MIME_TYPE_AUDIO);
+            } catch (IOException e) {
+                fail("failed to create " + MIME_TYPE_AUDIO + " decoder");
+            }
             audioDecoder.configure(decoderFormat, null, null, 0);
             audioDecoder.start();
         } finally {
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index c670b8c..275a648 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -37,7 +37,10 @@
 import android.os.SystemClock;
 import android.util.Log;
 
+import java.io.BufferedReader;
 import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.util.StringTokenizer;
 import java.util.UUID;
 import java.util.Vector;
@@ -117,11 +120,74 @@
         }
     }
 
-    public void testPlayAudio() throws Exception {
+    public void testPlayAudioFromDataURI() throws Exception {
         final int mp3Duration = 34909;
         final int tolerance = 70;
         final int seekDuration = 100;
+
+        // This is "R.raw.testmp3_2", base64-encoded.
+        final int resid = R.raw.testmp3_3;
+
+        InputStream is = mContext.getResources().openRawResource(resid);
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+        StringBuilder builder = new StringBuilder();
+        builder.append("data:;base64,");
+        builder.append(reader.readLine());
+        Uri uri = Uri.parse(builder.toString());
+
+        MediaPlayer mp = MediaPlayer.create(mContext, uri);
+
+        try {
+            mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
+            mp.setWakeMode(mContext, PowerManager.PARTIAL_WAKE_LOCK);
+
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            assertFalse(mp.isLooping());
+            mp.setLooping(true);
+            assertTrue(mp.isLooping());
+
+            assertEquals(mp3Duration, mp.getDuration(), tolerance);
+            int pos = mp.getCurrentPosition();
+            assertTrue(pos >= 0);
+            assertTrue(pos < mp3Duration - seekDuration);
+
+            mp.seekTo(pos + seekDuration);
+            assertEquals(pos + seekDuration, mp.getCurrentPosition(), tolerance);
+
+            // test pause and restart
+            mp.pause();
+            Thread.sleep(SLEEP_TIME);
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            // test stop and restart
+            mp.stop();
+            mp.reset();
+            mp.setDataSource(mContext, uri);
+            mp.prepare();
+            assertFalse(mp.isPlaying());
+            mp.start();
+            assertTrue(mp.isPlaying());
+
+            // waiting to complete
+            while(mp.isPlaying()) {
+                Thread.sleep(SLEEP_TIME);
+            }
+        } finally {
+            mp.release();
+        }
+    }
+
+    public void testPlayAudio() throws Exception {
         final int resid = R.raw.testmp3_2;
+        final int mp3Duration = 34909;
+        final int tolerance = 70;
+        final int seekDuration = 100;
 
         MediaPlayer mp = MediaPlayer.create(mContext, resid);
         try {
@@ -657,6 +723,14 @@
                 R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz, 480, 360);
     }
 
+    public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_128kbps_44110Hz_frag()
+            throws Exception {
+        playVideoTest(
+                R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_128kbps_44100hz_fragmented,
+                480, 360);
+    }
+
+
     public void testLocalVideo_MP4_H264_480x360_1350kbps_30fps_AAC_Stereo_192kbps_44110Hz()
             throws Exception {
         playVideoTest(
@@ -918,7 +992,7 @@
         mMediaPlayer.setOnTimedTextListener(new MediaPlayer.OnTimedTextListener() {
             @Override
             public void onTimedText(MediaPlayer mp, TimedText text) {
-                final int toleranceMs = 100;
+                final int toleranceMs = 150;
                 final int durationMs = 500;
                 int posMs = mMediaPlayer.getCurrentPosition();
                 if (text != null) {
@@ -1000,6 +1074,41 @@
         assertEquals(4, count);
     }
 
+    /*
+     *  This test assumes the resources being tested are between 8 and 14 seconds long
+     *  The ones being used here are 10 seconds long.
+     */
+    public void testResumeAtEnd() throws Throwable {
+        testResumeAtEnd(R.raw.loudsoftmp3);
+        testResumeAtEnd(R.raw.loudsoftwav);
+        testResumeAtEnd(R.raw.loudsoftogg);
+        testResumeAtEnd(R.raw.loudsoftitunes);
+        testResumeAtEnd(R.raw.loudsoftfaac);
+        testResumeAtEnd(R.raw.loudsoftaac);
+    }
+
+    private void testResumeAtEnd(int res) throws Throwable {
+
+        loadResource(res);
+        mMediaPlayer.prepare();
+        mOnCompletionCalled.reset();
+        mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+            @Override
+            public void onCompletion(MediaPlayer mp) {
+                mOnCompletionCalled.signal();
+                mMediaPlayer.start();
+            }
+        });
+        // skip the first part of the file so we reach EOF sooner
+        mMediaPlayer.seekTo(5000);
+        mMediaPlayer.start();
+        // sleep long enough that we restart playback at least once, but no more
+        Thread.sleep(10000);
+        assertTrue("MediaPlayer should still be playing", mMediaPlayer.isPlaying());
+        mMediaPlayer.reset();
+        assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
+    }
+
     public void testCallback() throws Throwable {
         final int mp4Duration = 8484;
 
diff --git a/tests/tests/media/src/android/media/cts/MediaScannerTest.java b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
index 2dafdc5..1d492bb 100644
--- a/tests/tests/media/src/android/media/cts/MediaScannerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaScannerTest.java
@@ -76,6 +76,7 @@
 
     @Override
     protected void tearDown() throws Exception {
+        cleanup();
         super.tearDown();
     }
 
@@ -84,10 +85,12 @@
             mMediaFile.delete();
         }
         if (mFileDir != null) {
-            new File(mFileDir + "/testmp3.mp3").delete();
-            new File(mFileDir + "/testmp3_2.mp3").delete();
-            new File(mFileDir + "/ctsmediascanplaylist1.pls").delete();
-            new File(mFileDir + "/ctsmediascanplaylist2.m3u").delete();
+            String files[] = new File(mFileDir).list();
+            if (files != null) {
+                for (String f: files) {
+                    new File(mFileDir + "/" + f).delete();
+                }
+            }
             new File(mFileDir).delete();
         }
 
@@ -367,6 +370,129 @@
         assertTrue(new File(path2).delete());
     }
 
+    static class MediaScanEntry {
+        MediaScanEntry(int r, String[] t) {
+            this.res = r;
+            this.tags = t;
+        }
+        int res;
+        String[] tags;
+    }
+
+    MediaScanEntry encodingtestfiles[] = {
+            new MediaScanEntry(R.raw.gb18030_1,
+                    new String[] {"罗志祥", "2009年11月新歌", "罗志祥", "爱不单行(TV Version)", null} ),
+            new MediaScanEntry(R.raw.gb18030_2,
+                    new String[] {"张杰", "明天过后", null, "明天过后", null} ),
+            new MediaScanEntry(R.raw.gb18030_3,
+                    new String[] {"电视原声带", "格斗天王(限量精装版)(预购版)", null, "11.Open Arms.( cn808.net )", null} ),
+            new MediaScanEntry(R.raw.gb18030_4,
+                    new String[] {"莫扎特", "黄金古典", "柏林爱乐乐团", "第25号交响曲", "莫扎特"} ),
+            new MediaScanEntry(R.raw.gb18030_5,
+                    new String[] {"光良", "童话", "光良", "02.童话", "鍏夎壇"} ),
+            new MediaScanEntry(R.raw.gb18030_6,
+                    new String[] {"张韶涵", "潘朵拉", "張韶涵", "隐形的翅膀", "王雅君"} ),
+            new MediaScanEntry(R.raw.gb18030_7, // this is actually utf-8
+                    new String[] {"五月天", "后青春期的诗", null, "突然好想你", null} ),
+            new MediaScanEntry(R.raw.gb18030_8,
+                    new String[] {"周杰伦", "Jay", null, "反方向的钟", null} ),
+            new MediaScanEntry(R.raw.big5_1,
+                    new String[] {"蘇永康", "So I Sing 08 Live", "蘇永康", "囍帖街", null} ),
+            new MediaScanEntry(R.raw.big5_2,
+                    new String[] {"蘇永康", "So I Sing 08 Live", "蘇永康", "從不喜歡孤單一個 - 蘇永康/吳雨霏", null} ),
+            new MediaScanEntry(R.raw.cp1251_v1,
+                    new String[] {"Екатерина Железнова", "Корабль игрушек", null, "Раз, два, три", null} ),
+            new MediaScanEntry(R.raw.cp1251_v1v2,
+                    new String[] {"Мельница", "Перевал", null, "Королевна", null} ),
+            new MediaScanEntry(R.raw.cp1251_3,
+                    new String[] {"Тату (tATu)", "200 По Встречной [Limited edi", null, "Я Сошла С Ума", null} ),
+            // The following 3 use cp1251 encoding, expanded to 16 bits and stored as utf16 
+            new MediaScanEntry(R.raw.cp1251_4,
+                    new String[] {"Александр Розенбаум", "Философия любви", null, "Разговор в гостинице (Как жить без веры)", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.cp1251_5,
+                    new String[] {"Александр Розенбаум", "Философия любви", null, "Четвертиночка", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.cp1251_6,
+                    new String[] {"Александр Розенбаум", "Философия ремесла", null, "Ну, вот...", "А.Розенбаум"} ),
+            new MediaScanEntry(R.raw.cp1251_7,
+                    new String[] {"Вопли Видоплясова", "Хвилі Амура", null, "Або або", null} ),
+            new MediaScanEntry(R.raw.cp1251_8,
+                    new String[] {"Вопли Видоплясова", "Хвилі Амура", null, "Таємнi сфери", null} ),
+            new MediaScanEntry(R.raw.shiftjis1,
+                    new String[] {"", "", null, "中島敦「山月記」(第1回)", null} ),
+            new MediaScanEntry(R.raw.shiftjis2,
+                    new String[] {"音人", "SoundEffects", null, "ファンファーレ", null} ),
+            new MediaScanEntry(R.raw.shiftjis3,
+                    new String[] {"音人", "SoundEffects", null, "シンキングタイム", null} ),
+            new MediaScanEntry(R.raw.shiftjis4,
+                    new String[] {"音人", "SoundEffects", null, "出題", null} ),
+            new MediaScanEntry(R.raw.shiftjis5,
+                    new String[] {"音人", "SoundEffects", null, "時報", null} ),
+            new MediaScanEntry(R.raw.shiftjis6,
+                    new String[] {"音人", "SoundEffects", null, "正解", null} ),
+            new MediaScanEntry(R.raw.shiftjis7,
+                    new String[] {"音人", "SoundEffects", null, "残念", null} ),
+            new MediaScanEntry(R.raw.shiftjis8,
+                    new String[] {"音人", "SoundEffects", null, "間違い", null} ),
+            new MediaScanEntry(R.raw.iso88591_1,
+                    new String[] {"Mozart", "Best of Mozart", null, "Overtüre (Die Hochzeit des Figaro)", null} ),
+            new MediaScanEntry(R.raw.iso88591_2, // actually UTF16, but only uses iso8859-1 chars
+                    new String[] {"Björk", "Telegram", "Björk", "Possibly Maybe (Lucy Mix)", null} ),
+            new MediaScanEntry(R.raw.hebrew,
+                    new String[] {"אריק סיני", "", null, "לי ולך", null } ),
+            new MediaScanEntry(R.raw.hebrew2,
+                    new String[] {"הפרוייקט של עידן רייכל", "Untitled - 11-11-02 (9)", null, "בואי", null } )
+    };
+
+    public void testEncodingDetection() throws Exception {
+        for (int i = 0; i< encodingtestfiles.length; i++) {
+            MediaScanEntry entry = encodingtestfiles[i];
+            String name = mContext.getResources().getResourceEntryName(entry.res);
+            String path =  mFileDir + "/" + name + ".mp3";
+            writeFile(entry.res, path);
+        }
+
+        startMediaScanAndWait();
+
+        String columns[] = {
+                MediaStore.Audio.Media.ARTIST,
+                MediaStore.Audio.Media.ALBUM,
+                MediaStore.Audio.Media.ALBUM_ARTIST,
+                MediaStore.Audio.Media.TITLE,
+                MediaStore.Audio.Media.COMPOSER
+        };
+        ContentResolver res = mContext.getContentResolver();
+        for (int i = 0; i< encodingtestfiles.length; i++) {
+            MediaScanEntry entry = encodingtestfiles[i];
+            String name = mContext.getResources().getResourceEntryName(entry.res);
+            String path =  mFileDir + "/" + name + ".mp3";
+            Cursor c = res.query(MediaStore.Audio.Media.getContentUri("external"), columns,
+                    MediaStore.Audio.Media.DATA + "=?", new String[] {path}, null);
+            assertNotNull("null cursor", c);
+            assertEquals("wrong number or results", 1, c.getCount());
+            assertTrue("failed to move cursor", c.moveToFirst());
+
+            for (int j =0; j < 5; j++) {
+                String expected = entry.tags[j];
+                if ("".equals(expected)) {
+                    // empty entry in the table means an unset id3 tag that is filled in by
+                    // the media scanner, e.g. by using "<unknown>". Since this may be localized,
+                    // don't check it for any particular value.
+                    assertNotNull("unexpected null entry " + i + " field " + j + "(" + path + ")",
+                            c.getString(j));
+                } else {
+                    assertEquals("mismatch on entry " + i + " field " + j + "(" + path + ")",
+                            expected, c.getString(j));
+                }
+            }
+            // clean up
+            new File(path).delete();
+            res.delete(MediaStore.Audio.Media.getContentUri("external"),
+                    MediaStore.Audio.Media.DATA + "=?", new String[] {path});
+
+            c.close();
+        }
+    }
+
     private void startMediaScanAndWait() throws InterruptedException {
         ScannerNotificationReceiver finishedReceiver = new ScannerNotificationReceiver(
                 Intent.ACTION_MEDIA_SCANNER_FINISHED);
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 8bac442..2b93064 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -16,9 +16,13 @@
 package android.media.cts;
 
 import android.media.MediaPlayer;
+import android.os.Looper;
+import android.os.SystemClock;
 import android.util.Log;
 import android.webkit.cts.CtsTestServer;
 
+import java.io.IOException;
+
 
 /**
  * Tests of MediaPlayer streaming capabilities.
@@ -259,6 +263,93 @@
         localHlsTest("hls.m3u8", false, true);
     }
 
+    private static class WorkerWithPlayer implements Runnable {
+        private final Object mLock = new Object();
+        private Looper mLooper;
+        private MediaPlayer mMediaPlayer;
+
+        /**
+         * Creates a worker thread with the given name. The thread
+         * then runs a {@link android.os.Looper}.
+         * @param name A name for the new thread
+         */
+        WorkerWithPlayer(String name) {
+            Thread t = new Thread(null, this, name);
+            t.setPriority(Thread.MIN_PRIORITY);
+            t.start();
+            synchronized (mLock) {
+                while (mLooper == null) {
+                    try {
+                        mLock.wait();
+                    } catch (InterruptedException ex) {
+                    }
+                }
+            }
+        }
+
+        public MediaPlayer getPlayer() {
+            return mMediaPlayer;
+        }
+
+        @Override
+        public void run() {
+            synchronized (mLock) {
+                Looper.prepare();
+                mLooper = Looper.myLooper();
+                mMediaPlayer = new MediaPlayer();
+                mLock.notifyAll();
+            }
+            Looper.loop();
+        }
+
+        public void quit() {
+            mLooper.quit();
+            mMediaPlayer.release();
+        }
+    }
+
+    public void testBlockingReadRelease() throws Throwable {
+
+        mServer = new CtsTestServer(mContext);
+
+        WorkerWithPlayer worker = new WorkerWithPlayer("player");
+        final MediaPlayer mp = worker.getPlayer();
+
+        try {
+            String path = mServer.getDelayedAssetUrl("noiseandchirps.ogg", 15000);
+            mp.setDataSource(path);
+            mp.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
+                @Override
+                public void onPrepared(MediaPlayer mp) {
+                    fail("prepare should not succeed");
+                }
+            });
+            mp.prepareAsync();
+            Thread.sleep(1000);
+            long start = SystemClock.elapsedRealtime();
+            mp.release();
+            long end = SystemClock.elapsedRealtime();
+            long releaseDuration = (end - start);
+            assertTrue("release took too long: " + releaseDuration, releaseDuration < 1000);
+        } catch (IllegalArgumentException e) {
+            fail(e.getMessage());
+        } catch (SecurityException e) {
+            fail(e.getMessage());
+        } catch (IllegalStateException e) {
+            fail(e.getMessage());
+        } catch (IOException e) {
+            fail(e.getMessage());
+        } catch (InterruptedException e) {
+            fail(e.getMessage());
+        } finally {
+            mServer.shutdown();
+        }
+
+        // give the worker a bit of time to start processing the message before shutting it down
+        Thread.sleep(5000);
+        worker.quit();
+    }
+
     private void localHlsTest(final String name, boolean appendQueryString, boolean redirect)
             throws Throwable {
         mServer = new CtsTestServer(mContext);
diff --git a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
new file mode 100644
index 0000000..45e4009
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
@@ -0,0 +1,1654 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaCodecInfo;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Looper;
+import android.os.Handler;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import com.android.cts.media.R;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Verification test for vp8 encoder and decoder.
+ *
+ * A raw yv12 stream is encoded at various settings and written to an IVF
+ * file. Encoded stream bitrate and key frame interval are checked against target values.
+ * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * calculate PSNR values for various bitrates.
+ */
+public class Vp8CodecTestBase extends AndroidTestCase {
+
+    protected static final String TAG = "VP8CodecTestBase";
+    private static final String VP8_MIME = "video/x-vnd.on2.vp8";
+    private static final String VPX_SW_DECODER_NAME = "OMX.google.vp8.decoder";
+    private static final String VPX_SW_ENCODER_NAME = "OMX.google.vp8.encoder";
+    private static final String OMX_SW_CODEC_PREFIX = "OMX.google";
+    protected static final String SDCARD_DIR =
+            Environment.getExternalStorageDirectory().getAbsolutePath();
+
+    // Default timeout for MediaCodec buffer dequeue - 200 ms.
+    protected static final long DEFAULT_TIMEOUT_US = 200000;
+    // Default sync frame interval in frames (zero means allow the encoder to auto-select
+    // key frame interval).
+    private static final int SYNC_FRAME_INTERVAL = 0;
+    // Video bitrate type - should be set to OMX_Video_ControlRateConstant from OMX_Video.h
+    protected static final int VIDEO_ControlRateVariable = 1;
+    protected static final int VIDEO_ControlRateConstant = 2;
+    // NV12 color format supported by QCOM codec, but not declared in MediaCodec -
+    // see /hardware/qcom/media/mm-core/inc/OMX_QCOMExtns.h
+    private static final int COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m = 0x7FA30C04;
+    // Allowable color formats supported by codec - in order of preference.
+    private static final int[] mSupportedColorList = {
+            CodecCapabilities.COLOR_FormatYUV420Planar,
+            CodecCapabilities.COLOR_FormatYUV420SemiPlanar,
+            CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar,
+            COLOR_QCOM_FORMATYUV420PackedSemiPlanar32m
+    };
+    // Scaled image cache list - contains scale factors, for which up-scaled frames
+    // were calculated and were written to yuv file.
+    ArrayList<Integer> mScaledImages = new ArrayList<Integer>();
+
+    private Resources mResources;
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        mResources = mContext.getResources();
+    }
+
+    /**
+     *  VP8 codec properties generated by getVp8CodecProperties() function.
+     */
+    private class CodecProperties {
+        CodecProperties(String codecName, int colorFormat) {
+            this.codecName = codecName;
+            this.colorFormat = colorFormat;
+        }
+        public boolean  isGoogleSwCodec() {
+            return codecName.startsWith(OMX_SW_CODEC_PREFIX);
+        }
+
+        public final String codecName; // OpenMax component name for VP8 codec.
+        public final int colorFormat;  // Color format supported by codec.
+    }
+
+    /**
+     * Function to find VP8 codec.
+     *
+     * Iterates through the list of available codecs and tries to find
+     * VP8 codec, which can support either YUV420 planar or NV12 color formats.
+     * If forceSwGoogleCodec parameter set to true the function always returns
+     * Google sw VP8 codec.
+     * If forceSwGoogleCodec parameter set to false the functions looks for platform
+     * specific VP8 codec first. If no platform specific codec exist, falls back to
+     * Google sw VP8 codec.
+     *
+     * @param isEncoder     Flag if encoder is requested.
+     * @param forceSwGoogleCodec  Forces to use Google sw codec.
+     */
+    private CodecProperties getVp8CodecProperties(boolean isEncoder,
+            boolean forceSwGoogleCodec) throws Exception {
+        CodecProperties codecProperties = null;
+
+        if (!forceSwGoogleCodec) {
+            // Loop through the list of omx components in case platform specific codec
+            // is requested.
+            for (int i = 0; i < MediaCodecList.getCodecCount(); i++) {
+                MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+                if (isEncoder != codecInfo.isEncoder()) {
+                    continue;
+                }
+                Log.v(TAG, codecInfo.getName());
+                // Check if this is sw Google codec - we should ignore it.
+                boolean isGoogleSwCodec = codecInfo.getName().startsWith(OMX_SW_CODEC_PREFIX);
+                if (isGoogleSwCodec) {
+                    continue;
+                }
+
+                for (String type : codecInfo.getSupportedTypes()) {
+                    if (!type.equalsIgnoreCase(VP8_MIME)) {
+                        continue;
+                    }
+                    CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(VP8_MIME);
+
+                    // Get candidate codec properties.
+                    Log.v(TAG, "Found candidate codec " + codecInfo.getName());
+                    for (int colorFormat : capabilities.colorFormats) {
+                        Log.v(TAG, "   Color: 0x" + Integer.toHexString(colorFormat));
+                    }
+
+                    // Check supported color formats.
+                    for (int supportedColorFormat : mSupportedColorList) {
+                        for (int codecColorFormat : capabilities.colorFormats) {
+                            if (codecColorFormat == supportedColorFormat) {
+                                codecProperties = new CodecProperties(codecInfo.getName(),
+                                        codecColorFormat);
+                                Log.v(TAG, "Found target codec " + codecProperties.codecName +
+                                        ". Color: 0x" + Integer.toHexString(codecColorFormat));
+                                return codecProperties;
+                            }
+                        }
+                    }
+                    // HW codec we found does not support one of necessary color formats.
+                    throw new RuntimeException("No hw codec with YUV420 or NV12 color formats");
+                }
+            }
+        }
+        // If no hw vp8 codec exist or sw codec is requested use default Google sw codec.
+        if (codecProperties == null) {
+            Log.v(TAG, "Use SW VP8 codec");
+            if (isEncoder) {
+                codecProperties = new CodecProperties(VPX_SW_ENCODER_NAME,
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            } else {
+                codecProperties = new CodecProperties(VPX_SW_DECODER_NAME,
+                        CodecCapabilities.COLOR_FormatYUV420Planar);
+            }
+        }
+
+        return codecProperties;
+    }
+
+    /**
+     * Parameters for encoded video stream.
+     */
+    protected class EncoderOutputStreamParameters {
+        // Name of raw YUV420 input file. When the value of this parameter
+        // is set to null input file descriptor from inputResourceId parameter
+        // is used instead.
+        public String inputYuvFilename;
+        // Name of scaled YUV420 input file.
+        public String scaledYuvFilename;
+        // File descriptor for the raw input file (YUV420). Used only if
+        // inputYuvFilename parameter is null.
+        int inputResourceId;
+        // Name of the IVF file to write encoded bitsream
+        public String outputIvfFilename;
+        // Force to use Google SW VP8 encoder.
+        boolean forceSwEncoder;
+        // Number of frames to encode.
+        int frameCount;
+        // Frame rate of input file in frames per second.
+        int frameRate;
+        // Encoded frame width.
+        public int frameWidth;
+        // Encoded frame height.
+        public int frameHeight;
+        // Encoding bitrate array in bits/second for every frame. If array length
+        // is shorter than the total number of frames, the last value is re-used for
+        // all remaining frames. For constant bitrate encoding single element
+        // array can be used with first element set to target bitrate value.
+        public int[] bitrateSet;
+        // Encoding bitrate type - VBR or CBR
+        public int bitrateType;
+        // Number of temporal layers
+        public int temporalLayers;
+        // Desired key frame interval - codec is asked to generate key frames
+        // at a period defined by this parameter.
+        public int syncFrameInterval;
+        // Optional parameter - forced key frame interval. Used to
+        // explicitly request the codec to generate key frames using
+        // MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME parameter.
+        public int syncForceFrameInterval;
+        // Buffer timeout
+        long timeoutDequeue;
+        // Flag if encoder should run in Looper thread.
+        boolean runInLooperThread;
+    }
+
+    /**
+     * Generates an array of default parameters for encoder output stream based on
+     * upscaling value.
+     */
+    protected ArrayList<EncoderOutputStreamParameters> getDefaultEncodingParameterList(
+            String inputYuvName,
+            String outputIvfBaseName,
+            int encodeSeconds,
+            int[] resolutionScales,
+            int frameWidth,
+            int frameHeight,
+            int frameRate,
+            int bitrateMode,
+            int[] bitrates,
+            boolean syncEncoding) {
+        assertTrue(resolutionScales.length == bitrates.length);
+        int numCodecs = resolutionScales.length;
+        ArrayList<EncoderOutputStreamParameters> outputParameters =
+                new ArrayList<EncoderOutputStreamParameters>(numCodecs);
+        for (int i = 0; i < numCodecs; i++) {
+            EncoderOutputStreamParameters params = new EncoderOutputStreamParameters();
+            if (inputYuvName != null) {
+                params.inputYuvFilename = SDCARD_DIR + File.separator + inputYuvName;
+            } else {
+                params.inputYuvFilename = null;
+            }
+            params.scaledYuvFilename = SDCARD_DIR + File.separator +
+                    outputIvfBaseName + resolutionScales[i]+ ".yuv";
+            params.inputResourceId = R.raw.football_qvga;
+            params.outputIvfFilename = SDCARD_DIR + File.separator +
+                    outputIvfBaseName + resolutionScales[i] + ".ivf";
+            params.forceSwEncoder = false;
+            params.frameCount = encodeSeconds * frameRate;
+            params.frameRate = frameRate;
+            params.frameWidth = Math.min(frameWidth * resolutionScales[i], 1280);
+            params.frameHeight = Math.min(frameHeight * resolutionScales[i], 720);
+            params.bitrateSet = new int[1];
+            params.bitrateSet[0] = bitrates[i];
+            params.bitrateType = bitrateMode;
+            params.temporalLayers = 0;
+            params.syncFrameInterval = SYNC_FRAME_INTERVAL;
+            params.syncForceFrameInterval = 0;
+            if (syncEncoding) {
+                params.timeoutDequeue = DEFAULT_TIMEOUT_US;
+                params.runInLooperThread = false;
+            } else {
+                params.timeoutDequeue = 0;
+                params.runInLooperThread = true;
+            }
+            outputParameters.add(params);
+        }
+        return outputParameters;
+    }
+
+    protected EncoderOutputStreamParameters getDefaultEncodingParameters(
+            String inputYuvName,
+            String outputIvfBaseName,
+            int encodeSeconds,
+            int frameWidth,
+            int frameHeight,
+            int frameRate,
+            int bitrateMode,
+            int bitrate,
+            boolean syncEncoding) {
+        int[] scaleValues = { 1 };
+        int[] bitrates = { bitrate };
+        return getDefaultEncodingParameterList(
+                inputYuvName,
+                outputIvfBaseName,
+                encodeSeconds,
+                scaleValues,
+                frameWidth,
+                frameHeight,
+                frameRate,
+                bitrateMode,
+                bitrates,
+                syncEncoding).get(0);
+    }
+
+    /**
+     * Converts (interleaves) YUV420 planar to NV12 (if hw) or NV21 (if sw).
+     * Assumes packed, macroblock-aligned frame with no cropping
+     * (visible/coded row length == stride).  Swap U/V if |sw|.
+     */
+    private static byte[] YUV420ToNV(int width, int height, byte[] yuv, boolean sw) {
+        byte[] nv = new byte[yuv.length];
+        // Y plane we just copy.
+        System.arraycopy(yuv, 0, nv, 0, width * height);
+
+        // U & V plane we interleave.
+        int u_offset = width * height;
+        int v_offset = u_offset + u_offset / 4;
+        int nv_offset = width * height;
+        if (sw) {
+            for (int i = 0; i < width * height / 4; i++) {
+                nv[nv_offset++] = yuv[v_offset++];
+                nv[nv_offset++] = yuv[u_offset++];
+            }
+        }
+        else {
+            for (int i = 0; i < width * height / 4; i++) {
+                nv[nv_offset++] = yuv[u_offset++];
+                nv[nv_offset++] = yuv[v_offset++];
+            }
+        }
+        return nv;
+    }
+
+    /**
+     * Converts (de-interleaves) NV12 to YUV420 planar.
+     * Stride may be greater than width, slice height may be greater than height.
+     */
+    private static byte[] NV12ToYUV420(int width, int height,
+            int stride, int sliceHeight, byte[] nv12) {
+        byte[] yuv = new byte[width * height * 3 / 2];
+
+        // Y plane we just copy.
+        for (int i = 0; i < height; i++) {
+            System.arraycopy(nv12, i * stride, yuv, i * width, width);
+        }
+
+        // U & V plane - de-interleave.
+        int u_offset = width * height;
+        int v_offset = u_offset + u_offset / 4;
+        int nv_offset;
+        for (int i = 0; i < height / 2; i++) {
+            nv_offset = stride * (sliceHeight + i);
+            for (int j = 0; j < width / 2; j++) {
+                yuv[u_offset++] = nv12[nv_offset++];
+                yuv[v_offset++] = nv12[nv_offset++];
+            }
+        }
+        return yuv;
+    }
+
+    private static void imageUpscale1To2(byte[] src, int srcByteOffset, int srcStride,
+            byte[] dst, int dstByteOffset, int dstWidth, int dstHeight) {
+        for (int i = 0; i < dstHeight/2 - 1; i++) {
+            int dstOffset0 = 2 * i * dstWidth + dstByteOffset;
+            int dstOffset1 = dstOffset0 + dstWidth;
+            int srcOffset0 = i * srcStride + srcByteOffset;
+            int srcOffset1 = srcOffset0 + srcStride;
+            int pixel00 = (int)src[srcOffset0++] & 0xff;
+            int pixel10 = (int)src[srcOffset1++] & 0xff;
+            for (int j = 0; j < dstWidth/2 - 1; j++) {
+                int pixel01 = (int)src[srcOffset0++] & 0xff;
+                int pixel11 = (int)src[srcOffset1++] & 0xff;
+                dst[dstOffset0++] = (byte)pixel00;
+                dst[dstOffset0++] = (byte)((pixel00 + pixel01 + 1) / 2);
+                dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+                dst[dstOffset1++] = (byte)((pixel00 + pixel01 + pixel10 + pixel11 + 2) / 4);
+                pixel00 = pixel01;
+                pixel10 = pixel11;
+            }
+            // last column
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+            dst[dstOffset1++] = (byte)((pixel00 + pixel10 + 1) / 2);
+        }
+
+        // last row
+        int dstOffset0 = (dstHeight - 2) * dstWidth + dstByteOffset;
+        int dstOffset1 = dstOffset0 + dstWidth;
+        int srcOffset0 = (dstHeight/2 - 1) * srcStride + srcByteOffset;
+        int pixel00 = (int)src[srcOffset0++] & 0xff;
+        for (int j = 0; j < dstWidth/2 - 1; j++) {
+            int pixel01 = (int)src[srcOffset0++] & 0xff;
+            dst[dstOffset0++] = (byte)pixel00;
+            dst[dstOffset0++] = (byte)((pixel00 + pixel01 + 1) / 2);
+            dst[dstOffset1++] = (byte)pixel00;
+            dst[dstOffset1++] = (byte)((pixel00 + pixel01 + 1) / 2);
+            pixel00 = pixel01;
+        }
+        // the very last pixel - bottom right
+        dst[dstOffset0++] = (byte)pixel00;
+        dst[dstOffset0++] = (byte)pixel00;
+        dst[dstOffset1++] = (byte)pixel00;
+        dst[dstOffset1++] = (byte)pixel00;
+    }
+
+    /**
+    * Up-scale image.
+    * Scale factor is defined by source and destination width ratio.
+    * Only 1:2 and 1:4 up-scaling is supported for now.
+    * For 640x480 -> 1280x720 conversion only top 640x360 part of the original
+    * image is scaled.
+    */
+    private static byte[] imageScale(byte[] src, int srcWidth, int srcHeight,
+            int dstWidth, int dstHeight) throws Exception {
+        int srcYSize = srcWidth * srcHeight;
+        int dstYSize = dstWidth * dstHeight;
+        byte[] dst = null;
+        if (dstWidth == 2 * srcWidth && dstHeight <= 2 * srcHeight) {
+            // 1:2 upscale
+            dst = new byte[dstWidth * dstHeight * 3 / 2];
+            imageUpscale1To2(src, 0, srcWidth,
+                    dst, 0, dstWidth, dstHeight);                                 // Y
+            imageUpscale1To2(src, srcYSize, srcWidth / 2,
+                    dst, dstYSize, dstWidth / 2, dstHeight / 2);                  // U
+            imageUpscale1To2(src, srcYSize * 5 / 4, srcWidth / 2,
+                    dst, dstYSize * 5 / 4, dstWidth / 2, dstHeight / 2);          // V
+        } else if (dstWidth == 4 * srcWidth && dstHeight <= 4 * srcHeight) {
+            // 1:4 upscale - in two steps
+            int midWidth = 2 * srcWidth;
+            int midHeight = 2 * srcHeight;
+            byte[] midBuffer = imageScale(src, srcWidth, srcHeight, midWidth, midHeight);
+            dst = imageScale(midBuffer, midWidth, midHeight, dstWidth, dstHeight);
+
+        } else {
+            throw new RuntimeException("Can not find proper scaling function");
+        }
+
+        return dst;
+    }
+
+    private void cacheScaledImage(
+            String srcYuvFilename, int srcResourceId, int srcFrameWidth, int srcFrameHeight,
+            String dstYuvFilename, int dstFrameWidth, int dstFrameHeight) throws Exception {
+        InputStream srcStream = OpenFileOrResourceId(srcYuvFilename, srcResourceId);
+        FileOutputStream dstFile = new FileOutputStream(dstYuvFilename, false);
+        int srcFrameSize = srcFrameWidth * srcFrameHeight * 3 / 2;
+        byte[] srcFrame = new byte[srcFrameSize];
+        byte[] dstFrame = null;
+        Log.d(TAG, "Scale to " + dstFrameWidth + " x " + dstFrameHeight + ". -> " + dstYuvFilename);
+        while (true) {
+            int bytesRead = srcStream.read(srcFrame);
+            if (bytesRead != srcFrame.length) {
+                break;
+            }
+            if (dstFrameWidth == srcFrameWidth && dstFrameHeight == srcFrameHeight) {
+                dstFrame = srcFrame;
+            } else {
+                dstFrame = imageScale(srcFrame, srcFrameWidth, srcFrameHeight,
+                        dstFrameWidth, dstFrameHeight);
+            }
+            dstFile.write(dstFrame);
+        }
+        srcStream.close();
+        dstFile.close();
+    }
+
+
+    /**
+     * A basic check if an encoded stream is decodable.
+     *
+     * The most basic confirmation we can get about a frame
+     * being properly encoded is trying to decode it.
+     * (Especially in realtime mode encode output is non-
+     * deterministic, therefore a more thorough check like
+     * md5 sum comparison wouldn't work.)
+     *
+     * Indeed, MediaCodec will raise an IllegalStateException
+     * whenever vp8 decoder fails to decode a frame, and
+     * this test uses that fact to verify the bitstream.
+     *
+     * @param inputIvfFilename  The name of the IVF file containing encoded bitsream.
+     * @param outputYuvFilename The name of the output YUV file (optional).
+     * @param frameRate         Frame rate of input file in frames per second
+     * @param forceSwDecoder    Force to use Googlw sw VP8 decoder.
+     */
+    protected ArrayList<MediaCodec.BufferInfo> decode(
+            String inputIvfFilename,
+            String outputYuvFilename,
+            int frameRate,
+            boolean forceSwDecoder) throws Exception {
+        ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
+        CodecProperties properties = getVp8CodecProperties(false, forceSwDecoder);
+        // Open input/output.
+        IvfReader ivf = new IvfReader(inputIvfFilename);
+        int frameWidth = ivf.getWidth();
+        int frameHeight = ivf.getHeight();
+        int frameCount = ivf.getFrameCount();
+        int frameStride = frameWidth;
+        int frameSliceHeight = frameHeight;
+        int frameColorFormat = properties.colorFormat;
+        assertTrue(frameWidth > 0);
+        assertTrue(frameHeight > 0);
+        assertTrue(frameCount > 0);
+
+        FileOutputStream yuv = null;
+        if (outputYuvFilename != null) {
+            yuv = new FileOutputStream(outputYuvFilename, false);
+        }
+
+        // Create decoder.
+        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME,
+                                                           ivf.getWidth(),
+                                                           ivf.getHeight());
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+        Log.d(TAG, "Creating decoder " + properties.codecName +
+                ". Color format: 0x" + Integer.toHexString(frameColorFormat) +
+                ". " + frameWidth + " x " + frameHeight);
+        Log.d(TAG, "  Format: " + format);
+        Log.d(TAG, "  In: " + inputIvfFilename + ". Out:" + outputYuvFilename);
+        MediaCodec decoder = MediaCodec.createByCodecName(properties.codecName);
+        decoder.configure(format,
+                          null,  // surface
+                          null,  // crypto
+                          0);    // flags
+        decoder.start();
+
+        ByteBuffer[] inputBuffers = decoder.getInputBuffers();
+        ByteBuffer[] outputBuffers = decoder.getOutputBuffers();
+        MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
+
+        // decode loop
+        int inputFrameIndex = 0;
+        int outputFrameIndex = 0;
+        long inPresentationTimeUs = 0;
+        long outPresentationTimeUs = 0;
+        boolean sawOutputEOS = false;
+        boolean sawInputEOS = false;
+
+        while (!sawOutputEOS) {
+            if (!sawInputEOS) {
+                int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
+                if (inputBufIndex >= 0) {
+                    byte[] frame = ivf.readFrame(inputFrameIndex);
+
+                    if (inputFrameIndex == frameCount - 1) {
+                        Log.d(TAG, "  Input EOS for frame # " + inputFrameIndex);
+                        sawInputEOS = true;
+                    }
+
+                    inputBuffers[inputBufIndex].clear();
+                    inputBuffers[inputBufIndex].put(frame);
+                    inputBuffers[inputBufIndex].rewind();
+                    inPresentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
+
+                    decoder.queueInputBuffer(
+                            inputBufIndex,
+                            0,  // offset
+                            frame.length,
+                            inPresentationTimeUs,
+                            sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+
+                    inputFrameIndex++;
+                }
+            }
+
+            int result = decoder.dequeueOutputBuffer(bufferInfo, DEFAULT_TIMEOUT_US);
+            while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ||
+                    result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    outputBuffers = decoder.getOutputBuffers();
+                } else  if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    // Process format change
+                    format = decoder.getOutputFormat();
+                    frameWidth = format.getInteger(MediaFormat.KEY_WIDTH);
+                    frameHeight = format.getInteger(MediaFormat.KEY_HEIGHT);
+                    frameColorFormat = format.getInteger(MediaFormat.KEY_COLOR_FORMAT);
+                    Log.d(TAG, "Decoder output format change. Color: 0x" +
+                            Integer.toHexString(frameColorFormat));
+                    Log.d(TAG, "Format: " + format.toString());
+
+                    // Parse frame and slice height from undocumented values
+                    if (format.containsKey("stride")) {
+                        frameStride = format.getInteger("stride");
+                    } else {
+                        frameStride = frameWidth;
+                    }
+                    if (format.containsKey("slice-height")) {
+                        frameSliceHeight = format.getInteger("slice-height");
+                    } else {
+                        frameSliceHeight = frameHeight;
+                    }
+                    Log.d(TAG, "Frame stride and slice height: " + frameStride +
+                            " x " + frameSliceHeight);
+                }
+                result = decoder.dequeueOutputBuffer(bufferInfo, DEFAULT_TIMEOUT_US);
+            }
+            if (result >= 0) {
+                int outputBufIndex = result;
+                outPresentationTimeUs = bufferInfo.presentationTimeUs;
+                Log.v(TAG, "Writing buffer # " + outputFrameIndex +
+                        ". Size: " + bufferInfo.size +
+                        ". InTime: " + (inPresentationTimeUs + 500)/1000 +
+                        ". OutTime: " + (outPresentationTimeUs + 500)/1000);
+                if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    sawOutputEOS = true;
+                    Log.d(TAG, "   Output EOS for frame # " + outputFrameIndex);
+                }
+
+                if (bufferInfo.size > 0) {
+                    // Save decoder output to yuv file.
+                    if (yuv != null) {
+                        byte[] frame = new byte[bufferInfo.size];
+                        outputBuffers[outputBufIndex].position(bufferInfo.offset);
+                        outputBuffers[outputBufIndex].get(frame, 0, bufferInfo.size);
+                        // Convert NV12 to YUV420 if necessary
+                        if (frameColorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
+                            frame = NV12ToYUV420(frameWidth, frameHeight,
+                                    frameStride, frameSliceHeight, frame);
+                        }
+                        yuv.write(frame);
+                    }
+                    outputFrameIndex++;
+
+                    // Update statistics - store presentation time delay in offset
+                    long presentationTimeUsDelta = inPresentationTimeUs - outPresentationTimeUs;
+                    MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                    bufferInfoCopy.set((int)presentationTimeUsDelta, bufferInfo.size,
+                            outPresentationTimeUs, bufferInfo.flags);
+                    bufferInfos.add(bufferInfoCopy);
+                }
+                decoder.releaseOutputBuffer(outputBufIndex, false);
+            }
+        }
+        decoder.stop();
+        decoder.release();
+        ivf.close();
+        if (yuv != null) {
+            yuv.close();
+        }
+
+        return bufferInfos;
+    }
+
+
+    /**
+     * Helper function to return InputStream from either filename (if set)
+     * or resource id (if filename is not set).
+     */
+    private InputStream OpenFileOrResourceId(String filename, int resourceId) throws Exception {
+        if (filename != null) {
+            return new FileInputStream(filename);
+        }
+        return mResources.openRawResource(resourceId);
+    }
+
+    /**
+     * Results of frame encoding.
+     */
+    protected class MediaEncoderOutput {
+        public long inPresentationTimeUs;
+        public long outPresentationTimeUs;
+        public boolean outputGenerated;
+        public int flags;
+        public byte[] buffer;
+    }
+
+    /**
+     * Video encoder wrapper class.
+     * Allows to run the encoder either in a callee's thread or in a looper thread
+     * using buffer dequeue ready notification callbacks.
+     *
+     * Function feedInput() is used to send raw video frame to the encoder input. When encoder
+     * is configured to run in async mode the function will run in a looper thread.
+     * Encoded frame can be retrieved by calling getOutput() function.
+     */
+    protected class MediaEncoderAsync extends Thread implements MediaCodec.NotificationCallback {
+        private int mId;
+        private MediaCodec mCodec;
+        private MediaFormat mFormat;
+        private ByteBuffer[] mInputBuffers;
+        private ByteBuffer[] mOutputBuffers;
+        private int mInputFrameIndex;
+        private int mOutputFrameIndex;
+        private int mInputBufIndex;
+        private int mFrameRate;
+        private long mTimeout;
+        private MediaCodec.BufferInfo mBufferInfo;
+        private long mInPresentationTimeUs;
+        private long mOutPresentationTimeUs;
+        private boolean mAsync;
+        // Flag indicating if input frame was consumed by the encoder in feedInput() call.
+        private boolean mConsumedInput;
+        // Result of frame encoding returned by getOutput() call.
+        private MediaEncoderOutput mOutput;
+        // Object used to signal that looper thread has started and Handler instance associated
+        // with looper thread has been allocated.
+        private final Object mThreadEvent = new Object();
+        // Object used to signal that MediaCodec buffer dequeue notification callback
+        // was received.
+        private final Object mCallbackEvent = new Object();
+        private Handler mHandler;
+        private boolean mCallbackReceived;
+
+        @Override
+        public void onCodecNotify(MediaCodec codec) {
+            synchronized (mCallbackEvent) {
+                Log.v(TAG, "MediaEncoder " + mId + " Event Callback");
+                mCallbackReceived = true;
+                mCallbackEvent.notify();
+            }
+            return;
+        }
+
+        private synchronized void requestStart() throws Exception {
+            mHandler = null;
+            start();
+            // Wait for Hander allocation
+            synchronized (mThreadEvent) {
+                while (mHandler == null) {
+                    mThreadEvent.wait();
+                }
+            }
+        }
+
+        @Override
+        public void run() {
+            Looper.prepare();
+            synchronized (mThreadEvent) {
+                mHandler = new Handler();
+                mThreadEvent.notify();
+            }
+            Looper.loop();
+        }
+
+        private void runCallable(final Callable<?> callable) throws Exception {
+            if (mAsync) {
+                final Exception[] exception = new Exception[1];
+                final CountDownLatch countDownLatch = new CountDownLatch(1);
+                mHandler.post( new Runnable() {
+                    @Override
+                    public void run() {
+                        try {
+                            callable.call();
+                        } catch (Exception e) {
+                            exception[0] = e;
+                        } finally {
+                            countDownLatch.countDown();
+                        }
+                    }
+                } );
+
+                // Wait for task completion
+                countDownLatch.await();
+                if (exception[0] != null) {
+                    throw exception[0];
+                }
+            } else {
+                callable.call();
+            }
+        }
+
+        private synchronized void requestStop() throws Exception {
+            mHandler.post( new Runnable() {
+                @Override
+                public void run() {
+                    // This will run on the Looper thread
+                    Log.v(TAG, "MediaEncoder looper quitting");
+                    Looper.myLooper().quitSafely();
+                }
+            } );
+            // Wait for completion
+            join();
+            mHandler = null;
+        }
+
+        private void createCodecInternal(final String name,
+                final MediaFormat format, final long timeout) throws Exception {
+            mBufferInfo = new MediaCodec.BufferInfo();
+            mFormat = format;
+            mFrameRate = format.getInteger(MediaFormat.KEY_FRAME_RATE);
+            mTimeout = timeout;
+            mInputFrameIndex = 0;
+            mOutputFrameIndex = 0;
+            mInPresentationTimeUs = 0;
+            mOutPresentationTimeUs = 0;
+
+            mCodec = MediaCodec.createByCodecName(name);
+            mCodec.configure(mFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+            mCodec.start();
+            if (mAsync) {
+                mCodec.setNotificationCallback(this);
+            }
+            mInputBuffers = mCodec.getInputBuffers();
+            mOutputBuffers = mCodec.getOutputBuffers();
+        }
+
+
+        public void createCodec(int id, final String name, final MediaFormat format,
+                final long timeout, boolean async)  throws Exception {
+            mId = id;
+            mAsync = async;
+            if (mAsync) {
+                requestStart(); // start looper thread
+            }
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    createCodecInternal(name, format, timeout);
+                    return null;
+                }
+            } );
+        }
+
+        private void feedInputInternal(final byte[] encFrame, final boolean inputEOS) {
+            mConsumedInput = false;
+            // Feed input
+            mInputBufIndex = mCodec.dequeueInputBuffer(mTimeout);
+
+            if (mInputBufIndex >= 0) {
+                mInputBuffers[mInputBufIndex].clear();
+                mInputBuffers[mInputBufIndex].put(encFrame);
+                mInputBuffers[mInputBufIndex].rewind();
+                int encFrameLength = encFrame.length;
+                int flags = 0;
+                if (inputEOS) {
+                    encFrameLength = 0;
+                    flags = MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+                }
+                if (!inputEOS) {
+                    Log.v(TAG, "Enc" + mId + ". Frame in # " + mInputFrameIndex +
+                            ". InTime: " + (mInPresentationTimeUs + 500)/1000);
+                    mInPresentationTimeUs = (mInputFrameIndex * 1000000) / mFrameRate;
+                    mInputFrameIndex++;
+                }
+
+                mCodec.queueInputBuffer(
+                        mInputBufIndex,
+                        0,  // offset
+                        encFrameLength,  // size
+                        mInPresentationTimeUs,
+                        flags);
+
+                mConsumedInput = true;
+            } else {
+                Log.v(TAG, "In " + mId + " - TRY_AGAIN_LATER");
+            }
+            mCallbackReceived = false;
+        }
+
+        public boolean feedInput(final byte[] encFrame, final boolean inputEOS) throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    feedInputInternal(encFrame, inputEOS);
+                    return null;
+                }
+            } );
+            return mConsumedInput;
+        }
+
+        private void getOutputInternal() {
+            mOutput = new MediaEncoderOutput();
+            mOutput.inPresentationTimeUs = mInPresentationTimeUs;
+            mOutput.outPresentationTimeUs = mOutPresentationTimeUs;
+            mOutput.outputGenerated = false;
+
+            // Get output from the encoder
+            int result = mCodec.dequeueOutputBuffer(mBufferInfo, mTimeout);
+            while (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED ||
+                    result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+                    mOutputBuffers = mCodec.getOutputBuffers();
+                } else if (result == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+                    mFormat = mCodec.getOutputFormat();
+                    Log.d(TAG, "Format changed: " + mFormat.toString());
+                }
+                result = mCodec.dequeueOutputBuffer(mBufferInfo, mTimeout);
+            }
+            if (result == MediaCodec.INFO_TRY_AGAIN_LATER) {
+                Log.v(TAG, "Out " + mId + " - TRY_AGAIN_LATER");
+            }
+
+            if (result >= 0) {
+                int outputBufIndex = result;
+                mOutput.buffer = new byte[mBufferInfo.size];
+                mOutputBuffers[outputBufIndex].position(mBufferInfo.offset);
+                mOutputBuffers[outputBufIndex].get(mOutput.buffer, 0, mBufferInfo.size);
+                mOutPresentationTimeUs = mBufferInfo.presentationTimeUs;
+
+                String logStr = "Enc" + mId + ". Frame # " + mOutputFrameIndex;
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
+                    logStr += " CONFIG. ";
+                }
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) {
+                    logStr += " KEY. ";
+                }
+                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    logStr += " EOS. ";
+                }
+                logStr += " Size: " + mBufferInfo.size;
+                logStr += ". InTime: " + (mInPresentationTimeUs + 500)/1000 +
+                        ". OutTime: " + (mOutPresentationTimeUs + 500)/1000;
+                Log.v(TAG, logStr);
+                if (mOutputFrameIndex == 0 &&
+                        ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0) ) {
+                    throw new RuntimeException("First frame is not a sync frame.");
+                }
+
+                if (mBufferInfo.size > 0) {
+                    mOutputFrameIndex++;
+                    mOutput.outPresentationTimeUs = mOutPresentationTimeUs;
+                }
+                mCodec.releaseOutputBuffer(outputBufIndex, false);
+
+                mOutput.flags = mBufferInfo.flags;
+                mOutput.outputGenerated = true;
+            }
+            mCallbackReceived = false;
+        }
+
+        public MediaEncoderOutput getOutput() throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    getOutputInternal();
+                    return null;
+                }
+            } );
+            return mOutput;
+        }
+
+        public void forceSyncFrame() throws Exception {
+            final Bundle syncFrame = new Bundle();
+            syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.setParameters(syncFrame);
+                    return null;
+                }
+            } );
+        }
+
+        public void updateBitrate(int bitrate) throws Exception {
+            final Bundle bitrateUpdate = new Bundle();
+            bitrateUpdate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, bitrate);
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.setParameters(bitrateUpdate);
+                    return null;
+                }
+            } );
+        }
+
+
+        public void waitForBufferEvent() throws Exception {
+            Log.v(TAG, "----Enc" + mId + " waiting for bufferEvent");
+            if (mAsync) {
+                synchronized (mCallbackEvent) {
+                    if (!mCallbackReceived) {
+                        mCallbackEvent.wait(1000); // wait 1 sec for a callback
+                        // throw an exception if callback was not received
+                        if (!mCallbackReceived) {
+                            throw new RuntimeException("MediaCodec callback was not received");
+                        }
+                    }
+                }
+            } else {
+                Thread.sleep(5);
+            }
+            Log.v(TAG, "----Waiting for bufferEvent done");
+        }
+
+        public void deleteCodec() throws Exception {
+            runCallable( new Callable<Void>() {
+                @Override
+                public Void call() throws Exception {
+                    mCodec.stop();
+                    mCodec.release();
+                    return null;
+                }
+            } );
+            if (mAsync) {
+                requestStop(); // Stop looper thread
+            }
+        }
+    }
+
+
+    /**
+     * Vp8 encoding loop supporting encoding single streams with an option
+     * to run in a looper thread and use buffer ready notification callbacks.
+     *
+     * Output stream is described by encodingParams parameters.
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param streamParams  Structure with encoder parameters
+     * @return              Returns array of encoded frames information for each frame.
+     */
+    protected ArrayList<MediaCodec.BufferInfo> encode(
+            EncoderOutputStreamParameters streamParams) throws Exception {
+
+        ArrayList<MediaCodec.BufferInfo> bufferInfos = new ArrayList<MediaCodec.BufferInfo>();
+        CodecProperties properties = getVp8CodecProperties(true, streamParams.forceSwEncoder);
+        Log.d(TAG, "Source reslution: " + streamParams.frameWidth + " x " +
+                streamParams.frameHeight);
+        int bitrate = streamParams.bitrateSet[0];
+
+        // Open input/output
+        InputStream yuvStream = OpenFileOrResourceId(
+                streamParams.inputYuvFilename, streamParams.inputResourceId);
+        IvfWriter ivf = new IvfWriter(
+                streamParams.outputIvfFilename, streamParams.frameWidth, streamParams.frameHeight);
+
+        // Create a media format signifying desired output.
+        MediaFormat format = MediaFormat.createVideoFormat(
+                VP8_MIME, streamParams.frameWidth, streamParams.frameHeight);
+        format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+        if (streamParams.bitrateType == VIDEO_ControlRateConstant) {
+            format.setInteger("bitrate-mode", VIDEO_ControlRateConstant); // set CBR
+        }
+        if (streamParams.temporalLayers > 0) {
+            format.setInteger("ts-layers", streamParams.temporalLayers); // 1 temporal layer
+        }
+        format.setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+        format.setInteger(MediaFormat.KEY_FRAME_RATE, streamParams.frameRate);
+        int syncFrameInterval = (streamParams.syncFrameInterval + streamParams.frameRate/2) /
+                streamParams.frameRate;
+        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, syncFrameInterval);
+
+        // Create encoder
+        Log.d(TAG, "Creating encoder " + properties.codecName +
+                ". Color format: 0x" + Integer.toHexString(properties.colorFormat)+ " : " +
+                streamParams.frameWidth + " x " + streamParams.frameHeight +
+                ". Bitrate: " + bitrate + " Bitrate type: " + streamParams.bitrateType +
+                ". Fps:" + streamParams.frameRate + ". TS Layers: " + streamParams.temporalLayers +
+                ". Key frame:" + syncFrameInterval * streamParams.frameRate +
+                ". Force keyFrame: " + streamParams.syncForceFrameInterval);
+        Log.d(TAG, "  Format: " + format);
+        Log.d(TAG, "  Output ivf:" + streamParams.outputIvfFilename);
+        MediaEncoderAsync codec = new MediaEncoderAsync();
+        codec.createCodec(0, properties.codecName, format,
+                streamParams.timeoutDequeue, streamParams.runInLooperThread);
+
+        // encode loop
+        boolean sawInputEOS = false;  // no more data
+        boolean consumedInputEOS = false; // EOS flag is consumed dy encoder
+        boolean sawOutputEOS = false;
+        boolean inputConsumed = true;
+        int inputFrameIndex = 0;
+        int lastBitrate = bitrate;
+        int srcFrameSize = streamParams.frameWidth * streamParams.frameHeight * 3 / 2;
+        byte[] srcFrame = new byte[srcFrameSize];
+
+        while (!sawOutputEOS) {
+
+            // Read and feed input frame
+            if (!consumedInputEOS) {
+
+                // Read new input buffers - if previous input was consumed and no EOS
+                if (inputConsumed && !sawInputEOS) {
+                    int bytesRead = yuvStream.read(srcFrame);
+
+                    // Check EOS
+                    if (streamParams.frameCount > 0 && inputFrameIndex >= streamParams.frameCount) {
+                        sawInputEOS = true;
+                        Log.d(TAG, "---Sending EOS empty frame for frame # " + inputFrameIndex);
+                    }
+
+                    if (!sawInputEOS && bytesRead == -1) {
+                        if (streamParams.frameCount == 0) {
+                            sawInputEOS = true;
+                            Log.d(TAG, "---Sending EOS empty frame for frame # " + inputFrameIndex);
+                        } else {
+                            yuvStream.close();
+                            yuvStream = OpenFileOrResourceId(
+                                    streamParams.inputYuvFilename, streamParams.inputResourceId);
+                            bytesRead = yuvStream.read(srcFrame);
+                        }
+                    }
+
+                    // Force sync frame if syncForceFrameinterval is set.
+                    if (!sawInputEOS && inputFrameIndex > 0 &&
+                            streamParams.syncForceFrameInterval > 0 &&
+                            (inputFrameIndex % streamParams.syncForceFrameInterval) == 0) {
+                        Log.d(TAG, "---Requesting sync frame # " + inputFrameIndex);
+                        codec.forceSyncFrame();
+                    }
+
+                    // Dynamic bitrate change.
+                    if (!sawInputEOS && streamParams.bitrateSet.length > inputFrameIndex) {
+                        int newBitrate = streamParams.bitrateSet[inputFrameIndex];
+                        if (newBitrate != lastBitrate) {
+                            Log.d(TAG, "--- Requesting new bitrate " + newBitrate +
+                                    " for frame " + inputFrameIndex);
+                            codec.updateBitrate(newBitrate);
+                            lastBitrate = newBitrate;
+                        }
+                    }
+
+                    // Convert YUV420 to NV12 if necessary
+                    if (properties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
+                        srcFrame = YUV420ToNV(streamParams.frameWidth, streamParams.frameHeight,
+                                srcFrame, properties.isGoogleSwCodec());
+                    }
+                }
+
+                inputConsumed = codec.feedInput(srcFrame, sawInputEOS);
+                if (inputConsumed) {
+                    inputFrameIndex++;
+                    consumedInputEOS = sawInputEOS;
+                }
+            }
+
+            // Get output from the encoder
+            MediaEncoderOutput out = codec.getOutput();
+            if (out.outputGenerated) {
+                // Detect output EOS
+                if ((out.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                    Log.d(TAG, "----Output EOS ");
+                    sawOutputEOS = true;
+                }
+
+                if (out.buffer.length > 0) {
+                    // Save frame
+                    ivf.writeFrame(out.buffer, out.outPresentationTimeUs);
+
+                    // Update statistics - store presentation time delay in offset
+                    long presentationTimeUsDelta = out.inPresentationTimeUs -
+                            out.outPresentationTimeUs;
+                    MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                    bufferInfoCopy.set((int)presentationTimeUsDelta, out.buffer.length,
+                            out.outPresentationTimeUs, out.flags);
+                    bufferInfos.add(bufferInfoCopy);
+                }
+            }
+
+            // If codec is not ready to accept input/poutput - wait for buffer ready callback
+            if ((!inputConsumed || consumedInputEOS) && !out.outputGenerated) {
+                codec.waitForBufferEvent();
+            }
+        }
+
+        codec.deleteCodec();
+        ivf.close();
+        yuvStream.close();
+
+        return bufferInfos;
+    }
+
+    /**
+     * Vp8 encoding loop supporting encoding multiple streams at a time.
+     * Each output stream is described by encodingParams parameters allowing
+     * simultaneous encoding of various resolutions, bitrates with an option to
+     * control key frame and dynamic bitrate for each output stream indepandently.
+     *
+     * MediaCodec will raise an IllegalStateException
+     * whenever vp8 encoder fails to encode a frame.
+     *
+     * Color format of input file should be YUV420, and frameWidth,
+     * frameHeight should be supplied correctly as raw input file doesn't
+     * include any header data.
+     *
+     * @param srcFrameWidth     Frame width of input yuv file
+     * @param srcFrameHeight    Frame height of input yuv file
+     * @param encodingParams    Encoder parameters
+     * @return                  Returns 2D array of encoded frames information for each stream and
+     *                          for each frame.
+     */
+    protected ArrayList<ArrayList<MediaCodec.BufferInfo>> encodeSimulcast(
+            int srcFrameWidth,
+            int srcFrameHeight,
+            ArrayList<EncoderOutputStreamParameters> encodingParams)  throws Exception {
+        int numEncoders = encodingParams.size();
+
+        // Create arrays of input/output, formats, bitrates etc
+        ArrayList<ArrayList<MediaCodec.BufferInfo>> bufferInfos =
+                new ArrayList<ArrayList<MediaCodec.BufferInfo>>(numEncoders);
+        InputStream yuvStream[] = new InputStream[numEncoders];
+        IvfWriter[] ivf = new IvfWriter[numEncoders];
+        FileOutputStream[] yuvScaled = new FileOutputStream[numEncoders];
+        MediaFormat[] format = new MediaFormat[numEncoders];
+        MediaEncoderAsync[] codec = new MediaEncoderAsync[numEncoders];
+        int[] inputFrameIndex = new int[numEncoders];
+        boolean[] sawInputEOS = new boolean[numEncoders];
+        boolean[] consumedInputEOS = new boolean[numEncoders];
+        boolean[] inputConsumed = new boolean[numEncoders];
+        boolean[] bufferConsumed = new boolean[numEncoders];
+        boolean[] sawOutputEOS = new boolean[numEncoders];
+        byte[][] srcFrame = new byte[numEncoders][];
+        boolean sawOutputEOSTotal = false;
+        boolean bufferConsumedTotal = false;
+        CodecProperties[] codecProperties = new CodecProperties[numEncoders];
+
+        for (int i = 0; i < numEncoders; i++) {
+            EncoderOutputStreamParameters params = encodingParams.get(i);
+            CodecProperties properties = getVp8CodecProperties(true, params.forceSwEncoder);
+
+            // Check if scaled image was created
+            int scale = params.frameWidth / srcFrameWidth;
+            if (!mScaledImages.contains(scale)) {
+                // resize image
+                cacheScaledImage(params.inputYuvFilename, params.inputResourceId,
+                        srcFrameWidth, srcFrameHeight,
+                        params.scaledYuvFilename, params.frameWidth, params.frameHeight);
+                mScaledImages.add(scale);
+            }
+
+            // Create buffer info storage
+            bufferInfos.add(new ArrayList<MediaCodec.BufferInfo>());
+
+            // Create YUV reader
+            yuvStream[i] = new FileInputStream(params.scaledYuvFilename);
+
+            // Create IVF writer
+            ivf[i] = new IvfWriter(params.outputIvfFilename, params.frameWidth, params.frameHeight);
+
+            // Frame buffer
+            int frameSize = params.frameWidth * params.frameHeight * 3 / 2;
+            srcFrame[i] = new byte[frameSize];
+
+            // Create a media format signifying desired output.
+            int bitrate = params.bitrateSet[0];
+            format[i] = MediaFormat.createVideoFormat(VP8_MIME,
+                    params.frameWidth, params.frameHeight);
+            format[i].setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+            if (params.bitrateType == VIDEO_ControlRateConstant) {
+                format[i].setInteger("bitrate-mode", VIDEO_ControlRateConstant); // set CBR
+            }
+            if (params.temporalLayers > 0) {
+                format[i].setInteger("ts-layers", params.temporalLayers); // 1 temporal layer
+            }
+            format[i].setInteger(MediaFormat.KEY_COLOR_FORMAT, properties.colorFormat);
+            format[i].setInteger(MediaFormat.KEY_FRAME_RATE, params.frameRate);
+            int syncFrameInterval = (params.syncFrameInterval + params.frameRate/2) /
+                    params.frameRate; // in sec
+            format[i].setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, syncFrameInterval);
+            // Create encoder
+            Log.d(TAG, "Creating encoder #" + i +" : " + properties.codecName +
+                    ". Color format: 0x" + Integer.toHexString(properties.colorFormat)+ " : " +
+                    params.frameWidth + " x " + params.frameHeight +
+                    ". Bitrate: " + bitrate + " Bitrate type: " + params.bitrateType +
+                    ". Fps:" + params.frameRate + ". TS Layers: " + params.temporalLayers +
+                    ". Key frame:" + syncFrameInterval * params.frameRate +
+                    ". Force keyFrame: " + params.syncForceFrameInterval);
+            Log.d(TAG, "  Format: " + format[i]);
+            Log.d(TAG, "  Output ivf:" + params.outputIvfFilename);
+
+            // Create encoder
+            codec[i] = new MediaEncoderAsync();
+            codec[i].createCodec(i, properties.codecName, format[i],
+                    params.timeoutDequeue, params.runInLooperThread);
+            codecProperties[i] = new CodecProperties(properties.codecName, properties.colorFormat);
+
+            inputConsumed[i] = true;
+        }
+
+        while (!sawOutputEOSTotal) {
+            // Feed input buffer to all encoders
+            for (int i = 0; i < numEncoders; i++) {
+                bufferConsumed[i] = false;
+                if (consumedInputEOS[i]) {
+                    continue;
+                }
+
+                EncoderOutputStreamParameters params = encodingParams.get(i);
+                // Read new input buffers - if previous input was consumed and no EOS
+                if (inputConsumed[i] && !sawInputEOS[i]) {
+                    int bytesRead = yuvStream[i].read(srcFrame[i]);
+
+                    // Check EOS
+                    if (params.frameCount > 0 && inputFrameIndex[i] >= params.frameCount) {
+                        sawInputEOS[i] = true;
+                        Log.d(TAG, "---Enc" + i +
+                                ". Sending EOS empty frame for frame # " + inputFrameIndex[i]);
+                    }
+
+                    if (!sawInputEOS[i] && bytesRead == -1) {
+                        if (params.frameCount == 0) {
+                            sawInputEOS[i] = true;
+                            Log.d(TAG, "---Enc" + i +
+                                    ". Sending EOS empty frame for frame # " + inputFrameIndex[i]);
+                        } else {
+                            yuvStream[i].close();
+                            yuvStream[i] = new FileInputStream(params.scaledYuvFilename);
+                            bytesRead = yuvStream[i].read(srcFrame[i]);
+                        }
+                    }
+
+                    // Convert YUV420 to NV12 if necessary
+                    if (codecProperties[i].colorFormat !=
+                            CodecCapabilities.COLOR_FormatYUV420Planar) {
+                        srcFrame[i] = YUV420ToNV(params.frameWidth, params.frameHeight, srcFrame[i],
+                                codecProperties[i].isGoogleSwCodec());
+                    }
+                }
+
+                inputConsumed[i] = codec[i].feedInput(srcFrame[i], sawInputEOS[i]);
+                if (inputConsumed[i]) {
+                    inputFrameIndex[i]++;
+                    consumedInputEOS[i] = sawInputEOS[i];
+                    bufferConsumed[i] = true;
+                }
+
+            }
+
+            // Get output from all encoders
+            for (int i = 0; i < numEncoders; i++) {
+                if (sawOutputEOS[i]) {
+                    continue;
+                }
+
+                MediaEncoderOutput out = codec[i].getOutput();
+                if (out.outputGenerated) {
+                    bufferConsumed[i] = true;
+                    // Detect output EOS
+                    if ((out.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+                        Log.d(TAG, "----Enc" + i + ". Output EOS ");
+                        sawOutputEOS[i] = true;
+                    }
+
+                    if (out.buffer.length > 0) {
+                        // Save frame
+                        ivf[i].writeFrame(out.buffer, out.outPresentationTimeUs);
+
+                        // Update statistics - store presentation time delay in offset
+                        long presentationTimeUsDelta = out.inPresentationTimeUs -
+                                out.outPresentationTimeUs;
+                        MediaCodec.BufferInfo bufferInfoCopy = new MediaCodec.BufferInfo();
+                        bufferInfoCopy.set((int)presentationTimeUsDelta, out.buffer.length,
+                                out.outPresentationTimeUs, out.flags);
+                        bufferInfos.get(i).add(bufferInfoCopy);
+                    }
+                }
+            }
+
+            // If codec is not ready to accept input/output - wait for buffer ready callback
+            bufferConsumedTotal = false;
+            for (boolean bufferConsumedCurrent : bufferConsumed) {
+                bufferConsumedTotal |= bufferConsumedCurrent;
+            }
+            if (!bufferConsumedTotal) {
+                // Pick the encoder to wait for
+                for (int i = 0; i < numEncoders; i++) {
+                    if (!bufferConsumed[i] && !sawOutputEOS[i]) {
+                        codec[i].waitForBufferEvent();
+                        break;
+                    }
+                }
+            }
+
+            // Check if EOS happened for all encoders
+            sawOutputEOSTotal = true;
+            for (boolean sawOutputEOSStream : sawOutputEOS) {
+                sawOutputEOSTotal &= sawOutputEOSStream;
+            }
+        }
+
+        for (int i = 0; i < numEncoders; i++) {
+            codec[i].deleteCodec();
+            ivf[i].close();
+            yuvStream[i].close();
+            if (yuvScaled[i] != null) {
+                yuvScaled[i].close();
+            }
+        }
+
+        return bufferInfos;
+    }
+
+    /**
+     * Some encoding statistics.
+     */
+    protected class Vp8EncodingStatistics {
+        Vp8EncodingStatistics() {
+            mBitrates = new ArrayList<Integer>();
+            mFrames = new ArrayList<Integer>();
+            mKeyFrames = new ArrayList<Integer>();
+            mMinimumKeyFrameInterval = Integer.MAX_VALUE;
+        }
+
+        public ArrayList<Integer> mBitrates;// Bitrate values for each second of the encoded stream.
+        public ArrayList<Integer> mFrames; // Number of frames in each second of the encoded stream.
+        public int mAverageBitrate;         // Average stream bitrate.
+        public ArrayList<Integer> mKeyFrames;// Stores the position of key frames in a stream.
+        public int mAverageKeyFrameInterval; // Average key frame interval.
+        public int mMaximumKeyFrameInterval; // Maximum key frame interval.
+        public int mMinimumKeyFrameInterval; // Minimum key frame interval.
+    }
+
+    /**
+     * Calculates average bitrate and key frame interval for the encoded streams.
+     * Output mBitrates field will contain bitrate values for every second
+     * of the encoded stream.
+     * Average stream bitrate will be stored in mAverageBitrate field.
+     * mKeyFrames array will contain the position of key frames in the encoded stream and
+     * mKeyFrameInterval - average key frame interval.
+     */
+    protected Vp8EncodingStatistics computeEncodingStatistics(int encoderId,
+            ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
+        Vp8EncodingStatistics statistics = new Vp8EncodingStatistics();
+
+        int totalSize = 0;
+        int frames = 0;
+        int framesPerSecond = 0;
+        int totalFrameSizePerSecond = 0;
+        int maxFrameSize = 0;
+        int currentSecond;
+        int nextSecond = 0;
+        String keyFrameList = "  IFrame List: ";
+        String bitrateList = "  Bitrate list: ";
+        String framesList = "  FPS list: ";
+
+
+        for (int j = 0; j < bufferInfos.size(); j++) {
+            MediaCodec.BufferInfo info = bufferInfos.get(j);
+            currentSecond = (int)(info.presentationTimeUs / 1000000);
+            boolean lastFrame = (j == bufferInfos.size() - 1);
+            if (!lastFrame) {
+                nextSecond = (int)(bufferInfos.get(j+1).presentationTimeUs / 1000000);
+            }
+
+            totalSize += info.size;
+            totalFrameSizePerSecond += info.size;
+            maxFrameSize = Math.max(maxFrameSize, info.size);
+            framesPerSecond++;
+            frames++;
+
+            // Update the bitrate statistics if the next frame will
+            // be for the next second
+            if (lastFrame || nextSecond > currentSecond) {
+                int currentBitrate = totalFrameSizePerSecond * 8;
+                bitrateList += (currentBitrate + " ");
+                framesList += (framesPerSecond + " ");
+                statistics.mBitrates.add(currentBitrate);
+                statistics.mFrames.add(framesPerSecond);
+                totalFrameSizePerSecond = 0;
+                framesPerSecond = 0;
+            }
+
+            // Update key frame statistics.
+            if ((info.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0) {
+                statistics.mKeyFrames.add(j);
+                keyFrameList += (j + "  ");
+            }
+        }
+        int duration = (int)(bufferInfos.get(bufferInfos.size() - 1).presentationTimeUs / 1000);
+        duration = (duration + 500) / 1000;
+        statistics.mAverageBitrate = (int)(((long)totalSize * 8) / duration);
+        Log.d(TAG, "Statistics for encoder # " + encoderId);
+        // Calculate average key frame interval in frames.
+        int keyFrames = statistics.mKeyFrames.size();
+        if (keyFrames > 1) {
+            statistics.mAverageKeyFrameInterval =
+                    statistics.mKeyFrames.get(keyFrames - 1) - statistics.mKeyFrames.get(0);
+            statistics.mAverageKeyFrameInterval =
+                    Math.round((float)statistics.mAverageKeyFrameInterval / (keyFrames - 1));
+            for (int j = 1; j < keyFrames; j++) {
+                int keyFrameInterval =
+                        statistics.mKeyFrames.get(j) - statistics.mKeyFrames.get(j - 1);
+                statistics.mMaximumKeyFrameInterval =
+                        Math.max(statistics.mMaximumKeyFrameInterval, keyFrameInterval);
+                statistics.mMinimumKeyFrameInterval =
+                        Math.min(statistics.mMinimumKeyFrameInterval, keyFrameInterval);
+            }
+            Log.d(TAG, "  Key frame intervals: Max: " + statistics.mMaximumKeyFrameInterval +
+                    ". Min: " + statistics.mMinimumKeyFrameInterval +
+                    ". Avg: " + statistics.mAverageKeyFrameInterval);
+        }
+        Log.d(TAG, "  Frames: " + frames + ". Duration: " + duration +
+                ". Total size: " + totalSize + ". Key frames: " + keyFrames);
+        Log.d(TAG, keyFrameList);
+        Log.d(TAG, bitrateList);
+        Log.d(TAG, framesList);
+        Log.d(TAG, "  Bitrate average: " + statistics.mAverageBitrate);
+        Log.d(TAG, "  Maximum frame size: " + maxFrameSize);
+
+        return statistics;
+    }
+
+    protected Vp8EncodingStatistics computeEncodingStatistics(
+            ArrayList<MediaCodec.BufferInfo> bufferInfos ) {
+        return computeEncodingStatistics(0, bufferInfos);
+    }
+
+    protected ArrayList<Vp8EncodingStatistics> computeSimulcastEncodingStatistics(
+            ArrayList<ArrayList<MediaCodec.BufferInfo>> bufferInfos) {
+        int numCodecs = bufferInfos.size();
+        ArrayList<Vp8EncodingStatistics> statistics = new ArrayList<Vp8EncodingStatistics>();
+
+        for (int i = 0; i < numCodecs; i++) {
+            Vp8EncodingStatistics currentStatistics =
+                    computeEncodingStatistics(i, bufferInfos.get(i));
+            statistics.add(currentStatistics);
+        }
+        return statistics;
+    }
+
+    /**
+     * Calculates maximum latency for encoder/decoder based on buffer info array
+     * generated either by encoder or decoder.
+     */
+    protected int maxPresentationTimeDifference(ArrayList<MediaCodec.BufferInfo> bufferInfos) {
+        int maxValue = 0;
+        for (MediaCodec.BufferInfo bufferInfo : bufferInfos) {
+            maxValue = Math.max(maxValue,  bufferInfo.offset);
+        }
+        maxValue = (maxValue + 500) / 1000; // mcs -> ms
+        return maxValue;
+    }
+
+    /**
+     * Decoding PSNR statistics.
+     */
+    protected class Vp8DecodingStatistics {
+        Vp8DecodingStatistics() {
+            mMinimumPSNR = Integer.MAX_VALUE;
+        }
+        public double mAveragePSNR;
+        public double mMinimumPSNR;
+    }
+
+    /**
+     * Calculates PSNR value between two video frames.
+     */
+    private double computePSNR(byte[] data0, byte[] data1) {
+        long squareError = 0;
+        assertTrue(data0.length == data1.length);
+        int length = data0.length;
+        for (int i = 0 ; i < length; i++) {
+            int diff = ((int)data0[i] & 0xff) - ((int)data1[i] & 0xff);
+            squareError += diff * diff;
+        }
+        double meanSquareError = (double)squareError / length;
+        double psnr = 10 * Math.log10((double)255 * 255 / meanSquareError);
+        return psnr;
+    }
+
+    /**
+     * Calculates average and minimum PSNR values between
+     * set of reference and decoded video frames.
+     * Runs PSNR calculation for the full duration of the decoded data.
+     */
+    protected Vp8DecodingStatistics computeDecodingStatistics(
+            String referenceYuvFilename,
+            int referenceYuvRawId,
+            String decodedYuvFilename,
+            int width,
+            int height) throws Exception {
+        Vp8DecodingStatistics statistics = new Vp8DecodingStatistics();
+        InputStream referenceStream =
+                OpenFileOrResourceId(referenceYuvFilename, referenceYuvRawId);
+        InputStream decodedStream = new FileInputStream(decodedYuvFilename);
+
+        int ySize = width * height;
+        int uvSize = width * height / 4;
+        byte[] yRef = new byte[ySize];
+        byte[] yDec = new byte[ySize];
+        byte[] uvRef = new byte[uvSize];
+        byte[] uvDec = new byte[uvSize];
+
+        int frames = 0;
+        double averageYPSNR = 0;
+        double averageUPSNR = 0;
+        double averageVPSNR = 0;
+        double minimumYPSNR = Integer.MAX_VALUE;
+        double minimumUPSNR = Integer.MAX_VALUE;
+        double minimumVPSNR = Integer.MAX_VALUE;
+        int minimumPSNRFrameIndex = 0;
+
+        while (true) {
+            // Calculate Y PSNR.
+            int bytesReadRef = referenceStream.read(yRef);
+            int bytesReadDec = decodedStream.read(yDec);
+            if (bytesReadDec == -1) {
+                break;
+            }
+            if (bytesReadRef == -1) {
+                // Reference file wrapping up
+                referenceStream.close();
+                referenceStream =
+                        OpenFileOrResourceId(referenceYuvFilename, referenceYuvRawId);
+                bytesReadRef = referenceStream.read(yRef);
+            }
+            double curYPSNR = computePSNR(yRef, yDec);
+            averageYPSNR += curYPSNR;
+            minimumYPSNR = Math.min(minimumYPSNR, curYPSNR);
+            double curMinimumPSNR = curYPSNR;
+
+            // Calculate U PSNR.
+            bytesReadRef = referenceStream.read(uvRef);
+            bytesReadDec = decodedStream.read(uvDec);
+            double curUPSNR = computePSNR(uvRef, uvDec);
+            averageUPSNR += curUPSNR;
+            minimumUPSNR = Math.min(minimumUPSNR, curUPSNR);
+            curMinimumPSNR = Math.min(curMinimumPSNR, curUPSNR);
+
+            // Calculate V PSNR.
+            bytesReadRef = referenceStream.read(uvRef);
+            bytesReadDec = decodedStream.read(uvDec);
+            double curVPSNR = computePSNR(uvRef, uvDec);
+            averageVPSNR += curVPSNR;
+            minimumVPSNR = Math.min(minimumVPSNR, curVPSNR);
+            curMinimumPSNR = Math.min(curMinimumPSNR, curVPSNR);
+
+            // Frame index for minimum PSNR value - help to detect possible distortions
+            if (curMinimumPSNR < statistics.mMinimumPSNR) {
+                statistics.mMinimumPSNR = curMinimumPSNR;
+                minimumPSNRFrameIndex = frames;
+            }
+
+            String logStr = String.format(Locale.US, "PSNR #%d: Y: %.2f. U: %.2f. V: %.2f",
+                    frames, curYPSNR, curUPSNR, curVPSNR);
+            Log.v(TAG, logStr);
+
+            frames++;
+        }
+
+        averageYPSNR /= frames;
+        averageUPSNR /= frames;
+        averageVPSNR /= frames;
+        statistics.mAveragePSNR = (4 * averageYPSNR + averageUPSNR + averageVPSNR) / 6;
+
+        Log.d(TAG, "PSNR statistics for " + frames + " frames.");
+        String logStr = String.format(Locale.US,
+                "Average PSNR: Y: %.1f. U: %.1f. V: %.1f. Average: %.1f",
+                averageYPSNR, averageUPSNR, averageVPSNR, statistics.mAveragePSNR);
+        Log.d(TAG, logStr);
+        logStr = String.format(Locale.US,
+                "Minimum PSNR: Y: %.1f. U: %.1f. V: %.1f. Overall: %.1f at frame %d",
+                minimumYPSNR, minimumUPSNR, minimumVPSNR,
+                statistics.mMinimumPSNR, minimumPSNRFrameIndex);
+        Log.d(TAG, logStr);
+
+        referenceStream.close();
+        decodedStream.close();
+        return statistics;
+    }
+}
+
diff --git a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
index 14e4055..19b4caa 100644
--- a/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/Vp8EncoderTest.java
@@ -16,594 +16,335 @@
 
 package android.media.cts;
 
-import android.content.Context;
-import android.content.res.Resources;
 import android.media.MediaCodec;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaFormat;
-import android.os.Bundle;
-import android.test.AndroidTestCase;
 import android.util.Log;
-
 import com.android.cts.media.R;
 
-import java.io.InputStream;
-import java.nio.ByteBuffer;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
 
 /**
- * Basic verification test for vp8 encoder.
+ * Verification test for vp8 encoder and decoder.
  *
- * A raw yv12 stream is encoded and written to an IVF
- * file, which is later decoded by vp8 decoder to verify
- * frames are at least decodable.
+ * A raw yv12 stream is encoded at various settings and written to an IVF
+ * file. Encoded stream bitrate and key frame interval are checked against target values.
+ * The stream is later decoded by vp8 decoder to verify frames are decodable and to
+ * calculate PSNR values for various bitrates.
  */
-public class Vp8EncoderTest extends AndroidTestCase {
+public class Vp8EncoderTest extends Vp8CodecTestBase {
 
-    private static final String TAG = "VP8EncoderTest";
-    private static final String VP8_MIME = "video/x-vnd.on2.vp8";
-    private static final String VPX_DECODER_NAME = "OMX.google.vp8.decoder";
-    private static final String VPX_ENCODER_NAME = "OMX.google.vp8.encoder";
-    private static final String BASIC_IVF = "video_176x144_vp8_basic.ivf";
-    private static final long DEFAULT_TIMEOUT_US = 5000;
+    private static final String ENCODED_IVF_BASE = "football";
+    private static final String INPUT_YUV = null;
+    private static final String OUTPUT_YUV = SDCARD_DIR + File.separator +
+            ENCODED_IVF_BASE + "_out.yuv";
 
-    private Resources mResources;
-    private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
-    private ByteBuffer[] mInputBuffers;
-    private ByteBuffer[] mOutputBuffers;
-
-    @Override
-    public void setContext(Context context) {
-        super.setContext(context);
-        mResources = mContext.getResources();
-    }
+    // YUV stream properties.
+    private static final int WIDTH = 320;
+    private static final int HEIGHT = 240;
+    private static final int FPS = 30;
+    // Default encoding bitrate.
+    private static final int BITRATE = 400000;
+    // Default encoding bitrate mode
+    private static final int BITRATE_MODE = VIDEO_ControlRateVariable;
+    // List of bitrates used in quality and basic bitrate tests.
+    private static final int[] TEST_BITRATES_SET = { 300000, 500000, 700000, 900000 };
+    // Maximum allowed bitrate variation from the target value.
+    private static final double MAX_BITRATE_VARIATION = 0.2;
+    // Average PSNR values for reference SW VP8 codec for the above bitrates.
+    private static final double[] REFERENCE_AVERAGE_PSNR = { 33.1, 35.2, 36.6, 37.8 };
+    // Minimum PSNR values for reference SW VP8 codec for the above bitrates.
+    private static final double[] REFERENCE_MINIMUM_PSNR = { 25.9, 27.5, 28.4, 30.3 };
+    // Maximum allowed average PSNR difference of HW encoder comparing to reference SW encoder.
+    private static final double MAX_AVERAGE_PSNR_DIFFERENCE = 2;
+    // Maximum allowed minimum PSNR difference of HW encoder comparing to reference SW encoder.
+    private static final double MAX_MINIMUM_PSNR_DIFFERENCE = 4;
+    // Maximum allowed average PSNR difference of the encoder running in a looper thread with 0 ms
+    // buffer dequeue timeout comparing to the encoder running in a callee's thread with 100 ms
+    // buffer dequeue timeout.
+    private static final double MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE = 0.5;
+    // Maximum allowed minimum PSNR difference of the encoder running in a looper thread
+    // comparing to the encoder running in a callee's thread.
+    private static final double MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE = 2;
+    // Maximum allowed average key frame interval variation from the target value.
+    private static final int MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION = 1;
+    // Maximum allowed key frame interval variation from the target value.
+    private static final int MAX_KEYFRAME_INTERVAL_VARIATION = 3;
 
     /**
      * A basic test for VP8 encoder.
      *
-     * Encodes a raw stream with default configuration options,
+     * Encodes 9 seconds of raw stream with default configuration options,
      * and then decodes it to verify the bitstream.
+     * Also checks the average bitrate is within MAX_BITRATE_VARIATION of the target value.
      */
     public void testBasic() throws Exception {
-        encode(BASIC_IVF,
-               R.raw.video_176x144_yv12,
-               176,  // width
-               144,  // height
-               30);  // framerate
-        decode(BASIC_IVF);
+        int encodeSeconds = 9;
+
+        for (int targetBitrate : TEST_BITRATES_SET) {
+            EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                    INPUT_YUV,
+                    ENCODED_IVF_BASE,
+                    encodeSeconds,
+                    WIDTH,
+                    HEIGHT,
+                    FPS,
+                    BITRATE_MODE,
+                    targetBitrate,
+                    true);
+            ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+            Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+
+            assertEquals("Stream bitrate " + statistics.mAverageBitrate +
+                    " is different from the target " + targetBitrate,
+                    targetBitrate, statistics.mAverageBitrate,
+                    MAX_BITRATE_VARIATION * targetBitrate);
+
+            decode(params.outputIvfFilename, null, FPS, params.forceSwEncoder);
+        }
+    }
+
+    /**
+     * Asynchronous encoding test for VP8 encoder.
+     *
+     * Encodes 9 seconds of raw stream using synchronous and asynchronous calls.
+     * Checks the PSNR difference between the encoded and decoded output and reference yuv input
+     * does not change much for two different ways of the encoder call.
+     */
+    public void testAsyncEncoding() throws Exception {
+        int encodeSeconds = 9;
+
+        // First test the encoder running in a looper thread with buffer callbacks enabled.
+        boolean syncEncoding = false;
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                syncEncoding);
+        ArrayList<MediaCodec.BufferInfo> bufInfos = encode(params);
+        computeEncodingStatistics(bufInfos);
+        decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+        Vp8DecodingStatistics statisticsAsync = computeDecodingStatistics(
+                params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                params.frameWidth, params.frameHeight);
+
+
+        // Test the encoder running in a callee's thread.
+        syncEncoding = true;
+        params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                syncEncoding);
+        bufInfos = encode(params);
+        computeEncodingStatistics(bufInfos);
+        decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+        Vp8DecodingStatistics statisticsSync = computeDecodingStatistics(
+                params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                params.frameWidth, params.frameHeight);
+
+        // Check PSNR difference.
+        Log.d(TAG, "PSNR Average: Async: " + statisticsAsync.mAveragePSNR +
+                ". Sync: " + statisticsSync.mAveragePSNR);
+        Log.d(TAG, "PSNR Minimum: Async: " + statisticsAsync.mMinimumPSNR +
+                ". Sync: " + statisticsSync.mMinimumPSNR);
+        if ((Math.abs(statisticsAsync.mAveragePSNR - statisticsSync.mAveragePSNR) >
+            MAX_ASYNC_AVERAGE_PSNR_DIFFERENCE) ||
+            (Math.abs(statisticsAsync.mMinimumPSNR - statisticsSync.mMinimumPSNR) >
+            MAX_ASYNC_MINIMUM_PSNR_DIFFERENCE)) {
+            throw new RuntimeException("Difference between PSNRs for async and sync encoders");
+        }
     }
 
     /**
      * Check if MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME is honored.
      *
-     * At frame 15, request a sync frame. If one does not occur by EOF the
-     * encoder fails. The test does not verify the output stream.
+     * Encodes 9 seconds of raw stream and requests a sync frame every second (30 frames).
+     * The test does not verify the output stream.
      */
     public void testSyncFrame() throws Exception {
-        encodeSyncFrame(R.raw.video_176x144_yv12,
-                        176, // width
-                        144, // height
-                        30); // framerate
+        int encodeSeconds = 9;
+
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                BITRATE,
+                true);
+        params.syncFrameInterval = encodeSeconds * FPS;
+        params.syncForceFrameInterval = FPS;
+        ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+        Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
+
+        // First check if we got expected number of key frames.
+        int actualKeyFrames = statistics.mKeyFrames.size();
+        if (actualKeyFrames != encodeSeconds) {
+            throw new RuntimeException("Number of key frames " + actualKeyFrames +
+                    " is different from the expected " + encodeSeconds);
+        }
+
+        // Check key frame intervals:
+        // Average value should be within +/- 1 frame of the target value,
+        // maximum value should not be greater than target value + 3,
+        // and minimum value should not be less that target value - 3.
+        if (Math.abs(statistics.mAverageKeyFrameInterval - FPS) >
+            MAX_AVERAGE_KEYFRAME_INTERVAL_VARIATION ||
+            (statistics.mMaximumKeyFrameInterval - FPS > MAX_KEYFRAME_INTERVAL_VARIATION) ||
+            (FPS - statistics.mMinimumKeyFrameInterval > MAX_KEYFRAME_INTERVAL_VARIATION)) {
+            throw new RuntimeException(
+                    "Key frame intervals are different from the expected " + FPS);
+        }
     }
 
     /**
      * Check if MediaCodec.PARAMETER_KEY_VIDEO_BITRATE is honored.
      *
-     * Run the sample multiple times. Request periodic changes to the
-     * bitrate and ensure the encoder responds.
+     * Run the the encoder for 12 seconds. Request changes to the
+     * bitrate after 6 seconds and ensure the encoder responds.
      */
-    public void testVariableBitrate() throws Exception {
-        encodeVariableBitrate(R.raw.video_176x144_yv12,
-                              176, // width
-                              144, // height
-                              30); // framerate
-    }
+     public void testDynamicBitrateChange() throws Exception {
+        int encodeSeconds = 12;    // Encoding sequence duration in seconds.
+        int[] bitrateTargetValues = { 400000, 800000 };  // List of bitrates to test.
 
-    /**
-     * A basic check if an encoded stream is decodable.
-     *
-     * The most basic confirmation we can get about a frame
-     * being properly encoded is trying to decode it.
-     * (Especially in realtime mode encode output is non-
-     * deterministic, therefore a more thorough check like
-     * md5 sum comparison wouldn't work.)
-     *
-     * Indeed, MediaCodec will raise an IllegalStateException
-     * whenever vp8 decoder fails to decode a frame, and
-     * this test uses that fact to verify the bitstream.
-     *
-     * @param filename  The name of the IVF file containing encoded bitsream.
-     */
-    private void decode(String filename) throws Exception {
-        IvfReader ivf = null;
-        try {
-            ivf = new IvfReader(filename);
-            int frameWidth = ivf.getWidth();
-            int frameHeight = ivf.getHeight();
-            int frameCount = ivf.getFrameCount();
+        EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                INPUT_YUV,
+                ENCODED_IVF_BASE,
+                encodeSeconds,
+                WIDTH,
+                HEIGHT,
+                FPS,
+                BITRATE_MODE,
+                bitrateTargetValues[0],
+                true);
 
-            assertTrue(frameWidth > 0);
-            assertTrue(frameHeight > 0);
-            assertTrue(frameCount > 0);
+        // Number of seconds for each bitrate
+        int stepSeconds = encodeSeconds / bitrateTargetValues.length;
+        // Fill the bitrates values.
+        params.bitrateSet = new int[encodeSeconds * FPS];
+        for (int i = 0; i < bitrateTargetValues.length ; i++) {
+            Arrays.fill(params.bitrateSet,
+                    i * encodeSeconds * FPS / bitrateTargetValues.length,
+                    (i + 1) * encodeSeconds * FPS / bitrateTargetValues.length,
+                    bitrateTargetValues[i]);
+        }
 
-            MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME,
-                                                               ivf.getWidth(),
-                                                               ivf.getHeight());
+        ArrayList<MediaCodec.BufferInfo> bufInfo = encode(params);
+        Vp8EncodingStatistics statistics = computeEncodingStatistics(bufInfo);
 
-            Log.d(TAG, "Creating decoder");
-            MediaCodec decoder = MediaCodec.createByCodecName(VPX_DECODER_NAME);
-            decoder.configure(format,
-                              null,  // surface
-                              null,  // crypto
-                              0);  // flags
-            decoder.start();
-
-            mInputBuffers = decoder.getInputBuffers();
-            mOutputBuffers = decoder.getOutputBuffers();
-
-            // decode loop
-            int frameIndex = 0;
-            boolean sawOutputEOS = false;
-            boolean sawInputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = decoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        byte[] frame = ivf.readFrame(frameIndex);
-
-                        if (frameIndex == frameCount - 1) {
-                            sawInputEOS = true;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        Log.d(TAG, "Decoding frame at index " + frameIndex);
-                        try {
-                            decoder.queueInputBuffer(
-                                    inputBufIndex,
-                                    0,  // offset
-                                    frame.length,
-                                    frameIndex,
-                                    sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-                        } catch (IllegalStateException ise) {
-                            //That is all what is passed from MediaCodec in case of
-                            //decode failure.
-                            fail("Failed to decode frame at index " + frameIndex);
-                        }
-                        frameIndex++;
-                    }
-                }
-
-                int result = decoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    int outputBufIndex = result;
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-                    decoder.releaseOutputBuffer(outputBufIndex, false);
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = decoder.getOutputBuffers();
-                }
+        // Calculate actual average bitrates  for every [stepSeconds] second.
+        int[] bitrateActualValues = new int[bitrateTargetValues.length];
+        for (int i = 0; i < bitrateTargetValues.length ; i++) {
+            bitrateActualValues[i] = 0;
+            for (int j = i * stepSeconds; j < (i + 1) * stepSeconds; j++) {
+                bitrateActualValues[i] += statistics.mBitrates.get(j);
             }
-            decoder.stop();
-            decoder.release();
-        } finally {
-            if (ivf != null) {
-                ivf.close();
+            bitrateActualValues[i] /= stepSeconds;
+            Log.d(TAG, "Actual bitrate for interval #" + i + " : " + bitrateActualValues[i] +
+                    ". Target: " + bitrateTargetValues[i]);
+
+            // Compare actual bitrate values to make sure at least same increasing/decreasing
+            // order as the target bitrate values.
+            for (int j = 0; j < i; j++) {
+                long differenceTarget = bitrateTargetValues[i] - bitrateTargetValues[j];
+                long differenceActual = bitrateActualValues[i] - bitrateActualValues[j];
+                if (differenceTarget * differenceActual < 0) {
+                    throw new RuntimeException("Target bitrates: " +
+                            bitrateTargetValues[j] + " , " + bitrateTargetValues[i] +
+                            ". Actual bitrates: "
+                            + bitrateActualValues[j] + " , " + bitrateActualValues[i]);
+                }
             }
         }
     }
 
     /**
-     * A basic vp8 encode loop.
+     * Check the encoder quality for various bitrates by calculating PSNR
      *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * In addition to that written IVF file can be tested
-     * to be decodable in order to verify the bitstream produced.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param outputFilename  The name of the IVF file to write encoded bitsream
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
+     * Run the the encoder for 9 seconds for each bitrate and calculate PSNR
+     * for each encoded stream.
+     * Video streams with higher bitrates should have higher PSNRs.
+     * Also compares average and minimum PSNR of HW codec with PSNR values of reference SW codec.
      */
-    private void encode(String outputFilename, int rawInputFd,
-                       int frameWidth, int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
+    public void testEncoderQuality() throws Exception {
+        int encodeSeconds = 9;      // Encoding sequence duration in seconds for each bitrate.
+        double[] psnrPlatformCodecAverage = new double[TEST_BITRATES_SET.length];
+        double[] psnrPlatformCodecMin = new double[TEST_BITRATES_SET.length];
 
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
+        // Run platform specific encoder for different bitrates
+        // and compare PSNR of hw codec with PSNR of reference sw codec.
+        for (int i = 0; i < TEST_BITRATES_SET.length; i++) {
+            EncoderOutputStreamParameters params = getDefaultEncodingParameters(
+                    INPUT_YUV,
+                    ENCODED_IVF_BASE,
+                    encodeSeconds,
+                    WIDTH,
+                    HEIGHT,
+                    FPS,
+                    BITRATE_MODE,
+                    TEST_BITRATES_SET[i],
+                    true);
+            encode(params);
 
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
+            decode(params.outputIvfFilename, OUTPUT_YUV, FPS, params.forceSwEncoder);
+            Vp8DecodingStatistics statistics = computeDecodingStatistics(
+                    params.inputYuvFilename, R.raw.football_qvga, OUTPUT_YUV,
+                    params.frameWidth, params.frameHeight);
+            psnrPlatformCodecAverage[i] = statistics.mAveragePSNR;
+            psnrPlatformCodecMin[i] = statistics.mMinimumPSNR;
+        }
 
-        InputStream rawStream = null;
-        IvfWriter ivf = null;
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            ivf = new IvfWriter(outputFilename, frameWidth, frameHeight);
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            int outputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        // YUV420 has 3 planes. Y is full size. U and V are each half size (1/4 the
-                        // pixels).
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            sawInputEOS = true;
-                            bytesRead = 0;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        Log.d(TAG, "Encoding frame at index " + inputFrameIndex);
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
+        // First do a sanity check - higher bitrates should results in higher PSNR.
+        for (int i = 1; i < TEST_BITRATES_SET.length ; i++) {
+            for (int j = 0; j < i; j++) {
+                double differenceBitrate = TEST_BITRATES_SET[i] - TEST_BITRATES_SET[j];
+                double differencePSNR = psnrPlatformCodecAverage[i] - psnrPlatformCodecAverage[j];
+                if (differenceBitrate * differencePSNR < 0) {
+                    throw new RuntimeException("Target bitrates: " +
+                            TEST_BITRATES_SET[j] + ", " + TEST_BITRATES_SET[i] +
+                            ". Actual PSNRs: "
+                            + psnrPlatformCodecAverage[j] + ", " + psnrPlatformCodecAverage[i]);
                 }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    int outputBufIndex = result;
-                    byte[] buffer = new byte[mBufferInfo.size];
-                    mOutputBuffers[outputBufIndex].rewind();
-                    mOutputBuffers[outputBufIndex].get(buffer, 0, mBufferInfo.size);
-
-                    if ((outputFrameIndex == 0)
-                        && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 0)) {
-                      throw new RuntimeException("First frame is not a sync frame.");
-
-                    }
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    } else {
-                        ivf.writeFrame(buffer, mBufferInfo.presentationTimeUs);
-                    }
-                    encoder.releaseOutputBuffer(outputBufIndex,
-                                                false);  // render
-
-                    outputFrameIndex++;
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
-            }
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (ivf != null) {
-                ivf.close();
-            }
-
-            if (rawStream != null) {
-                rawStream.close();
             }
         }
-    }
 
-
-    /**
-     * Request Sync Frames
-     *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * This presumes a file with 28 frames. Under normal circumstances there
-     * would only be one sync frame: the first one. This test will request an
-     * additional sync frame at 15 and ensure that it occurs by EOF.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
-     */
-    private void encodeSyncFrame(int rawInputFd, int frameWidth,
-                                 int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 100000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
-
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
-
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
-
-        InputStream rawStream = null;
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-            boolean syncFrameRequested = false;
-            boolean matchedSyncFrame = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            sawInputEOS = true;
-                            bytesRead = 0;
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        if (inputFrameIndex == 15) {
-                            Log.d(TAG, "Requesting sync frame at index " + inputFrameIndex);
-                            Bundle syncFrame = new Bundle();
-                            syncFrame.putInt(MediaCodec.PARAMETER_KEY_REQUEST_SYNC_FRAME, 0);
-                            encoder.setParameters(syncFrame);
-                            syncFrameRequested = true;
-                        }
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
-                }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-                    if (syncFrameRequested && ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0)) {
-                        Log.d(TAG, "Found sync frame");
-                        matchedSyncFrame = true;
-                    }
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-
-                    encoder.releaseOutputBuffer(result,
-                                                false);  // render
-
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
+        // Then compare average and minimum PSNR of platform codec with reference sw codec -
+        // average PSNR for platform codec should be no more than 2 dB less than reference PSNR
+        // and minumum PSNR - no more than 4 dB less than reference minimum PSNR.
+        // These PSNR difference numbers are arbitrary for now, will need further estimation
+        // when more devices with hw VP8 codec will appear.
+        for (int i = 0; i < TEST_BITRATES_SET.length ; i++) {
+            Log.d(TAG, "Bitrate " + TEST_BITRATES_SET[i]);
+            Log.d(TAG, "Reference: Average: " + REFERENCE_AVERAGE_PSNR[i] + ". Minimum: " +
+                    REFERENCE_MINIMUM_PSNR[i]);
+            Log.d(TAG, "Platform:  Average: " + psnrPlatformCodecAverage[i] + ". Minimum: " +
+                    psnrPlatformCodecMin[i]);
+            if (psnrPlatformCodecAverage[i] < REFERENCE_AVERAGE_PSNR[i] -
+                    MAX_AVERAGE_PSNR_DIFFERENCE) {
+                throw new RuntimeException("Low average PSNR " + psnrPlatformCodecAverage[i] +
+                        " comparing to reference PSNR " + REFERENCE_AVERAGE_PSNR[i] +
+                        " for bitrate " + TEST_BITRATES_SET[i]);
             }
-
-            if (!matchedSyncFrame) {
-                throw new RuntimeException("Requested sync frame did not occur");
-            }
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (rawStream != null) {
-                rawStream.close();
-            }
-        }
-    }
-
-
-    /**
-     * Adjust bitrate
-     *
-     * MediaCodec will raise an IllegalStateException
-     * whenever vp8 encoder fails to encode a frame.
-     *
-     * Encode the file three times: once at the initial bitrate, once at an
-     * increased bitrate, and once at a decreased bitrate. Record the frame
-     * sizes that are returned and verify a strict ordering.
-     *
-     * Color format of input file should be YUV420, and frameWidth,
-     * frameHeight should be supplied correctly as raw input file doesn't
-     * include any header data.
-     *
-     * @param rawInputFd      File descriptor for the raw input file (YUV420)
-     * @param frameWidth      Frame width of input file
-     * @param frameHeight     Frame height of input file
-     * @param frameRate       Frame rate of input file in frames per second
-     */
-    private void encodeVariableBitrate(int rawInputFd, int frameWidth,
-                                       int frameHeight, int frameRate) throws Exception {
-        // Create a media format signifying desired output
-        MediaFormat format = MediaFormat.createVideoFormat(VP8_MIME, frameWidth, frameHeight);
-        format.setInteger(MediaFormat.KEY_BIT_RATE, 75000);
-        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
-                          CodecCapabilities.COLOR_FormatYUV420Planar);
-        format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
-
-        Log.d(TAG, "Creating encoder");
-        MediaCodec encoder;
-        encoder = MediaCodec.createByCodecName(VPX_ENCODER_NAME);
-        encoder.configure(format,
-                          null,  // surface
-                          null,  // crypto
-                          MediaCodec.CONFIGURE_FLAG_ENCODE);
-        encoder.start();
-
-        mInputBuffers = encoder.getInputBuffers();
-        mOutputBuffers = encoder.getOutputBuffers();
-
-        InputStream rawStream = null;
-
-        int iteration = 0;
-        int[] bits = new int[100];
-
-        try {
-            rawStream = mResources.openRawResource(rawInputFd);
-            /* Doc says this is not the default:
-             * http://developer.android.com/reference/java/io/InputStream.html#markSupported()
-             * but it returns true so using .reset() instead of close/open
-             */
-            if (rawStream.markSupported()) Log.d(TAG, "Stream marking supported");
-            rawStream.mark(1000000);
-
-            // encode loop
-            long presentationTimeUs = 0;
-            int inputFrameIndex = 0;
-            int outputFrameIndex = 0;
-            boolean sawInputEOS = false;
-            boolean sawOutputEOS = false;
-
-            while (!sawOutputEOS) {
-                if (!sawInputEOS) {
-                    int inputBufIndex = encoder.dequeueInputBuffer(DEFAULT_TIMEOUT_US);
-                    if (inputBufIndex >= 0) {
-                        int frameSize = frameWidth * frameHeight * 3 / 2;
-
-                        byte[] frame = new byte[frameSize];
-                        int bytesRead = rawStream.read(frame);
-
-                        if (bytesRead == -1) {
-                            if (iteration < 2) {
-                                rawStream.reset();
-                                Bundle bitrate = new Bundle();
-                                if (iteration == 0) {
-                                    bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 150000);
-                                    Log.d(TAG, "Setting bitrate to 150000");
-                                } else {
-                                    bitrate.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE, 25000);
-                                    Log.d(TAG, "Setting bitrate to 25000");
-                                }
-                                encoder.setParameters(bitrate);
-
-                                iteration++;
-                                continue;
-                            } else {
-                                sawInputEOS = true;
-                                bytesRead = 0;
-                            }
-                        }
-
-                        mInputBuffers[inputBufIndex].clear();
-                        mInputBuffers[inputBufIndex].put(frame);
-                        mInputBuffers[inputBufIndex].rewind();
-
-                        presentationTimeUs = (inputFrameIndex * 1000000) / frameRate;
-                        encoder.queueInputBuffer(
-                                inputBufIndex,
-                                0,  // offset
-                                bytesRead,  // size
-                                presentationTimeUs,
-                                sawInputEOS ? MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
-
-                        inputFrameIndex++;
-                    }
-                }
-
-                int result = encoder.dequeueOutputBuffer(mBufferInfo, DEFAULT_TIMEOUT_US);
-                if (result >= 0) {
-
-                    bits[outputFrameIndex] = mBufferInfo.size;
-
-                    if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
-                        sawOutputEOS = true;
-                    }
-
-                    encoder.releaseOutputBuffer(result,
-                                                false);  // render
-
-                    outputFrameIndex++;
-
-                } else if (result == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
-                    mOutputBuffers = encoder.getOutputBuffers();
-                }
-            }
-
-            // 29 frames per run
-            int i;
-            int sum = 0;
-            int frames = 29;
-            for(i = 0; i < frames; i++)
-              sum += bits[i];
-            int midBitrateAvg = sum / frames;
-
-            sum = 0;
-            for(; i < frames * 2; i++)
-              sum += bits[i];
-            int highBitrateAvg = sum / frames;
-
-            sum = 0;
-            for(; i < frames * 3; i++)
-              sum += bits[i];
-            int lowBitrateAvg = sum / frames;
-
-            // For the given bitrates we expect mid ~= 350, high ~= 575 and low ~= 150
-            // bytes per frame
-            if ((midBitrateAvg + 100) > highBitrateAvg)
-                throw new RuntimeException("Bitrate did not increase when requesting higher bitrate");
-            if ((lowBitrateAvg + 100) > midBitrateAvg)
-                throw new RuntimeException("Bitrate did not decrease when requesting lower bitrate");
-
-
-            encoder.stop();
-            encoder.release();
-        } finally {
-            if (rawStream != null) {
-                rawStream.close();
+            if (psnrPlatformCodecMin[i] < REFERENCE_MINIMUM_PSNR[i] -
+                    MAX_MINIMUM_PSNR_DIFFERENCE) {
+                throw new RuntimeException("Low minimum PSNR " + psnrPlatformCodecMin[i] +
+                        " comparing to sw PSNR " + REFERENCE_MINIMUM_PSNR[i] +
+                        " for bitrate " + TEST_BITRATES_SET[i]);
             }
         }
     }
 }
+
diff --git a/tests/tests/mediastress/Android.mk b/tests/tests/mediastress/Android.mk
index 9f43597..5c4930b 100644
--- a/tests/tests/mediastress/Android.mk
+++ b/tests/tests/mediastress/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsmediastress_jni
diff --git a/tests/tests/mediastress/AndroidManifest.xml b/tests/tests/mediastress/AndroidManifest.xml
index 931a774..9d48c8c 100644
--- a/tests/tests/mediastress/AndroidManifest.xml
+++ b/tests/tests/mediastress/AndroidManifest.xml
@@ -39,8 +39,11 @@
         <activity android:name="android.mediastress.cts.NativeMediaActivity"
                   android:label="NativeMedia" />
     </application>
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.mediastress"
-            android:label="Media stress tests InstrumentationRunner" />
+            android:label="Media stress tests InstrumentationRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/nativeopengl/Android.mk b/tests/tests/nativeopengl/Android.mk
index dd19548..672eb5c 100644
--- a/tests/tests/nativeopengl/Android.mk
+++ b/tests/tests/nativeopengl/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctswrappedgtest
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativeopengltests
diff --git a/tests/tests/ndef/Android.mk b/tests/tests/ndef/Android.mk
index 70853d9..ba78f29 100644
--- a/tests/tests/ndef/Android.mk
+++ b/tests/tests/ndef/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/ndef/AndroidManifest.xml b/tests/tests/ndef/AndroidManifest.xml
index a7ebb6e..e0244e1 100644
--- a/tests/tests/ndef/AndroidManifest.xml
+++ b/tests/tests/ndef/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.ndef"
-                     android:label="CTS tests of NDEF data classes"/>
+                     android:label="CTS tests of NDEF data classes">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/net/Android.mk b/tests/tests/net/Android.mk
index 82abd62..da19a4d 100644
--- a/tests/tests/net/Android.mk
+++ b/tests/tests/net/Android.mk
@@ -21,7 +21,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner voip-common
+LOCAL_JAVA_LIBRARIES := voip-common conscrypt
 
 LOCAL_JNI_SHARED_LIBRARIES := libnativedns_jni
 
@@ -33,7 +33,7 @@
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestserver ctsdeviceutil ctstestrunner \
                                core-tests-support
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13249961 is fixed
 #LOCAL_SDK_VERSION := current
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/net/AndroidManifest.xml b/tests/tests/net/AndroidManifest.xml
index ade6728..652262d 100644
--- a/tests/tests/net/AndroidManifest.xml
+++ b/tests/tests/net/AndroidManifest.xml
@@ -32,9 +32,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.net"
-                     android:label="CTS tests of android.net"/>
+                     android:label="CTS tests of android.net">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
index cb8aeaf..6175923 100644
--- a/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
+++ b/tests/tests/net/src/android/net/cts/SSLCertificateSocketFactoryTest.java
@@ -26,8 +26,6 @@
 import android.net.SSLCertificateSocketFactory;
 import android.test.AndroidTestCase;
 
-import dalvik.annotation.BrokenTest;
-
 import libcore.javax.net.ssl.SSLDefaultConfigurationAsserts;
 
 public class SSLCertificateSocketFactoryTest extends AndroidTestCase {
diff --git a/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java b/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
new file mode 100644
index 0000000..9c0d774
--- /dev/null
+++ b/tests/tests/net/src/android/net/http/cts/X509TrustManagerExtensionsTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 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.net.http.cts;
+
+import android.net.http.X509TrustManagerExtensions;
+import android.util.Base64;
+
+import java.io.File;
+import java.io.ByteArrayInputStream;
+
+import java.security.KeyStore;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+
+import com.android.org.conscrypt.TrustedCertificateStore;
+import com.android.org.conscrypt.TrustManagerImpl;
+
+public class X509TrustManagerExtensionsTest extends TestCase {
+
+    public void testIsUserAddedCert() throws Exception {
+        final String testCert =
+            "MIICfjCCAeegAwIBAgIJAMefIzKHY5H4MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV" +
+            "BAYTAlVTMQswCQYDVQQIDAJDQTEWMBQGA1UEBwwNTW91bnRhaW4gVmlldzEPMA0G" +
+            "A1UECgwGR2V3Z3VsMRMwEQYDVQQDDApnZXdndWwuY29tMB4XDTEzMTEwNTAwNDE0" +
+            "MFoXDTEzMTIwNTAwNDE0MFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRYw" +
+            "FAYDVQQHDA1Nb3VudGFpbiBWaWV3MQ8wDQYDVQQKDAZHZXdndWwxEzARBgNVBAMM" +
+            "Cmdld2d1bC5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKpc/I0Ss4sm" +
+            "yV2iX5xRMM7+XXAhiWrceGair4MpvDrGIa1kFj2phtx4IqTfDnNU7AhRJYkDYmJQ" +
+            "fUJ8i6F+I08uNiGVO4DtPJbZcBXg9ME9EMaJCslm995ueeNWSw1Ky8zM0tt4p+94" +
+            "BcXJ7PC3N2WgkvtE8xwNbaeUfhGPzJKXAgMBAAGjUDBOMB0GA1UdDgQWBBQQ/iW7" +
+            "JCkSI2sbn4nTBiZ9PSiO8zAfBgNVHSMEGDAWgBQQ/iW7JCkSI2sbn4nTBiZ9PSiO" +
+            "8zAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GBABQBrUOWTCSIl3vkRR3w" +
+            "3bPzh3BpqDmxH9xe4rZr+MVKKjpGjY1z2m2EEtyNz3tbgVQym5+si00DUHFL0IP1" +
+            "SuRULmPyEpTBVbV+PA5Kc967ZcDgYt4JtdMcCeKbIFaU6r8oEYEL2PTlNZmgbunM" +
+            "pXktkhVvNxZeSa8yM9bPhXkN";
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        X509Certificate cert = (X509Certificate)cf.generateCertificate(
+            new ByteArrayInputStream(Base64.decode(testCert, Base64.DEFAULT)));
+
+        // Test without adding cert to keystore.
+        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        X509TrustManagerExtensions tmeNegative =
+            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore));
+        assertEquals(false, tmeNegative.isUserAddedCertificate(cert));
+
+        // Test with cert added to keystore.
+        final File DIR_TEMP = new File(System.getProperty("java.io.tmpdir"));
+        final File DIR_TEST = new File(DIR_TEMP, "test");
+        final File system = new File(DIR_TEST, "system-test");
+        final File added = new File(DIR_TEST, "added-test");
+        final File deleted = new File(DIR_TEST, "deleted-test");
+
+        TrustedCertificateStore tcs = new TrustedCertificateStore(system, added, deleted);
+        added.mkdirs();
+        tcs.installCertificate(cert);
+        X509TrustManagerExtensions tmePositive =
+            new X509TrustManagerExtensions(new TrustManagerImpl(keyStore, null, tcs));
+        assertEquals(true, tmePositive.isUserAddedCertificate(cert));
+    }
+}
diff --git a/tests/tests/opengl/Android.mk b/tests/tests/opengl/Android.mk
index 98f11e9..a14ee7a 100644
--- a/tests/tests/opengl/Android.mk
+++ b/tests/tests/opengl/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_JNI_SHARED_LIBRARIES := libopengltest_jni
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
diff --git a/tests/tests/opengl/AndroidManifest.xml b/tests/tests/opengl/AndroidManifest.xml
index 266216f..914b2d2 100644
--- a/tests/tests/opengl/AndroidManifest.xml
+++ b/tests/tests/opengl/AndroidManifest.xml
@@ -22,8 +22,11 @@
     <uses-sdk android:minSdkVersion="14" />
     <uses-feature android:glEsVersion="0x00020000"/>
     <instrumentation
-        android:name="android.test.InstrumentationCtsTestRunner"
-        android:targetPackage="com.android.cts.opengl" />
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.opengl" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
     <application
         android:icon="@drawable/ic_launcher"
         android:label="@string/app_name" >
@@ -35,7 +38,7 @@
           <activity
             android:label="@string/app_name"
             android:name="android.opengl.cts.OpenGLES20ActivityTwo">
-         </activity> 
+         </activity>
          <uses-library  android:name="android.test.runner" />
          <activity
             android:name="android.opengl.cts.OpenGLES20NativeActivityOne"
diff --git a/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java b/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java
new file mode 100644
index 0000000..4ca3a99
--- /dev/null
+++ b/tests/tests/opengl/src/android/opengl/cts/FramebufferTest.java
@@ -0,0 +1,653 @@
+/*
+ * 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.opengl.cts;
+
+import android.graphics.Bitmap;
+import android.graphics.SurfaceTexture;
+import android.opengl.EGL14;
+import android.opengl.EGLConfig;
+import android.opengl.EGLContext;
+import android.opengl.EGLDisplay;
+import android.opengl.EGLExt;
+import android.opengl.EGLSurface;
+import android.opengl.GLES20;
+import android.opengl.GLES30;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+
+/**
+ * Test some GLES framebuffer stuff.
+ */
+public class FramebufferTest extends AndroidTestCase {
+    private static final String TAG = "FramebufferTest";
+
+
+    /**
+     * Tests very basic glBlitFramebuffer() features by copying from one offscreen framebuffer
+     * to another.
+     * <p>
+     * Requires GLES3.
+     */
+    public void testBlitFramebuffer() throws Throwable {
+        final int WIDTH = 640;
+        final int HEIGHT = 480;
+        final int BYTES_PER_PIXEL = 4;
+        final int TEST_RED = 255;
+        final int TEST_GREEN = 0;
+        final int TEST_BLUE = 127;
+        final int TEST_ALPHA = 255;
+        final byte expectedBytes[] = new byte[] {
+                (byte) TEST_RED, (byte) TEST_GREEN, (byte) TEST_BLUE, (byte) TEST_ALPHA
+        };
+        EglCore eglCore = null;
+        OffscreenSurface surface1 = null;
+        OffscreenSurface surface2 = null;
+
+        try {
+            eglCore = new EglCore(null, EglCore.FLAG_TRY_GLES3);
+            if (eglCore.getGlVersion() < 3) {
+                Log.d(TAG, "GLES3 not available, skipping test");
+                return;
+            }
+
+            // Create two surfaces, and clear surface1
+            surface1 = new OffscreenSurface(eglCore, WIDTH, HEIGHT);
+            surface2 = new OffscreenSurface(eglCore, WIDTH, HEIGHT);
+            surface1.makeCurrent();
+            GLES30.glClearColor(TEST_RED / 255.0f, TEST_GREEN / 255.0f, TEST_BLUE / 255.0f,
+                    TEST_ALPHA / 255.0f);
+            GLES30.glClear(GLES30.GL_COLOR_BUFFER_BIT);
+            checkGlError("glClear");
+
+            // Set surface2 as "draw", surface1 as "read", and blit.
+            surface2.makeCurrentReadFrom(surface1);
+            GLES30.glBlitFramebuffer(0, 0, WIDTH, HEIGHT, 0, 0, WIDTH, HEIGHT,
+                    GLES30.GL_COLOR_BUFFER_BIT, GLES30.GL_NEAREST);
+            checkGlError("glBlitFramebuffer");
+
+            ByteBuffer pixelBuf = ByteBuffer.allocateDirect(WIDTH * HEIGHT * BYTES_PER_PIXEL);
+            pixelBuf.order(ByteOrder.LITTLE_ENDIAN);
+            byte testBytes[] = new byte[4];
+
+            // Confirm that surface1 has the color by testing a pixel from the center.
+            surface1.makeCurrent();
+            pixelBuf.clear();
+            GLES30.glReadPixels(0, 0, WIDTH, HEIGHT, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
+                    pixelBuf);
+            checkGlError("glReadPixels");
+            pixelBuf.position((WIDTH * (HEIGHT / 2) + (WIDTH / 2)) * BYTES_PER_PIXEL);
+            pixelBuf.get(testBytes, 0, 4);
+            Log.v(TAG, "testBytes1 = " + Arrays.toString(testBytes));
+            assertTrue(Arrays.equals(testBytes, expectedBytes));
+
+            // Confirm that surface2 has the color.
+            surface2.makeCurrent();
+            pixelBuf.clear();
+            GLES30.glReadPixels(0, 0, WIDTH, HEIGHT, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE,
+                    pixelBuf);
+            checkGlError("glReadPixels");
+            pixelBuf.position((WIDTH * (HEIGHT / 2) + (WIDTH / 2)) * BYTES_PER_PIXEL);
+            pixelBuf.get(testBytes, 0, 4);
+            Log.v(TAG, "testBytes2 = " + Arrays.toString(testBytes));
+            assertTrue(Arrays.equals(testBytes, expectedBytes));
+        } finally {
+            if (surface1 != null) {
+                surface1.release();
+            }
+            if (surface2 != null) {
+                surface2.release();
+            }
+            if (eglCore != null) {
+                eglCore.release();
+            }
+        }
+    }
+
+    /**
+     * Checks to see if a GLES error has been raised.
+     */
+    private static void checkGlError(String op) {
+        int error = GLES20.glGetError();
+        if (error != GLES20.GL_NO_ERROR) {
+            String msg = op + ": glError 0x" + Integer.toHexString(error);
+            Log.e(TAG, msg);
+            throw new RuntimeException(msg);
+        }
+    }
+
+
+    /**
+     * Core EGL state (display, context, config).
+     */
+    private static final class EglCore {
+        /**
+         * Constructor flag: surface must be recordable.  This discourages EGL from using a
+         * pixel format that cannot be converted efficiently to something usable by the video
+         * encoder.
+         */
+        public static final int FLAG_RECORDABLE = 0x01;
+
+        /**
+         * Constructor flag: ask for GLES3, fall back to GLES2 if not available.  Without this
+         * flag, GLES2 is used.
+         */
+        public static final int FLAG_TRY_GLES3 = 0x02;
+
+        // Android-specific extension.
+        private static final int EGL_RECORDABLE_ANDROID = 0x3142;
+
+        private EGLDisplay mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+        private EGLContext mEGLContext = EGL14.EGL_NO_CONTEXT;
+        private EGLConfig mEGLConfig = null;
+        private int mGlVersion = -1;
+
+
+        /**
+         * Prepares EGL display and context.
+         * <p>
+         * Equivalent to EglCore(null, 0).
+         */
+        public EglCore() {
+            this(null, 0);
+        }
+
+        /**
+         * Prepares EGL display and context.
+         * <p>
+         * @param sharedContext The context to share, or null if sharing is not desired.
+         * @param flags Configuration bit flags, e.g. FLAG_RECORDABLE.
+         */
+        public EglCore(EGLContext sharedContext, int flags) {
+            if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                throw new RuntimeException("EGL already set up");
+            }
+
+            if (sharedContext == null) {
+                sharedContext = EGL14.EGL_NO_CONTEXT;
+            }
+
+            mEGLDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                throw new RuntimeException("unable to get EGL14 display");
+            }
+            int[] version = new int[2];
+            if (!EGL14.eglInitialize(mEGLDisplay, version, 0, version, 1)) {
+                mEGLDisplay = null;
+                throw new RuntimeException("unable to initialize EGL14");
+            }
+
+            // Try to get a GLES3 context, if requested.
+            if ((flags & FLAG_TRY_GLES3) != 0) {
+                //Log.d(TAG, "Trying GLES 3");
+                EGLConfig config = getConfig(flags, 3);
+                if (config != null) {
+                    int[] attrib3_list = {
+                            EGL14.EGL_CONTEXT_CLIENT_VERSION, 3,
+                            EGL14.EGL_NONE
+                    };
+                    EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+                            attrib3_list, 0);
+
+                    if (EGL14.eglGetError() == EGL14.EGL_SUCCESS) {
+                        //Log.d(TAG, "Got GLES 3 config");
+                        mEGLConfig = config;
+                        mEGLContext = context;
+                        mGlVersion = 3;
+                    }
+                }
+            }
+            if (mEGLContext == EGL14.EGL_NO_CONTEXT) {  // GLES 2 only, or GLES 3 attempt failed
+                //Log.d(TAG, "Trying GLES 2");
+                EGLConfig config = getConfig(flags, 2);
+                if (config == null) {
+                    throw new RuntimeException("Unable to find a suitable EGLConfig");
+                }
+                int[] attrib2_list = {
+                        EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
+                        EGL14.EGL_NONE
+                };
+                EGLContext context = EGL14.eglCreateContext(mEGLDisplay, config, sharedContext,
+                        attrib2_list, 0);
+                checkEglError("eglCreateContext");
+                mEGLConfig = config;
+                mEGLContext = context;
+                mGlVersion = 2;
+            }
+
+            // Confirm with query.
+            int[] values = new int[1];
+            EGL14.eglQueryContext(mEGLDisplay, mEGLContext, EGL14.EGL_CONTEXT_CLIENT_VERSION,
+                    values, 0);
+            Log.d(TAG, "EGLContext created, client version " + values[0]);
+        }
+
+        /**
+         * Finds a suitable EGLConfig.
+         *
+         * @param flags Bit flags from constructor.
+         * @param version Must be 2 or 3.
+         */
+        private EGLConfig getConfig(int flags, int version) {
+            int renderableType = EGL14.EGL_OPENGL_ES2_BIT;
+            if (version >= 3) {
+                renderableType |= EGLExt.EGL_OPENGL_ES3_BIT_KHR;
+            }
+
+            // The actual surface is generally RGBA or RGBX, so situationally omitting alpha
+            // doesn't really help.  It can also lead to a huge performance hit on glReadPixels()
+            // when reading into a GL_RGBA buffer.
+            int[] attribList = {
+                    EGL14.EGL_RED_SIZE, 8,
+                    EGL14.EGL_GREEN_SIZE, 8,
+                    EGL14.EGL_BLUE_SIZE, 8,
+                    EGL14.EGL_ALPHA_SIZE, 8,
+                    //EGL14.EGL_DEPTH_SIZE, 16,
+                    //EGL14.EGL_STENCIL_SIZE, 8,
+                    EGL14.EGL_RENDERABLE_TYPE, renderableType,
+                    EGL14.EGL_NONE, 0,      // placeholder for recordable [@-3]
+                    EGL14.EGL_NONE
+            };
+            if ((flags & FLAG_RECORDABLE) != 0) {
+                attribList[attribList.length - 3] = EGL_RECORDABLE_ANDROID;
+                attribList[attribList.length - 2] = 1;
+            }
+            EGLConfig[] configs = new EGLConfig[1];
+            int[] numConfigs = new int[1];
+            if (!EGL14.eglChooseConfig(mEGLDisplay, attribList, 0, configs, 0, configs.length,
+                    numConfigs, 0)) {
+                Log.w(TAG, "unable to find RGB8888 / " + version + " EGLConfig");
+                return null;
+            }
+            return configs[0];
+        }
+
+        /**
+         * Discard all resources held by this class, notably the EGL context.
+         */
+        public void release() {
+            if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                // Android is unusual in that it uses a reference-counted EGLDisplay.  So for
+                // every eglInitialize() we need an eglTerminate().
+                EGL14.eglDestroyContext(mEGLDisplay, mEGLContext);
+                EGL14.eglReleaseThread();
+                EGL14.eglTerminate(mEGLDisplay);
+            }
+
+            mEGLDisplay = EGL14.EGL_NO_DISPLAY;
+            mEGLContext = EGL14.EGL_NO_CONTEXT;
+            mEGLConfig = null;
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            try {
+                if (mEGLDisplay != EGL14.EGL_NO_DISPLAY) {
+                    // We're limited here -- finalizers don't run on the thread that holds
+                    // the EGL state, so if a surface or context is still current on another
+                    // thread we can't fully release it here.  Exceptions thrown from here
+                    // are quietly discarded.  Complain in the log file.
+                    Log.w(TAG, "WARNING: EglCore was not explicitly released -- state may be leaked");
+                    release();
+                }
+            } finally {
+                super.finalize();
+            }
+        }
+
+        /**
+         * Destroys the specified surface.  Note the EGLSurface won't actually be destroyed if it's
+         * still current in a context.
+         */
+        public void releaseSurface(EGLSurface eglSurface) {
+            EGL14.eglDestroySurface(mEGLDisplay, eglSurface);
+        }
+
+        /**
+         * Creates an EGL surface associated with a Surface.
+         * <p>
+         * If this is destined for MediaCodec, the EGLConfig should have the "recordable" attribute.
+         */
+        public EGLSurface createWindowSurface(Object surface) {
+            if (!(surface instanceof Surface) && !(surface instanceof SurfaceTexture)) {
+                throw new RuntimeException("invalid surface: " + surface);
+            }
+
+            // Create a window surface, and attach it to the Surface we received.
+            int[] surfaceAttribs = {
+                    EGL14.EGL_NONE
+            };
+            EGLSurface eglSurface = EGL14.eglCreateWindowSurface(mEGLDisplay, mEGLConfig, surface,
+                    surfaceAttribs, 0);
+            checkEglError("eglCreateWindowSurface");
+            if (eglSurface == null) {
+                throw new RuntimeException("surface was null");
+            }
+            return eglSurface;
+        }
+
+        /**
+         * Creates an EGL surface associated with an offscreen buffer.
+         */
+        public EGLSurface createOffscreenSurface(int width, int height) {
+            int[] surfaceAttribs = {
+                    EGL14.EGL_WIDTH, width,
+                    EGL14.EGL_HEIGHT, height,
+                    EGL14.EGL_NONE
+            };
+            EGLSurface eglSurface = EGL14.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig,
+                    surfaceAttribs, 0);
+            checkEglError("eglCreatePbufferSurface");
+            if (eglSurface == null) {
+                throw new RuntimeException("surface was null");
+            }
+            return eglSurface;
+        }
+
+        /**
+         * Makes our EGL context current, using the supplied surface for both "draw" and "read".
+         */
+        public void makeCurrent(EGLSurface eglSurface) {
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                // called makeCurrent() before create?
+                Log.d(TAG, "NOTE: makeCurrent w/o display");
+            }
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, eglSurface, eglSurface, mEGLContext)) {
+                throw new RuntimeException("eglMakeCurrent failed");
+            }
+        }
+
+        /**
+         * Makes our EGL context current, using the supplied "draw" and "read" surfaces.
+         */
+        public void makeCurrent(EGLSurface drawSurface, EGLSurface readSurface) {
+            if (mEGLDisplay == EGL14.EGL_NO_DISPLAY) {
+                // called makeCurrent() before create?
+                Log.d(TAG, "NOTE: makeCurrent w/o display");
+            }
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, drawSurface, readSurface, mEGLContext)) {
+                throw new RuntimeException("eglMakeCurrent(draw,read) failed");
+            }
+        }
+
+        /**
+         * Makes no context current.
+         */
+        public void makeNothingCurrent() {
+            if (!EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE,
+                    EGL14.EGL_NO_CONTEXT)) {
+                throw new RuntimeException("eglMakeCurrent failed");
+            }
+        }
+
+        /**
+         * Calls eglSwapBuffers.  Use this to "publish" the current frame.
+         *
+         * @return false on failure
+         */
+        public boolean swapBuffers(EGLSurface eglSurface) {
+            return EGL14.eglSwapBuffers(mEGLDisplay, eglSurface);
+        }
+
+        /**
+         * Sends the presentation time stamp to EGL.  Time is expressed in nanoseconds.
+         */
+        public void setPresentationTime(EGLSurface eglSurface, long nsecs) {
+            EGLExt.eglPresentationTimeANDROID(mEGLDisplay, eglSurface, nsecs);
+        }
+
+        /**
+         * Returns true if our context and the specified surface are current.
+         */
+        public boolean isCurrent(EGLSurface eglSurface) {
+            return mEGLContext.equals(EGL14.eglGetCurrentContext()) &&
+                    eglSurface.equals(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW));
+        }
+
+        /**
+         * Performs a simple surface query.
+         */
+        public int querySurface(EGLSurface eglSurface, int what) {
+            int[] value = new int[1];
+            EGL14.eglQuerySurface(mEGLDisplay, eglSurface, what, value, 0);
+            return value[0];
+        }
+
+        /**
+         * Returns the GLES version this context is configured for (2 or 3).
+         */
+        public int getGlVersion() {
+            return mGlVersion;
+        }
+
+        /**
+         * Writes the current display, context, and surface to the log.
+         */
+        public static void logCurrent(String msg) {
+            EGLDisplay display;
+            EGLContext context;
+            EGLSurface surface;
+
+            display = EGL14.eglGetCurrentDisplay();
+            context = EGL14.eglGetCurrentContext();
+            surface = EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW);
+            Log.i(TAG, "Current EGL (" + msg + "): display=" + display + ", context=" + context +
+                    ", surface=" + surface);
+        }
+
+        /**
+         * Checks for EGL errors.
+         */
+        private void checkEglError(String msg) {
+            int error;
+            if ((error = EGL14.eglGetError()) != EGL14.EGL_SUCCESS) {
+                throw new RuntimeException(msg + ": EGL error: 0x" + Integer.toHexString(error));
+            }
+        }
+    }
+
+
+    /**
+     * Common base class for EGL surfaces.
+     * <p>
+     * There can be multiple surfaces associated with a single context.
+     */
+    private static class EglSurfaceBase {
+        // EglCore object we're associated with.  It may be associated with multiple surfaces.
+        protected EglCore mEglCore;
+
+        private EGLSurface mEGLSurface = EGL14.EGL_NO_SURFACE;
+        private int mWidth = -1;
+        private int mHeight = -1;
+
+        protected EglSurfaceBase(EglCore eglCore) {
+            mEglCore = eglCore;
+        }
+
+        /**
+         * Creates a window surface.
+         * <p>
+         * @param surface May be a Surface or SurfaceTexture.
+         */
+        public void createWindowSurface(Object surface) {
+            if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+                throw new IllegalStateException("surface already created");
+            }
+            mEGLSurface = mEglCore.createWindowSurface(surface);
+            mWidth = mEglCore.querySurface(mEGLSurface, EGL14.EGL_WIDTH);
+            mHeight = mEglCore.querySurface(mEGLSurface, EGL14.EGL_HEIGHT);
+        }
+
+        /**
+         * Creates an off-screen surface.
+         */
+        public void createOffscreenSurface(int width, int height) {
+            if (mEGLSurface != EGL14.EGL_NO_SURFACE) {
+                throw new IllegalStateException("surface already created");
+            }
+            mEGLSurface = mEglCore.createOffscreenSurface(width, height);
+            mWidth = width;
+            mHeight = height;
+        }
+
+        /**
+         * Returns the surface's width, in pixels.
+         */
+        public int getWidth() {
+            return mWidth;
+        }
+
+        /**
+         * Returns the surface's height, in pixels.
+         */
+        public int getHeight() {
+            return mHeight;
+        }
+
+        /**
+         * Release the EGL surface.
+         */
+        public void releaseEglSurface() {
+            mEglCore.releaseSurface(mEGLSurface);
+            mEGLSurface = EGL14.EGL_NO_SURFACE;
+            mWidth = mHeight = -1;
+        }
+
+        /**
+         * Makes our EGL context and surface current.
+         */
+        public void makeCurrent() {
+            mEglCore.makeCurrent(mEGLSurface);
+        }
+
+        /**
+         * Makes our EGL context and surface current for drawing, using the supplied surface
+         * for reading.
+         */
+        public void makeCurrentReadFrom(EglSurfaceBase readSurface) {
+            mEglCore.makeCurrent(mEGLSurface, readSurface.mEGLSurface);
+        }
+
+        /**
+         * Calls eglSwapBuffers.  Use this to "publish" the current frame.
+         *
+         * @return false on failure
+         */
+        public boolean swapBuffers() {
+            boolean result = mEglCore.swapBuffers(mEGLSurface);
+            if (!result) {
+                Log.d(TAG, "WARNING: swapBuffers() failed");
+            }
+            return result;
+        }
+
+        /**
+         * Sends the presentation time stamp to EGL.
+         *
+         * @param nsecs Timestamp, in nanoseconds.
+         */
+        public void setPresentationTime(long nsecs) {
+            mEglCore.setPresentationTime(mEGLSurface, nsecs);
+        }
+
+        /**
+         * Saves the EGL surface to a file.
+         * <p>
+         * Expects that this object's EGL surface is current.
+         */
+        public void saveFrame(File file) throws IOException {
+            if (!mEglCore.isCurrent(mEGLSurface)) {
+                throw new RuntimeException("Expected EGL context/surface is not current");
+            }
+
+            // glReadPixels gives us a ByteBuffer filled with what is essentially big-endian RGBA
+            // data (i.e. a byte of red, followed by a byte of green...).  We need an int[] filled
+            // with little-endian ARGB data to feed to Bitmap.
+            //
+            // If we implement this as a series of buf.get() calls, we can spend 2.5 seconds just
+            // copying data around for a 720p frame.  It's better to do a bulk get() and then
+            // rearrange the data in memory.  (For comparison, the PNG compress takes about 500ms
+            // for a trivial frame.)
+            //
+            // So... we set the ByteBuffer to little-endian, which should turn the bulk IntBuffer
+            // get() into a straight memcpy on most Android devices.  Our ints will hold ABGR data.
+            // Swapping B and R gives us ARGB.
+            //
+            // Making this even more interesting is the upside-down nature of GL, which means
+            // our output will look upside-down relative to what appears on screen if the
+            // typical GL conventions are used.
+
+            String filename = file.toString();
+
+            ByteBuffer buf = ByteBuffer.allocateDirect(mWidth * mHeight * 4);
+            buf.order(ByteOrder.LITTLE_ENDIAN);
+            GLES20.glReadPixels(0, 0, mWidth, mHeight,
+                    GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf);
+            checkGlError("glReadPixels");
+            buf.rewind();
+
+            int pixelCount = mWidth * mHeight;
+            int[] colors = new int[pixelCount];
+            buf.asIntBuffer().get(colors);
+            for (int i = 0; i < pixelCount; i++) {
+                int c = colors[i];
+                colors[i] = (c & 0xff00ff00) | ((c & 0x00ff0000) >> 16) | ((c & 0x000000ff) << 16);
+            }
+
+            BufferedOutputStream bos = null;
+            try {
+                bos = new BufferedOutputStream(new FileOutputStream(filename));
+                Bitmap bmp = Bitmap.createBitmap(colors, mWidth, mHeight, Bitmap.Config.ARGB_8888);
+                bmp.compress(Bitmap.CompressFormat.PNG, 90, bos);
+                bmp.recycle();
+            } finally {
+                if (bos != null) bos.close();
+            }
+            Log.d(TAG, "Saved " + mWidth + "x" + mHeight + " frame as '" + filename + "'");
+        }
+    }
+
+    /**
+     * Off-screen EGL surface (pbuffer).
+     * <p>
+     * It's good practice to explicitly release() the surface, preferably from a "finally" block.
+     */
+    private static class OffscreenSurface extends EglSurfaceBase {
+        /**
+         * Creates an off-screen surface with the specified width and height.
+         */
+        public OffscreenSurface(EglCore eglCore, int width, int height) {
+            super(eglCore);
+            createOffscreenSurface(width, height);
+        }
+
+        /**
+         * Releases any resources associated with the surface.
+         */
+        public void release() {
+            releaseEglSurface();
+        }
+    }
+}
diff --git a/tests/tests/openglperf/Android.mk b/tests/tests/openglperf/Android.mk
index 55c39f2..1d57263 100644
--- a/tests/tests/openglperf/Android.mk
+++ b/tests/tests/openglperf/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
 
 LOCAL_JNI_SHARED_LIBRARIES := libctsopenglperf_jni
diff --git a/tests/tests/openglperf/AndroidManifest.xml b/tests/tests/openglperf/AndroidManifest.xml
index 1934f35..a213e51 100644
--- a/tests/tests/openglperf/AndroidManifest.xml
+++ b/tests/tests/openglperf/AndroidManifest.xml
@@ -27,10 +27,16 @@
     <!-- Two activities are used -->
     <instrumentation
         android:targetPackage="com.replica.replicaisland"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
     <instrumentation
         android:targetPackage="com.android.cts.openglperf"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application>
         <uses-library android:name="android.test.runner" />
diff --git a/tests/tests/os/Android.mk b/tests/tests/os/Android.mk
index f43043b..0007a54 100644
--- a/tests/tests/os/Android.mk
+++ b/tests/tests/os/Android.mk
@@ -23,8 +23,6 @@
 
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src) \
@@ -34,7 +32,8 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
+# uncomment when b/13282254 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/os/AndroidManifest.xml b/tests/tests/os/AndroidManifest.xml
index 2418132..155e772 100644
--- a/tests/tests/os/AndroidManifest.xml
+++ b/tests/tests/os/AndroidManifest.xml
@@ -34,8 +34,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.os"/>
+                     android:label="CTS tests of android.os">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/os/src/android/os/cts/MessageTest.java b/tests/tests/os/src/android/os/cts/MessageTest.java
index dc56a23..cc45c4b 100644
--- a/tests/tests/os/src/android/os/cts/MessageTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageTest.java
@@ -231,6 +231,70 @@
         assertFalse(message.isAsynchronous());
     }
 
+    public void testRecycleThrowsIfMessageAlreadyRecycled() {
+        Message message = Message.obtain();
+        message.recycle();
+
+        try {
+            message.recycle();
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    public void testSendMessageThrowsIfMessageAlreadyRecycled() {
+        Message message = Message.obtain();
+        message.recycle();
+
+        try {
+            mHandler.sendMessage(message);
+            fail("should throw IllegalStateException");
+        } catch (IllegalStateException ex) {
+            // expected
+        }
+    }
+
+    public void testRecycleThrowsIfMessageIsBeingDelivered() {
+        final Exception[] caught = new Exception[1];
+        Handler handler = new Handler(mHandler.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                try {
+                    msg.recycle();
+                } catch (IllegalStateException ex) {
+                    caught[0] = ex; // expected
+                }
+            }
+        };
+        handler.sendEmptyMessage(WHAT);
+        sleep(SLEEP_TIME);
+
+        if (caught[0] == null) {
+            fail("should throw IllegalStateException");
+        }
+    }
+
+    public void testSendMessageThrowsIfMessageIsBeingDelivered() {
+        final Exception[] caught = new Exception[1];
+        Handler handler = new Handler(mHandler.getLooper()) {
+            @Override
+            public void handleMessage(Message msg) {
+                try {
+                    mHandler.sendMessage(msg);
+                } catch (IllegalStateException ex) {
+                    caught[0] = ex; // expected
+                }
+            }
+        };
+        handler.sendEmptyMessage(WHAT);
+        sleep(SLEEP_TIME);
+
+        if (caught[0] == null) {
+            fail("should throw IllegalStateException");
+        }
+    }
+
     private void sleep(long time) {
         try {
             Thread.sleep(time);
diff --git a/tests/tests/permission/Android.mk b/tests/tests/permission/Android.mk
index 07f20d8..2e88006 100644
--- a/tests/tests/permission/Android.mk
+++ b/tests/tests/permission/Android.mk
@@ -19,9 +19,9 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common
+LOCAL_JAVA_LIBRARIES := telephony-common
 
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_JNI_SHARED_LIBRARIES := libctspermission_jni
 
@@ -29,8 +29,9 @@
 
 LOCAL_PACKAGE_NAME := CtsPermissionTestCases
 
-# uncomment when dalvik test annotations are removed or part of SDK
+# uncomment when b/13249777 is fixed
 #LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
 
diff --git a/tests/tests/permission/AndroidManifest.xml b/tests/tests/permission/AndroidManifest.xml
index 945a303..fa03335 100644
--- a/tests/tests/permission/AndroidManifest.xml
+++ b/tests/tests/permission/AndroidManifest.xml
@@ -40,9 +40,12 @@
         package. That runner cannot be added to this package either, since it
         relies on hidden APIs.
     -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.permission"
-                     android:label="CTS tests of com.android.cts.permission"/>
+                     android:label="CTS tests of com.android.cts.permission">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
index 272bbdc..8f32027 100644
--- a/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
+++ b/tests/tests/permission/jni/android_permission_cts_FileUtils.cpp
@@ -25,6 +25,9 @@
 #include <grp.h>
 #include <pwd.h>
 #include <string.h>
+#include <ScopedLocalRef.h>
+#include <ScopedPrimitiveArray.h>
+#include <ScopedUtfChars.h>
 
 static jfieldID gFileStatusDevFieldID;
 static jfieldID gFileStatusInoFieldID;
@@ -46,14 +49,15 @@
  * Copied from hidden API: frameworks/base/core/jni/android_os_FileUtils.cpp
  */
 
-jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env, jobject thiz,
-        jstring path, jobject fileStatus, jboolean statLinks)
+jboolean android_permission_cts_FileUtils_getFileStatus(JNIEnv* env,
+        jobject /* thiz */, jstring path, jobject fileStatus, jboolean statLinks)
 {
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
+    ScopedUtfChars cPath(env, path);
     jboolean ret = false;
     struct stat s;
 
-    int res = statLinks == true ? lstat(pathStr, &s) : stat(pathStr, &s);
+    int res = statLinks == true ? lstat(cPath.c_str(), &s)
+            : stat(cPath.c_str(), &s);
 
     if (res == 0) {
         ret = true;
@@ -73,20 +77,18 @@
         }
     }
 
-    env->ReleaseStringUTFChars(path, pathStr);
-
     return ret;
 }
 
-jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env, jobject thiz,
-        jint uid)
+jstring android_permission_cts_FileUtils_getUserName(JNIEnv* env,
+        jobject /* thiz */, jint uid)
 {
     struct passwd *pwd = getpwuid(uid);
     return env->NewStringUTF(pwd->pw_name);
 }
 
-jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env, jobject thiz,
-        jint gid)
+jstring android_permission_cts_FileUtils_getGroupName(JNIEnv* env,
+        jobject /* thiz */, jint gid)
 {
     struct group *grp = getgrgid(gid);
     return env->NewStringUTF(grp->gr_name);
@@ -94,42 +96,106 @@
 
 static jboolean isPermittedCapBitSet(JNIEnv* env, jstring path, size_t capId)
 {
-    const char* pathStr = env->GetStringUTFChars(path, NULL);
-    jboolean ret = false;
-
     struct vfs_cap_data capData;
     memset(&capData, 0, sizeof(capData));
 
-    ssize_t result = getxattr(pathStr, XATTR_NAME_CAPS, &capData,
+    ScopedUtfChars cPath(env, path);
+    ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &capData,
                               sizeof(capData));
-    if (result > 0) {
-      ret = (capData.data[CAP_TO_INDEX(capId)].permitted &
-             CAP_TO_MASK(capId)) != 0;
-      ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call succeeded, "
-            "cap bit %u %s",
-            pathStr, capId, ret ? "set" : "unset");
-    } else {
-      ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
-            "return %d (error: %s (%d))\n",
-            pathStr, result, strerror(errno), errno);
+    if (result <= 0)
+    {
+          ALOGD("isPermittedCapBitSet(): getxattr(\"%s\") call failed: "
+                  "return %d (error: %s (%d))\n",
+                  cPath.c_str(), result, strerror(errno), errno);
+          return false;
     }
 
-    env->ReleaseStringUTFChars(path, pathStr);
-    return ret;
+    return (capData.data[CAP_TO_INDEX(capId)].permitted &
+            CAP_TO_MASK(capId)) != 0;
 }
 
 jboolean android_permission_cts_FileUtils_hasSetUidCapability(JNIEnv* env,
-        jobject clazz, jstring path)
+        jobject /* clazz */, jstring path)
 {
     return isPermittedCapBitSet(env, path, CAP_SETUID);
 }
 
 jboolean android_permission_cts_FileUtils_hasSetGidCapability(JNIEnv* env,
-        jobject clazz, jstring path)
+        jobject /* clazz */, jstring path)
 {
     return isPermittedCapBitSet(env, path, CAP_SETGID);
 }
 
+static bool throwNamedException(JNIEnv* env, const char* className,
+        const char* message)
+{
+    ScopedLocalRef<jclass> eClazz(env, env->FindClass(className));
+    if (eClazz.get() == NULL)
+    {
+        ALOGE("throwNamedException(): failed to find class %s, cannot throw",
+                className);
+        return false;
+    }
+
+    env->ThrowNew(eClazz.get(), message);
+    return true;
+}
+
+// fill vfs_cap_data's permitted caps given a Java int[] of cap ids
+static bool fillPermittedCaps(vfs_cap_data* capData, JNIEnv* env, jintArray capIds)
+{
+    ScopedIntArrayRO cCapIds(env, capIds);
+    const size_t capCount = cCapIds.size();
+
+    for (size_t i = 0; i < capCount; ++i)
+    {
+        const jint capId = cCapIds[i];
+        if (!cap_valid(capId))
+        {
+            char message[64];
+            snprintf(message, sizeof(message),
+                    "capability id %d out of valid range", capId);
+            throwNamedException(env, "java/lang/IllegalArgumentException",
+                    message);
+
+            return false;
+        }
+        capData->data[CAP_TO_INDEX(capId)].permitted |= CAP_TO_MASK(capId);
+    }
+    return true;
+}
+
+jboolean android_permission_cts_FileUtils_CapabilitySet_fileHasOnly(JNIEnv* env,
+        jobject /* clazz */, jstring path, jintArray capIds)
+{
+    struct vfs_cap_data expectedCapData;
+    memset(&expectedCapData, 0, sizeof(expectedCapData));
+
+    expectedCapData.magic_etc = VFS_CAP_REVISION | VFS_CAP_FLAGS_EFFECTIVE;
+    if (!fillPermittedCaps(&expectedCapData, env, capIds))
+    {
+        // exception thrown
+        return false;
+    }
+
+    struct vfs_cap_data actualCapData;
+    memset(&actualCapData, 0, sizeof(actualCapData));
+
+    ScopedUtfChars cPath(env, path);
+    ssize_t result = getxattr(cPath.c_str(), XATTR_NAME_CAPS, &actualCapData,
+            sizeof(actualCapData));
+    if (result <= 0)
+    {
+        ALOGD("fileHasOnly(): getxattr(\"%s\") call failed: "
+                "return %d (error: %s (%d))\n",
+                cPath.c_str(), result, strerror(errno), errno);
+        return false;
+    }
+
+    return (memcmp(&expectedCapData, &actualCapData,
+            sizeof(struct vfs_cap_data)) == 0);
+}
+
 static JNINativeMethod gMethods[] = {
     {  "getFileStatus", "(Ljava/lang/String;Landroid/permission/cts/FileUtils$FileStatus;Z)Z",
             (void *) android_permission_cts_FileUtils_getFileStatus  },
@@ -143,6 +209,11 @@
             (void *) android_permission_cts_FileUtils_hasSetGidCapability   },
 };
 
+static JNINativeMethod gCapabilitySetMethods[] = {
+    {  "fileHasOnly", "(Ljava/lang/String;[I)Z",
+            (void *) android_permission_cts_FileUtils_CapabilitySet_fileHasOnly  },
+};
+
 int register_android_permission_cts_FileUtils(JNIEnv* env)
 {
     jclass clazz = env->FindClass("android/permission/cts/FileUtils");
@@ -161,6 +232,16 @@
     gFileStatusMtimeFieldID = env->GetFieldID(fileStatusClass, "mtime", "J");
     gFileStatusCtimeFieldID = env->GetFieldID(fileStatusClass, "ctime", "J");
 
-    return env->RegisterNatives(clazz, gMethods, 
-            sizeof(gMethods) / sizeof(JNINativeMethod)); 
+    jint result = env->RegisterNatives(clazz, gMethods,
+            sizeof(gMethods) / sizeof(JNINativeMethod));
+    if (result)
+    {
+      return result;
+    }
+
+    // register FileUtils.CapabilitySet native methods
+    jclass capClazz = env->FindClass("android/permission/cts/FileUtils$CapabilitySet");
+
+    return env->RegisterNatives(capClazz, gCapabilitySetMethods,
+            sizeof(gCapabilitySetMethods) / sizeof(JNINativeMethod));
 }
diff --git a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
index c5f8ea5..006fb6d 100644
--- a/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/DevicePowerPermissionTest.java
@@ -20,7 +20,6 @@
 import android.os.PowerManager;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import dalvik.annotation.KnownFailure;
 
 /**
  * Verify that various PowerManagement functionality requires Permission.
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 85af555..61998e7 100755
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -19,6 +19,7 @@
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.os.Environment;
+import android.system.OsConstants;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.test.suitebuilder.annotation.LargeTest;
@@ -237,6 +238,17 @@
         assertFileOwnedByGroup(f, "net_bw_stats");
     }
 
+    @MediumTest
+    public void testTcpDefaultRwndSane() throws Exception {
+        File f = new File("/proc/sys/net/ipv4/tcp_default_init_rwnd");
+        assertTrue(f.canRead());
+        assertFalse(f.canWrite());
+        assertFalse(f.canExecute());
+
+        assertFileOwnedBy(f, "root");
+        assertFileOwnedByGroup(f, "root");
+    }
+
     /**
      * Assert that a file is owned by a specific owner. This is a noop if the
      * file does not exist.
@@ -838,6 +850,30 @@
         assertFileOwnedByGroup(f, "system");
     }
 
+    public void testFileHasOnlyCapsThrowsOnInvalidCaps() throws Exception {
+        try {
+            // Ensure negative cap id fails.
+            new FileUtils.CapabilitySet()
+                    .add(-1)
+                    .fileHasOnly("/system/bin/run-as");
+            fail();
+        }
+        catch (IllegalArgumentException e) {
+            // expected
+        }
+
+        try {
+            // Ensure too-large cap throws.
+            new FileUtils.CapabilitySet()
+                    .add(OsConstants.CAP_LAST_CAP + 1)
+                    .fileHasOnly("/system/bin/run-as");
+            fail();
+        }
+        catch (IllegalArgumentException e) {
+            // expected
+        }
+    }
+
     /**
      * Test that the /system/bin/run-as command has setuid and setgid
      * attributes set on the file.  If these calls fail, debugger
@@ -860,6 +896,12 @@
         // ensure file has setuid/setgid enabled
         assertTrue(FileUtils.hasSetUidCapability(filename));
         assertTrue(FileUtils.hasSetGidCapability(filename));
+
+        // ensure file has *only* setuid/setgid attributes enabled
+        assertTrue(new FileUtils.CapabilitySet()
+                .add(OsConstants.CAP_SETUID)
+                .add(OsConstants.CAP_SETGID)
+                .fileHasOnly("/system/bin/run-as"));
     }
 
     private static Set<File>
diff --git a/tests/tests/permission/src/android/permission/cts/FileUtils.java b/tests/tests/permission/src/android/permission/cts/FileUtils.java
index 9cd4999..af44a1c 100644
--- a/tests/tests/permission/src/android/permission/cts/FileUtils.java
+++ b/tests/tests/permission/src/android/permission/cts/FileUtils.java
@@ -16,6 +16,13 @@
  * limitations under the License.
  */
 
+import com.google.common.primitives.Ints;
+
+import android.system.OsConstants;
+
+import java.util.HashSet;
+import java.util.Set;
+
 /** Bits and pieces copied from hidden API of android.os.FileUtils. */
 public class FileUtils {
 
@@ -82,6 +89,27 @@
         }
     }
 
+    public static class CapabilitySet {
+
+        private final Set<Integer> mCapabilities = new HashSet<Integer>();
+
+        public CapabilitySet add(int capability) {
+            if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
+                throw new IllegalArgumentException(String.format(
+                        "capability id %d out of valid range", capability));
+            }
+            mCapabilities.add(capability);
+            return this;
+        }
+
+        private native static boolean fileHasOnly(String path,
+                int[] capabilities);
+
+        public boolean fileHasOnly(String path) {
+            return fileHasOnly(path, Ints.toArray(mCapabilities));
+        }
+    }
+
     /**
      * @param path of the file to stat
      * @param status object to set the fields on
diff --git a/tests/tests/permission2/Android.mk b/tests/tests/permission2/Android.mk
index 86a8bc7..5a7f5b7 100755
--- a/tests/tests/permission2/Android.mk
+++ b/tests/tests/permission2/Android.mk
@@ -19,7 +19,7 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common mms-common
+LOCAL_JAVA_LIBRARIES := telephony-common mms-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
diff --git a/tests/tests/permission2/AndroidManifest.xml b/tests/tests/permission2/AndroidManifest.xml
index 1c1a0d8..c0b78c4 100755
--- a/tests/tests/permission2/AndroidManifest.xml
+++ b/tests/tests/permission2/AndroidManifest.xml
@@ -56,9 +56,12 @@
             android:name="android.permission.FLASHLIGHT"
             android:maxSdkVersion="9000" />
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.permission2"
-                     android:label="More CTS tests for permissions"/>
+                     android:label="More CTS tests for permissions">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/preference/Android.mk b/tests/tests/preference/Android.mk
index cc2b210..5860406 100644
--- a/tests/tests/preference/Android.mk
+++ b/tests/tests/preference/Android.mk
@@ -20,8 +20,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/preference/AndroidManifest.xml b/tests/tests/preference/AndroidManifest.xml
index 3477192..e4c6b52 100644
--- a/tests/tests/preference/AndroidManifest.xml
+++ b/tests/tests/preference/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.os"/>
+                     android:label="CTS tests of android.os">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/preference2/Android.mk b/tests/tests/preference2/Android.mk
index 47b081d..59fedc8 100644
--- a/tests/tests/preference2/Android.mk
+++ b/tests/tests/preference2/Android.mk
@@ -22,8 +22,6 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/preference2/AndroidManifest.xml b/tests/tests/preference2/AndroidManifest.xml
index 23b085d..2dbd53a 100644
--- a/tests/tests/preference2/AndroidManifest.xml
+++ b/tests/tests/preference2/AndroidManifest.xml
@@ -35,9 +35,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.preference2"
-                     android:label="CTS Test Cases for android.preference"/>
+                     android:label="CTS Test Cases for android.preference">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/print/Android.mk b/tests/tests/print/Android.mk
new file mode 100644
index 0000000..516f6a0
--- /dev/null
+++ b/tests/tests/print/Android.mk
@@ -0,0 +1,33 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    src/android/print/cts/IPrivilegedOperations.aidl
+
+LOCAL_PACKAGE_NAME := CtsPrintTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ctstestrunner ub-uiautomator
+
+# This test runner sets up/cleans up the device before/after running the tests.
+LOCAL_CTS_TEST_RUNNER := com.android.cts.tradefed.testtype.PrintTestRunner
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/print/AndroidManifest.xml b/tests/tests/print/AndroidManifest.xml
new file mode 100644
index 0000000..4c94fd5
--- /dev/null
+++ b/tests/tests/print/AndroidManifest.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.print">
+
+    <application android:allowBackup="false" >
+
+        <uses-library android:name="android.test.runner"/>
+
+        <activity android:name="android.print.cts.PrintDocumentActivity"/>
+
+        <service
+            android:name="android.print.cts.services.FirstPrintService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+            <intent-filter>
+                <action android:name="android.printservice.PrintService" />
+            </intent-filter>
+            <meta-data
+               android:name="android.printservice"
+               android:resource="@xml/printservice">
+            </meta-data>
+        </service>
+
+        <service
+            android:name="android.print.cts.services.SecondPrintService"
+            android:permission="android.permission.BIND_PRINT_SERVICE">
+            <intent-filter>
+                <action android:name="android.printservice.PrintService" />
+            </intent-filter>
+            <meta-data
+               android:name="android.printservice"
+               android:resource="@xml/printservice">
+            </meta-data>
+        </service>
+
+        <activity
+            android:name="android.print.cts.services.SettingsActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+        <activity
+            android:name="android.print.cts.services.AddPrintersActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+        <activity
+            android:name="android.print.cts.services.CustomPrintOptionsActivity"
+            android:permission="android.permission.START_PRINT_SERVICE_CONFIG_ACTIVITY"
+            android:exported="true">
+        </activity>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+          android:targetPackage="com.android.cts.print"
+          android:label="Tests for the print APIs."/>
+
+</manifest>
diff --git a/tests/tests/print/res/values/strings.xml b/tests/tests/print/res/values/strings.xml
new file mode 100644
index 0000000..6d869e9
--- /dev/null
+++ b/tests/tests/print/res/values/strings.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<resources>
+
+    <string name="resolution_200x200">200x200</string>
+    <string name="resolution_300x300">300x300</string>
+    <string name="resolution_600x600">600x600</string>
+
+</resources>
diff --git a/tests/tests/print/res/xml/printservice.xml b/tests/tests/print/res/xml/printservice.xml
new file mode 100644
index 0000000..5579b81
--- /dev/null
+++ b/tests/tests/print/res/xml/printservice.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+    Copyright (C) 2014 The Android Open Source Project
+
+    Licensed under the Apache License, Version 2.0 (the "License");
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing, software
+    distributed under the License is distributed on an "AS IS" BASIS,
+    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+    See the License for the specific language governing permissions and
+    limitations under the License.
+-->
+
+<print-service  xmlns:android="http://schemas.android.com/apk/res/android"
+     android:settingsActivity="android.print.services.SettingsActivity"
+     android:addPrintersActivity="android.print.services.AddPrintersActivity"
+     android:advancedPrintOptionsActivity="android.print.services.CustomPrintOptionsActivity"/>
diff --git a/tests/tests/print/src/android/print/cts/BasePrintTest.java b/tests/tests/print/src/android/print/cts/BasePrintTest.java
new file mode 100644
index 0000000..d193bb0
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/BasePrintTest.java
@@ -0,0 +1,382 @@
+/*
+ * 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.argThat;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doCallRealMethod;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.uiautomator.UiAutomatorTestCase;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+import android.util.DisplayMetrics;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.mockito.InOrder;
+import org.mockito.stubbing.Answer;
+
+import java.util.List;
+import java.util.Locale;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This is the base class for print tests.
+ */
+public abstract class BasePrintTest extends UiAutomatorTestCase {
+
+    private static final long OPERATION_TIMEOUT = 10000;
+
+    private static final String ARG_PRIVILEGED_OPS = "ARG_PRIVILEGED_OPS";
+
+    private static final String PRINT_SPOOLER_PACKAGE_NAME = "com.android.printspooler";
+
+    protected static final String PRINT_JOB_NAME = "Test";
+
+    private PrintDocumentActivity mActivity;
+
+    private Locale mOldLocale;
+
+    private CallCounter mLayoutCallCounter;
+    private CallCounter mWriteCallCounter;
+    private CallCounter mFinishCallCounter;
+    private CallCounter mPrintJobQueuedCallCounter;
+    private CallCounter mDestroySessionCallCounter;
+
+    @Override
+    public void setUp() throws Exception {
+        // Make sure we start with a clean slate.
+        clearPrintSpoolerData();
+
+        // Workaround for dexmaker bug: https://code.google.com/p/dexmaker/issues/detail?id=2
+        // Dexmaker is used by mockito.
+        System.setProperty("dexmaker.dexcache", getInstrumentation()
+                .getTargetContext().getCacheDir().getPath());
+
+        // Set to US locale.
+        Resources resources = getInstrumentation().getTargetContext().getResources();
+        Configuration oldConfiguration = resources.getConfiguration();
+        if (!oldConfiguration.locale.equals(Locale.US)) {
+            mOldLocale = oldConfiguration.locale;
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(oldConfiguration);
+            newConfiguration.locale = Locale.US;
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Initialize the latches.
+        mLayoutCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mWriteCallCounter = new CallCounter();
+        mFinishCallCounter = new CallCounter();
+        mPrintJobQueuedCallCounter = new CallCounter();
+        mDestroySessionCallCounter = new CallCounter();
+
+        // Create the activity for the right locale.
+        createActivity();
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        // Done with the activity.
+        getActivity().finish();
+
+        // Restore the locale if needed.
+        if (mOldLocale != null) {
+            Resources resources = getInstrumentation().getTargetContext().getResources();
+            DisplayMetrics displayMetrics = resources.getDisplayMetrics();
+            Configuration newConfiguration = new Configuration(resources.getConfiguration());
+            newConfiguration.locale = mOldLocale;
+            mOldLocale = null;
+            resources.updateConfiguration(newConfiguration, displayMetrics);
+        }
+
+        // Make sure the spooler is cleaned.
+        clearPrintSpoolerData();
+    }
+
+    protected void print(final PrintDocumentAdapter adapter) {
+        // Initiate printing as if coming from the app.
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                PrintManager printManager = (PrintManager) getActivity()
+                        .getSystemService(Context.PRINT_SERVICE);
+                printManager.print("Print job", adapter, null);
+            }
+        });
+    }
+
+    protected void onLayoutCalled() {
+        mLayoutCallCounter.call();
+    }
+
+    protected void onWriteCalled() {
+        mWriteCallCounter.call();
+    }
+
+    protected void onFinishCalled() {
+        mFinishCallCounter.call();
+    }
+
+    protected void onPrintJobQueuedCalled() {
+        mPrintJobQueuedCallCounter.call();
+    }
+
+    protected void onPrinterDiscoverySessionDestroyCalled() {
+        mDestroySessionCallCounter.call();
+    }
+
+    protected void waitForPrinterDiscoverySessionDestroyCallbackCalled() {
+        waitForCallbackCallCount(mDestroySessionCallCounter, 1,
+                "Did not get expected call to onDestroyPrinterDiscoverySession.");
+    }
+
+    protected void waitForServiceOnPrintJobQueuedCallbackCalled() {
+        waitForCallbackCallCount(mPrintJobQueuedCallCounter, 1,
+                "Did not get expected call to onPrintJobQueued.");
+    }
+
+    protected void waitForAdapterFinishCallbackCalled() {
+        waitForCallbackCallCount(mFinishCallCounter, 1,
+                "Did not get expected call to finish.");
+    }
+
+    protected void waitForLayoutAdapterCallbackCount(int count) {
+        waitForCallbackCallCount(mLayoutCallCounter, count,
+                "Did not get expected call to layout.");
+    }
+
+    protected void waitForWriteForAdapterCallback() {
+        waitForCallbackCallCount(mWriteCallCounter, 1, "Did not get expected call to write.");
+    }
+
+    private void waitForCallbackCallCount(CallCounter counter, int count, String message) {
+        try {
+            counter.waitForCount(count, OPERATION_TIMEOUT);
+        } catch (TimeoutException te) {
+            fail(message);
+        }
+    }
+
+    protected void selectPrinter(String printerName) throws UiObjectNotFoundException {
+        UiObject destinationSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/destination_spinner"));
+        destinationSpinner.click();
+        UiObject printerOption = new UiObject(new UiSelector().text(printerName));
+        printerOption.click();
+    }
+
+    protected void changeOrientation(String orientation) throws UiObjectNotFoundException {
+        UiObject orientationSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/orientation_spinner"));
+        orientationSpinner.click();
+        UiObject orientationOption = new UiObject(new UiSelector().text(orientation));
+        orientationOption.click();
+    }
+
+    protected void changeMediaSize(String mediaSize) throws UiObjectNotFoundException {
+        UiObject mediaSizeSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/paper_size_spinner"));
+        mediaSizeSpinner.click();
+        UiObject mediaSizeOption = new UiObject(new UiSelector().text(mediaSize));
+        mediaSizeOption.click();
+    }
+
+    protected void changeColor(String color) throws UiObjectNotFoundException {
+        UiObject colorSpinner = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/color_spinner"));
+        colorSpinner.click();
+        UiObject colorOption = new UiObject(new UiSelector().text(color));
+        colorOption.click();
+    }
+
+    protected void clickPrintButton() throws UiObjectNotFoundException {
+        UiObject printButton = new UiObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/print_button"));
+        printButton.click();
+    }
+
+    protected PrintDocumentActivity getActivity() {
+        return mActivity;
+    }
+
+    private void createActivity() {
+        mActivity = launchActivity(
+                getInstrumentation().getTargetContext().getPackageName(),
+                PrintDocumentActivity.class, null);
+    }
+
+    protected void clearPrintSpoolerData() throws Exception {
+        IPrivilegedOperations privilegedOps = IPrivilegedOperations.Stub.asInterface(
+                getParams().getBinder(ARG_PRIVILEGED_OPS));
+        privilegedOps.clearApplicationUserData(PRINT_SPOOLER_PACKAGE_NAME);
+    }
+
+    protected void verifyLayoutCall(InOrder inOrder, PrintDocumentAdapter mock,
+            PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            final boolean forPreview) {
+        inOrder.verify(mock).onLayout(eq(oldAttributes), eq(newAttributes),
+                any(CancellationSignal.class), any(LayoutResultCallback.class), argThat(
+                        new BaseMatcher<Bundle>() {
+                            @Override
+                            public boolean matches(Object item) {
+                                Bundle bundle = (Bundle) item;
+                                return forPreview == bundle.getBoolean(
+                                        PrintDocumentAdapter.EXTRA_PRINT_PREVIEW);
+                            }
+
+                            @Override
+                            public void describeTo(Description description) {
+                                /* do nothing */
+                            }
+                        }));
+    }
+
+    protected PrintDocumentAdapter createMockPrintDocumentAdapter(Answer<Void> layoutAnswer,
+            Answer<Void> writeAnswer, Answer<Void> finishAnswer) {
+        // Create a mock print adapter.
+        PrintDocumentAdapter adapter = mock(PrintDocumentAdapter.class);
+        if (layoutAnswer != null) {
+            doAnswer(layoutAnswer).when(adapter).onLayout(any(PrintAttributes.class),
+                    any(PrintAttributes.class), any(CancellationSignal.class),
+                    any(LayoutResultCallback.class), any(Bundle.class));
+        }
+        if (writeAnswer != null) {
+            doAnswer(writeAnswer).when(adapter).onWrite(any(PageRange[].class),
+                    any(ParcelFileDescriptor.class), any(CancellationSignal.class),
+                    any(WriteResultCallback.class));
+        }
+        if (finishAnswer != null) {
+            doAnswer(finishAnswer).when(adapter).onFinish();
+        }
+        return adapter;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected PrinterDiscoverySessionCallbacks createMockPrinterDiscoverySessionCallbacks(
+            Answer<Void> onStartPrinterDiscovery, Answer<Void> onStopPrinterDiscovery,
+            Answer<Void> onValidatePrinters, Answer<Void> onStartPrinterStateTracking,
+            Answer<Void> onStopPrinterStateTracking, Answer<Void> onDestroy) {
+        PrinterDiscoverySessionCallbacks callbacks = mock(PrinterDiscoverySessionCallbacks.class);
+
+        doCallRealMethod().when(callbacks).setSession(any(StubbablePrinterDiscoverySession.class));
+        when(callbacks.getSession()).thenCallRealMethod();
+
+        if (onStartPrinterDiscovery != null) {
+            doAnswer(onStartPrinterDiscovery).when(callbacks).onStartPrinterDiscovery(
+                    any(List.class));
+        }
+        if (onStopPrinterDiscovery != null) {
+            doAnswer(onStopPrinterDiscovery).when(callbacks).onStopPrinterDiscovery();
+        }
+        if (onValidatePrinters != null) {
+            doAnswer(onValidatePrinters).when(callbacks).onValidatePrinters(
+                    any(List.class));
+        }
+        if (onStartPrinterStateTracking != null) {
+            doAnswer(onStartPrinterStateTracking).when(callbacks).onStartPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onStopPrinterStateTracking != null) {
+            doAnswer(onStopPrinterStateTracking).when(callbacks).onStopPrinterStateTracking(
+                    any(PrinterId.class));
+        }
+        if (onDestroy != null) {
+            doAnswer(onDestroy).when(callbacks).onDestroy();
+        }
+
+        return callbacks;
+    }
+
+    protected PrintServiceCallbacks createMockPrintServiceCallbacks(
+            Answer<PrinterDiscoverySessionCallbacks> onCreatePrinterDiscoverySessionCallbacks,
+            Answer<Void> onPrintJobQueued, Answer<Void> onRequestCancelPrintJob) {
+        final PrintServiceCallbacks service = mock(PrintServiceCallbacks.class);
+
+        doCallRealMethod().when(service).setService(any(PrintService.class));
+        when(service.getService()).thenCallRealMethod();
+
+        if (onCreatePrinterDiscoverySessionCallbacks != null) {
+            doAnswer(onCreatePrinterDiscoverySessionCallbacks).when(service)
+                    .onCreatePrinterDiscoverySessionCallbacks();
+        }
+        if (onPrintJobQueued != null) {
+            doAnswer(onPrintJobQueued).when(service).onPrintJobQueued(any(PrintJob.class));
+        }
+        if (onRequestCancelPrintJob != null) {
+            doAnswer(onRequestCancelPrintJob).when(service).onRequestCancelPrintJob(
+                    any(PrintJob.class));
+        }
+
+        return service;
+    }
+
+    protected final class CallCounter {
+        private final Object mLock = new Object();
+
+        private int mCallCount;
+
+        public void call() {
+            synchronized (mLock) {
+                mCallCount++;
+            }
+        }
+
+        public void waitForCount(int count, long timeoutMIllis) throws TimeoutException {
+            synchronized (mLock) {
+                final long startTimeMillis = SystemClock.uptimeMillis();
+                while (mCallCount < count) {
+                    try {
+                        final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                        final long remainingTimeMillis = timeoutMIllis - elapsedTimeMillis;
+                        if (remainingTimeMillis <= 0) {
+                            throw new TimeoutException();
+                        }
+                        mLock.wait(timeoutMIllis);
+                    } catch (InterruptedException ie) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl b/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
new file mode 100644
index 0000000..93c8c3e
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/IPrivilegedOperations.aidl
@@ -0,0 +1,21 @@
+/*
+ * 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.print.cts;
+
+interface IPrivilegedOperations {
+    boolean clearApplicationUserData(String packageName);
+}
diff --git a/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
new file mode 100644
index 0000000..6b1bb3f
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PageRangeAdjustmentTest.java
@@ -0,0 +1,509 @@
+/*
+ * 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.never;
+
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiSelector;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This test verifies that the system correctly adjust the
+ * page ranges to be printed depending whether the app gave
+ * the requested pages, more pages, etc.
+ */
+public class PageRangeAdjustmentTest extends BasePrintTest {
+
+    private static final String FIRST_PRINTER = "First printer";
+
+    public void testAllPagesWantedAndAllPagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                assert(pages.length == 1 && PageRange.ALL_PAGES.equals(pages[0]));
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndAllPagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                // We always as for the first page for preview and in this
+                // case we write all, i.e. more that needed.
+                assertTrue(pages.length == 1 && pages[0].getStart() == 1
+                        && pages[0].getEnd() == 1);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select only the second page.
+        selectPages("2");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndSomeMorePagesWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                PageRange[] pages = printJob.getInfo().getPages();
+                assert(pages.length == 1 && pages[0].getStart() == 1
+                        && pages[0].getEnd() == 2);
+                printJob.complete();
+                onPrintJobQueuedCalled();
+                return null;
+            }
+        }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                // We expect a single range as it is either the first page
+                // or the page we selected in the UI.
+                assertSame(pages.length, 1);
+                fd.close();
+
+                PageRange reqeustedPages = pages[0];
+                if (reqeustedPages.getStart() == reqeustedPages.getEnd()
+                        && reqeustedPages.getEnd() == 0) {
+                    // If asked for the first page, which is for preview
+                    // then write it...
+                    callback.onWriteFinished(pages);
+                } else {
+                    // otherwise write a page more that the one we selected.
+                    callback.onWriteFinished(new PageRange[] {new PageRange(2, 3)});
+                }
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select only the third page.
+        selectPages("3");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Wait for the print job.
+        waitForServiceOnPrintJobQueuedCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // Next we wait for a call with the print job.
+        inOrder.verify(firstServiceCallbacks).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    public void testSomePagesWantedAndNotWritten() throws Exception {
+        // Create a callback for the target print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+            new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                    return createMockFirstPrinterDiscoverySessionCallbacks();
+                }
+            },
+            null, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                assertSame(pages.length, 1);
+                fd.close();
+
+                // We should be asked for the first page...
+                assertSame(pages[0].getStart(), 0);
+                assertSame(pages[0].getEnd(), 0);
+
+                // ...just write a the wring page.
+                callback.onWriteFinished(new PageRange[] {new PageRange(1, 1)});
+
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstServiceCallbacks);
+
+        // We create a new session first.
+        inOrder.verify(firstServiceCallbacks)
+                .onCreatePrinterDiscoverySessionCallbacks();
+
+        // We should not receive a print job callback.
+        inOrder.verify(firstServiceCallbacks, never()).onPrintJobQueued(
+                any(PrintJob.class));
+    }
+
+    private void selectPages(String pages) throws UiObjectNotFoundException {
+        UiObject pagesSpinner = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/range_options_spinner"));
+        pagesSpinner.click();
+
+        UiObject rangeOption = getUiDevice().findObject(new UiSelector().text("Range"));
+        rangeOption.click();
+
+        UiObject pagesEditText = getUiDevice().findObject(new UiSelector().resourceId(
+                "com.android.printspooler:id/page_range_edittext"));
+        pagesEditText.setText(pages);
+    }
+
+    private PrinterDiscoverySessionCallbacks createMockFirstPrinterDiscoverySessionCallbacks() {
+        return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+                        invocation.getMock();
+
+                StubbablePrinterDiscoverySession session = mock.getSession();
+                PrintService service = session.getService();
+
+                if (session.getPrinters().isEmpty()) {
+                          List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add one printer.
+                    PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+                    PrinterCapabilitiesInfo firstCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            FIRST_PRINTER, PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(firstCapabilities)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    session.addPrinters(printers);
+                }
+
+                return null;
+            }
+        }, null, null, null, null, null);
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java b/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
new file mode 100644
index 0000000..6a191a6
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.print.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PrintDocumentActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
new file mode 100644
index 0000000..d445692
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -0,0 +1,1481 @@
+/*
+ * 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.print.cts;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This test verifies that the system respects the {@link PrintDocumentAdapter}
+ * contract and invokes all callbacks as expected.
+ */
+public class PrintDocumentAdapterContractTest extends BasePrintTest {
+
+    public void testNoPrintOptionsOrPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        // Since we passed false to the layout callback meaning that the content
+        // didn't change, there shouldn't be a next call to write.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNoPrintOptionsOrPrinterChangeCanceled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel the printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndNoPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Change the orientation.
+        changeOrientation("Landscape");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(3);
+
+        // Change the media size.
+        changeMediaSize("ISO A4");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Change the color.
+        changeColor("Black & White");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(5);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        // Since we passed false to the layout callback meaning that the content
+        // didn't change, there shouldn't be a next call to write.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // We changed the orientation which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes thirdOldAttributes = secondNewAttributes;
+        PrintAttributes thirdNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, thirdOldAttributes, thirdNewAttributes, true);
+
+        // We changed the media size which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes fourthOldAttributes = thirdNewAttributes;
+        PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fourthOldAttributes, fourthNewAttributes, true);
+
+        // We changed the color which triggers a layout. Since we passed
+        // false to the layout callback meaning that the content didn't change,
+        // there shouldn't be a next call to write.
+        PrintAttributes fifthOldAttributes = fourthNewAttributes;
+        PrintAttributes fifthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4.asLandscape())
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS)
+                .setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fifthOldAttributes, fifthNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, fifthNewAttributes, fifthNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndPrinterChange() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback)
+                        invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                    .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                    .setPageCount(1)
+                    .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Change the color.
+        changeColor("Black & White");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(3);
+
+        // Change the printer to one which supports the current media size.
+        // Select the second printer.
+        selectPrinter("First printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We changed the printer and the new printer does not support the
+        // selected media size in which case the default media size of the
+        // printer is used resulting in a layout pass. Same for margins.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(0, 0, 0, 0))
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // We changed the printer and the new printer does not support the
+        // current color in which case the default color for the selected
+        // printer is used resulting in a layout pass.
+        PrintAttributes thirdOldAttributes = secondNewAttributes;
+        PrintAttributes thirdNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(0, 0, 0, 0))
+                .setColorMode(PrintAttributes.COLOR_MODE_MONOCHROME)
+                .build();
+        verifyLayoutCall(inOrder, adapter, thirdOldAttributes, thirdNewAttributes, true);
+
+        // We changed the printer to one that does not support the current
+        // media size in which case we pick the default media size for the
+        // new printer which results in a layout pass. Same for color.
+        PrintAttributes fourthOldAttributes = thirdNewAttributes;
+        PrintAttributes fourthNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A4)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(new Margins(200, 200, 200, 200))
+                .setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, fourthOldAttributes, fourthNewAttributes, true);
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, fourthNewAttributes, fourthNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] secondPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testPrintOptionsChangeAndNoPrinterChangeAndContentChange()
+            throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                // The content changes after every layout.
+                callback.onLayoutFinished(info, true);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // In the layout callback we reported that the content changed,
+        // so the previously written page has to be written again.
+        PageRange[] secondPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(secondPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] thirdPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(thirdPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNewPrinterSupportsSelectedPrintOptions() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                // The content changes after every layout.
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the third printer.
+        selectPrinter("Third printer");
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, firstNewAttributes, firstNewAttributes, false);
+
+        // When print is pressed we ask for all selected pages.
+        PageRange[] thirdPages = new PageRange[] {PageRange.ALL_PAGES};
+        inOrder.verify(adapter).onWrite(eq(thirdPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testNothingChangesAllPagesWrittenFirstTime() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(new PageRange[] {PageRange.ALL_PAGES});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Select the second printer.
+        selectPrinter("Second printer");
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // We selected the second printer which does not support the media
+        // size that was selected, so a new layout happens as the size changed.
+        PrintAttributes secondOldAttributes = firstNewAttributes;
+        PrintAttributes secondNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.ISO_A3)
+                .setResolution(new Resolution("300x300", "300x300", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, secondOldAttributes, secondNewAttributes, true);
+
+        // In the layout callback we reported that the content didn't change,
+        // and we wrote all pages in the write call while being asked only
+        // for the first page. Hence, all pages were written and they didn't
+        // change, therefore no subsequent write call should happen.
+
+        // When print is pressed we ask for a layout which is *not* for preview.
+        verifyLayoutCall(inOrder, adapter, secondNewAttributes, secondNewAttributes, false);
+
+        // In the layout callback we reported that the content didn't change,
+        // and we wrote all pages in the write call while being asked only
+        // for the first page. Hence, all pages were written and they didn't
+        // change, therefore no subsequent write call should happen.
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testCancelLongRunningLayout() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                CancellationSignal cancellation = (CancellationSignal) invocation.getArguments()[2];
+                final LayoutResultCallback callback = (LayoutResultCallback) invocation
+                        .getArguments()[3];
+                cancellation.setOnCancelListener(new OnCancelListener() {
+                    @Override
+                    public void onCancel() {
+                        callback.onLayoutCancelled();
+                    }
+                });
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testCancelLongRunningWrite() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                final ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                final CancellationSignal cancellation = (CancellationSignal) args[2];
+                final WriteResultCallback callback = (WriteResultCallback) args[3];
+                cancellation.setOnCancelListener(new OnCancelListener() {
+                    @Override
+                    public void onCancel() {
+                        try {
+                            fd.close();
+                        } catch (IOException ioe) {
+                            /* ignore */
+                        }
+                        callback.onWriteCancelled();
+                    }
+                });
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testFailedLayout() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                callback.onLayoutFailed(null);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // No write as layout failed.
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testFailedWrite() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFailed(null);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testRequestedPagesNotWritten() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                      .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                      .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                // Write wrong pages.
+                callback.onWriteFinished(new PageRange[] {
+                        new PageRange(Integer.MAX_VALUE,Integer.MAX_VALUE)});
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testLayoutCallbackNotCalled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Break the contract and never call the callback.
+                // Mark layout called.
+                onLayoutCalled();
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for layout.
+        waitForLayoutAdapterCallbackCount(1);
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    public void testWriteCallbackNotCalled() throws Exception {
+        // Configure the print services.
+        FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a mock print adapter.
+        final PrintDocumentAdapter adapter = createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT).setPageCount(1)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                fd.close();
+                // Break the contract and never call the callback.
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write.
+        waitForWriteForAdapterCallback();
+
+        // Cancel printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for a finish.
+        waitForAdapterFinishCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(adapter);
+
+        // Start is always called first.
+        inOrder.verify(adapter).onStart();
+
+        // Start is always followed by a layout. The PDF printer is selected if
+        // there are other printers but none of them was used.
+        PrintAttributes firstOldAttributes = new PrintAttributes.Builder().build();
+        PrintAttributes firstNewAttributes = new PrintAttributes.Builder()
+                .setMediaSize(MediaSize.NA_LETTER)
+                .setResolution(new Resolution("PDF resolution", "PDF resolution", 300, 300))
+                .setMinMargins(Margins.NO_MARGINS).setColorMode(PrintAttributes.COLOR_MODE_COLOR)
+                .build();
+        verifyLayoutCall(inOrder, adapter, firstOldAttributes, firstNewAttributes, true);
+
+        // We always ask for the first page for preview.
+        PageRange[] firstPages = new PageRange[] {new PageRange(0, 0)};
+        inOrder.verify(adapter).onWrite(eq(firstPages), any(ParcelFileDescriptor.class),
+                any(CancellationSignal.class), any(WriteResultCallback.class));
+
+        // Finish is always called last.
+        inOrder.verify(adapter).onFinish();
+
+        // No other call are expected.
+        verifyNoMoreInteractions(adapter);
+    }
+
+    private PrintServiceCallbacks createFirstMockPrintServiceCallbacks() {
+        final PrinterDiscoverySessionCallbacks callbacks =
+                createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                PrinterDiscoverySessionCallbacks mock = (PrinterDiscoverySessionCallbacks)
+                        invocation.getMock();
+
+                StubbablePrinterDiscoverySession session = mock.getSession();
+                PrintService service = session.getService();
+
+                if (session.getPrinters().isEmpty()) {
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add the first printer.
+                    PrinterId firstPrinterId = service.generatePrinterId("first_printer");
+                    PrinterCapabilitiesInfo firstCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(firstPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            "First printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(firstCapabilities)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    // Add the second printer.
+                    PrinterId secondPrinterId = service.generatePrinterId("second_printer");
+                    PrinterCapabilitiesInfo secondCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(secondPrinterId)
+                        .addMediaSize(MediaSize.ISO_A3, true)
+                        .addMediaSize(MediaSize.ISO_A4, false)
+                        .addResolution(new Resolution("200x200", "200x200", 200, 200), true)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), false)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR
+                                | PrintAttributes.COLOR_MODE_MONOCHROME,
+                                PrintAttributes.COLOR_MODE_MONOCHROME)
+                        .build();
+                    PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+                            "Second printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(secondCapabilities)
+                        .build();
+                    printers.add(secondPrinter);
+
+                    // Add the third printer.
+                    PrinterId thirdPrinterId = service.generatePrinterId("third_printer");
+                    PrinterCapabilitiesInfo thirdCapabilities =
+                            new PrinterCapabilitiesInfo.Builder(thirdPrinterId)
+                        .addMediaSize(MediaSize.NA_LETTER, true)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo thirdPrinter = new PrinterInfo.Builder(thirdPrinterId,
+                            "Third printer", PrinterInfo.STATUS_IDLE)
+                        .setCapabilities(thirdCapabilities)
+                        .build();
+                    printers.add(thirdPrinter);
+
+                    session.addPrinters(printers);
+                }
+                return null;
+            }
+        }, null, null, null, null, null);
+        return createMockPrintServiceCallbacks(new Answer<PrinterDiscoverySessionCallbacks>() {
+            @Override
+            public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                return callbacks;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                printJob.complete();
+                return null;
+            }
+        }, null);
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
new file mode 100644
index 0000000..b9ea280
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -0,0 +1,406 @@
+/*
+ * 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.print.cts;
+
+import static org.mockito.Mockito.inOrder;
+
+import android.os.ParcelFileDescriptor;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintDocumentAdapter;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.print.PrinterCapabilitiesInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.print.cts.services.FirstPrintService;
+import android.print.cts.services.PrintServiceCallbacks;
+import android.print.cts.services.PrinterDiscoverySessionCallbacks;
+import android.print.cts.services.SecondPrintService;
+import android.print.cts.services.StubbablePrinterDiscoverySession;
+import android.printservice.PrintJob;
+import android.printservice.PrinterDiscoverySession;
+
+import org.mockito.InOrder;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This test verifies that the system respects the {@link PrinterDiscoverySession}
+ * contract is respected.
+ */
+public class PrinterDiscoverySessionLifecycleTest extends BasePrintTest {
+    private static final String FIRST_PRINTER_NAME = "First printer";
+    private static final String SECOND_PRINTER_NAME = "Second printer";
+
+    private static final String FIRST_PRINTER_LOCAL_ID= "first_printer";
+    private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
+
+    public void testNormalLifecycle() throws Exception {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createFirstMockPrinterDiscoverySessionCallbacks();
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                new Answer<PrinterDiscoverySessionCallbacks>() {
+                @Override
+                public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                        return firstSessionCallbacks;
+                    }
+                },
+                new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                    // We pretend the job is handled immediately.
+                    printJob.complete();
+                    return null;
+                }
+            }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a print adapter that respects the print contract.
+        PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write of the first page.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER_NAME);
+
+        // Wait for layout as the printer has different capabilities.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // Select the second printer (same capabilities as the other
+        // one so no layout should happen).
+        selectPrinter(SECOND_PRINTER_NAME);
+
+        // While the printer discovery session is still alive store the
+        // ids of printers as we want to make some assertions about them
+        // but only the print service can create printer ids which means
+        // that we need to get the created ones.
+        PrinterId firstPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+                FIRST_PRINTER_LOCAL_ID);
+        PrinterId secondPrinterId = getAddedPrinterIdForLocalId(firstSessionCallbacks,
+                SECOND_PRINTER_LOCAL_ID);
+        assertNotNull("Coundn't find printer:" + FIRST_PRINTER_LOCAL_ID, firstPrinterId);
+        assertNotNull("Coundn't find printer:" + SECOND_PRINTER_LOCAL_ID, secondPrinterId);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for all print jobs to be handled after which the session destroyed.
+        waitForPrinterDiscoverySessionDestroyCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstSessionCallbacks);
+
+        // We start discovery as the print dialog was up.
+        List<PrinterId> emptyPrinterIdList = Collections.emptyList();
+        inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
+                emptyPrinterIdList);
+
+        // We selected the first printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                secondPrinterId);
+
+        // The print dialog went away so we first stop the printer tracking...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                secondPrinterId);
+
+        // ... next we stop printer discovery...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
+
+        // ... last the session is destroyed.
+        inOrder.verify(firstSessionCallbacks).onDestroy();
+    }
+
+    public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Exception {
+        // Create the session callbacks that we will be checking.
+        final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
+                createFirstMockPrinterDiscoverySessionCallbacks();
+
+        // Create the service callbacks for the first print service.
+        PrintServiceCallbacks firstServiceCallbacks = createMockPrintServiceCallbacks(
+                new Answer<PrinterDiscoverySessionCallbacks>() {
+                @Override
+                public PrinterDiscoverySessionCallbacks answer(InvocationOnMock invocation) {
+                        return firstSessionCallbacks;
+                    }
+                },
+                new Answer<Void>() {
+                @Override
+                public Void answer(InvocationOnMock invocation) {
+                    PrintJob printJob = (PrintJob) invocation.getArguments()[0];
+                    // We pretend the job is handled immediately.
+                    printJob.complete();
+                    return null;
+                }
+            }, null);
+
+        // Configure the print services.
+        FirstPrintService.setCallbacks(firstServiceCallbacks);
+        SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
+
+        // Create a print adapter that respects the print contract.
+        PrintDocumentAdapter adapter = createMockPrintDocumentAdapter();
+
+        // Start printing.
+        print(adapter);
+
+        // Wait for write of the first page.
+        waitForWriteForAdapterCallback();
+
+        // Select the first printer.
+        selectPrinter(FIRST_PRINTER_NAME);
+
+        // Wait for a layout to finish - first layout was for the
+        // PDF printer, second for the first printer in preview mode.
+        waitForLayoutAdapterCallbackCount(2);
+
+        // While the printer discovery session is still alive store the
+        // ids of printer as we want to make some assertions about it
+        // but only the print service can create printer ids which means
+        // that we need to get the created one.
+        PrinterId firstPrinterId = getAddedPrinterIdForLocalId(
+                firstSessionCallbacks, FIRST_PRINTER_LOCAL_ID);
+
+        // Click the print button.
+        clickPrintButton();
+
+        // Wait for the print to complete.
+        waitForAdapterFinishCallbackCalled();
+
+        // Now print again as we want to confirm that the start
+        // printer discovery passes in the priority list.
+        print(adapter);
+
+        // Wait for a layout to finish - first layout was for the
+        // PDF printer, second for the first printer in preview mode,
+        // the third for the first printer in non-preview mode, and
+        // now a fourth for the PDF printer as we are printing again.
+        waitForLayoutAdapterCallbackCount(4);
+
+        // Cancel the printing.
+        getUiDevice().pressBack(); // wakes up the device.
+        getUiDevice().pressBack();
+
+        // Wait for all print jobs to be handled after which the session destroyed.
+        waitForPrinterDiscoverySessionDestroyCallbackCalled();
+
+        // Verify the expected calls.
+        InOrder inOrder = inOrder(firstSessionCallbacks);
+
+        // We start discovery as the print dialog was up.
+        List<PrinterId> priorityList = new ArrayList<PrinterId>();
+        priorityList.add(firstPrinterId);
+        inOrder.verify(firstSessionCallbacks).onStartPrinterDiscovery(
+                priorityList);
+
+        // We selected the first printer and now it should be tracked.
+        inOrder.verify(firstSessionCallbacks).onStartPrinterStateTracking(
+                firstPrinterId);
+
+        // We selected the second printer so the first should not be tracked.
+        inOrder.verify(firstSessionCallbacks).onStopPrinterStateTracking(
+                firstPrinterId);
+
+        // ...next we stop printer discovery...
+        inOrder.verify(firstSessionCallbacks).onStopPrinterDiscovery();
+
+        // ...last the session is destroyed.
+        inOrder.verify(firstSessionCallbacks).onDestroy();
+    }
+
+    private PrinterId getAddedPrinterIdForLocalId(
+            final PrinterDiscoverySessionCallbacks sessionCallbacks, String printerLocalId) {
+        final List<PrinterInfo> reportedPrinters = new ArrayList<PrinterInfo>();
+        getInstrumentation().runOnMainSync(new Runnable() {
+            @Override
+            public void run() {
+                // Grab the printer ids as only the service can create such.
+                StubbablePrinterDiscoverySession session = sessionCallbacks.getSession();
+                reportedPrinters.addAll(session.getPrinters());
+            }
+        });
+
+        final int reportedPrinterCount = reportedPrinters.size();
+        for (int i = 0; i < reportedPrinterCount; i++) {
+            PrinterInfo reportedPrinter = reportedPrinters.get(i);
+            String localId = reportedPrinter.getId().getLocalId();
+            if (printerLocalId.equals(localId)) {
+                return reportedPrinter.getId();
+            }
+        }
+
+        return null;
+    }
+
+    private PrintServiceCallbacks createSecondMockPrintServiceCallbacks() {
+        return createMockPrintServiceCallbacks(null, null, null);
+    }
+
+    private PrinterDiscoverySessionCallbacks createFirstMockPrinterDiscoverySessionCallbacks() {
+        return createMockPrinterDiscoverySessionCallbacks(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) {
+                // Get the session.
+                StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
+                        invocation.getMock()).getSession();
+
+                if (session.getPrinters().isEmpty()) {
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+
+                    // Add the first printer.
+                    PrinterId firstPrinterId = session.getService().generatePrinterId(
+                            FIRST_PRINTER_LOCAL_ID);
+                    PrinterInfo firstPrinter = new PrinterInfo.Builder(firstPrinterId,
+                            FIRST_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+                        .build();
+                    printers.add(firstPrinter);
+
+                    // Add the first printer.
+                    PrinterId secondPrinterId = session.getService().generatePrinterId(
+                            SECOND_PRINTER_LOCAL_ID);
+                    PrinterInfo secondPrinter = new PrinterInfo.Builder(secondPrinterId,
+                            SECOND_PRINTER_NAME, PrinterInfo.STATUS_IDLE)
+                        .build();
+                    printers.add(secondPrinter);
+
+                    session.addPrinters(printers);
+                }
+                return null;
+            }
+        }, null, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Get the session.
+                StubbablePrinterDiscoverySession session = ((PrinterDiscoverySessionCallbacks)
+                        invocation.getMock()).getSession();
+
+                PrinterId trackedPrinterId = (PrinterId) invocation.getArguments()[0];
+                List<PrinterInfo> reportedPrinters = session.getPrinters();
+
+                // We should be tracking a printer that we added.
+                PrinterInfo trackedPrinter = null;
+                final int reportedPrinterCount = reportedPrinters.size();
+                for (int i = 0; i < reportedPrinterCount; i++) {
+                    PrinterInfo reportedPrinter = reportedPrinters.get(i);
+                    if (reportedPrinter.getId().equals(trackedPrinterId)) {
+                        trackedPrinter = reportedPrinter;
+                        break;
+                    }
+                }
+                assertNotNull("Can track only added printers", trackedPrinter);
+
+                // If the printer does not have capabilities reported add them.
+                if (trackedPrinter.getCapabilities() == null) {
+
+                    // Add the capabilities to emulate lazy discovery.
+                    // Same for each printer is fine for what we test.
+                    PrinterCapabilitiesInfo capabilities =
+                            new PrinterCapabilitiesInfo.Builder(trackedPrinterId)
+                        .setMinMargins(new Margins(200, 200, 200, 200))
+                        .addMediaSize(MediaSize.ISO_A4, true)
+                        .addMediaSize(MediaSize.ISO_A5, false)
+                        .addResolution(new Resolution("300x300", "300x300", 300, 300), true)
+                        .setColorModes(PrintAttributes.COLOR_MODE_COLOR,
+                                PrintAttributes.COLOR_MODE_COLOR)
+                        .build();
+                    PrinterInfo updatedPrinter = new PrinterInfo.Builder(trackedPrinter)
+                        .setCapabilities(capabilities)
+                        .build();
+
+                    // Update the printer.
+                    List<PrinterInfo> printers = new ArrayList<PrinterInfo>();
+                    printers.add(updatedPrinter);
+                    session.addPrinters(printers);
+                }
+
+                return null;
+            }
+        }, null, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Take a note onDestroy was called.
+                onPrinterDiscoverySessionDestroyCalled();
+                return null;
+            }
+        });
+    }
+
+    public PrintDocumentAdapter createMockPrintDocumentAdapter() {
+        return createMockPrintDocumentAdapter(
+            new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                LayoutResultCallback callback = (LayoutResultCallback) invocation.getArguments()[3];
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder(PRINT_JOB_NAME)
+                        .setContentType(PrintDocumentInfo.CONTENT_TYPE_DOCUMENT)
+                        .setPageCount(3)
+                        .build();
+                callback.onLayoutFinished(info, false);
+                // Mark layout was called.
+                onLayoutCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                Object[] args = invocation.getArguments();
+                PageRange[] pages = (PageRange[]) args[0];
+                ParcelFileDescriptor fd = (ParcelFileDescriptor) args[1];
+                WriteResultCallback callback = (WriteResultCallback) args[3];
+                fd.close();
+                callback.onWriteFinished(pages);
+                // Mark write was called.
+                onWriteCalled();
+                return null;
+            }
+        }, new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                // Mark finish was called.
+                onFinishCalled();
+                return null;
+            }
+        });
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
new file mode 100644
index 0000000..c72d6f9
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/AddPrintersActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class AddPrintersActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
new file mode 100644
index 0000000..9d26d81
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/CustomPrintOptionsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class CustomPrintOptionsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/FirstPrintService.java b/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
new file mode 100644
index 0000000..a234de4
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/FirstPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.print.cts.services;
+
+public class FirstPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
new file mode 100644
index 0000000..ff0245f
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/PrintServiceCallbacks.java
@@ -0,0 +1,39 @@
+/*
+ * 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.print.cts.services;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+
+public abstract class PrintServiceCallbacks {
+
+    private PrintService mService;
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    public void setService(PrintService service) {
+        mService = service;
+    }
+
+    public abstract PrinterDiscoverySessionCallbacks onCreatePrinterDiscoverySessionCallbacks();
+
+    public abstract void onRequestCancelPrintJob(PrintJob printJob);
+
+    public abstract void onPrintJobQueued(PrintJob printJob);
+}
diff --git a/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java b/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
new file mode 100644
index 0000000..6b2c3a9
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/PrinterDiscoverySessionCallbacks.java
@@ -0,0 +1,46 @@
+/*
+ * 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.print.cts.services;
+
+import android.print.PrinterId;
+
+import java.util.List;
+
+public abstract class PrinterDiscoverySessionCallbacks {
+
+    private StubbablePrinterDiscoverySession mSession;
+
+    public void setSession(StubbablePrinterDiscoverySession session) {
+        mSession = session;
+    }
+
+    public StubbablePrinterDiscoverySession getSession() {
+        return mSession;
+    }
+
+    public abstract void onStartPrinterDiscovery(List<PrinterId> priorityList);
+
+    public abstract void onStopPrinterDiscovery();
+
+    public abstract void onValidatePrinters(List<PrinterId> printerIds);
+
+    public abstract void onStartPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onStopPrinterStateTracking(PrinterId printerId);
+
+    public abstract void onDestroy();
+}
diff --git a/tests/tests/print/src/android/print/cts/services/SecondPrintService.java b/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
new file mode 100644
index 0000000..1029a8e
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/SecondPrintService.java
@@ -0,0 +1,40 @@
+/*
+ * 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.print.cts.services;
+
+public class SecondPrintService extends StubbablePrintService {
+
+    private static final Object sLock = new Object();
+
+    private static PrintServiceCallbacks sCallbacks;
+
+    public static void setCallbacks(PrintServiceCallbacks callbacks) {
+        synchronized (sLock) {
+            sCallbacks = callbacks;
+        }
+    }
+
+    @Override
+    protected PrintServiceCallbacks getCallbacks() {
+        synchronized (sLock) {
+            if (sCallbacks != null) {
+                sCallbacks.setService(this);
+            }
+            return sCallbacks;
+        }
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/SettingsActivity.java b/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
new file mode 100644
index 0000000..eb23574
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/SettingsActivity.java
@@ -0,0 +1,28 @@
+/*
+ * 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.print.cts.services;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class SettingsActivity extends Activity {
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
new file mode 100644
index 0000000..2686b41
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrintService.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.cts.services;
+
+import android.printservice.PrintJob;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+public abstract class StubbablePrintService extends PrintService {
+
+    @Override
+    public PrinterDiscoverySession onCreatePrinterDiscoverySession() {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            return new StubbablePrinterDiscoverySession(this,
+                    getCallbacks().onCreatePrinterDiscoverySessionCallbacks());
+        }
+        return null;
+    }
+
+    @Override
+    public void onRequestCancelPrintJob(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onRequestCancelPrintJob(printJob);
+        }
+    }
+
+    @Override
+    public void onPrintJobQueued(PrintJob printJob) {
+        PrintServiceCallbacks callbacks = getCallbacks();
+        if (callbacks != null) {
+            callbacks.onPrintJobQueued(printJob);
+        }
+    }
+
+    protected abstract PrintServiceCallbacks getCallbacks();
+}
diff --git a/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
new file mode 100644
index 0000000..fdc2713
--- /dev/null
+++ b/tests/tests/print/src/android/print/cts/services/StubbablePrinterDiscoverySession.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.print.cts.services;
+
+import android.print.PrinterId;
+import android.printservice.PrintService;
+import android.printservice.PrinterDiscoverySession;
+
+import java.util.List;
+
+public class StubbablePrinterDiscoverySession extends PrinterDiscoverySession {
+    private final PrintService mService;
+    private final PrinterDiscoverySessionCallbacks mCallbacks;
+
+    public StubbablePrinterDiscoverySession(PrintService service,
+            PrinterDiscoverySessionCallbacks callbacks) {
+        mService = service;
+        mCallbacks = callbacks;
+        if (mCallbacks != null) {
+            mCallbacks.setSession(this);
+        }
+    }
+
+    public PrintService getService() {
+        return mService;
+    }
+
+    @Override
+    public void onStartPrinterDiscovery(List<PrinterId> priorityList) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterDiscovery(priorityList);
+        }
+    }
+
+    @Override
+    public void onStopPrinterDiscovery() {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterDiscovery();
+        }
+    }
+
+    @Override
+    public void onValidatePrinters(List<PrinterId> printerIds) {
+        if (mCallbacks != null) {
+            mCallbacks.onValidatePrinters(printerIds);
+        }
+    }
+
+    @Override
+    public void onStartPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStartPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onStopPrinterStateTracking(PrinterId printerId) {
+        if (mCallbacks != null) {
+            mCallbacks.onStopPrinterStateTracking(printerId);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mCallbacks != null) {
+            mCallbacks.onDestroy();
+        }
+    }
+}
diff --git a/tests/tests/provider/AndroidManifest.xml b/tests/tests/provider/AndroidManifest.xml
index 94dc408..7bf44b7 100644
--- a/tests/tests/provider/AndroidManifest.xml
+++ b/tests/tests/provider/AndroidManifest.xml
@@ -40,9 +40,12 @@
 
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.provider"/>
+                     android:label="CTS tests of android.provider">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <instrumentation android:name="android.provider.cts.CalendarTest$CalendarEmmaTestRunner"
                      android:targetPackage="com.android.cts.stub"
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
index 9654e43..ffeb2a1 100644
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
@@ -28,9 +28,6 @@
 import android.provider.Browser;
 import android.provider.Browser.BookmarkColumns;
 import android.provider.Browser.SearchColumns;
-import android.provider.BrowserContract;
-import android.provider.BrowserContract.Bookmarks;
-import android.provider.BrowserContract.History;
 import android.test.ActivityInstrumentationTestCase2;
 
 import java.util.ArrayList;
@@ -73,39 +70,30 @@
         ContentResolver.setMasterSyncAutomatically(false);
 
         // backup the current contents in database
-        Cursor cursor = mProvider.query(Bookmarks.CONTENT_URI, null, null, null, null, null);
-        if (cursor.moveToFirst()) {
+        Cursor cursor = mProvider.query(Browser.BOOKMARKS_URI, null, null, null, null, null);
+        while (cursor.moveToNext()) {
             String[] colNames = cursor.getColumnNames();
-            while (!cursor.isAfterLast()) {
-                ContentValues value = new ContentValues();
+            ContentValues value = new ContentValues();
 
-                for (int i = 0; i < colNames.length; i++) {
-                    if (Bookmarks.PARENT_SOURCE_ID.equals(colNames[i])
-                            || Bookmarks.INSERT_AFTER_SOURCE_ID.equals(colNames[i])
-                            || Bookmarks.TYPE.equals(colNames[i])) {
-                        // These aren't actual columns, so skip them in the backup
-                        continue;
-                    }
-                    switch (cursor.getType(i)) {
-                    case Cursor.FIELD_TYPE_BLOB:
-                        value.put(colNames[i], cursor.getBlob(i));
-                        break;
-                    case Cursor.FIELD_TYPE_FLOAT:
-                        value.put(colNames[i], cursor.getFloat(i));
-                        break;
-                    case Cursor.FIELD_TYPE_INTEGER:
-                        value.put(colNames[i], cursor.getLong(i));
-                        break;
-                    case Cursor.FIELD_TYPE_STRING:
-                        value.put(colNames[i], cursor.getString(i));
-                        break;
-                    }
+            for (int i = 0; i < colNames.length; i++) {
+                switch (cursor.getType(i)) {
+                case Cursor.FIELD_TYPE_BLOB:
+                    value.put(colNames[i], cursor.getBlob(i));
+                    break;
+                case Cursor.FIELD_TYPE_FLOAT:
+                    value.put(colNames[i], cursor.getFloat(i));
+                    break;
+                case Cursor.FIELD_TYPE_INTEGER:
+                    value.put(colNames[i], cursor.getLong(i));
+                    break;
+                case Cursor.FIELD_TYPE_STRING:
+                    value.put(colNames[i], cursor.getString(i));
+                    break;
                 }
-                mBookmarksBackup.add(value);
-
-                cursor.moveToNext();
-            };
+            }
+            mBookmarksBackup.add(value);
         }
+
         cursor.close();
 
         cursor = mProvider.query(Browser.SEARCHES_URI, null, null, null, null, null);
@@ -123,12 +111,8 @@
         }
         cursor.close();
 
-        Uri uri = Bookmarks.CONTENT_URI.buildUpon()
-                .appendQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, "true")
-                .build();
-        mProvider.delete(uri, null, null);
+        mProvider.delete(Browser.BOOKMARKS_URI, null, null);
         mProvider.delete(Browser.SEARCHES_URI, null, null);
-        mProvider.delete(History.CONTENT_URI, null, null);
 
         mActivity = getActivity();
     }
@@ -136,17 +120,13 @@
     @Override
     protected void tearDown() throws Exception {
         try {
-
             // clear all new contents added in test cases.
-            Uri uri = Bookmarks.CONTENT_URI.buildUpon()
-                .appendQueryParameter(BrowserContract.CALLER_IS_SYNCADAPTER, "true")
-                .build();
-            mProvider.delete(uri, null, null);
+            mProvider.delete(Browser.BOOKMARKS_URI, null, null);
             mProvider.delete(Browser.SEARCHES_URI, null, null);
 
             // recover the old backup contents
             for (ContentValues value : mBookmarksBackup) {
-                mProvider.insert(uri, value);
+                mProvider.insert(Browser.BOOKMARKS_URI, value);
             }
 
             for (ContentValues value : mSearchesBackup) {
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index bd8e06d..a8f547b 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -41,7 +41,6 @@
 import android.provider.CalendarContract.Instances;
 import android.provider.CalendarContract.Reminders;
 import android.provider.CalendarContract.SyncState;
-import android.test.InstrumentationCtsTestRunner;
 import android.test.InstrumentationTestCase;
 import android.test.suitebuilder.annotation.MediumTest;
 import android.text.TextUtils;
@@ -3706,7 +3705,8 @@
     /**
      * Special version of the test runner that does some remote Emma coverage housekeeping.
      */
-    public static class CalendarEmmaTestRunner extends InstrumentationCtsTestRunner {
+    // TODO: find if this is still used and if so convert to AndroidJUnitRunner framework
+    public static class CalendarEmmaTestRunner extends android.test.InstrumentationTestRunner {
         private static final Uri EMMA_CONTENT_URI =
             Uri.parse("content://" + CalendarContract.AUTHORITY + "/emma");
         private ContentResolver mContentResolver;
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
index 7cfb183..bbfa259 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_DataTest.java
@@ -26,10 +26,12 @@
 import android.net.Uri;
 import android.os.SystemClock;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Callable;
 import android.provider.ContactsContract.CommonDataKinds.Contactables;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.SipAddress;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Data;
@@ -49,6 +51,60 @@
     private ContentResolver mResolver;
     private ContactsContract_TestDataBuilder mBuilder;
 
+    private static ContentValues[] sContentValues = new ContentValues[7];
+    static {
+        ContentValues cv1 = new ContentValues();
+        cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv1.put(Email.DATA, "tamale@acme.com");
+        cv1.put(Email.TYPE, Email.TYPE_HOME);
+        sContentValues[0] = cv1;
+
+        ContentValues cv2 = new ContentValues();
+        cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        cv2.put(Phone.DATA, "510-123-5769");
+        cv2.put(Phone.TYPE, Phone.TYPE_HOME);
+        sContentValues[1] = cv2;
+
+        ContentValues cv3 = new ContentValues();
+        cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv3.put(Email.DATA, "hot@google.com");
+        cv3.put(Email.TYPE, Email.TYPE_WORK);
+        sContentValues[2] = cv3;
+
+        ContentValues cv4 = new ContentValues();
+        cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv4.put(Email.DATA, "eggs@farmers.org");
+        cv4.put(Email.TYPE, Email.TYPE_HOME);
+        sContentValues[3] = cv4;
+
+        ContentValues cv5 = new ContentValues();
+        cv5.put(Contacts.DISPLAY_NAME, "John Doe");
+        cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        cv5.put(Email.DATA, "doeassociates@deer.com");
+        cv5.put(Email.TYPE, Email.TYPE_WORK);
+        sContentValues[4] = cv5;
+
+        ContentValues cv6 = new ContentValues();
+        cv6.put(Contacts.DISPLAY_NAME, "John Doe");
+        cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        cv6.put(Phone.DATA, "518-354-1111");
+        cv6.put(Phone.TYPE, Phone.TYPE_HOME);
+        sContentValues[5] = cv6;
+
+        ContentValues cv7 = new ContentValues();
+        cv7.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        cv7.put(Data.MIMETYPE, SipAddress.CONTENT_ITEM_TYPE);
+        cv7.put(SipAddress.DATA, "mysip@sipaddress.com");
+        cv7.put(SipAddress.TYPE, SipAddress.TYPE_HOME);
+        sContentValues[6] = cv7;
+    }
+
+    private TestRawContact[] mRawContacts = new TestRawContact[3];
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -182,6 +238,37 @@
         assertCursorStoredValuesWithRawContactsFilter(filterUri, ids, new ContentValues[0]);
     }
 
+    /**
+     * Verifies that Callable.CONTENT_URI returns only data items that can be called (i.e.
+     * phone numbers and sip addresses)
+     */
+    public void testCallableUri_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Callable.CONTENT_URI;
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1],
+                sContentValues[5], sContentValues[6]);
+    }
+
+    public void testCallableFilterByNameOrOrganization_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "doe");
+        // Only callables belonging to John Doe (name) and Cold Tamago (organization) are returned.
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[5],
+                sContentValues[6]);
+    }
+
+    public void testCallableFilterByNumber_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "510");
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1]);
+    }
+
+    public void testCallableFilterBySipAddress_returnsCorrectDataRows() throws Exception {
+        long[] ids = setupContactablesTestData();
+        Uri uri = Uri.withAppendedPath(Callable.CONTENT_FILTER_URI, "mysip");
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[6]);
+    }
+
     public void testDataInsert_updatesContactLastUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
         long baseTime = ContactUtil.queryContactLastUpdatedTimestamp(mResolver, ids.mContactId);
@@ -213,6 +300,70 @@
         RawContactUtil.delete(mResolver, ids.mRawContactId, true);
     }
 
+    /**
+     * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES}
+     * boolean parameter correctly results in deduped phone numbers.
+     */
+    public void testPhoneQuery_removeDuplicateEntries() throws Exception{
+        long[] ids = setupContactablesTestData();
+
+        // Insert duplicate data entry for raw contact 3. (existing phone number 518-354-1111)
+        mRawContacts[2].newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "518-354-1111")
+                .with(Phone.TYPE, Phone.TYPE_HOME)
+                .insert();
+
+        ContentValues dupe = new ContentValues();
+        dupe.put(Contacts.DISPLAY_NAME, "John Doe");
+        dupe.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+        dupe.put(Phone.DATA, "518-354-1111");
+        dupe.put(Phone.TYPE, Phone.TYPE_HOME);
+
+        // Query for all phone numbers in the contacts database (without deduping).
+        // The phone number above should be listed twice, in its duplicated forms.
+        assertCursorStoredValuesWithRawContactsFilter(Phone.CONTENT_URI, ids, sContentValues[1],
+                sContentValues[5], dupe);
+
+        // Now query for all phone numbers in the contacts database but request deduping.
+        // The phone number should now be listed only once.
+        Uri uri = Phone.CONTENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build();
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[1],
+                sContentValues[5]);
+    }
+
+    /**
+     * Tests that specifying the {@link android.provider.ContactsContract#REMOVE_DUPLICATE_ENTRIES}
+     * boolean parameter correctly results in deduped email addresses.
+     */
+    public void testEmailQuery_removeDuplicateEntries() throws Exception{
+        long[] ids = setupContactablesTestData();
+
+        // Insert duplicate data entry for raw contact 3. (existing email doeassociates@deer.com)
+        mRawContacts[2].newDataRow(Email.CONTENT_ITEM_TYPE)
+                .with(Email.DATA, "doeassociates@deer.com")
+                .with(Email.TYPE, Email.TYPE_WORK)
+                .insert();
+
+        ContentValues dupe = new ContentValues();
+        dupe.put(Contacts.DISPLAY_NAME, "John Doe");
+        dupe.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
+        dupe.put(Email.DATA, "doeassociates@deer.com");
+        dupe.put(Email.TYPE, Email.TYPE_WORK);
+
+        // Query for all email addresses in the contacts database (without deduping).
+        // The email address above should be listed twice, in its duplicated forms.
+        assertCursorStoredValuesWithRawContactsFilter(Email.CONTENT_URI, ids, sContentValues[0],
+                sContentValues[2], sContentValues[3], sContentValues[4], dupe);
+
+        // Now query for all email addresses in the contacts database but request deduping.
+        // The email address should now be listed only once.
+        Uri uri = Email.CONTENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.REMOVE_DUPLICATE_ENTRIES, "true").build();
+        assertCursorStoredValuesWithRawContactsFilter(uri, ids, sContentValues[0],
+                sContentValues[2], sContentValues[3], sContentValues[4]);
+    }
+
     public void testDataUpdate_updatesContactLastUpdatedTimestamp() {
         DatabaseAsserts.ContactIdPair ids = DatabaseAsserts.assertAndCreateContact(mResolver);
         long dataId = createData(ids.mRawContactId);
@@ -277,6 +428,7 @@
                 .with(Phone.DATA, "510-123-5769")
                 .with(Email.TYPE, Phone.TYPE_HOME)
                 .insert();
+        mRawContacts[0] = rawContact;
 
         TestRawContact rawContact2 = mBuilder.newRawContact()
                 .with(RawContacts.ACCOUNT_TYPE, "test_account")
@@ -289,6 +441,14 @@
                 .with(Email.DATA, "eggs@farmers.org")
                 .with(Email.TYPE, Email.TYPE_HOME)
                 .insert();
+        rawContact2.newDataRow(SipAddress.CONTENT_ITEM_TYPE)
+                .with(SipAddress.DATA, "mysip@sipaddress.com")
+                .with(SipAddress.TYPE, SipAddress.TYPE_HOME)
+                .insert();
+        rawContact2.newDataRow(Organization.CONTENT_ITEM_TYPE)
+                .with(Organization.COMPANY, "Doe Corp")
+                .insert();
+        mRawContacts[1] = rawContact2;
 
         TestRawContact rawContact3 = mBuilder.newRawContact()
                 .with(RawContacts.ACCOUNT_TYPE, "test_account")
@@ -308,55 +468,12 @@
         rawContact3.newDataRow(Organization.CONTENT_ITEM_TYPE)
                 .with(Organization.DATA, "Doe Industries")
                 .insert();
+        mRawContacts[2] = rawContact3;
         return new long[] {rawContact.getId(), rawContact2.getId(), rawContact3.getId()};
     }
 
     // Provides functionality to set up content values for the Contactables tests
     private static class ContactablesTestHelper {
-        private static ContentValues[] sContentValues = new ContentValues[6];
-        static {
-            ContentValues cv1 = new ContentValues();
-            cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv1.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv1.put(Email.DATA, "tamale@acme.com");
-            cv1.put(Email.TYPE, Email.TYPE_HOME);
-            sContentValues[0] = cv1;
-
-            ContentValues cv2 = new ContentValues();
-            cv2.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv2.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-            cv2.put(Phone.DATA, "510-123-5769");
-            cv2.put(Phone.TYPE, Phone.TYPE_HOME);
-            sContentValues[1] = cv2;
-
-            ContentValues cv3 = new ContentValues();
-            cv3.put(Contacts.DISPLAY_NAME, "Hot Tamale");
-            cv3.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv3.put(Email.DATA, "hot@google.com");
-            cv3.put(Email.TYPE, Email.TYPE_WORK);
-            sContentValues[2] = cv3;
-
-            ContentValues cv4 = new ContentValues();
-            cv4.put(Contacts.DISPLAY_NAME, "Cold Tamago");
-            cv4.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv4.put(Email.DATA, "eggs@farmers.org");
-            cv4.put(Email.TYPE, Email.TYPE_HOME);
-            sContentValues[3] = cv4;
-
-            ContentValues cv5 = new ContentValues();
-            cv5.put(Contacts.DISPLAY_NAME, "John Doe");
-            cv5.put(Data.MIMETYPE, Email.CONTENT_ITEM_TYPE);
-            cv5.put(Email.DATA, "doeassociates@deer.com");
-            cv5.put(Email.TYPE, Email.TYPE_WORK);
-            sContentValues[4] = cv5;
-
-            ContentValues cv6 = new ContentValues();
-            cv6.put(Contacts.DISPLAY_NAME, "John Doe");
-            cv6.put(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
-            cv6.put(Phone.DATA, "518-354-1111");
-            cv6.put(Phone.TYPE, Phone.TYPE_HOME);
-            sContentValues[5] = cv6;
-        }
 
         /**
          * @return An arraylist of contentValues that correspond to the provided raw contacts
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java
new file mode 100644
index 0000000..2a8e806
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_PinnedPositionsTest.java
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+import static android.provider.cts.contacts.ContactUtil.newContentValues;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PinnedPositions;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.contacts.CommonDatabaseUtils;
+import android.provider.cts.contacts.ContactUtil;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.provider.cts.contacts.RawContactUtil;
+import android.test.AndroidTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.PinnedPositions} API
+ */
+public class ContactsContract_PinnedPositionsTest extends AndroidTestCase {
+    private ContentResolver mResolver;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getContext().getContentResolver();
+    }
+
+    /**
+     * Tests that the ContactsProvider automatically stars/unstars a pinned/unpinned contact if
+     * {@link PinnedPositions#STAR_WHEN_PINNING} boolean parameter is set to true, and that the
+     * values are correctly propogated to the contact's constituent raw contacts.
+     */
+    public void testPinnedPositionsUpdateForceStar() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                values, null, null);
+
+        // Pinning a contact should automatically star it if we specified the boolean parameter.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 1));
+
+        // Make sure the values are propagated to raw contacts.
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, 2));
+
+        final ContentValues unpin = newContentValues(i3.mContactId, unpinned);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                unpin, null, null);
+
+        // Unpinning a contact should automatically unstar it.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 1));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that the ContactsProvider does not automatically star/unstar a pinned/unpinned contact
+     * if {@link PinnedPositions#STAR_WHEN_PINNING} boolean parameter not set to true or not
+     * provided.
+     */
+    public void testPinnedPositionsUpdateDontForceStar() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 1, i3.mContactId, 3, i4.mContactId, 2);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+
+        // Pinning a contact should not automatically star it since we didn't specify the
+        // STAR_WHEN_PINNING boolean parameter.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 0));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        // Make sure the values are propagated to raw contacts.
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        // Manually star contact 3.
+        assertEquals(1,
+                updateItemForContact(Contacts.CONTENT_URI, i3.mContactId, Contacts.STARRED, "1"));
+
+        // Check the third contact and raw contact is starred.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, 3, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        final ContentValues unpin = newContentValues(i3.mContactId, unpinned);
+
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, unpin, null, null);
+
+        // Unpinning a contact should not automatically unstar it.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 1, Contacts.STARRED, 0));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 0));
+        assertValuesForContact(i3.mContactId,
+                newContentValues(Contacts.PINNED, unpinned, Contacts.STARRED, 1));
+        assertValuesForContact(i4.mContactId,
+                newContentValues(Contacts.PINNED, 2, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 0));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, unpinned, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 0));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that updating the ContactsProvider with illegal pinned position correctly
+     * throws an IllegalArgumentException.
+     */
+    public void testPinnedPositionsUpdateIllegalValues() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final int unpinned = PinnedPositions.UNPINNED;
+
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i4.mContactId, newContentValues(Contacts.PINNED, unpinned));
+
+        // Unsupported string should throw an IllegalArgumentException.
+        final ContentValues values = newContentValues(i1.mContactId, 1, i3.mContactId, 3,
+                i4.mContactId, "undemotemeplease!");
+        try {
+            mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values, null, null);
+            fail("Pinned position must be an integer.");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // Unsupported pinned position (e.g. float value) should throw an IllegalArgumentException.
+        final ContentValues values2 = newContentValues(i1.mContactId, "1.1");
+        try {
+            mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI, values2, null, null);
+            fail("Pinned position must be an integer");
+        } catch (IllegalArgumentException expected) {
+        }
+
+        // Nothing should have been changed.
+
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, unpinned));
+        assertValuesForContact(i4.mContactId, newContentValues(Contacts.PINNED, unpinned));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+        assertValuesForRawContact(i4.mRawContactId, newContentValues(RawContacts.PINNED, unpinned));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, i3.mContactId);
+        ContactUtil.delete(mResolver, i4.mContactId);
+    }
+
+    /**
+     * Tests that pinned positions are correctly handled after the ContactsProvider aggregates
+     * and splits raw contacts.
+     */
+    public void testPinnedPositionsAfterJoinAndSplit() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i3 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i4 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i5 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i6 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final ContentValues values = newContentValues(i1.mContactId, 1, i2.mContactId, 2,
+                i3.mContactId, 3, i5.mContactId, 5, i6.mContactId, 6);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
+                .appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").build(),
+                values, null, null);
+
+        // Aggregate raw contact 1 and 4 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i1.mRawContactId, i4.mRawContactId);
+
+        // If only one contact is pinned, the resulting contact should inherit the pinned position.
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(i3.mContactId, newContentValues(Contacts.PINNED, 3));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 1, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+        assertValuesForRawContact(i5.mRawContactId,
+                newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i6.mRawContactId,
+                newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
+
+        // Aggregate raw contact 2 and 3 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i2.mRawContactId, i3.mRawContactId);
+
+        // If both raw contacts are pinned, the resulting contact should inherit the lower
+        // pinned position.
+        assertValuesForContact(i1.mContactId, newContentValues(Contacts.PINNED, 1));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 5));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        assertValuesForRawContact(i1.mRawContactId, newContentValues(RawContacts.PINNED, 1));
+        assertValuesForRawContact(i2.mRawContactId, newContentValues(RawContacts.PINNED, 2));
+        assertValuesForRawContact(i3.mRawContactId, newContentValues(RawContacts.PINNED, 3));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForRawContact(i5.mRawContactId, newContentValues(RawContacts.PINNED, 5));
+        assertValuesForRawContact(i6.mRawContactId, newContentValues(RawContacts.PINNED, 6));
+
+        // Split the aggregated raw contacts.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_SEPARATE,
+            i1.mRawContactId, i4.mRawContactId);
+
+        // Raw contacts should be unpinned after being split, but still starred.
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, 2, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i3.mRawContactId,
+                newContentValues(RawContacts.PINNED, 3, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i4.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+        assertValuesForRawContact(i5.mRawContactId,
+                newContentValues(RawContacts.PINNED, 5, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i6.mRawContactId,
+                newContentValues(RawContacts.PINNED, 6, RawContacts.STARRED, 1));
+
+        // Now demote contact 5.
+        final ContentValues cv = newContentValues(i5.mContactId, PinnedPositions.DEMOTED);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().build(),
+                cv, null, null);
+
+        // Get new contact Ids for contacts composing of raw contacts 1 and 4 because they have
+        // changed.
+        final long cId1 = RawContactUtil.queryContactIdByRawContactId(mResolver, i1.mRawContactId);
+        final long cId4 = RawContactUtil.queryContactIdByRawContactId(mResolver, i4.mRawContactId);
+
+        assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i5.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED));
+        assertValuesForContact(i6.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        // Aggregate contacts 5 and 6 together.
+        ContactUtil.setAggregationException(mResolver, AggregationExceptions.TYPE_KEEP_TOGETHER,
+                i5.mRawContactId, i6.mRawContactId);
+
+        // The resulting contact should have a pinned value of 6.
+        assertValuesForContact(cId1, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i2.mContactId, newContentValues(Contacts.PINNED, 2));
+        assertValuesForContact(cId4, newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED));
+        assertValuesForContact(i5.mContactId, newContentValues(Contacts.PINNED, 6));
+
+        ContactUtil.delete(mResolver, cId1);
+        ContactUtil.delete(mResolver, i2.mContactId);
+        ContactUtil.delete(mResolver, cId4);
+        ContactUtil.delete(mResolver, i5.mContactId);
+    }
+
+    /**
+     * Tests that pinned positions are correctly handled for contacts that have been demoted
+     * or undemoted.
+     */
+    public void testPinnedPositionsAfterDemoteAndUndemote() {
+        final DatabaseAsserts.ContactIdPair i1 = DatabaseAsserts.assertAndCreateContact(mResolver);
+        final DatabaseAsserts.ContactIdPair i2 = DatabaseAsserts.assertAndCreateContact(mResolver);
+
+        final ContentValues values =
+                newContentValues(i1.mContactId, 0, i2.mContactId, PinnedPositions.DEMOTED);
+
+        // Pin contact 1 and demote contact 2.
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().
+                appendQueryParameter(PinnedPositions.STAR_WHEN_PINNING, "true").
+                build(), values, null, null);
+
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 0, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.DEMOTED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 0, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.DEMOTED, RawContacts.STARRED, 0));
+
+        // Now undemote both contacts.
+        final ContentValues values2 = newContentValues(i1.mContactId, PinnedPositions.UNDEMOTE,
+                i2.mContactId, PinnedPositions.UNDEMOTE);
+        mResolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon().
+                build(), values2, null, null);
+
+        // Contact 1 remains pinned at 0, while contact 2 becomes unpinned.
+        assertValuesForContact(i1.mContactId,
+                newContentValues(Contacts.PINNED, 0, Contacts.STARRED, 1));
+        assertValuesForContact(i2.mContactId,
+                newContentValues(Contacts.PINNED, PinnedPositions.UNPINNED, Contacts.STARRED, 0));
+
+        assertValuesForRawContact(i1.mRawContactId,
+                newContentValues(RawContacts.PINNED, 0, RawContacts.STARRED, 1));
+        assertValuesForRawContact(i2.mRawContactId,
+                newContentValues(RawContacts.PINNED, PinnedPositions.UNPINNED, RawContacts.STARRED,
+                        0));
+
+        ContactUtil.delete(mResolver, i1.mContactId);
+        ContactUtil.delete(mResolver, i2.mContactId);
+    }
+
+    /**
+     * Verifies that the stored values for the contact that corresponds to the given contactId
+     * contain the exact same name-value pairs in the given ContentValues.
+     *
+     * @param contactId Id of a valid contact in the contacts database.
+     * @param contentValues A valid ContentValues object.
+     */
+    private void assertValuesForContact(long contactId, ContentValues contentValues) {
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, Contacts.CONTENT_URI.
+                buildUpon().appendEncodedPath(String.valueOf(contactId)).build(), contentValues);
+    }
+
+    /**
+     * Verifies that the stored values for the raw contact that corresponds to the given
+     * rawContactId contain the exact same name-value pairs in the given ContentValues.
+     *
+     * @param rawContactId Id of a valid contact in the contacts database
+     * @param contentValues A valid ContentValues object
+     */
+    private void assertValuesForRawContact(long rawContactId, ContentValues contentValues) {
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, RawContacts.CONTENT_URI.
+                buildUpon().appendEncodedPath(String.valueOf(rawContactId)).build(), contentValues);
+    }
+
+    /**
+     * Updates the contacts provider for a contact or raw contact corresponding to the given
+     * contact with key-value pairs as specified in the provided string parameters. Throws an
+     * exception if the number of provided string parameters is not zero or non-even.
+     *
+     * @param uri base URI that the provided ID will be appended onto, in order to creating the
+     * resulting URI
+     * @param id id of the contact of raw contact to perform the update for
+     * @param extras an even number of string parameters that correspond to name-value pairs
+     *
+     * @return the number of rows that were updated
+     */
+    private int updateItemForContact(Uri uri, long id, String... extras) {
+        Uri itemUri = ContentUris.withAppendedId(uri, id);
+        return updateItemForUri(itemUri, extras);
+    }
+
+    /**
+     * Updates the contacts provider for the given YRU with key-value pairs as specified in the
+     * provided string parameters. Throws an exception if the number of provided string parameters
+     * is not zero or non-even.
+     *
+     * @param uri URI to perform the update for
+     * @param extras an even number of string parameters that correspond to name-value pairs
+     *
+     * @return the number of rows that were updated
+     */
+    private int updateItemForUri(Uri uri, String... extras) {
+        ContentValues values = new ContentValues();
+        CommonDatabaseUtils.extrasVarArgsToValues(values, extras);
+        return mResolver.update(uri, values, null, null);
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_StrequentsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_StrequentsTest.java
new file mode 100644
index 0000000..d188d18
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_StrequentsTest.java
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.provider.cts;
+
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Contactables;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.DataUsageFeedback;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.InstrumentationTestCase;
+
+import java.util.ArrayList;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} and
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI} apis.
+ */
+public class ContactsContract_StrequentsTest extends InstrumentationTestCase {
+    private ContentResolver mResolver;
+    private ContactsContract_TestDataBuilder mBuilder;
+
+    public static ContentValues[] sContentValues = new ContentValues[3];
+    static {
+        ContentValues cv1 = new ContentValues();
+        cv1.put(Contacts.DISPLAY_NAME, "Hot Tamale");
+        sContentValues[0] = cv1;
+
+        ContentValues cv2 = new ContentValues();
+        cv2.put(Contacts.DISPLAY_NAME, "Cold Tamago");
+        sContentValues[1] = cv2;
+
+        ContentValues cv3 = new ContentValues();
+        cv3.put(Contacts.DISPLAY_NAME, "John Doe");
+        sContentValues[2] = cv3;
+    }
+
+    private long[] mDataIds = new long[3];
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mResolver = getInstrumentation().getTargetContext().getContentResolver();
+        ContentProviderClient provider =
+                mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+        mBuilder = new ContactsContract_TestDataBuilder(provider);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        mBuilder.cleanup();
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * no contacts if there are no starred or frequent contacts in the user's contacts.
+     */
+    public void testStrequents_noStarredOrFrequents() throws Exception {
+        long[] ids = setupTestData();
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * starred contacts in the correct order if there are only starred contacts in the user's
+     * contacts.
+     */
+    public void testStrequents_starredOnlyInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Star/favorite the first and third contact.
+        starContact(ids[0]);
+        starContact(ids[1]);
+
+        // Only the starred contacts should be returned, ordered alphabetically by name
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                sContentValues[1], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * frequent contacts in the correct order if there are only frequent contacts in the user's
+     * contacts.
+     */
+    public void testStrequents_frequentsOnlyInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact thrice.
+        markDataAsUsed(mDataIds[1], 3);
+
+        // Contact the third contact twice.
+        markDataAsUsed(mDataIds[2], 2);
+
+        // The strequents uri should now return contact 2, 3, 1 in order due to ranking by
+        // data usage.
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                sContentValues[1], sContentValues[2], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_URI} returns
+     * first starred, then frequent contacts in their respective correct orders if there are both
+     * starred and frequent contacts in the user's contacts.
+     */
+    public void testStrequents_starredAndFrequentsInCorrectOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact thrice.
+        markDataAsUsed(mDataIds[1], 3);
+
+        // Contact the third contact twice, and mark it as used
+        markDataAsUsed(mDataIds[2], 2);
+        starContact(ids[2]);
+
+        // The strequents uri should now return contact 3, 2, 1 in order. Contact 3 is ranked first
+        // because it is starred, followed by contacts 2 and 1 due to their data usage ranking.
+        // Note that contact 3 is only returned once (as a starred contact) even though it is also
+        // a frequently contacted contact.
+        assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_STREQUENT_URI, ids,
+                sContentValues[2], sContentValues[1], sContentValues[0]);
+    }
+
+    /**
+     * Tests that {@link android.provider.ContactsContract.Contacts#CONTENT_STREQUENT_FILTER_URI}
+     * correctly filters the returned contacts with the given user input.
+     */
+    public void testStrequents_withFilter() throws Exception {
+        long[] ids = setupTestData();
+
+        //Star all 3 contacts
+        starContact(ids[0]);
+        starContact(ids[1]);
+        starContact(ids[2]);
+
+        // Construct a uri that filters for the query string "ta".
+        Uri uri = Contacts.CONTENT_STREQUENT_FILTER_URI.buildUpon().appendEncodedPath("ta").build();
+
+        // Only contact 1 and 2 should be returned (sorted in alphabetical order) due to the
+        // filtered query.
+        assertCursorStoredValuesWithContactsFilter(uri, ids, sContentValues[1], sContentValues[0]);
+    }
+
+    public void testStrequents_phoneOnly() throws Exception {
+        long[] ids = setupTestData();
+
+        // Star all 3 contacts
+        starContact(ids[0]);
+        starContact(ids[1]);
+        starContact(ids[2]);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        // Only the contacts with phone numbers are returned, in alphabetical order. Filtering
+        // is done with data ids instead of contact ids since each row contains a single data item.
+        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, sContentValues[0],
+                sContentValues[2]);
+    }
+
+    public void testStrequents_phoneOnlyFrequentsOrder() throws Exception {
+        long[] ids = setupTestData();
+
+        // Contact the first contact once.
+        markDataAsUsed(mDataIds[0], 1);
+
+        // Contact the second contact twice.
+        markDataAsUsed(mDataIds[1], 2);
+
+        // Contact the third contact thrice.
+        markDataAsUsed(mDataIds[2], 3);
+
+        // Construct a uri for phone only favorites.
+        Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+                appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+        // Only the contacts with phone numbers are returned, in frequency ranking order.
+        assertCursorStoredValuesWithContactsFilter(uri, mDataIds, sContentValues[2],
+                sContentValues[0]);
+    }
+
+    /**
+     * Given a uri, performs a query on the contacts provider for that uri and asserts that the
+     * cursor returned from the query matches the expected results.
+     *
+     * @param uri Uri to perform the query for
+     * @param contactsId Array of contact IDs that serves as an additional filter on the result
+     * set. This is needed to limit the output to temporary test contacts that were created for
+     * purposes of the test, so that the tests do not fail on devices with existing contacts on
+     * them
+     * @param expected An array of ContentValues corresponding to the expected output of the query
+     */
+    private void assertCursorStoredValuesWithContactsFilter(Uri uri, long[] contactsId,
+            ContentValues... expected) {
+        // We need this helper function to add a filter for specific contacts because
+        // otherwise tests will fail if performed on a device with existing contacts data
+        StringBuilder sb = new StringBuilder();
+        sb.append(Contacts._ID + " in ");
+        sb.append("(");
+        for (int i = 0; i < contactsId.length; i++) {
+            if (i != 0) sb.append(",");
+            sb.append(contactsId[i]);
+        }
+        sb.append(")");
+        DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null, sb.toString(),
+                null, null, expected);
+    }
+
+    /**
+     * Given a contact id, update the contact corresponding to that contactId so that it now shows
+     * up in the user's favorites/starred contacts.
+     *
+     * @param contactId Contact ID corresponding to the contact to star
+     */
+    private void starContact(long contactId) {
+        ContentValues values = new ContentValues();
+        values.put(Contacts.STARRED, 1);
+        mResolver.update(ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId), values,
+                null, null);
+    }
+
+    /**
+     * Given a data id, increment the data usage stats by a given number of usages to simulate
+     * the user making a call to the given data item.
+     *
+     * @param dataId Id of the data item to increment data usage stats for
+     * @param numTimes The number of times to increase the data usage stats by
+     */
+    private void markDataAsUsed(long dataId, int numTimes) {
+        Uri uri = ContactsContract.DataUsageFeedback.FEEDBACK_URI.buildUpon().
+                appendPath(String.valueOf(dataId)).
+                appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                        DataUsageFeedback.USAGE_TYPE_CALL).build();
+        for (int i = 1; i <= numTimes; i++) {
+            mResolver.update(uri, new ContentValues(), null, null);
+        }
+    }
+
+    /**
+     * Setup the contacts database with temporary contacts used for testing. These contacts will
+     * be removed during teardown.
+     *
+     * @return An array of long values corresponding to the ids of the created contacts
+     *
+     * @throws Exception
+     */
+    private long[] setupTestData() throws Exception {
+        TestRawContact rawContact = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Hot Tamale")
+                .insert();
+        mDataIds[0] = rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "510-123-5769")
+                .with(Email.TYPE, Phone.TYPE_HOME)
+                .insert().load().getId();
+        rawContact.load();
+        TestContact contact = rawContact.getContact().load();
+
+        TestRawContact rawContact2 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "Cold Tamago")
+                .insert();
+        mDataIds[1] = rawContact2.newDataRow(Email.CONTENT_ITEM_TYPE)
+                .with(Email.DATA, "eggs@farmers.org")
+                .with(Email.TYPE, Email.TYPE_HOME)
+                .insert().load().getId();
+        rawContact2.load();
+        TestContact contact2 = rawContact2.getContact().load();
+
+        TestRawContact rawContact3 = mBuilder.newRawContact()
+                .with(RawContacts.ACCOUNT_TYPE, "test_account")
+                .with(RawContacts.ACCOUNT_NAME, "test_name")
+                .insert();
+        rawContact3.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+                .with(StructuredName.DISPLAY_NAME, "John Doe")
+                .insert();
+        mDataIds[2] = rawContact3.newDataRow(Phone.CONTENT_ITEM_TYPE)
+                .with(Phone.DATA, "518-354-1111")
+                .with(Phone.TYPE, Phone.TYPE_HOME)
+                .insert().load().getId();
+        rawContact3.load();
+        TestContact contact3 = rawContact3.getContact().load();
+
+        return new long[] {contact.getId(), contact2.getId(), contact3.getId()};
+    }
+}
+
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
index 43c249e..471f895 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_TestDataBuilder.java
@@ -334,6 +334,10 @@
             return getLong(Data.RAW_CONTACT_ID);
         }
 
+        public long getId() {
+            return getLong(Data._ID);
+        }
+
         public TestRawContact getRawContact() throws Exception {
             return mRawContact;
         }
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
index b496007..db1c4f7 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsTest.java
@@ -413,7 +413,10 @@
         final String[] CALLS_PROJECTION = new String[] {
                 Calls._ID, Calls.NUMBER, Calls.DATE, Calls.DURATION, Calls.TYPE,
                 Calls.NEW, Calls.CACHED_NAME, Calls.CACHED_NUMBER_TYPE,
-                Calls.CACHED_NUMBER_LABEL};
+                Calls.CACHED_NUMBER_LABEL, Calls.CACHED_FORMATTED_NUMBER,
+                Calls.CACHED_MATCHED_NUMBER, Calls.CACHED_NORMALIZED_NUMBER,
+                Calls.CACHED_LOOKUP_URI, Calls.CACHED_PHOTO_ID, Calls.COUNTRY_ISO,
+                Calls.GEOCODED_LOCATION};
         final int ID_INDEX = 0;
         final int NUMBER_INDEX = 1;
         final int DATE_INDEX = 2;
@@ -423,15 +426,30 @@
         final int CACHED_NAME_INDEX = 6;
         final int CACHED_NUMBER_TYPE_INDEX = 7;
         final int CACHED_NUMBER_LABEL_INDEX = 8;
+        final int CACHED_FORMATTED_NUMBER_INDEX = 9;
+        final int CACHED_MATCHED_NUMBER_INDEX = 10;
+        final int CACHED_NORMALIZED_NUMBER_INDEX = 11;
+        final int CACHED_LOOKUP_URI_INDEX = 12;
+        final int CACHED_PHOTO_ID_INDEX = 13;
+        final int COUNTRY_ISO_INDEX = 14;
+        final int GEOCODED_LOCATION_INDEX = 15;
 
         String insertCallsNumber = "0123456789";
         int insertCallsDuration = 120;
         String insertCallsName = "cached_name_insert";
         String insertCallsNumberLabel = "cached_label_insert";
-        String updateCallsNumber = "9876543210";
+
+        String updateCallsNumber = "987654321";
         int updateCallsDuration = 310;
         String updateCallsName = "cached_name_update";
         String updateCallsNumberLabel = "cached_label_update";
+        String updateCachedFormattedNumber = "987-654-4321";
+        String updateCachedMatchedNumber = "987-654-4321";
+        String updateCachedNormalizedNumber = "+1987654321";
+        String updateCachedLookupUri = "cached_lookup_uri_update";
+        long updateCachedPhotoId = 100;
+        String updateCountryIso = "hk";
+        String updateGeocodedLocation = "Hong Kong";
 
         try {
             // Test: insert
@@ -463,7 +481,8 @@
             int id = cursor.getInt(ID_INDEX);
             cursor.close();
 
-            // Test: update
+            // Test: update. Also add new cached fields to simulate extra cached fields being
+            // inserted into the call log after the initial lookup.
             int now = (int) new Date().getTime();
             value.clear();
             value.put(Calls.NUMBER, updateCallsNumber);
@@ -474,6 +493,13 @@
             value.put(Calls.CACHED_NAME, updateCallsName);
             value.put(Calls.CACHED_NUMBER_TYPE, Phones.TYPE_CUSTOM);
             value.put(Calls.CACHED_NUMBER_LABEL, updateCallsNumberLabel);
+            value.put(Calls.CACHED_FORMATTED_NUMBER, updateCachedFormattedNumber);
+            value.put(Calls.CACHED_MATCHED_NUMBER, updateCachedMatchedNumber);
+            value.put(Calls.CACHED_NORMALIZED_NUMBER, updateCachedNormalizedNumber);
+            value.put(Calls.CACHED_PHOTO_ID, updateCachedPhotoId);
+            value.put(Calls.COUNTRY_ISO, updateCountryIso);
+            value.put(Calls.GEOCODED_LOCATION, updateGeocodedLocation);
+            value.put(Calls.CACHED_LOOKUP_URI, updateCachedLookupUri);
 
             mCallLogProvider.update(uri, value, null, null);
             cursor = mCallLogProvider.query(Calls.CONTENT_URI, CALLS_PROJECTION,
@@ -487,6 +513,15 @@
             assertEquals(updateCallsName, cursor.getString(CACHED_NAME_INDEX));
             assertEquals(Phones.TYPE_CUSTOM, cursor.getInt(CACHED_NUMBER_TYPE_INDEX));
             assertEquals(updateCallsNumberLabel, cursor.getString(CACHED_NUMBER_LABEL_INDEX));
+            assertEquals(updateCachedFormattedNumber,
+                    cursor.getString(CACHED_FORMATTED_NUMBER_INDEX));
+            assertEquals(updateCachedMatchedNumber, cursor.getString(CACHED_MATCHED_NUMBER_INDEX));
+            assertEquals(updateCachedNormalizedNumber,
+                    cursor.getString(CACHED_NORMALIZED_NUMBER_INDEX));
+            assertEquals(updateCachedPhotoId, cursor.getLong(CACHED_PHOTO_ID_INDEX));
+            assertEquals(updateCountryIso, cursor.getString(COUNTRY_ISO_INDEX));
+            assertEquals(updateGeocodedLocation, cursor.getString(GEOCODED_LOCATION_INDEX));
+            assertEquals(updateCachedLookupUri, cursor.getString(CACHED_LOOKUP_URI_INDEX));
             cursor.close();
 
             // Test: delete
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index a362df3..356fe3c 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -26,14 +26,21 @@
 import android.net.Uri;
 import android.os.Environment;
 import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
 import android.provider.MediaStore;
 import android.provider.MediaStore.MediaColumns;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 import java.io.File;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
+import java.io.FilenameFilter;
 import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
 
 public class MediaStore_FilesTest extends AndroidTestCase {
 
@@ -43,6 +50,40 @@
     protected void setUp() throws Exception {
         super.setUp();
         mResolver = mContext.getContentResolver();
+        cleanup();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        cleanup();
+    }
+
+    void cleanup() {
+        final String testName = getClass().getCanonicalName();
+        mResolver.delete(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+                "_data LIKE ?1", new String[] {"%" + testName + "%"});
+        File ext = Environment.getExternalStorageDirectory();
+        File[] junk = ext.listFiles(new FilenameFilter() {
+
+            @Override
+            public boolean accept(File dir, String filename) {
+                return filename.contains(testName);
+            }
+        });
+        for (File f: junk) {
+            deleteAll(f);
+        }
+    }
+
+    void deleteAll(File f) {
+        if (f.isDirectory()) {
+            File [] sub = f.listFiles();
+            for (File s: sub) {
+                deleteAll(s);
+            }
+        }
+        f.delete();
     }
 
     public void testGetContentUri() {
@@ -153,6 +194,15 @@
         }
     }
 
+    String realPathFor(ParcelFileDescriptor pfd) {
+        File real = new File("/proc/self/fd/" + pfd.getFd());
+        try {
+            return real.getCanonicalPath();
+        } catch (IOException e) {
+            return null;
+        }
+    }
+
     public void testAccess() throws IOException {
         // clean up from previous run
         mResolver.delete(MediaStore.Images.Media.INTERNAL_CONTENT_URI,
@@ -255,19 +305,19 @@
 
                 // get the real path from the file descriptor (this relies on the media provider
                 // having opened the path via the real path instead of the emulated path).
-                File real = new File("/proc/self/fd/" + pfd.getFd());
                 values = new ContentValues();
-                values.put("_data", real.getCanonicalPath());
+                values.put("_data", realPathFor(pfd));
                 mResolver.update(uri, values, null, null);
                 pfd.close();
 
                 // we shouldn't be able to access this
                 try {
                     pfd = mResolver.openFileDescriptor(uri, "r");
-                    pfd.close();
-                    fail("shouldn't be here");
+                    fail("shouldn't have fd for " + realPathFor(pfd));
                 } catch (FileNotFoundException e) {
                     // expected
+                } finally {
+                    pfd.close();
                 }
             } catch (FileNotFoundException e) {
                 fail("couldn't open file");
@@ -279,6 +329,165 @@
         if (sdfile != null) {
             assertEquals(true, sdfile.delete());
         }
+
+        // test secondary storage if present
+        List<File> allpaths = getSecondaryPackageSpecificPaths(mContext);
+        List<String> trimmedPaths = new ArrayList<String>();
+
+        for (File extpath: allpaths) {
+            assertNotNull("Valid media must be inserted during CTS", extpath);
+            assertEquals("Valid media must be inserted for " + extpath
+                    + " during CTS", Environment.MEDIA_MOUNTED,
+                    Environment.getStorageState(extpath));
+
+            File child = extpath;
+            while (true) {
+                File parent = child.getParentFile();
+                if (parent == null) {
+                    fail("didn't expect to be here");
+                }
+                if (!Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(parent))) {
+                    // we went past the root
+                    String abspath = child.getAbsolutePath();
+                    if (!trimmedPaths.contains(abspath)) {
+                        trimmedPaths.add(abspath);
+                    }
+                    break;
+                }
+                child = parent;
+            }
+        }
+
+        String fileDir = Environment.getExternalStorageDirectory() +
+                "/" + getClass().getCanonicalName() + "-" + SystemClock.elapsedRealtime();
+        String fileName = fileDir + "/TestSecondary.Mp3";
+        writeFile(R.raw.testmp3_2, fileName); // file without album art
+
+
+        // insert temp file
+        values = new ContentValues();
+        values.put(MediaStore.Audio.Media.DATA, fileName);
+        values.put(MediaStore.Audio.Media.ARTIST, "Artist-" + SystemClock.elapsedRealtime());
+        values.put(MediaStore.Audio.Media.ALBUM, "Album-" + SystemClock.elapsedRealtime());
+        values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
+        Uri fileUri = mResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
+        // give media provider some time to realize there's no album art
+        //SystemClock.sleep(1000);
+        // get its album id
+        Cursor c = mResolver.query(fileUri, new String[] { MediaStore.Audio.Media.ALBUM_ID},
+                null, null, null);
+        assertTrue(c.moveToFirst());
+        int albumid = c.getInt(0);
+        Uri albumArtUriBase = Uri.parse("content://media/external/audio/albumart");
+        Uri albumArtUri = ContentUris.withAppendedId(albumArtUriBase, albumid);
+        try {
+            pfd = mResolver.openFileDescriptor(albumArtUri, "r");
+            fail("no album art, shouldn't be here. Got: " + realPathFor(pfd));
+        } catch (Exception e) {
+            // expected
+        }
+
+        // replace file with one that has album art
+        writeFile(R.raw.testmp3, fileName); // file with album art
+
+        for (String s: trimmedPaths) {
+            File dir = new File(s + "/foobardir-" + SystemClock.elapsedRealtime());
+            assertFalse("please remove " + dir.getAbsolutePath()
+                    + " before running", dir.exists());
+            File file = new File(dir, "foobar");
+            values = new ContentValues();
+            values.put(MediaStore.Audio.Media.ALBUM_ID, albumid);
+            values.put(MediaStore.Audio.Media.DATA, file.getAbsolutePath());
+            mResolver.insert(albumArtUriBase, values);
+            try {
+                pfd = mResolver.openFileDescriptor(albumArtUri, "r");
+                fail("shouldn't have fd for album " + albumid + ", got " + realPathFor(pfd));
+            } catch (Exception e) {
+                // expected
+            } finally {
+                pfd.close();
+            }
+            assertFalse(dir.getAbsolutePath() + " was created", dir.exists());
+        }
+        mResolver.delete(fileUri, null, null);
+        new File(fileName).delete();
+
+        // try creating files in root
+        for (String s: trimmedPaths) {
+            File dir = new File(s);
+            File file = new File(dir, "foobar.jpg");
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
+            fileUri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            assertNotNull(fileUri);
+
+            // check that adding the file doesn't cause it to be created
+            assertFalse(file.exists());
+
+            // check if opening the file for write works
+            try {
+                mResolver.openOutputStream(fileUri).close();
+                fail("shouldn't have been able to create output stream");
+            } catch (SecurityException e) {
+                // expected
+            }
+            // check that deleting the file doesn't cause it to be created
+            mResolver.delete(fileUri, null, null);
+            assertFalse(file.exists());
+        }
+
+        // try creating files in new subdir
+        for (String s: trimmedPaths) {
+            File dir = new File(s + "/foobardir");
+            File file = new File(dir, "foobar.jpg");
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, dir.getAbsolutePath());
+
+            Uri dirUri = mResolver.insert(MediaStore.Files.getContentUri("external"), values);
+            assertNotNull(dirUri);
+
+            values = new ContentValues();
+            values.put(MediaStore.Files.FileColumns.DATA, file.getAbsolutePath());
+            fileUri = mResolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
+            assertNotNull(fileUri);
+
+            // check that adding the file or its folder didn't cause either one to be created
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+
+            // check if opening the file for write works
+            try {
+                mResolver.openOutputStream(fileUri).close();
+                fail("shouldn't have been able to create output stream");
+            } catch (SecurityException e) {
+                // expected
+            }
+            // check that deleting the file or its folder doesn't cause either one to be created
+            mResolver.delete(fileUri, null, null);
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+            mResolver.delete(dirUri, null, null);
+            assertFalse(dir.exists());
+            assertFalse(file.exists());
+        }
+    }
+
+    public static List<File> getSecondaryPackageSpecificPaths(Context context) {
+        final List<File> paths = new ArrayList<File>();
+        Collections.addAll(paths, dropFirst(context.getExternalCacheDirs()));
+        Collections.addAll(paths, dropFirst(context.getExternalFilesDirs(null)));
+        Collections.addAll(
+                paths, dropFirst(context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)));
+        Collections.addAll(paths, dropFirst(context.getObbDirs()));
+        return paths;
+    }
+
+    private static File[] dropFirst(File[] before) {
+        final File[] after = new File[before.length - 1];
+        System.arraycopy(before, 1, after, 0, after.length);
+        return after;
     }
 
     private void writeFile(int resid, String path) throws IOException {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
index 3f28a34..e68286f 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_MediaTest.java
@@ -78,13 +78,6 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
-
-        String campath = Environment.getExternalStorageDirectory() + File.separator +
-                Environment.DIRECTORY_DCIM + File.separator + "Camera";
-        File camfile = new File(campath);
-        if (!camfile.exists()) {
-            assertTrue("failed to create " + campath, camfile.mkdir());
-        }
     }
 
     public void testInsertImageWithImagePath() throws Exception {
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
index 03adad7..e8a13a9 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_Images_ThumbnailsTest.java
@@ -68,13 +68,6 @@
 
         mHelper = new FileCopyHelper(mContext);
         mRowsAdded = new ArrayList<Uri>();
-
-        String campath = Environment.getExternalStorageDirectory() + File.separator +
-                Environment.DIRECTORY_DCIM + File.separator + "Camera";
-        File camfile = new File(campath);
-        if (!camfile.exists()) {
-            assertTrue("failed to create " + campath, camfile.mkdir());
-        }
     }
 
     public void testQueryInternalThumbnails() throws Exception {
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java b/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
index 833de64..d89e06c 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/CommonDatabaseUtils.java
@@ -16,8 +16,11 @@
 
 package android.provider.cts.contacts;
 
+import android.content.ContentValues;
 import android.database.Cursor;
 
+import junit.framework.Assert;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -67,4 +70,21 @@
             cursor.close();
         }
     }
+
+    /**
+     * Verifies that the number of string parameters is either zero or even, and inserts them
+     * into the provided ContentValues object as a set of name-value pairs. Throws an exception if
+     * the number of string parameters is odd, or a single null parameter was provided.
+     *
+     * @param values ContentValues object to insert name-value pairs into
+     * @param extras Zero or even number of string parameters
+     */
+    public static void extrasVarArgsToValues(ContentValues values, String... extras) {
+        Assert.assertNotNull(extras);
+        // Check that the number of provided string parameters is even.
+        Assert.assertEquals(0, extras.length % 2);
+        for (int i = 0; i < extras.length; i += 2) {
+            values.put(extras[i], extras[i + 1]);
+        }
+    }
 }
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
index 2a53781..f6d67b9 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/ContactUtil.java
@@ -22,6 +22,9 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
+import android.provider.ContactsContract.AggregationExceptions;
+
+import junit.framework.Assert;
 
 /**
  * Convenience methods for operating on the Contacts table.
@@ -71,4 +74,64 @@
         }
         return CommonDatabaseUtils.NOT_FOUND;
     }
+
+    /**
+     * Verifies that the number of object parameters is either zero or even, inserts them
+     * into a new ContentValues object as a set of name-value pairs, and returns the newly created
+     * ContentValues object. Throws an exception if the number of string parameters is odd, or a
+     * single null parameter was provided.
+     *
+     * @param namesAndValues Zero or even number of object parameters to convert into name-value
+     * pairs
+     *
+     * @return newly created ContentValues containing the provided name-value pairs
+     */
+    public static ContentValues newContentValues(Object... namesAndValues) {
+        // Checks that the number of provided parameters is zero or even.
+        Assert.assertEquals(0, namesAndValues.length % 2);
+        final ContentValues contentValues = new ContentValues();
+        for (int i = 0; i < namesAndValues.length - 1; i += 2) {
+            Assert.assertNotNull(namesAndValues[i]);
+            final String name = namesAndValues[i].toString();
+            final Object value = namesAndValues[i + 1];
+            if (value == null) {
+                contentValues.putNull(name);
+            } else if (value instanceof String) {
+                contentValues.put(name, (String) value);
+            } else if (value instanceof Integer) {
+                contentValues.put(name, (Integer) value);
+            } else if (value instanceof Long) {
+                contentValues.put(name, (Long) value);
+            } else {
+                Assert.fail("Unsupported value type: " + value.getClass().getSimpleName() + " for "
+                    + " name: " + name);
+            }
+        }
+        return contentValues;
+    }
+
+    /**
+     * Updates the content resolver with two given raw contact ids and an aggregation type to
+     * manually trigger the forced aggregation, splitting of two raw contacts or specify that
+     * the provider should automatically decide whether or not to aggregate the two raw contacts.
+     *
+     * @param resolver ContentResolver from a valid context
+     * @param type One of the following aggregation exception types:
+     * {@link AggregationExceptions#TYPE_AUTOMATIC},
+     * {@link AggregationExceptions#TYPE_KEEP_SEPARATE},
+     * {@link AggregationExceptions#TYPE_KEEP_TOGETHER}
+     * @param rawContactId1 Id of the first raw contact
+     * @param rawContactId2 Id of the second raw contact
+     */
+    public static void setAggregationException(ContentResolver resolver, int type,
+        long rawContactId1, long rawContactId2) {
+        ContentValues values = new ContentValues();
+        values.put(AggregationExceptions.RAW_CONTACT_ID1, rawContactId1);
+        values.put(AggregationExceptions.RAW_CONTACT_ID2, rawContactId2);
+        values.put(AggregationExceptions.TYPE, type);
+        // Actually set the aggregation exception in the contacts database, and check that a
+        // single row was updated.
+        Assert.assertEquals(1, resolver.update(AggregationExceptions.CONTENT_URI, values, null,
+                  null));
+  }
 }
diff --git a/tests/tests/renderscript/Android.mk b/tests/tests/renderscript/Android.mk
index e5ef654..77dc210 100644
--- a/tests/tests/renderscript/Android.mk
+++ b/tests/tests/renderscript/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_JNI_SHARED_LIBRARIES := libcoremathtestcpp_jni
 
diff --git a/tests/tests/renderscript/AndroidManifest.xml b/tests/tests/renderscript/AndroidManifest.xml
index 49fca1e..2a23090 100644
--- a/tests/tests/renderscript/AndroidManifest.xml
+++ b/tests/tests/renderscript/AndroidManifest.xml
@@ -27,9 +27,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of Renderscript component"/>
+                     android:label="CTS tests of Renderscript component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
index c2c7275..21f4417 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
@@ -58,16 +58,10 @@
             bv[i] = (byte)r.nextInt(256);
         }
 
-        Type.Builder tb = new Type.Builder(mRS, Element.U8(mRS));
-        tb.setX(w);
-        tb.setY(h);
-        ay = Allocation.createTyped(mRS, tb.create());
-
-        tb = new Type.Builder(mRS, Element.U8(mRS));
-        tb.setX(w >> 1);
-        tb.setY(h >> 1);
-        au = Allocation.createTyped(mRS, tb.create());
-        av = Allocation.createTyped(mRS, tb.create());
+        ay = Allocation.createTyped(mRS, Type.createXY(mRS, Element.U8(mRS), w, h));
+        final Type tuv = Type.createXY(mRS, Element.U8(mRS), w >> 1, h >> 1);
+        au = Allocation.createTyped(mRS, tuv);
+        av = Allocation.createTyped(mRS, tuv);
 
         ay.copyFrom(by);
         au.copyFrom(bu);
@@ -75,11 +69,7 @@
     }
 
     public Allocation makeOutput() {
-        Type.Builder tb = new Type.Builder(mRS, Element.RGBA_8888(mRS));
-        tb.setX(width);
-        tb.setY(height);
-        Type t = tb.create();
-        return Allocation.createTyped(mRS, t);
+        return Allocation.createTyped(mRS, Type.createXY(mRS, Element.RGBA_8888(mRS), width, height));
     }
 
     // Test for the API 17 conversion path
diff --git a/tests/tests/rscpp/Android.mk b/tests/tests/rscpp/Android.mk
index 6f01cab..1f32dd5 100644
--- a/tests/tests/rscpp/Android.mk
+++ b/tests/tests/rscpp/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 LOCAL_JNI_SHARED_LIBRARIES := librscpptest_jni
 
diff --git a/tests/tests/rscpp/AndroidManifest.xml b/tests/tests/rscpp/AndroidManifest.xml
index b3ab43a..c014382 100644
--- a/tests/tests/rscpp/AndroidManifest.xml
+++ b/tests/tests/rscpp/AndroidManifest.xml
@@ -23,9 +23,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of RenderScript C++ component"/>
+                     android:label="CTS tests of RenderScript C++ component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/rsg/Android.mk b/tests/tests/rsg/Android.mk
index 9ff554c..c58a4b0 100644
--- a/tests/tests/rsg/Android.mk
+++ b/tests/tests/rsg/Android.mk
@@ -25,9 +25,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/rsg/AndroidManifest.xml b/tests/tests/rsg/AndroidManifest.xml
index 886a395..031cbc2 100644
--- a/tests/tests/rsg/AndroidManifest.xml
+++ b/tests/tests/rsg/AndroidManifest.xml
@@ -27,9 +27,12 @@
     </application>
 
     <!-- This is a self-instrumenting test package. -->
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of Renderscript Graphics component"/>
+                     android:label="CTS tests of Renderscript Graphics component">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/sax/Android.mk b/tests/tests/sax/Android.mk
index 5270ae5..2ed7644 100644
--- a/tests/tests/sax/Android.mk
+++ b/tests/tests/sax/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
@@ -31,4 +29,6 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/sax/AndroidManifest.xml b/tests/tests/sax/AndroidManifest.xml
index 4fbf840..d1a6f91 100644
--- a/tests/tests/sax/AndroidManifest.xml
+++ b/tests/tests/sax/AndroidManifest.xml
@@ -22,8 +22,11 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.sax"/>
+                     android:label="CTS tests of android.sax">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
diff --git a/tests/tests/security/Android.mk b/tests/tests/security/Android.mk
index f1a6bfb..d0fefa1 100644
--- a/tests/tests/security/Android.mk
+++ b/tests/tests/security/Android.mk
@@ -18,8 +18,6 @@
 
 LOCAL_MODULE_TAGS := tests
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner guava
 
 LOCAL_JNI_SHARED_LIBRARIES := libctssecurity_jni
@@ -32,8 +30,6 @@
 
 LOCAL_SDK_VERSION := current
 
-LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
-
 include $(BUILD_CTS_PACKAGE)
 
 include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml
index 101c01c..da95e5c 100644
--- a/tests/tests/security/AndroidManifest.xml
+++ b/tests/tests/security/AndroidManifest.xml
@@ -31,9 +31,12 @@
                  android:exported="true"/>
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of com.android.cts.stub"/>
+                     android:label="CTS tests of com.android.cts.security">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index 6bbc426..2f3fb79 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -15,27 +15,28 @@
  */
 
 #include <jni.h>
-#include <netlink.h>
-#include <sock_diag.h>
+#include <linux/netlink.h>
+#include <linux/sock_diag.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <sys/types.h>
 #include <sys/syscall.h>
+#include <unistd.h>
 #include <sys/prctl.h>
 #include <sys/ptrace.h>
 #include <sys/wait.h>
 #include <signal.h>
-#include <unistd.h>
-#include <errno.h>
-
-#define PASSED 0
-#define UNKNOWN_ERROR -1
 #include <stdlib.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <cutils/log.h>
 #include <linux/perf_event.h>
+#include <errno.h>
+#include <inttypes.h>
+
+#define PASSED 0
+#define UNKNOWN_ERROR -1
 
 /*
  * Returns true iff this device is vulnerable to CVE-2013-2094.
@@ -85,142 +86,6 @@
     return true;
 }
 
-#define SEARCH_SIZE 0x4000
-
-static int secret;
-
-static bool isValidChildAddress(pid_t child, uintptr_t addr) {
-    long word;
-    long ret = syscall(__NR_ptrace, PTRACE_PEEKDATA, child, addr, &word);
-    return (ret == 0);
-}
-
-/* A lazy, do nothing child. GET A JOB. */
-static void child() {
-    int res;
-    ALOGE("in child");
-    secret = 0xbaadadd4;
-    res = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
-    if (res != 0) {
-        ALOGE("prctl failed");
-    }
-    res = ptrace(PTRACE_TRACEME, 0, 0, 0);
-    if (res != 0) {
-        ALOGE("child ptrace failed");
-    }
-    signal(SIGSTOP, SIG_IGN);
-    kill(getpid(), SIGSTOP);
-}
-
-static jboolean parent(pid_t child) {
-    int status;
-    // Wait for the child to suspend itself so we can trace it.
-    waitpid(child, &status, 0);
-    jboolean result = true;
-
-    uintptr_t addr;
-    for (addr = 0x00000000; addr < 0xFFFF1000; addr+=SEARCH_SIZE) {
-        if (isValidChildAddress(child, addr)) {
-            // Don't scribble on our memory.
-            // (which has the same mapping as our child)
-            // We don't want to corrupt ourself.
-            continue;
-        }
-
-        errno = 0;
-        syscall(__NR_ptrace, PTRACE_PEEKDATA, child, &secret, addr);
-        if (errno == 0) {
-            result = false;
-            // We found an address which isn't in our our, or our child's,
-            // address space, but yet which is still writable. Scribble
-            // all over it.
-            ALOGE("parent: found writable at %x", addr);
-            uintptr_t addr2;
-            for (addr2 = addr; addr2 < addr + SEARCH_SIZE; addr2++) {
-                syscall(__NR_ptrace, PTRACE_PEEKDATA, child, &secret, addr2);
-            }
-        }
-    }
-
-    ptrace(PTRACE_DETACH, child, 0, 0);
-    return result;
-}
-
-/*
- * Prior to https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/arch/arm/include/asm/uaccess.h?id=8404663f81d212918ff85f493649a7991209fa04
- * there was a flaw in the kernel's handling of get_user and put_user
- * requests. Normally, get_user and put_user are supposed to guarantee
- * that reads/writes outside the process's address space are not
- * allowed.
- *
- * In this test, we use prctl(PTRACE_PEEKDATA) to force a write to
- * an address outside of our address space. Without the patch applied,
- * this write succeeds, because prctl(PTRACE_PEEKDATA) uses the
- * vulnerable put_user call.
- */
-static jboolean android_security_cts_NativeCodeTest_doVrootTest(JNIEnv*, jobject)
-{
-    ALOGE("Starting doVrootTest");
-    pid_t pid = fork();
-    if (pid == -1) {
-        return false;
-    }
-
-    if (pid == 0) {
-        child();
-        exit(0);
-    }
-
-    return parent(pid);
-}
-
-static void* mmap_syscall(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
-{
-    return (void*) syscall(__NR_mmap2, addr, len, prot, flags, fd, offset);
-}
-
-#define KBASE_REG_COOKIE_TB         2
-#define KBASE_REG_COOKIE_MTP        3
-
-/*
- * Returns true if the device is immune to CVE-2014-1710,
- * false if the device is vulnerable.
- */
-static jboolean android_security_cts_NativeCodeTest_doCVE20141710Test(JNIEnv*, jobject)
-{
-    jboolean result = false;
-    int fd = open("/dev/mali0", O_RDWR);
-    if (fd < 0) {
-        return true; /* not vulnerable */
-    }
-
-    void* a = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_MTP);
-    void* b = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_TB);
-
-    if (a == MAP_FAILED) {
-        result = true; /* assume not vulnerable */
-        goto done;
-    }
-
-    if (b == MAP_FAILED) {
-        result = true; /* assume not vulnerable */
-        goto done;
-    }
-
-    /* mprotect should return an error if not vulnerable */
-    result = (mprotect(b, 0x1000, PROT_READ | PROT_WRITE) == -1);
-
- done:
-    if (a != MAP_FAILED) {
-        munmap(a, 0x1000);
-    }
-    if (b != MAP_FAILED) {
-        munmap(b, 0x1000);
-    }
-    close(fd);
-    return result;
-}
-
 /*
  * Will hang if vulnerable, return 0 if successful, -1 on unforseen
  * error.
@@ -288,6 +153,147 @@
     return UNKNOWN_ERROR;
 }
 
+#define SEARCH_SIZE 0x4000
+
+static int secret;
+
+static bool isValidChildAddress(pid_t child, uintptr_t addr) {
+    long word;
+    long ret = syscall(__NR_ptrace, PTRACE_PEEKDATA, child, addr, &word);
+    return (ret == 0);
+}
+
+/* A lazy, do nothing child. GET A JOB. */
+static void child() {
+    int res;
+    ALOGE("in child");
+    secret = 0xbaadadd4;
+    res = prctl(PR_SET_DUMPABLE, 1, 0, 0, 0);
+    if (res != 0) {
+        ALOGE("prctl failed");
+    }
+    res = ptrace(PTRACE_TRACEME, 0, 0, 0);
+    if (res != 0) {
+        ALOGE("child ptrace failed");
+    }
+    signal(SIGSTOP, SIG_IGN);
+    kill(getpid(), SIGSTOP);
+}
+
+static jboolean parent(pid_t child) {
+    int status;
+    // Wait for the child to suspend itself so we can trace it.
+    waitpid(child, &status, 0);
+    jboolean result = true;
+
+    uintptr_t addr;
+    for (addr = 0x00000000; addr < 0xFFFF1000; addr+=SEARCH_SIZE) {
+        if (isValidChildAddress(child, addr)) {
+            // Don't scribble on our memory.
+            // (which has the same mapping as our child)
+            // We don't want to corrupt ourself.
+            continue;
+        }
+
+        errno = 0;
+        syscall(__NR_ptrace, PTRACE_PEEKDATA, child, &secret, addr);
+        if (errno == 0) {
+            result = false;
+            // We found an address which isn't in our our, or our child's,
+            // address space, but yet which is still writable. Scribble
+            // all over it.
+            ALOGE("parent: found writable at %" PRIxPTR, addr);
+            uintptr_t addr2;
+            for (addr2 = addr; addr2 < addr + SEARCH_SIZE; addr2++) {
+                syscall(__NR_ptrace, PTRACE_PEEKDATA, child, &secret, addr2);
+            }
+        }
+    }
+
+    ptrace(PTRACE_DETACH, child, 0, 0);
+    return result;
+}
+
+/*
+ * Prior to https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/arch/arm/include/asm/uaccess.h?id=8404663f81d212918ff85f493649a7991209fa04
+ * there was a flaw in the kernel's handling of get_user and put_user
+ * requests. Normally, get_user and put_user are supposed to guarantee
+ * that reads/writes outside the process's address space are not
+ * allowed.
+ *
+ * In this test, we use prctl(PTRACE_PEEKDATA) to force a write to
+ * an address outside of our address space. Without the patch applied,
+ * this write succeeds, because prctl(PTRACE_PEEKDATA) uses the
+ * vulnerable put_user call.
+ */
+static jboolean android_security_cts_NativeCodeTest_doVrootTest(JNIEnv*, jobject)
+{
+    ALOGE("Starting doVrootTest");
+    pid_t pid = fork();
+    if (pid == -1) {
+        return false;
+    }
+
+    if (pid == 0) {
+        child();
+        exit(0);
+    }
+
+    return parent(pid);
+}
+
+static void* mmap_syscall(void* addr, size_t len, int prot, int flags, int fd, off_t offset)
+{
+#ifdef __LP64__
+    return mmap(addr, len, prot, flags, fd, offset);
+#else
+    return (void*) syscall(__NR_mmap2, addr, len, prot, flags, fd, offset);
+#endif
+}
+
+#define KBASE_REG_COOKIE_TB         2
+#define KBASE_REG_COOKIE_MTP        3
+
+/*
+ * Returns true if the device is immune to CVE-2014-1710,
+ * false if the device is vulnerable.
+ */
+static jboolean android_security_cts_NativeCodeTest_doCVE20141710Test(JNIEnv*, jobject)
+{
+    jboolean result = false;
+    int fd = open("/dev/mali0", O_RDWR);
+    if (fd < 0) {
+        return true; /* not vulnerable */
+    }
+
+    void* a = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_MTP);
+    void* b = mmap_syscall(NULL, 0x1000, PROT_READ, MAP_SHARED, fd, KBASE_REG_COOKIE_TB);
+
+    if (a == MAP_FAILED) {
+        result = true; /* assume not vulnerable */
+        goto done;
+    }
+
+    if (b == MAP_FAILED) {
+        result = true; /* assume not vulnerable */
+        goto done;
+    }
+
+    /* mprotect should return an error if not vulnerable */
+    result = (mprotect(b, 0x1000, PROT_READ | PROT_WRITE) == -1);
+
+ done:
+    if (a != MAP_FAILED) {
+        munmap(a, 0x1000);
+    }
+    if (b != MAP_FAILED) {
+        munmap(b, 0x1000);
+    }
+    close(fd);
+    return result;
+}
+
+
 static JNINativeMethod gMethods[] = {
     {  "doPerfEventTest", "()Z",
             (void *) android_security_cts_NativeCodeTest_doPerfEventTest },
diff --git a/tests/tests/security/jni/netlink.h b/tests/tests/security/jni/netlink.h
deleted file mode 100644
index b5567b0..0000000
--- a/tests/tests/security/jni/netlink.h
+++ /dev/null
@@ -1,170 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   This header was automatically generated from a Linux kernel header
- ***   of the same name, to make information necessary for userspace to
- ***   call into the kernel available to libc.  It contains only constants,
- ***   structures, and macros generated from the original header, and thus,
- ***   contains no copyrightable information.
- ***
- ***   To edit the content of this header, modify the corresponding
- ***   source file (e.g. under external/kernel-headers/original/) then
- ***   run bionic/libc/kernel/tools/update_all.py
- ***
- ***   Any manual change here will be lost the next time this script will
- ***   be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI__LINUX_NETLINK_H
-#define _UAPI__LINUX_NETLINK_H
-#include <linux/kernel.h>
-#include <linux/socket.h>
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#include <linux/types.h>
-#define NETLINK_ROUTE 0
-#define NETLINK_UNUSED 1
-#define NETLINK_USERSOCK 2
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_FIREWALL 3
-#define NETLINK_SOCK_DIAG 4
-#define NETLINK_NFLOG 5
-#define NETLINK_XFRM 6
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_SELINUX 7
-#define NETLINK_ISCSI 8
-#define NETLINK_AUDIT 9
-#define NETLINK_FIB_LOOKUP 10
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_CONNECTOR 11
-#define NETLINK_NETFILTER 12
-#define NETLINK_IP6_FW 13
-#define NETLINK_DNRTMSG 14
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_KOBJECT_UEVENT 15
-#define NETLINK_GENERIC 16
-#define NETLINK_SCSITRANSPORT 18
-#define NETLINK_ECRYPTFS 19
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_RDMA 20
-#define NETLINK_CRYPTO 21
-#define NETLINK_INET_DIAG NETLINK_SOCK_DIAG
-#define MAX_LINKS 32
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct sockaddr_nl {
- __kernel_sa_family_t nl_family;
- unsigned short nl_pad;
- __u32 nl_pid;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 nl_groups;
-};
-struct nlmsghdr {
- __u32 nlmsg_len;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u16 nlmsg_type;
- __u16 nlmsg_flags;
- __u32 nlmsg_seq;
- __u32 nlmsg_pid;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-};
-#define NLM_F_REQUEST 1
-#define NLM_F_MULTI 2
-#define NLM_F_ACK 4
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_ECHO 8
-#define NLM_F_DUMP_INTR 16
-#define NLM_F_ROOT 0x100
-#define NLM_F_MATCH 0x200
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_ATOMIC 0x400
-#define NLM_F_DUMP (NLM_F_ROOT|NLM_F_MATCH)
-#define NLM_F_REPLACE 0x100
-#define NLM_F_EXCL 0x200
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLM_F_CREATE 0x400
-#define NLM_F_APPEND 0x800
-#define NLMSG_ALIGNTO 4U
-#define NLMSG_ALIGN(len) ( ((len)+NLMSG_ALIGNTO-1) & ~(NLMSG_ALIGNTO-1) )
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_HDRLEN ((int) NLMSG_ALIGN(sizeof(struct nlmsghdr)))
-#define NLMSG_LENGTH(len) ((len) + NLMSG_HDRLEN)
-#define NLMSG_SPACE(len) NLMSG_ALIGN(NLMSG_LENGTH(len))
-#define NLMSG_DATA(nlh) ((void*)(((char*)nlh) + NLMSG_LENGTH(0)))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len),   (struct nlmsghdr*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len)))
-#define NLMSG_OK(nlh,len) ((len) >= (int)sizeof(struct nlmsghdr) &&   (nlh)->nlmsg_len >= sizeof(struct nlmsghdr) &&   (nlh)->nlmsg_len <= (len))
-#define NLMSG_PAYLOAD(nlh,len) ((nlh)->nlmsg_len - NLMSG_SPACE((len)))
-#define NLMSG_NOOP 0x1
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLMSG_ERROR 0x2
-#define NLMSG_DONE 0x3
-#define NLMSG_OVERRUN 0x4
-#define NLMSG_MIN_TYPE 0x10
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct nlmsgerr {
- int error;
- struct nlmsghdr msg;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_ADD_MEMBERSHIP 1
-#define NETLINK_DROP_MEMBERSHIP 2
-#define NETLINK_PKTINFO 3
-#define NETLINK_BROADCAST_ERROR 4
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NETLINK_NO_ENOBUFS 5
-#define NETLINK_RX_RING 6
-#define NETLINK_TX_RING 7
-struct nl_pktinfo {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 group;
-};
-struct nl_mmap_req {
- unsigned int nm_block_size;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- unsigned int nm_block_nr;
- unsigned int nm_frame_size;
- unsigned int nm_frame_nr;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct nl_mmap_hdr {
- unsigned int nm_status;
- unsigned int nm_len;
- __u32 nm_group;
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u32 nm_pid;
- __u32 nm_uid;
- __u32 nm_gid;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-enum nl_mmap_status {
- NL_MMAP_STATUS_UNUSED,
- NL_MMAP_STATUS_RESERVED,
- NL_MMAP_STATUS_VALID,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- NL_MMAP_STATUS_COPY,
- NL_MMAP_STATUS_SKIP,
-};
-#define NL_MMAP_MSG_ALIGNMENT NLMSG_ALIGNTO
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NL_MMAP_MSG_ALIGN(sz) __ALIGN_KERNEL(sz, NL_MMAP_MSG_ALIGNMENT)
-#define NL_MMAP_HDRLEN NL_MMAP_MSG_ALIGN(sizeof(struct nl_mmap_hdr))
-#define NET_MAJOR 36
-enum {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- NETLINK_UNCONNECTED = 0,
- NETLINK_CONNECTED,
-};
-struct nlattr {
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- __u16 nla_len;
- __u16 nla_type;
-};
-#define NLA_F_NESTED (1 << 15)
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLA_F_NET_BYTEORDER (1 << 14)
-#define NLA_TYPE_MASK ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER)
-#define NLA_ALIGNTO 4
-#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-#define NLA_HDRLEN ((int) NLA_ALIGN(sizeof(struct nlattr)))
-#endif
diff --git a/tests/tests/security/jni/sock_diag.h b/tests/tests/security/jni/sock_diag.h
deleted file mode 100644
index 0dc2902..0000000
--- a/tests/tests/security/jni/sock_diag.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/****************************************************************************
- ****************************************************************************
- ***
- ***   This header was automatically generated from a Linux kernel header
- ***   of the same name, to make information necessary for userspace to
- ***   call into the kernel available to libc.  It contains only constants,
- ***   structures, and macros generated from the original header, and thus,
- ***   contains no copyrightable information.
- ***
- ***   To edit the content of this header, modify the corresponding
- ***   source file (e.g. under external/kernel-headers/original/) then
- ***   run bionic/libc/kernel/tools/update_all.py
- ***
- ***   Any manual change here will be lost the next time this script will
- ***   be run. You've been warned!
- ***
- ****************************************************************************
- ****************************************************************************/
-#ifndef _UAPI__SOCK_DIAG_H__
-#define _UAPI__SOCK_DIAG_H__
-#include <linux/types.h>
-#define SOCK_DIAG_BY_FAMILY 20
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-struct sock_diag_req {
- __u8 sdiag_family;
- __u8 sdiag_protocol;
-};
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
-enum {
- SK_MEMINFO_RMEM_ALLOC,
- SK_MEMINFO_RCVBUF,
- SK_MEMINFO_WMEM_ALLOC,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- SK_MEMINFO_SNDBUF,
- SK_MEMINFO_FWD_ALLOC,
- SK_MEMINFO_WMEM_QUEUED,
- SK_MEMINFO_OPTMEM,
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
- SK_MEMINFO_BACKLOG,
- SK_MEMINFO_VARS,
-};
-#endif
-/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 6634310..5e3ffa4 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -68,7 +68,13 @@
     private static native boolean doPerfEventTest2();
 
     /**
-     * ANDROID-11234878
+     * Hangs if device is vulnerable to CVE-2013-1763, returns -1 if
+     * unexpected error occurs, 0 otherwise.
+     */
+    private static native int doSockDiagTest();
+
+    /**
+     * ANDROID-11234878 / CVE-2013-6282
      *
      * Returns true if the device is patched against the vroot
      * vulnerability. Returns false if there was some problem running
@@ -90,10 +96,4 @@
      * false if the device is vulnerable.
      */
     private static native boolean doCVE20141710Test();
-
-    /**
-     * Hangs if device is vulnerable to CVE-2013-1763, returns -1 if
-     * unexpected error occurs, 0 otherwise.
-     */
-    private static native int doSockDiagTest();
 }
diff --git a/tests/tests/speech/Android.mk b/tests/tests/speech/Android.mk
index 60acf90..75f7e4c 100755
--- a/tests/tests/speech/Android.mk
+++ b/tests/tests/speech/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/speech/AndroidManifest.xml b/tests/tests/speech/AndroidManifest.xml
index 93576b1..788f7cc 100755
--- a/tests/tests/speech/AndroidManifest.xml
+++ b/tests/tests/speech/AndroidManifest.xml
@@ -25,9 +25,12 @@
 
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.speech"/>
+                     android:label="CTS tests of android.speech">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/telephony/Android.mk b/tests/tests/telephony/Android.mk
index e7a3336..a64fa11 100644
--- a/tests/tests/telephony/Android.mk
+++ b/tests/tests/telephony/Android.mk
@@ -22,7 +22,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner telephony-common mms-common
+LOCAL_JAVA_LIBRARIES := telephony-common mms-common
 
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
@@ -32,7 +32,8 @@
 
 LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
 
-# uncomment when dalvik.annotation.Test* are removed or part of SDK
-# #LOCAL_SDK_VERSION := current
+# uncomment when b/13250611 is fixed
+#LOCAL_SDK_VERSION := current
+LOCAL_JAVA_LIBRARIES += android.test.runner
 
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/telephony/AndroidManifest.xml b/tests/tests/telephony/AndroidManifest.xml
index 1dfd68d..36d1e5e 100644
--- a/tests/tests/telephony/AndroidManifest.xml
+++ b/tests/tests/telephony/AndroidManifest.xml
@@ -22,9 +22,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.telephony"/>
+                     android:label="CTS tests of android.telephony">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 3fc5b28..d96743c 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -301,4 +301,29 @@
         assertTrue(PhoneNumberUtils.isWellFormedSmsAddress("+17005554141"));
         assertFalse(PhoneNumberUtils.isWellFormedSmsAddress("android"));
     }
+
+    public void testIsUriNumber() {
+        assertTrue(PhoneNumberUtils.isUriNumber("foo@google.com"));
+        assertTrue(PhoneNumberUtils.isUriNumber("xyz@zzz.org"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+15103331245"));
+        assertFalse(PhoneNumberUtils.isUriNumber("+659231235"));
+    }
+
+    public void testGetUsernameFromUriNumber() {
+        assertEquals("john", PhoneNumberUtils.getUsernameFromUriNumber("john@myorg.com"));
+        assertEquals("tim_123", PhoneNumberUtils.getUsernameFromUriNumber("tim_123@zzz.org"));
+        assertEquals("5103331245", PhoneNumberUtils.getUsernameFromUriNumber("5103331245"));
+    }
+
+    public void testConvertAndStrip() {
+        // Untouched number.
+        assertEquals("123456789", PhoneNumberUtils.convertAndStrip("123456789"));
+        // Dashes should be stripped, legal separators (i.e. wild character remain untouched)
+        assertEquals("+15103331245*123", PhoneNumberUtils.convertAndStrip("+1-510-333-1245*123"));
+        // Arabic digits should be converted
+        assertEquals("5567861616", PhoneNumberUtils.convertAndStrip("٥‎٥‎٦‎٧‎٨‎٦‎١‎٦‎١‎٦‎"));
+        // Arabic digits converted and spaces stripped
+        assertEquals("5567861616", PhoneNumberUtils.convertAndStrip("٥‎ ٥‎٦‎ ٧‎ ٨‎ ٦‎ ١‎ ٦‎ ١‎ ٦‎"));
+
+    }
 }
diff --git a/tests/tests/text/AndroidManifest.xml b/tests/tests/text/AndroidManifest.xml
index 16ba2d9..63f6d59 100644
--- a/tests/tests/text/AndroidManifest.xml
+++ b/tests/tests/text/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.text"/>
+                     android:label="CTS tests of android.text">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/textureview/Android.mk b/tests/tests/textureview/Android.mk
index 30cc4ff..f85a738 100644
--- a/tests/tests/textureview/Android.mk
+++ b/tests/tests/textureview/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/textureview/AndroidManifest.xml b/tests/tests/textureview/AndroidManifest.xml
index 63cd233..9ec3f17 100644
--- a/tests/tests/textureview/AndroidManifest.xml
+++ b/tests/tests/textureview/AndroidManifest.xml
@@ -25,7 +25,10 @@
 
     <instrumentation
         android:targetPackage="com.android.cts.textureview"
-        android:name="android.test.InstrumentationCtsTestRunner" />
+        android:name="android.support.test.runner.AndroidJUnitRunner" >
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
     <application
         android:label="@string/app_name"
diff --git a/tests/tests/theme/Android.mk b/tests/tests/theme/Android.mk
index 5846426..134af7c 100644
--- a/tests/tests/theme/Android.mk
+++ b/tests/tests/theme/Android.mk
@@ -24,9 +24,6 @@
 # When built, explicitly put it in the data partition.
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-# All tests should include android.test.runner.
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/theme/AndroidManifest.xml b/tests/tests/theme/AndroidManifest.xml
index 0edc836..8232d2b 100644
--- a/tests/tests/theme/AndroidManifest.xml
+++ b/tests/tests/theme/AndroidManifest.xml
@@ -19,12 +19,15 @@
 
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <application>
-        <uses-library android:name="android.test.runner" />        
-        <activity android:name="android.theme.cts.DeviceDefaultActivity" />        
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="android.theme.cts.DeviceDefaultActivity" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
             android:targetPackage="com.android.cts.theme"
-            android:label="CTS tests for themes"/>
+            android:label="CTS tests for themes">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/tv/Android.mk b/tests/tests/tv/Android.mk
new file mode 100644
index 0000000..b45129d
--- /dev/null
+++ b/tests/tests/tv/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsTvTestCases
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
new file mode 100644
index 0000000..f00361e
--- /dev/null
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.tv.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <application>
+        <uses-library android:name="android.test.runner"/>
+        <service android:name="android.tv.cts.TvInputManagerTest$MockTvInputService"
+                android:permission="android.permission.BIND_TV_INPUT">
+            <intent-filter>
+                <action android:name="android.tv.TvInputService" />
+            </intent-filter>
+        </service>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+            android:targetPackage="android.tv.cts"
+            android:label="Tests for the TV APIs.">
+        <meta-data android:name="listener"
+                android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
diff --git a/tests/tests/tv/src/android/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/tv/cts/TvInputManagerTest.java
new file mode 100644
index 0000000..931b38f
--- /dev/null
+++ b/tests/tests/tv/src/android/tv/cts/TvInputManagerTest.java
@@ -0,0 +1,217 @@
+/*
+ * 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.tv.cts;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.tv.TvInputInfo;
+import android.tv.TvInputManager;
+import android.tv.TvInputManager.Session;
+import android.tv.TvInputManager.SessionCreateCallback;
+import android.tv.TvInputManager.TvInputListener;
+import android.tv.TvInputService;
+import android.tv.TvInputService.TvInputSessionImpl;
+import android.util.Log;
+import android.view.Surface;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Test for {@link android.tv.TvInputManager}.
+ */
+public class TvInputManagerTest extends AndroidTestCase {
+    private static final String TAG = "TvInputManagerTest";
+    private static final long OPERATION_TIMEOUT_MS = 500;
+
+    private TvInputManager mManager;
+    private Session mSession;
+    private SessionCreateCallback mSessionCallback;
+    private boolean mAvailability;
+    private TvInputListener mTvInputListener;
+    private HandlerThread mCallbackThread;
+    private Handler mCallbackHandler;
+    private CountDownLatch mAvailabilityChangeLatch;
+    private CountDownLatch mSessionCreationLatch;
+
+    public TvInputManagerTest() {
+        mSessionCallback = new MockSessionCreateCallback();
+    }
+
+    @Override
+    public void setContext(Context context) {
+        super.setContext(context);
+        if (MockTvInputService.sComponentName == null) {
+            MockTvInputService.sComponentName = new ComponentName(
+                    context.getPackageName(), MockTvInputService.class.getName());
+        }
+        mManager = (TvInputManager) context.getSystemService(Context.TV_INPUT_SERVICE);
+    }
+
+    @Override
+    protected void setUp() {
+        mAvailability = false;
+        mSession = null;
+        MockTvInputService.sInstance = null;
+        MockTvInputService.sSession = null;
+        MockTvInputService.sFailOnCreateSession = false;
+        mCallbackThread = new HandlerThread("CallbackThread");
+        mCallbackThread.start();
+        mCallbackHandler = new Handler(mCallbackThread.getLooper());
+    }
+
+    @Override
+    protected void tearDown() throws InterruptedException {
+        if (mTvInputListener != null) {
+            mManager.unregisterListener(MockTvInputService.sComponentName, mTvInputListener);
+            mTvInputListener = null;
+        }
+        mCallbackThread.quit();
+        mCallbackThread.join();
+    }
+
+    public void testGetTvInputList() throws Exception {
+        // Check if the returned list includes the mock tv input service.
+        boolean mockServiceInstalled = false;
+        for (TvInputInfo info : mManager.getTvInputList()) {
+            if (MockTvInputService.sComponentName.equals(info.getComponent())) {
+                mockServiceInstalled = true;
+            }
+        }
+
+        // Verify the result.
+        assertTrue("Mock service must be listed", mockServiceInstalled);
+    }
+
+    public void testCreateSession() throws Exception {
+        mSessionCreationLatch = new CountDownLatch(1);
+        // Make the mock service return a session on request.
+        mManager.createSession(MockTvInputService.sComponentName, mSessionCallback,
+                mCallbackHandler);
+
+        // Verify the result.
+        assertTrue(mSessionCreationLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertNotNull(mSession);
+        mSession.release();
+    }
+
+    public void testCreateSessionFailure() throws Exception {
+        mSessionCreationLatch = new CountDownLatch(1);
+        // Make the mock service return {@code null} on request.
+        MockTvInputService.sFailOnCreateSession = true;
+        mManager.createSession(MockTvInputService.sComponentName, mSessionCallback,
+                mCallbackHandler);
+
+        // Verify the result.
+        assertTrue(mSessionCreationLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertNull(mSession);
+    }
+
+    public void testAvailabilityChanged() throws Exception {
+        // Register a listener for availability change.
+        MockTvInputService.sInstanceLatch = new CountDownLatch(1);
+        mTvInputListener = new MockTvInputListener();
+        mManager.registerListener(MockTvInputService.sComponentName, mTvInputListener,
+                mCallbackHandler);
+
+        // Make sure that the mock service is created.
+        if (MockTvInputService.sInstance == null) {
+            assertTrue(MockTvInputService.sInstanceLatch.await(
+                    OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        }
+
+        // Change the availability of the mock service.
+        mAvailability = mManager.getAvailability(MockTvInputService.sComponentName);
+        boolean newAvailiability = !mAvailability;
+        mAvailabilityChangeLatch = new CountDownLatch(1);
+        MockTvInputService.sInstance.setAvailable(newAvailiability);
+
+        // Verify the result.
+        assertTrue(mAvailabilityChangeLatch.await(OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS));
+        assertEquals(newAvailiability, mAvailability);
+    }
+
+    private class MockTvInputListener extends TvInputListener {
+        @Override
+        public void onAvailabilityChanged(ComponentName name, boolean isAvailable) {
+            assertEquals(MockTvInputService.sComponentName, name);
+            mAvailability = isAvailable;
+            if (mAvailabilityChangeLatch != null) {
+                mAvailabilityChangeLatch.countDown();
+            }
+        }
+    }
+
+    private class MockSessionCreateCallback implements SessionCreateCallback {
+        @Override
+        public void onSessionCreated(Session session) {
+            mSession = session;
+            if (mSessionCreationLatch != null) {
+                mSessionCreationLatch.countDown();
+            }
+        }
+    }
+
+    public static class MockTvInputService extends TvInputService {
+        static ComponentName sComponentName;
+        static CountDownLatch sInstanceLatch;
+        static MockTvInputService sInstance;
+        static TvInputSessionImpl sSession;
+
+        static boolean sFailOnCreateSession;
+
+        @Override
+        public void onCreate() {
+            super.onCreate();
+            sInstance = this;
+            sSession = new MockTvInputSessionImpl();
+            if (sInstanceLatch != null) {
+                sInstanceLatch.countDown();
+            }
+        }
+
+        @Override
+        public TvInputSessionImpl onCreateSession() {
+            return sFailOnCreateSession ? null : sSession;
+        }
+
+        class MockTvInputSessionImpl extends TvInputSessionImpl {
+            public MockTvInputSessionImpl() { }
+
+            @Override
+            public void onRelease() { }
+
+            @Override
+            public boolean onSetSurface(Surface surface) {
+                return false;
+            }
+
+            @Override
+            public void onSetVolume(float volume) { }
+
+            @Override
+            public boolean onTune(Uri channelUri) {
+                return false;
+            }
+        }
+    }
+}
diff --git a/tests/tests/uiautomation/Android.mk b/tests/tests/uiautomation/Android.mk
new file mode 100644
index 0000000..bb0fc19
--- /dev/null
+++ b/tests/tests/uiautomation/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsUiAutomationTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/tests/tests/uiautomation/AndroidManifest.xml b/tests/tests/uiautomation/AndroidManifest.xml
new file mode 100644
index 0000000..06b31c8
--- /dev/null
+++ b/tests/tests/uiautomation/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="android.app.cts.uiautomation">
+
+  <application android:theme="@android:style/Theme.Holo.NoActionBar" >
+
+      <uses-library android:name="android.test.runner"/>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestFirstActivity"
+          android:exported="true">
+      </activity>
+
+      <activity
+          android:name="android.app.uiautomation.cts.UiAutomationTestSecondActivity"
+          android:exported="true">
+      </activity>
+
+  </application>
+
+  <instrumentation android:name="android.support.test.uiautomator.UiAutomatorInstrumentationTestRunner"
+                   android:targetPackage="android.app.cts.uiautomation">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+
+</manifest>
diff --git a/tests/tests/uiautomation/res/layout/ui_automation_test.xml b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
new file mode 100644
index 0000000..fb9621d
--- /dev/null
+++ b/tests/tests/uiautomation/res/layout/ui_automation_test.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<ListView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/list_view"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent">
+</ListView>
diff --git a/tests/tests/uiautomation/res/values/strings.xml b/tests/tests/uiautomation/res/values/strings.xml
new file mode 100644
index 0000000..7e4e4e4
--- /dev/null
+++ b/tests/tests/uiautomation/res/values/strings.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<resources>
+
+    <string name="uiautomation_test_activity">Cheeses</string>
+
+    <string-array name="some_cheeses">
+        <item>Abbaye de Belloc</item>
+        <item>Abbaye de Belval</item>
+        <item>Abbaye de Citeaux</item>
+        <item>Abbaye du Mont des Cats</item>
+        <item>Abbot’s Gold</item>
+        <item>Acapella</item>
+        <item>Acorn</item>
+        <item>Adelost</item>
+        <item>Affidelice au Chablis</item>
+        <item>Afuega\'l Pitu</item>
+        <item>Aged Gouda</item>
+        <item>Airag</item>
+        <item>Airedale</item>
+        <item>Aisy Cendre</item>
+        <item>Allgauer Emmentaler</item>
+        <item>Babybel</item>
+        <item>Baby Swiss</item>
+        <item>Baguette Laonnaise</item>
+        <item>Bakers</item>
+        <item>Balaton</item>
+        <item>Bandal</item>
+        <item>Banon</item>
+        <item>Barry\'s Bay Cheddar</item>
+        <item>Basing</item>
+        <item>Basket Cheese</item>
+        <item>Bath Cheese</item>
+        <item>Bavarian Bergkase</item>
+        <item>Baylough</item>
+        <item>Beauvoorde</item>
+        <item>Beemster 2% Milk</item>
+    </string-array>
+
+</resources>
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
new file mode 100644
index 0000000..6d80819
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -0,0 +1,291 @@
+/*
+ * 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.app.uiautomation.cts;
+
+import android.accessibilityservice.AccessibilityServiceInfo;
+import android.app.Activity;
+import android.app.UiAutomation;
+import android.content.Intent;
+import android.view.FrameStats;
+import android.view.WindowAnimationFrameStats;
+import android.view.WindowContentFrameStats;
+import android.view.accessibility.AccessibilityWindowInfo;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.support.test.uiautomator.UiAutomatorTestCase;
+
+import java.util.List;
+
+/**
+ * Tests for the UiAutomation APIs.
+ */
+public class UiAutomationTest extends UiAutomatorTestCase {
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        AccessibilityServiceInfo info = getInstrumentation().getUiAutomation().getServiceInfo();
+        info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS;
+        getInstrumentation().getUiAutomation().setServiceInfo(info);
+    }
+
+    public void testWindowContentFrameStats() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Find the list to scroll around.
+            UiScrollable listView = new UiScrollable(new UiSelector().resourceId(
+                    "android.app.cts.uiautomation:id/list_view"));
+
+            // Scoll a bit.
+            listView.scrollToEnd(Integer.MAX_VALUE);
+            listView.scrollToBeginning(Integer.MAX_VALUE);
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowContentTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowContentFrameStatsNoAnimation() throws Exception {
+        Activity activity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start an activity.
+            Intent intent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity = getInstrumentation().startActivitySync(intent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Find the application window.
+            final int windowId = findAppWindowId(uiAutomation.getWindows());
+            assertTrue(windowId >= 0);
+
+            // Clear stats to be with a clean slate.
+            assertTrue(uiAutomation.clearWindowContentFrameStats(windowId));
+
+            // Get the frame stats.
+            WindowContentFrameStats stats = uiAutomation.getWindowContentFrameStats(windowId);
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is no data.
+            assertTrue(stats.getFrameCount() == 0);
+
+            // The start and end times are undefibed as we have no data.
+            assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+            assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        } finally {
+            // Clean up.
+            if (activity != null) {
+                activity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStats() throws Exception {
+        Activity firstActivity = null;
+        Activity secondActivity = null;
+        try {
+            UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+            // Start the frist activity.
+            Intent firstIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestFirstActivity.class);
+            firstIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            firstActivity = getInstrumentation().startActivitySync(firstIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Clear the window animation stats to be with a clean slate.
+            uiAutomation.clearWindowAnimationFrameStats();
+
+            // Start the second activity
+            Intent secondIntent = new Intent(getInstrumentation().getContext(),
+                    UiAutomationTestSecondActivity.class);
+            secondIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            secondActivity = getInstrumentation().startActivitySync(secondIntent);
+
+            // Wait for things to settle.
+            getUiDevice().waitForIdle();
+
+            // Get the frame stats.
+            WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+            // Check the frame stats...
+
+            // We should have somethong.
+            assertNotNull(stats);
+
+            // The refresh presiod is always positive.
+            assertTrue(stats.getRefreshPeriodNano() > 0);
+
+            // There is some frame data.
+            final int frameCount = stats.getFrameCount();
+            assertTrue(frameCount > 0);
+
+            // The frames are ordered in ascending order.
+            assertWindowAnimationTimestampsInAscendingOrder(stats);
+
+            // The start and end times are based on first and last frame.
+            assertEquals(stats.getStartTimeNano(), stats.getFramePresentedTimeNano(0));
+            assertEquals(stats.getEndTimeNano(), stats.getFramePresentedTimeNano(frameCount - 1));
+        } finally {
+            // Clean up.
+            if (firstActivity != null) {
+                firstActivity.finish();
+            }
+            if (secondActivity != null) {
+                secondActivity.finish();
+            }
+        }
+    }
+
+    public void testWindowAnimationFrameStatsNoAnimation() throws Exception {
+        UiAutomation uiAutomation = getInstrumentation().getUiAutomation();
+
+        // Wait for things to settle.
+        getUiDevice().waitForIdle();
+
+        // Clear the window animation stats to be with a clean slate.
+        uiAutomation.clearWindowAnimationFrameStats();
+
+        // Get the frame stats.
+        WindowAnimationFrameStats stats = uiAutomation.getWindowAnimationFrameStats();
+
+        // Check the frame stats...
+
+        // We should have somethong.
+        assertNotNull(stats);
+
+        // The refresh presiod is always positive.
+        assertTrue(stats.getRefreshPeriodNano() > 0);
+
+        // There is no data.
+        assertTrue(stats.getFrameCount() == 0);
+
+        // The start and end times are undefibed as we have no data.
+        assertEquals(stats.getStartTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+        assertEquals(stats.getEndTimeNano(), FrameStats.UNDEFINED_TIME_NANO);
+    }
+
+    private void assertWindowContentTimestampsInAscendingOrder(WindowContentFrameStats stats) {
+        long lastExpectedTimeNano = 0;
+        long lastPresentedTimeNano = 0;
+        long lastPreparedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long expectedTimeNano = stats.getFramePostedTimeNano(i);
+            assertTrue(expectedTimeNano > lastExpectedTimeNano);
+            lastExpectedTimeNano = expectedTimeNano;
+
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+
+            final long preparedTimeNano = stats.getFrameReadyTimeNano(i);
+            if (lastPreparedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (preparedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(preparedTimeNano > lastPreparedTimeNano);
+            }
+            lastPreparedTimeNano = preparedTimeNano;
+        }
+    }
+
+    private void assertWindowAnimationTimestampsInAscendingOrder(WindowAnimationFrameStats stats) {
+        long lastPresentedTimeNano = 0;
+
+        final int frameCount = stats.getFrameCount();
+        for (int i = 0; i < frameCount; i++) {
+            final long presentedTimeNano = stats.getFramePresentedTimeNano(i);
+            if (lastPresentedTimeNano == FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano == FrameStats.UNDEFINED_TIME_NANO);
+            } else if (presentedTimeNano != FrameStats.UNDEFINED_TIME_NANO) {
+                assertTrue(presentedTimeNano > lastPresentedTimeNano);
+            }
+            lastPresentedTimeNano = presentedTimeNano;
+        }
+    }
+
+    private int findAppWindowId(List<AccessibilityWindowInfo> windows) {
+        final int windowCount = windows.size();
+        for (int i = 0; i < windowCount; i++) {
+            AccessibilityWindowInfo window = windows.get(i);
+            if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
+                return window.getId();
+            }
+        }
+        return -1;
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
new file mode 100644
index 0000000..49791ab
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestFirstActivity.java
@@ -0,0 +1,42 @@
+/*
+* 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.app.uiautomation.cts;
+
+import android.app.cts.uiautomation.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestFirstActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}
diff --git a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
new file mode 100644
index 0000000..7def379
--- /dev/null
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTestSecondActivity.java
@@ -0,0 +1,41 @@
+/*
+* 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.app.uiautomation.cts;
+
+import android.app.Activity;
+import android.app.cts.uiautomation.R;
+import android.os.Bundle;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+/**
+* Activity for testing the UiAutomatoin APIs.
+*/
+public class UiAutomationTestSecondActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.ui_automation_test);
+
+        String[] cheeses = getResources().getStringArray(R.array.some_cheeses);
+        ArrayAdapter<String> cheeseAdapter = new ArrayAdapter<String>(this,
+                android.R.layout.simple_list_item_1, cheeses);
+
+        ListView listView = (ListView) findViewById(R.id.list_view);
+        listView.setAdapter(cheeseAdapter);
+    }
+}
diff --git a/tests/tests/uidisolation/Android.mk b/tests/tests/uidisolation/Android.mk
index ba82eb5..8529407 100644
--- a/tests/tests/uidisolation/Android.mk
+++ b/tests/tests/uidisolation/Android.mk
@@ -21,12 +21,12 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctstestserver
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
 LOCAL_PACKAGE_NAME := CtsUidIsolationTestCases
 
+LOCAL_SDK_VERSION := current
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/uidisolation/AndroidManifest.xml b/tests/tests/uidisolation/AndroidManifest.xml
index e456a50..a8c6848 100644
--- a/tests/tests/uidisolation/AndroidManifest.xml
+++ b/tests/tests/uidisolation/AndroidManifest.xml
@@ -31,8 +31,11 @@
 
    <uses-permission android:name="android.permission.INTERNET"/>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.uidisolation"
-                     android:label="CTS tests of android.uidisolation"/>
+                     android:label="CTS tests of android.uidisolation">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 </manifest>
 
diff --git a/tests/tests/util/Android.mk b/tests/tests/util/Android.mk
index f1c75dc..6ede3fb 100644
--- a/tests/tests/util/Android.mk
+++ b/tests/tests/util/Android.mk
@@ -21,8 +21,6 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/tests/tests/util/AndroidManifest.xml b/tests/tests/util/AndroidManifest.xml
index 3969ac8..ab417bd 100644
--- a/tests/tests/util/AndroidManifest.xml
+++ b/tests/tests/util/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.util"/>
+                     android:label="CTS tests of android.util">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 233dc44..4cdeab2 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.view"/>
+                     android:label="CTS tests of android.view">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
index b33a312..07c6e8c 100644
--- a/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/LayoutInflaterTest.java
@@ -19,7 +19,6 @@
 import com.android.cts.stub.R;
 import com.android.internal.util.XmlUtils;
 
-
 import org.xmlpull.v1.XmlPullParser;
 
 import android.app.cts.MockActivity;
@@ -28,9 +27,12 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.res.Resources;
+import android.content.res.Resources.Theme;
 import android.content.res.XmlResourceParser;
 import android.test.AndroidTestCase;
+import android.text.TextUtils;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.util.Xml;
 import android.view.Gravity;
 import android.view.InflateException;
@@ -42,20 +44,21 @@
 import android.widget.LinearLayout;
 
 public class LayoutInflaterTest extends AndroidTestCase {
-
     private LayoutInflater mLayoutInflater;
-    private Context mContext;
-    private final Factory mFactory = new Factory() {
-        public View onCreateView(String name, Context context,
-                AttributeSet attrs) {
 
+    @SuppressWarnings("hiding")
+    private Context mContext;
+
+    private final Factory mFactory = new Factory() {
+        @Override
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
             return null;
         }
     };
     private boolean isOnLoadClass;
     private final Filter mFilter = new Filter() {
-
-        @SuppressWarnings("unchecked")
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        @Override
         public boolean onLoadClass(Class clazz) {
             isOnLoadClass = true;
             return true;
@@ -148,7 +151,8 @@
         mLayoutInflater = LayoutInflater.from(mContext);
         isOnLoadClass = false;
         mLayoutInflater.setFilter(new Filter() {
-            @SuppressWarnings("unchecked")
+            @SuppressWarnings({ "unchecked", "rawtypes" })
+            @Override
             public boolean onLoadClass(Class clazz) {
                 isOnLoadClass = true;
                 return false;
@@ -307,60 +311,99 @@
     }
 
     public void testInflate4() {
-       XmlResourceParser parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       View view = mLayoutInflater.inflate(parser, null, false);
-       assertNotNull(view);
-       view = null;
-       try {
-           view = mLayoutInflater.inflate(null, null, false);
-           fail("should throw exception");
-       } catch (NullPointerException e) {
-       }
-       LinearLayout mLayout;
-       mLayout = new LinearLayout(mContext);
-       mLayout.setOrientation(LinearLayout.VERTICAL);
-       mLayout.setHorizontalGravity(Gravity.LEFT);
-       mLayout.setLayoutParams(new ViewGroup.LayoutParams(
-               ViewGroup.LayoutParams.MATCH_PARENT,
-               ViewGroup.LayoutParams.MATCH_PARENT));
-       assertEquals(0, mLayout.getChildCount());
+        XmlResourceParser parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        View view = mLayoutInflater.inflate(parser, null, false);
+        assertNotNull(view);
+        view = null;
+        try {
+            view = mLayoutInflater.inflate(null, null, false);
+            fail("should throw exception");
+        } catch (NullPointerException e) {
+        }
+        LinearLayout mLayout;
+        mLayout = new LinearLayout(mContext);
+        mLayout.setOrientation(LinearLayout.VERTICAL);
+        mLayout.setHorizontalGravity(Gravity.LEFT);
+        mLayout.setLayoutParams(new ViewGroup.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        assertEquals(0, mLayout.getChildCount());
 
-       try {
-           view = mLayoutInflater.inflate(parser, mLayout, false);
-           fail("should throw exception");
-       } catch (NullPointerException e) {
-       }
-       parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       view = mLayoutInflater.inflate(parser, mLayout, false);
-       assertNull(view.getParent());
-       assertNotNull(view);
-       assertEquals(0, mLayout.getChildCount());
-       parser = getContext().getResources().getLayout(
-               R.layout.inflater_layout);
-       assertEquals(0, mLayout.getChildCount());
-       view = mLayoutInflater.inflate(parser, mLayout, true);
-       assertNotNull(view);
-       assertNull(view.getParent());
-       assertEquals(1, mLayout.getChildCount());
+        try {
+            view = mLayoutInflater.inflate(parser, mLayout, false);
+            fail("should throw exception");
+        } catch (NullPointerException e) {
+        }
+        parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        view = mLayoutInflater.inflate(parser, mLayout, false);
+        assertNull(view.getParent());
+        assertNotNull(view);
+        assertEquals(0, mLayout.getChildCount());
+        parser = getContext().getResources().getLayout(
+                R.layout.inflater_layout);
+        assertEquals(0, mLayout.getChildCount());
+        view = mLayoutInflater.inflate(parser, mLayout, true);
+        assertNotNull(view);
+        assertNull(view.getParent());
+        assertEquals(1, mLayout.getChildCount());
 
-       parser = null;
-       parser = getParser();
-       try {
-           view = mLayoutInflater.inflate(parser, mLayout, false);
-           fail("should throw exception");
-       } catch (InflateException e) {
-       }
+        parser = null;
+        parser = getParser();
+        try {
+            view = mLayoutInflater.inflate(parser, mLayout, false);
+            fail("should throw exception");
+        } catch (InflateException e) {
+        }
 
-       parser = null;
-       view = null;
-       parser = getParser();
+        parser = null;
+        view = null;
+        parser = getParser();
 
-       view = mLayoutInflater.inflate(parser, mLayout, true);
-       assertNotNull(view);
-       assertEquals(2, mLayout.getChildCount());
-   }
+        view = mLayoutInflater.inflate(parser, mLayout, true);
+        assertNotNull(view);
+        assertEquals(2, mLayout.getChildCount());
+    }
+
+    public void testOverrideTheme() {
+        View container = mLayoutInflater.inflate(R.layout.inflater_override_theme_layout, null);
+        verifyThemeType(container, "view_outer", R.id.view_outer, 1);
+        verifyThemeType(container, "view_inner", R.id.view_inner, 2);
+        verifyThemeType(container, "view_attr", R.id.view_attr, 3);
+    }
+
+    private void verifyThemeType(View container, String tag, int id, int type) {
+        TypedValue outValue = new TypedValue();
+        View view = container.findViewById(id);
+        assertNotNull("Found " + tag, view);
+        Theme theme = view.getContext().getTheme();
+        boolean resolved = theme.resolveAttribute(R.attr.themeType, outValue, true);
+        assertTrue("Resolved themeType for " + tag, resolved);
+        assertEquals(tag + " has themeType " + type, type, outValue.data);
+    }
+
+    public void testInflateTags() {
+        final View view = mLayoutInflater.inflate(
+                com.android.cts.stub.R.layout.inflater_layout_tags, null);
+        assertNotNull(view);
+
+        checkViewTag(view, R.id.viewlayout_root, R.id.tag_viewlayout_root, R.string.tag1);
+        checkViewTag(view, R.id.mock_view, R.id.tag_mock_view, R.string.tag2);
+    }
+
+    private void checkViewTag(View parent, int viewId, int tagId, int valueResId) {
+        final View target = parent.findViewById(viewId);
+        assertNotNull("Found target view for " + viewId, target);
+
+        final Object tag = target.getTag(tagId);
+        assertNotNull("Tag is set", tag);
+        assertTrue("Tag is a character sequence", tag instanceof CharSequence);
+
+        final Context targetContext = target.getContext();
+        final CharSequence expectedValue = targetContext.getString(valueResId);
+        assertEquals(tagId + " has tag " + expectedValue, expectedValue, tag);
+    }
 
     static class MockLayoutInflater extends LayoutInflater {
 
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 95a365f..5c9516e 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -3574,6 +3574,34 @@
             View source, int changeType) {
 
         }
+
+        @Override
+        public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) {
+            return false;
+        }
+
+        @Override
+        public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes) {
+        }
+
+        @Override
+        public void onStopNestedScroll(View target) {
+        }
+
+        @Override
+        public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+                                   int dxUnconsumed, int dyUnconsumed) {
+        }
+
+        @Override
+        public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) {
+        }
+
+        @Override
+        public boolean onNestedFling(View target, float velocityX, float velocityY,
+                boolean consumed) {
+            return false;
+        }
     }
 
     private final class OnCreateContextMenuListenerImpl implements OnCreateContextMenuListener {
diff --git a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
index 9f92d23..b4d65a5 100644
--- a/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
+++ b/tests/tests/view/src/android/view/inputmethod/cts/InputMethodInfoTest.java
@@ -164,6 +164,7 @@
         mInputMethodInfo.writeToParcel(p, 0);
         p.setDataPosition(0);
         final InputMethodInfo imi = InputMethodInfo.CREATOR.createFromParcel(p);
+        p.recycle();
 
         assertEquals(mInputMethodInfo.getPackageName(), imi.getPackageName());
         assertEquals(mInputMethodInfo.getServiceName(), imi.getServiceName());
@@ -178,6 +179,7 @@
         mInputMethodSubtype.writeToParcel(p, 0);
         p.setDataPosition(0);
         final InputMethodSubtype subtype = InputMethodSubtype.CREATOR.createFromParcel(p);
+        p.recycle();
 
         assertEquals(mInputMethodSubtype.containsExtraValueKey(mSubtypeExtraValue_key),
                 subtype.containsExtraValueKey(mSubtypeExtraValue_key));
diff --git a/tests/tests/webkit/AndroidManifest.xml b/tests/tests/webkit/AndroidManifest.xml
index f4424a8..776f695 100644
--- a/tests/tests/webkit/AndroidManifest.xml
+++ b/tests/tests/webkit/AndroidManifest.xml
@@ -28,9 +28,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.webkit"/>
+                     android:label="CTS tests of android.webkit">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
index 11cc1a5..cc39f48 100644
--- a/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
+++ b/tests/tests/webkit/src/android/webkit/cts/TestHtmlConstants.java
@@ -65,6 +65,7 @@
     public static final String DATABASE_ACCESS_URL = "webkit/test_databaseaccess.html";
     public static final String STOP_LOADING_URL = "webkit/test_stop_loading.html";
     public static final String BLANK_TAG_URL = "webkit/blank_tag.html";
+    public static final String PAGE_WITH_LINK_URL = "webkit/page_with_link.html";
 
     // Must match the title of the page at
     // android/frameworks/base/core/res/res/raw/loaderror.html
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
index 5c9c958..344b568 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebChromeClientTest.java
@@ -112,6 +112,7 @@
         Thread.sleep(100); // Wait for open to be received on the icon db thread.
 
         assertFalse(webChromeClient.hadOnReceivedIcon());
+        assertNull(mOnUiThread.getFavicon());
 
         String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
@@ -122,6 +123,7 @@
                 return webChromeClient.hadOnReceivedIcon();
             }
         }.run();
+        assertNotNull(mOnUiThread.getFavicon());
     }
 
     public void runWindowTest(boolean expectWindowClose) throws Exception {
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
index f4df82a..9aca8c7 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewClientTest.java
@@ -80,11 +80,12 @@
                 "<a href=\"" + TEST_URL + "\" id=\"link\">new page</a>" +
                 "</body></html>";
         mOnUiThread.loadDataAndWaitForCompletion(data, "text/html", null);
-        clickOnLinkUsingJs("link");
+        clickOnLinkUsingJs("link", mOnUiThread);
         assertEquals(TEST_URL, webViewClient.getLastShouldOverrideUrl());
     }
 
     // Verify shouldoverrideurlloading called on webview called via onCreateWindow
+    // TODO(sgurun) upstream this test to Aw.
     public void testShouldOverrideUrlLoadingOnCreateWindow() throws Exception {
         mWebServer = new CtsTestServer(getActivity());
         // WebViewClient for main window
@@ -95,12 +96,14 @@
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
         mOnUiThread.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
         mOnUiThread.getSettings().setSupportMultipleWindows(true);
+
+        final WebView childWebView = mOnUiThread.createWebView();
+
         mOnUiThread.setWebChromeClient(new WebChromeClient() {
             @Override
             public boolean onCreateWindow(
                 WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
                 WebView.WebViewTransport transport = (WebView.WebViewTransport) resultMsg.obj;
-                WebView childWebView = new WebView(view.getContext());
                 childWebView.setWebViewClient(childWebViewClient);
                 childWebView.getSettings().setJavaScriptEnabled(true);
                 transport.setWebView(childWebView);
@@ -119,13 +122,30 @@
                 return childWebViewClient.hasOnPageFinishedCalled();
             }
         }.run();
-        assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL),
+        assertEquals(mWebServer.getAssetUrl(TestHtmlConstants.PAGE_WITH_LINK_URL),
                 childWebViewClient.getLastShouldOverrideUrl());
+
+        // Now test a navigation within the page
+        //TODO(hush) Enable this portion when b/12804986 is fixed.
+        /*
+        WebViewOnUiThread childWebViewOnUiThread = new WebViewOnUiThread(this, childWebView);
+        final int childCallCount = childWebViewClient.getShouldOverrideUrlLoadingCallCount();
+        final int mainCallCount = mainWebViewClient.getShouldOverrideUrlLoadingCallCount();
+        clickOnLinkUsingJs("link", childWebViewOnUiThread);
+        new PollingCheck(TEST_TIMEOUT) {
+            @Override
+            protected boolean check() {
+                return childWebViewClient.getShouldOverrideUrlLoadingCallCount() > childCallCount;
+            }
+        }.run();
+        assertEquals(mainCallCount, mainWebViewClient.getShouldOverrideUrlLoadingCallCount());
+        assertEquals(TEST_URL, childWebViewClient.getLastShouldOverrideUrl());
+        */
     }
 
-    private void clickOnLinkUsingJs(final String linkId) {
+    private void clickOnLinkUsingJs(final String linkId, WebViewOnUiThread webViewOnUiThread) {
         EvaluateJsResultPollingCheck jsResult = new EvaluateJsResultPollingCheck("null");
-        mOnUiThread.evaluateJavascript(
+        webViewOnUiThread.evaluateJavascript(
                 "document.getElementById('" + linkId + "').click();" +
                 "console.log('element with id [" + linkId + "] clicked');", jsResult);
         jsResult.run();
@@ -294,6 +314,7 @@
         private boolean mOnReceivedHttpAuthRequestCalled;
         private boolean mOnUnhandledKeyEventCalled;
         private boolean mOnScaleChangedCalled;
+        private int mShouldOverrideUrlLoadingCallCount;
         private String mLastShouldOverrideUrl;
 
         public MockWebViewClient() {
@@ -336,6 +357,10 @@
             return mOnScaleChangedCalled;
         }
 
+        public int getShouldOverrideUrlLoadingCallCount() {
+            return mShouldOverrideUrlLoadingCallCount;
+        }
+
         public String getLastShouldOverrideUrl() {
             return mLastShouldOverrideUrl;
         }
@@ -402,6 +427,7 @@
         @Override
         public boolean shouldOverrideUrlLoading(WebView view, String url) {
             mLastShouldOverrideUrl = url;
+            mShouldOverrideUrlLoadingCallCount++;
             return false;
         }
     }
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
index 332573d..3f2a8f7 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebViewTest.java
@@ -80,14 +80,20 @@
 import java.io.IOException;
 import java.util.Collections;
 import java.util.Date;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.FutureTask;
 import java.util.concurrent.TimeUnit;
 import java.util.HashMap;
 
 import org.apache.http.Header;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpEntityEnclosingRequest;
 import org.apache.http.HttpRequest;
+import org.apache.http.util.EncodingUtils;
+import org.apache.http.util.EntityUtils;
 
 public class WebViewTest extends ActivityInstrumentationTestCase2<WebViewStubActivity> {
     private static final String LOGTAG = "WebViewTest";
@@ -142,7 +148,7 @@
     protected void tearDown() throws Exception {
         mOnUiThread.cleanUp();
         if (mWebServer != null) {
-            mWebServer.shutdown();
+            stopWebServer();
         }
         if (mIconDb != null) {
             mIconDb.removeAllIcons();
@@ -361,6 +367,36 @@
     }
 
     @UiThreadTest
+    public void testPostUrlWithNonNetworkUrl() throws Exception {
+        final String nonNetworkUrl = "file:///android_asset/" + TestHtmlConstants.HELLO_WORLD_URL;
+
+        mOnUiThread.postUrlAndWaitForCompletion(nonNetworkUrl, new byte[1]);
+
+        // Test if the nonNetworkUrl is loaded
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mWebView.getTitle());
+    }
+
+    @UiThreadTest
+    public void testPostUrlWithNetworkUrl() throws Exception {
+        startWebServer(false);
+        final String networkUrl = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final String postDataString = "username=my_username&password=my_password";
+        final byte[] postData = EncodingUtils.getBytes(postDataString, "BASE64");
+
+        mOnUiThread.postUrlAndWaitForCompletion(networkUrl, postData);
+
+        HttpRequest request = mWebServer.getLastRequest(TestHtmlConstants.HELLO_WORLD_URL);
+        // The last request should be POST
+        assertEquals(request.getRequestLine().getMethod(), "POST");
+
+        // The last request should have a request body
+        assertTrue(request instanceof HttpEntityEnclosingRequest);
+        HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity();
+        String entityString = EntityUtils.toString(entity);
+        assertEquals(entityString, postDataString);
+    }
+
+    @UiThreadTest
     public void testLoadUrlDoesNotStripParamsWhenLoadingContentUrls() throws Exception {
         Uri.Builder uriBuilder = new Uri.Builder().scheme(
                 ContentResolver.SCHEME_CONTENT).authority(MockContentProvider.AUTHORITY);
@@ -564,6 +600,30 @@
         String url = mWebServer.getAssetUrl(TestHtmlConstants.ADD_JAVA_SCRIPT_INTERFACE_URL);
         mOnUiThread.loadUrlAndWaitForCompletion(url);
         assertEquals("Original title", obj.waitForResult());
+
+        // Verify that only methods annotated with @JavascriptInterface are exposed
+        // on the JavaScript interface object.
+        mOnUiThread.evaluateJavascript("typeof dummy.provideResult",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"function\"", result);
+                    }
+                });
+        mOnUiThread.evaluateJavascript("typeof dummy.wasProvideResultCalled",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"undefined\"", result);
+                    }
+                });
+        mOnUiThread.evaluateJavascript("typeof dummy.getClass",
+                new ValueCallback<String>() {
+                    @Override
+                    public void onReceiveValue(String result) {
+                        assertEquals("\"undefined\"", result);
+                    }
+                });
     }
 
     @UiThreadTest
@@ -666,6 +726,42 @@
         assertEquals("removedObject", resultObject.getResult());
     }
 
+    public void testAddJavascriptInterfaceExceptions() throws Exception {
+        WebSettings settings = mOnUiThread.getSettings();
+        settings.setJavaScriptEnabled(true);
+        settings.setJavaScriptCanOpenWindowsAutomatically(true);
+
+        final AtomicBoolean mJsInterfaceWasCalled = new AtomicBoolean(false) {
+            @JavascriptInterface
+            public synchronized void call() {
+                set(true);
+                // The main purpose of this test is to ensure an exception here does not
+                // crash the implementation.
+                throw new RuntimeException("Javascript Interface exception");
+            }
+        };
+
+        mOnUiThread.addJavascriptInterface(mJsInterfaceWasCalled, "dummy");
+
+        mOnUiThread.loadUrlAndWaitForCompletion("about:blank");
+
+        assertFalse(mJsInterfaceWasCalled.get());
+
+        final CountDownLatch resultLatch = new CountDownLatch(1);
+        mOnUiThread.evaluateJavascript(
+                "try {dummy.call(); 'fail'; } catch (exception) { 'pass'; } ",
+                new ValueCallback<String>() {
+                        @Override
+                        public void onReceiveValue(String result) {
+                            assertEquals("\"pass\"", result);
+                            resultLatch.countDown();
+                        }
+                    });
+
+        assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
+        assertTrue(mJsInterfaceWasCalled.get());
+    }
+
     private final class TestPictureListener implements PictureListener {
         public int callCount;
 
@@ -878,7 +974,6 @@
         // Verify that the resource request makes it to the server.
         assertTrue(mWebServer.wasResourceRequested(imgUrl));
         assertEquals(historyUrl, mWebView.getUrl());
-        assertEquals(historyUrl, mWebView.getOriginalUrl());
 
         // Check that reported URL is "about:blank" when supplied history URL
         // is null.
@@ -888,7 +983,6 @@
                 "text/html", "UTF-8", null);
         assertTrue(mWebServer.wasResourceRequested(imgUrl));
         assertEquals("about:blank", mWebView.getUrl());
-        assertEquals("about:blank", mWebView.getOriginalUrl());
 
         // Test that JavaScript can access content from the same origin as the base URL.
         mWebView.getSettings().setJavaScriptEnabled(true);
@@ -913,19 +1007,18 @@
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("http://www.foo.com",
                 HTML_HEADER + "<title>Hello World%21</title><body>bar</body></html>",
                 "text/html", "UTF-8", null);
-        assertEquals("Hello World%21", mWebView.getTitle());
+        assertEquals("Hello World%21", mOnUiThread.getTitle());
 
         // Check that when a data: base URL is used, we treat the String to load as a data: URL
         // and run load steps such as decoding URL entities (i.e., contrary to the test case
         // above.)
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion("data:foo",
                 HTML_HEADER + "<title>Hello World%21</title></html>", "text/html", "UTF-8", null);
-        assertEquals("Hello World!", mWebView.getTitle());
+        assertEquals("Hello World!", mOnUiThread.getTitle());
 
         // Check the method is null input safe.
         mOnUiThread.loadDataWithBaseURLAndWaitForCompletion(null, null, null, null, null);
-        assertEquals("about:blank", mWebView.getUrl());
-        assertEquals("about:blank", mWebView.getOriginalUrl());
+        assertEquals("about:blank", mOnUiThread.getUrl());
     }
 
     private static class WaitForFindResultsListener extends FutureTask<Integer>
@@ -1469,16 +1562,6 @@
     }
 
     @UiThreadTest
-    public void testGetFavicon() throws Exception {
-        startWebServer(false);
-        String url = mWebServer.getAssetUrl(TestHtmlConstants.TEST_FAVICON_URL);
-        mOnUiThread.loadUrlAndWaitForCompletion(url);
-        mWebView.getFavicon();
-        // ToBeFixed: Favicon is not loaded automatically.
-        // assertNotNull(mWebView.getFavicon());
-    }
-
-    @UiThreadTest
     public void testClearHistory() throws Exception {
         startWebServer(false);
         String url1 = mWebServer.getAssetUrl(TestHtmlConstants.HTML_URL1);
@@ -1540,7 +1623,7 @@
         assertNotNull(restoreList);
         assertEquals(3, restoreList.getSize());
         assertEquals(2, saveList.getCurrentIndex());
-        /* ToBeFixed: The WebHistoryItems do not get inflated. Uncomment remaining tests when fixed.
+
         // wait for the list items to get inflated
         new PollingCheck(TEST_TIMEOUT) {
             @Override
@@ -1561,7 +1644,6 @@
         assertEquals(url1, copyListAfterRestore.getItemAtIndex(0).getUrl());
         assertEquals(url2, copyListAfterRestore.getItemAtIndex(1).getUrl());
         assertEquals(url3, copyListAfterRestore.getItemAtIndex(2).getUrl());
-        */
     }
 
     public void testSetWebViewClient() throws Throwable {
@@ -1775,6 +1857,35 @@
         assertEquals("Second page", mOnUiThread.getTitle());
     }
 
+    public void testSecureServerRequestingClientCertDoesNotCancelRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.WANTS_CLIENT_AUTH);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // Page loaded OK...
+        assertTrue(webViewClient.wasOnReceivedSslErrorCalled());
+        assertEquals(TestHtmlConstants.HELLO_WORLD_TITLE, mOnUiThread.getTitle());
+        assertEquals(0, webViewClient.onReceivedErrorCode());
+    }
+
+    public void testSecureServerRequiringClientCertDoesCancelRequest() throws Throwable {
+        mWebServer = new CtsTestServer(getActivity(), CtsTestServer.SslMode.NEEDS_CLIENT_AUTH);
+        final String url = mWebServer.getAssetUrl(TestHtmlConstants.HELLO_WORLD_URL);
+        final SslErrorWebViewClient webViewClient = new SslErrorWebViewClient();
+        mOnUiThread.setWebViewClient(webViewClient);
+        mOnUiThread.clearSslPreferences();
+        mOnUiThread.loadUrlAndWaitForCompletion(url);
+        // Page NOT loaded OK...
+        // In this case, we must NOT have received the onReceivedSslError callback as that is for
+        // recoverable (e.g. server auth) errors, whereas failing mandatory client auth is non-
+        // recoverable and should drop straight through to a load error.
+        assertFalse(webViewClient.wasOnReceivedSslErrorCalled());
+        assertFalse(TestHtmlConstants.HELLO_WORLD_TITLE.equals(mOnUiThread.getTitle()));
+        assertEquals(WebViewClient.ERROR_FAILED_SSL_HANDSHAKE, webViewClient.onReceivedErrorCode());
+    }
+
     public void testRequestChildRectangleOnScreen() throws Throwable {
         DisplayMetrics metrics = mOnUiThread.getDisplayMetrics();
         final int dimension = 2 * Math.max(metrics.widthPixels, metrics.heightPixels);
@@ -1799,12 +1910,12 @@
     }
 
     public void testSetDownloadListener() throws Throwable {
+        final CountDownLatch resultLatch = new CountDownLatch(1);
         final class MyDownloadListener implements DownloadListener {
             public String url;
             public String mimeType;
             public long contentLength;
             public String contentDisposition;
-            public boolean called;
 
             @Override
             public void onDownloadStart(String url, String userAgent, String contentDisposition,
@@ -1813,7 +1924,7 @@
                 this.mimeType = mimetype;
                 this.contentLength = contentLength;
                 this.contentDisposition = contentDisposition;
-                this.called = true;
+                resultLatch.countDown();
             }
         }
 
@@ -1830,27 +1941,17 @@
         // the WebView will load the new URL.
         mOnUiThread.setDownloadListener(listener);
         mOnUiThread.getSettings().setJavaScriptEnabled(true);
-
-        // See b/13675265 for discussion on why the setTimeout is necessary.
-        // Works around a Blink bug.
         mOnUiThread.loadDataAndWaitForCompletion(
-                "<html><body onload=\"setTimeout(" +
-                "function() { window.location = \'" + url + "\'; }, 100);\">" +
-                "</body></html>", "text/html", null);
+                "<html><body onload=\"window.location = \'" + url + "\'\"></body></html>",
+                "text/html", null);
         // Wait for layout to complete before setting focus.
         getInstrumentation().waitForIdleSync();
 
-        new PollingCheck(TEST_TIMEOUT) {
-            @Override
-            protected boolean check() {
-                return listener.called;
-            }
-        }.run();
+        assertTrue(resultLatch.await(TEST_TIMEOUT, TimeUnit.MILLISECONDS));
         assertEquals(url, listener.url);
         assertTrue(listener.contentDisposition.contains("test.bin"));
-        // ToBeFixed: uncomment the following tests after fixing the framework
-        // assertEquals(mimeType, listener.mimeType);
-        // assertEquals(length, listener.contentLength);
+        assertEquals(length, listener.contentLength);
+        assertEquals(mimeType, listener.mimeType);
     }
 
     @UiThreadTest
@@ -2113,11 +2214,6 @@
         });
     }
 
-    @UiThreadTest
-    public void testInternals() {
-        // Do not test these APIs. They are implementation details.
-    }
-
     private static class HrefCheckHandler extends Handler {
         private boolean mHadRecieved;
 
@@ -2212,51 +2308,6 @@
         return true;
     }
 
-    // Find b1 inside b2
-    private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2) {
-        int w = b1.getWidth();
-        int h = b1.getHeight();
-
-        for (int i = 0; i < (b2.getWidth()-w+1); i++) {
-            for (int j = 0; j < (b2.getHeight()-h+1); j++) {
-                if (checkBitmapInsideAnother(b1, b2, i, j))
-                    return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean comparePixel(int p1, int p2, int maxError) {
-        int err;
-        err = Math.abs(((p1&0xff000000)>>>24) - ((p2&0xff000000)>>>24));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x00ff0000)>>>16) - ((p2&0x00ff0000)>>>16));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x0000ff00)>>>8) - ((p2&0x0000ff00)>>>8));
-        if (err > maxError)
-            return false;
-
-        err = Math.abs(((p1&0x000000ff)>>>0) - ((p2&0x000000ff)>>>0));
-        if (err > maxError)
-            return false;
-
-        return true;
-    }
-
-    private boolean checkBitmapInsideAnother(Bitmap b1, Bitmap b2, int x, int y) {
-        for (int i = 0; i < b1.getWidth(); i++)
-            for (int j = 0; j < b1.getHeight(); j++) {
-                if (!comparePixel(b1.getPixel(i, j), b2.getPixel(x + i, y + j), 10)) {
-                    return false;
-                }
-            }
-        return true;
-    }
-
     /**
      * Waits at least MIN_SCROLL_WAIT_MS for scrolling to start. Once started,
      * scrolling is checked every SCROLL_WAIT_INTERVAL_MS for changes. Once
@@ -2286,6 +2337,7 @@
     final class SslErrorWebViewClient extends WaitForLoadedClient {
         private boolean mWasOnReceivedSslErrorCalled;
         private String mErrorUrl;
+        private int mErrorCode;
 
         public SslErrorWebViewClient() {
             super(mOnUiThread);
@@ -2296,6 +2348,11 @@
             mErrorUrl = error.getUrl();
             handler.proceed();
         }
+        @Override
+        public void onReceivedError(WebView view, int errorCode, String description,
+                String failingUrl) {
+            mErrorCode = errorCode;
+        }
         public void resetWasOnReceivedSslErrorCalled() {
             mWasOnReceivedSslErrorCalled = false;
         }
@@ -2305,6 +2362,9 @@
         public String errorUrl() {
             return mErrorUrl;
         }
+        public int onReceivedErrorCode() {
+            return mErrorCode;
+        }
     }
 
     private void pollingCheckForCanZoomIn() {
diff --git a/tests/tests/widget/AndroidManifest.xml b/tests/tests/widget/AndroidManifest.xml
index e69a7d5..3c7fe5f 100644
--- a/tests/tests/widget/AndroidManifest.xml
+++ b/tests/tests/widget/AndroidManifest.xml
@@ -23,9 +23,12 @@
         <uses-library android:name="android.test.runner" />
     </application>
 
-    <instrumentation android:name="android.test.InstrumentationCtsTestRunner"
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
                      android:targetPackage="com.android.cts.stub"
-                     android:label="CTS tests of android.widget"/>
+                     android:label="CTS tests of android.widget">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
 
 </manifest>
 
diff --git a/tools/cts-holo-generation/Android.mk b/tools/cts-holo-generation/Android.mk
index 5f12ef0..2affc5e 100644
--- a/tools/cts-holo-generation/Android.mk
+++ b/tools/cts-holo-generation/Android.mk
@@ -23,7 +23,7 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
diff --git a/tools/cts-holo-generation/AndroidManifest.xml b/tools/cts-holo-generation/AndroidManifest.xml
index d7de891..ad4ae22 100644
--- a/tools/cts-holo-generation/AndroidManifest.xml
+++ b/tools/cts-holo-generation/AndroidManifest.xml
@@ -7,7 +7,7 @@
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 
     <instrumentation
-        android:name="android.test.InstrumentationTestRunner"
+        android:name="android.support.test.runner.AndroidJUnitRunner"
         android:targetPackage="com.android.cts.holo_capture" />
 
     <application>
diff --git a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
index e56a7cf..06951b9 100644
--- a/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
+++ b/tools/cts-java-scanner/src/com/android/cts/javascanner/DocletRunner.java
@@ -77,8 +77,6 @@
         sourcePath.add("./cts/tests/src");
         sourcePath.add("./cts/libs/commonutil/src");
         sourcePath.add("./cts/libs/deviceutil/src");
-        sourcePath.add("./frameworks/testing/uiautomator/library/testrunner-src");
-        sourcePath.add("./frameworks/testing/uiautomator_test_libraries/src");
         sourcePath.add(sourceDir.toString());
         return join(sourcePath, ":");
     }
@@ -86,6 +84,7 @@
     private String getClassPath() {
         List<String> classPath = new ArrayList<String>();
         classPath.add("./prebuilts/misc/common/tradefed/tradefed-prebuilt.jar");
+        classPath.add("./prebuilts/misc/common/ub-uiautomator/ub-uiautomator.jar");
         return join(classPath, ":");
     }
 
diff --git a/tools/cts-reference-app-lib/Android.mk b/tools/cts-reference-app-lib/Android.mk
index 8341970..fae85b4 100644
--- a/tools/cts-reference-app-lib/Android.mk
+++ b/tools/cts-reference-app-lib/Android.mk
@@ -24,7 +24,7 @@
 # and when built explicitly put it in the data partition
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_SDK_VERSION := current
 
 LOCAL_MODULE := android.cts.refapp
 
diff --git a/tools/device-setup/TestDeviceSetup/Android.mk b/tools/device-setup/TestDeviceSetup/Android.mk
index ba1998c..44e66bb 100644
--- a/tools/device-setup/TestDeviceSetup/Android.mk
+++ b/tools/device-setup/TestDeviceSetup/Android.mk
@@ -25,8 +25,6 @@
 LOCAL_DEX_PREOPT := false
 LOCAL_PROGUARD_ENABLED := disabled
 
-LOCAL_JAVA_LIBRARIES := android.test.runner
-
 LOCAL_SDK_VERSION := current
 
 LOCAL_PACKAGE_NAME := TestDeviceSetup
diff --git a/tools/tradefed-host/.classpath b/tools/tradefed-host/.classpath
index 0b1866d..b82e340 100644
--- a/tools/tradefed-host/.classpath
+++ b/tools/tradefed-host/.classpath
@@ -2,10 +2,11 @@
 <classpath>
 	<classpathentry kind="src" path="src"/>
 	<classpathentry kind="src" path="res"/>
+	<classpathentry kind="src" path="commonutil-src"/>
 	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ctsdeviceinfolib_intermediates/javalib.jar"/>
 	<classpathentry exported="true" kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/hosttestlib_intermediates/javalib.jar"/>
 	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/ddmlib-prebuilt_intermediates/ddmlib-prebuilt.jar" sourcepath="/SDK_SRC_ROOT"/>
-	<classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
+        <classpathentry kind="var" path="CTS_SRC_ROOT/out/host/common/obj/JAVA_LIBRARIES/tradefed-prebuilt_intermediates/tradefed-prebuilt.jar" sourcepath="/TRADEFED_ROOT/tools/tradefederation/src"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/tools/tradefed-host/.project b/tools/tradefed-host/.project
index 990c63e..ffd16af 100644
--- a/tools/tradefed-host/.project
+++ b/tools/tradefed-host/.project
@@ -14,4 +14,11 @@
 	<natures>
 		<nature>org.eclipse.jdt.core.javanature</nature>
 	</natures>
+	<linkedResources>
+		<link>
+			<name>commonutil-src</name>
+			<type>2</type>
+			<locationURI>CTS_SRC_ROOT/cts/libs/commonutil/src</locationURI>
+		</link>
+	</linkedResources>
 </projectDescription>
diff --git a/tools/tradefed-host/etc/cts-tradefed b/tools/tradefed-host/etc/cts-tradefed
index bc5c07a..485740e 100755
--- a/tools/tradefed-host/etc/cts-tradefed
+++ b/tools/tradefed-host/etc/cts-tradefed
@@ -51,17 +51,21 @@
 
 
 # check if in Android build env
-if [ ! -z ${ANDROID_BUILD_TOP} ]; then
-    HOST=`uname`
-    if [ "$HOST" == "Linux" ]; then
-        OS="linux-x86"
-    elif [ "$HOST" == "Darwin" ]; then
-        OS="darwin-x86"
+if [ ! -z "${ANDROID_BUILD_TOP}" ]; then
+    if [ ! -z "${ANDROID_HOST_OUT}" ]; then
+      CTS_ROOT=${ANDROID_HOST_OUT}/cts
     else
-        echo "Unrecognized OS"
-        exit
-    fi;
-    CTS_ROOT=${ANDROID_BUILD_TOP}/out/host/${OS}/cts
+      HOST=`uname`
+      if [ "$HOST" == "Linux" ]; then
+          OS="linux-x86"
+      elif [ "$HOST" == "Darwin" ]; then
+          OS="darwin-x86"
+      else
+          echo "Unrecognized OS"
+          exit
+      fi
+      CTS_ROOT=${ANDROID_BUILD_TOP}/${OUT_DIR:-out}/host/${OS}/cts
+    fi
     if [ ! -d ${CTS_ROOT} ]; then
         echo "Could not find $CTS_ROOT in Android build environment. Try 'make cts'"
         exit
diff --git a/tools/tradefed-host/res/config/cts.xml b/tools/tradefed-host/res/config/cts.xml
index 158f49d..416b400 100644
--- a/tools/tradefed-host/res/config/cts.xml
+++ b/tools/tradefed-host/res/config/cts.xml
@@ -22,6 +22,7 @@
     <test class="com.android.cts.tradefed.testtype.CtsTest" />
     <logger class="com.android.tradefed.log.FileLogger" />
     <result_reporter class="com.android.cts.tradefed.result.CtsXmlResultReporter" />
+    <result_reporter class="com.android.cts.tradefed.result.CtsTestLogReporter" />
     <result_reporter class="com.android.cts.tradefed.result.IssueReporter" />
 
 </configuration>
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
new file mode 100644
index 0000000..bbdcb05
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsTestLogReporter.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.result;
+
+import com.android.cts.tradefed.device.DeviceInfoCollector;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.Log.LogLevel;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.config.Option;
+import com.android.tradefed.config.OptionCopier;
+import com.android.tradefed.log.LogUtil.CLog;
+import com.android.tradefed.result.IShardableListener;
+import com.android.tradefed.result.StubTestInvocationListener;
+
+import java.util.Map;
+
+/**
+ * Dumps tests in progress to stdout
+ */
+public class CtsTestLogReporter extends StubTestInvocationListener implements IShardableListener {
+
+    @Option(name = "quiet-output", description = "Mute display of test results.")
+    private boolean mQuietOutput = false;
+
+    protected IBuildInfo mBuildInfo;
+    private String mDeviceSerial;
+    private TestResults mResults = new TestResults();
+    private TestPackageResult mCurrentPkgResult = null;
+    private boolean mIsDeviceInfoRun = false;
+
+    @Override
+    public void invocationStarted(IBuildInfo buildInfo) {
+        mDeviceSerial = buildInfo.getDeviceSerial() == null ? "unknown_device" : buildInfo.getDeviceSerial();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testRunStarted(String name, int numTests) {
+        if (mCurrentPkgResult != null && !name.equals(mCurrentPkgResult.getAppPackageName())) {
+            // display results from previous run
+            logCompleteRun(mCurrentPkgResult);
+        }
+        mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
+        if (mIsDeviceInfoRun) {
+            logResult("Collecting device info");
+        } else  {
+            if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) {
+                logResult("-----------------------------------------");
+                logResult("Test package %s started", name);
+                logResult("-----------------------------------------");
+            }
+            mCurrentPkgResult = mResults.getOrCreatePackage(name);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testStarted(TestIdentifier test) {
+        mCurrentPkgResult.insertTest(test);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testFailed(TestFailure status, TestIdentifier test, String trace) {
+        mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
+        mCurrentPkgResult.reportTestEnded(test);
+        Test result = mCurrentPkgResult.findTest(test);
+        String stack = result.getStackTrace() == null ? "" : "\n" + result.getStackTrace();
+        logResult("%s#%s %s %s", test.getClassName(), test.getTestName(), result.getResult(),
+                stack);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void invocationEnded(long elapsedTime) {
+        // display the results of the last completed run
+        if (mCurrentPkgResult != null) {
+            logCompleteRun(mCurrentPkgResult);
+        }
+    }
+
+    private void logResult(String format, Object... args) {
+        if (mQuietOutput) {
+            CLog.i(format, args);
+        } else {
+            Log.logAndDisplay(LogLevel.INFO, mDeviceSerial, String.format(format, args));
+        }
+    }
+
+    private void logCompleteRun(TestPackageResult pkgResult) {
+        if (pkgResult.getAppPackageName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
+            logResult("Device info collection complete");
+            return;
+        }
+        logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
+                pkgResult.getAppPackageName(), pkgResult.countTests(CtsTestStatus.PASS),
+                pkgResult.countTests(CtsTestStatus.FAIL),
+                pkgResult.countTests(CtsTestStatus.NOT_EXECUTED));
+    }
+
+    @Override
+    public IShardableListener clone() {
+        CtsTestLogReporter clone = new CtsTestLogReporter();
+        OptionCopier.copyOptionsNoThrow(this, clone);
+        return clone;
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
index 6643934..1787180 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/result/CtsXmlResultReporter.java
@@ -69,6 +69,9 @@
     static final String PLAN_ATTR = "testPlan";
     static final String STARTTIME_ATTR = "starttime";
 
+    @Option(name = "quiet-output", description = "Mute display of test results.")
+    private boolean mQuietOutput = false;
+
     private static final String REPORT_DIR_NAME = "output-file-path";
     @Option(name=REPORT_DIR_NAME, description="root file system path to directory to store xml " +
             "test results and associated logs. If not specified, results will be stored at " +
@@ -83,9 +86,6 @@
     @Option(name = CtsTest.CONTINUE_OPTION, description = "the test result session to continue.")
     private Integer mContinueSessionId = null;
 
-    @Option(name = "quiet-output", description = "Mute display of test results.")
-    private boolean mQuietOutput = false;
-
     @Option(name = "result-server", description = "Server to publish test results.")
     private String mResultServer;
 
@@ -220,27 +220,11 @@
         return new LogFileSaver(mLogDir);
     }
 
-    /**
-     * {@inheritDoc}
-     */
+
     @Override
     public void testRunStarted(String name, int numTests) {
-        if (mCurrentPkgResult != null && !name.equals(mCurrentPkgResult.getAppPackageName())) {
-            // display results from previous run
-            logCompleteRun(mCurrentPkgResult);
-        }
+        mCurrentPkgResult = mResults.getOrCreatePackage(name);
         mIsDeviceInfoRun = name.equals(DeviceInfoCollector.APP_PACKAGE_NAME);
-        if (mIsDeviceInfoRun) {
-            logResult("Collecting device info");
-        } else  {
-            if (mCurrentPkgResult == null || !name.equals(mCurrentPkgResult.getAppPackageName())) {
-                logResult("-----------------------------------------");
-                logResult("Test package %s started", name);
-                logResult("-----------------------------------------");
-            }
-            mCurrentPkgResult = mResults.getOrCreatePackage(name);
-        }
-
     }
 
     /**
@@ -266,10 +250,6 @@
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
         collectCtsResults(test, testMetrics);
         mCurrentPkgResult.reportTestEnded(test);
-        Test result = mCurrentPkgResult.findTest(test);
-        String stack = result.getStackTrace() == null ? "" : "\n" + result.getStackTrace();
-        logResult("%s#%s %s %s", test.getClassName(), test.getTestName(), result.getResult(),
-                stack);
     }
 
     /**
@@ -314,10 +294,6 @@
      */
     @Override
     public void invocationEnded(long elapsedTime) {
-        // display the results of the last completed run
-        if (mCurrentPkgResult != null) {
-            logCompleteRun(mCurrentPkgResult);
-        }
         if (mReportDir == null || mStartTime == null) {
             // invocationStarted must have failed, abort
             CLog.w("Unable to create XML report");
@@ -344,17 +320,6 @@
         }
     }
 
-    private void logCompleteRun(TestPackageResult pkgResult) {
-        if (pkgResult.getAppPackageName().equals(DeviceInfoCollector.APP_PACKAGE_NAME)) {
-            logResult("Device info collection complete");
-            return;
-        }
-        logResult("%s package complete: Passed %d, Failed %d, Not Executed %d",
-                pkgResult.getAppPackageName(), pkgResult.countTests(CtsTestStatus.PASS),
-                pkgResult.countTests(CtsTestStatus.FAIL),
-                pkgResult.countTests(CtsTestStatus.NOT_EXECUTED));
-    }
-
     /**
      * Creates a report file and populates it with the report data from the completed tests.
      */
@@ -382,7 +347,7 @@
         } catch (IOException e) {
             Log.e(LOG_TAG, "Failed to generate report data");
         } finally {
-            StreamUtil.closeStream(stream);
+            StreamUtil.close(stream);
         }
     }
 
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index c6977e5..2f35266 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -27,6 +27,7 @@
 import com.android.tradefed.config.ConfigurationException;
 import com.android.tradefed.config.Option;
 import com.android.tradefed.config.Option.Importance;
+import com.android.tradefed.config.OptionCopier;
 import com.android.tradefed.device.DeviceNotAvailableException;
 import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.device.TestDeviceOptions;
@@ -43,14 +44,13 @@
 import com.android.tradefed.util.RunUtil;
 import com.android.tradefed.util.xml.AbstractXmlParser.ParseException;
 
+import junit.framework.Test;
+
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.InputStream;
-import java.lang.InterruptedException;
-import java.lang.System;
-import java.lang.Thread;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -63,8 +63,6 @@
 import java.util.Queue;
 import java.util.Set;
 
-import junit.framework.Test;
-
 /**
  * A {@link Test} for running CTS tests.
  * <p/>
@@ -170,8 +168,8 @@
     /** data structure for a {@link IRemoteTest} and its known tests */
     class TestPackage {
         private final IRemoteTest mTestForPackage;
-        private final Collection<TestIdentifier> mKnownTests;
         private final ITestPackageDef mPackageDef;
+        private final Collection<TestIdentifier> mKnownTests;
 
         TestPackage(ITestPackageDef packageDef, IRemoteTest testForPackage,
                 Collection<TestIdentifier> knownTests) {
@@ -470,6 +468,12 @@
 
             uninstallPrequisiteApks(uninstallPackages);
 
+        } catch (RuntimeException e) {
+            CLog.e(e);
+            throw e;
+        } catch (Error e) {
+            CLog.e(e);
+            throw e;
         } finally {
             filter.reportUnexecutedTests();
         }
@@ -600,8 +604,13 @@
             for (String uri : plan.getTestUris()) {
                 if (!mExcludedPackageNames.contains(uri)) {
                     ITestPackageDef testPackage = testRepo.getTestPackage(uri);
-                    testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
-                    testPkgDefs.add(testPackage);
+                    if (testPackage != null) {
+                        testPackage.setExcludedTestFilter(plan.getExcludedTestFilter(uri));
+                        testPkgDefs.add(testPackage);
+                    } else {
+                        CLog.e("Could not find test package uri %s referenced in plan %s", uri,
+                                mPlanName);
+                    }
                 }
             }
         } else if (mPackageNames.size() > 0){
@@ -734,6 +743,8 @@
         // don't create more shards than the number of tests we have!
         for (int i = 0; i < mShards && i < allTests.size(); i++) {
             CtsTest shard = new CtsTest();
+            OptionCopier.copyOptionsNoThrow(this, shard);
+            shard.mShards = 0;
             shard.mRemainingTestPkgs = new LinkedList<TestPackage>();
             shardQueue.add(shard);
         }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
index 68d6743..0c3d9bc 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/ITestPackageDef.java
@@ -39,10 +39,6 @@
      * Creates a runnable {@link IRemoteTest} from info stored in this definition.
      *
      * @param testCaseDir {@link File} representing directory of test case data
-     * @param className the test class to restrict this run to or <code>null</code> to run all tests
-     *            in package
-     * @param methodName the optional test method to restrict this run to, or <code>null</code> to
-     *            run all tests in class/package
      * @return a {@link IRemoteTest} with all necessary data populated to run the test or
      *         <code>null</code> if test could not be created
      */
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
index 809696a..01c3370 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/InstrumentationApkTest.java
@@ -23,12 +23,12 @@
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.InstrumentationTest;
 
+import junit.framework.Assert;
+
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Collection;
 
-import junit.framework.Assert;
-
 /**
  * A {@link InstrumentationTest] that will install CTS apks before test execution,
  * and uninstall on execution completion.
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
new file mode 100644
index 0000000..3d92eb3
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.testtype;
+
+import com.android.ddmlib.AdbCommandRejectedException;
+import com.android.ddmlib.IShellEnabledDevice;
+import com.android.ddmlib.Log;
+import com.android.ddmlib.ShellCommandUnresponsiveException;
+import com.android.ddmlib.TimeoutException;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.ITestRunListener;
+import com.android.ddmlib.testrunner.InstrumentationResultParser;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.TimeUnit;
+
+public class PrintTestRemoteTestRunner implements IRemoteAndroidTestRunner {
+
+    private final String mPackageName;
+    private final String mRunnerName;
+    private IShellEnabledDevice mRemoteDevice;
+    // default to no timeout
+    private long mMaxTimeToOutputResponse = 0;
+    private TimeUnit mMaxTimeUnits = TimeUnit.MILLISECONDS;
+    private String mRunName = null;
+
+    /** map of name-value instrumentation argument pairs */
+    private Map<String, String> mArgMap;
+    private InstrumentationResultParser mParser;
+
+    private static final String LOG_TAG = "RemoteAndroidTest";
+    private static final String DEFAULT_RUNNER_NAME = "android.test.InstrumentationTestRunner";
+
+    private static final char CLASS_SEPARATOR = ',';
+    private static final char METHOD_SEPARATOR = '#';
+    private static final char RUNNER_SEPARATOR = '/';
+
+    // defined instrumentation argument names
+    private static final String CLASS_ARG_NAME = "class";
+    private static final String LOG_ARG_NAME = "log";
+    private static final String DEBUG_ARG_NAME = "debug";
+    private static final String COVERAGE_ARG_NAME = "coverage";
+    private static final String PACKAGE_ARG_NAME = "package";
+    private static final String SIZE_ARG_NAME = "size";
+
+    // This command starts a shell Java program (installed by this class)
+    // in the folder owned by the shell user. This app creates a proxy
+    // which does privileged operations such as wiping a package's user
+    // data and then starts the tests passing the proxy. This enables
+    // the tests to clear the print spooler data.
+    private static final String INSTRUMENTATION_COMMAND =
+            "chmod 755 /data/local/tmp/print-instrument && "
+            + "/data/local/tmp/print-instrument instrument -w -r %1$s %2$s";
+
+    /**
+     * Creates a remote Android test runner.
+     *
+     * @param packageName the Android application package that contains the
+     *            tests to run
+     * @param runnerName the instrumentation test runner to execute. If null,
+     *            will use default runner
+     * @param remoteDevice the Android device to execute tests on
+     */
+    public PrintTestRemoteTestRunner(String packageName, String runnerName,
+            IShellEnabledDevice remoteDevice) {
+
+        mPackageName = packageName;
+        mRunnerName = runnerName;
+        mRemoteDevice = remoteDevice;
+        mArgMap = new Hashtable<String, String>();
+    }
+
+    /**
+     * Alternate constructor. Uses default instrumentation runner.
+     *
+     * @param packageName the Android application package that contains the
+     *            tests to run
+     * @param remoteDevice the Android device to execute tests on
+     */
+    public PrintTestRemoteTestRunner(String packageName, IShellEnabledDevice remoteDevice) {
+        this(packageName, null, remoteDevice);
+    }
+
+    @Override
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    @Override
+    public String getRunnerName() {
+        if (mRunnerName == null) {
+            return DEFAULT_RUNNER_NAME;
+        }
+        return mRunnerName;
+    }
+
+    /**
+     * Returns the complete instrumentation component path.
+     */
+    private String getRunnerPath() {
+        return getPackageName() + RUNNER_SEPARATOR + getRunnerName();
+    }
+
+    @Override
+    public void setClassName(String className) {
+        addInstrumentationArg(CLASS_ARG_NAME, className);
+    }
+
+    @Override
+    public void setClassNames(String[] classNames) {
+        StringBuilder classArgBuilder = new StringBuilder();
+
+        for (int i = 0; i < classNames.length; i++) {
+            if (i != 0) {
+                classArgBuilder.append(CLASS_SEPARATOR);
+            }
+            classArgBuilder.append(classNames[i]);
+        }
+        setClassName(classArgBuilder.toString());
+    }
+
+    @Override
+    public void setMethodName(String className, String testName) {
+        setClassName(className + METHOD_SEPARATOR + testName);
+    }
+
+    @Override
+    public void setTestPackageName(String packageName) {
+        addInstrumentationArg(PACKAGE_ARG_NAME, packageName);
+    }
+
+    @Override
+    public void addInstrumentationArg(String name, String value) {
+        if (name == null || value == null) {
+            throw new IllegalArgumentException("name or value arguments cannot be null");
+        }
+        mArgMap.put(name, value);
+    }
+
+    @Override
+    public void removeInstrumentationArg(String name) {
+        if (name == null) {
+            throw new IllegalArgumentException("name argument cannot be null");
+        }
+        mArgMap.remove(name);
+    }
+
+    @Override
+    public void addBooleanArg(String name, boolean value) {
+        addInstrumentationArg(name, Boolean.toString(value));
+    }
+
+    @Override
+    public void setLogOnly(boolean logOnly) {
+        addBooleanArg(LOG_ARG_NAME, logOnly);
+    }
+
+    @Override
+    public void setDebug(boolean debug) {
+        addBooleanArg(DEBUG_ARG_NAME, debug);
+    }
+
+    @Override
+    public void setCoverage(boolean coverage) {
+        addBooleanArg(COVERAGE_ARG_NAME, coverage);
+    }
+
+    @Override
+    public void setTestSize(TestSize size) {
+        addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
+    }
+
+    @Override
+    public void setMaxtimeToOutputResponse(int maxTimeToOutputResponse) {
+        setMaxTimeToOutputResponse(maxTimeToOutputResponse, TimeUnit.MILLISECONDS);
+    }
+
+    @Override
+    public void setMaxTimeToOutputResponse(long maxTimeToOutputResponse, TimeUnit maxTimeUnits) {
+        mMaxTimeToOutputResponse = maxTimeToOutputResponse;
+        mMaxTimeUnits = maxTimeUnits;
+    }
+
+    @Override
+    public void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    @Override
+    public void run(ITestRunListener... listeners) throws TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
+        run(Arrays.asList(listeners));
+    }
+
+    @Override
+    public void run(Collection<ITestRunListener> listeners) throws TimeoutException,
+            AdbCommandRejectedException, ShellCommandUnresponsiveException, IOException {
+        final String runCaseCommandStr = String.format(INSTRUMENTATION_COMMAND,
+              getArgsCommand(), getRunnerPath());
+        Log.i(LOG_TAG,
+                String.format("Running %1$s on %2$s", runCaseCommandStr, mRemoteDevice.getName()));
+        String runName = mRunName == null ? mPackageName : mRunName;
+        mParser = new InstrumentationResultParser(runName, listeners);
+
+        try {
+            mRemoteDevice.executeShellCommand(runCaseCommandStr, mParser, mMaxTimeToOutputResponse,
+                    mMaxTimeUnits);
+        } catch (IOException e) {
+            Log.w(LOG_TAG, String.format("IOException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            // rely on parser to communicate results to listeners
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        } catch (ShellCommandUnresponsiveException e) {
+            Log.w(LOG_TAG, String.format(
+                    "ShellCommandUnresponsiveException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(String
+                    .format("Failed to receive adb shell test output within %1$d ms. "
+                            + "Test may have timed out, or adb connection to device became"
+                            + "unresponsive", mMaxTimeToOutputResponse));
+            throw e;
+        } catch (TimeoutException e) {
+            Log.w(LOG_TAG, String.format("TimeoutException when running tests %1$s on %2$s",
+                    getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        } catch (AdbCommandRejectedException e) {
+            Log.w(LOG_TAG, String.format(
+                    "AdbCommandRejectedException %1$s when running tests %2$s on %3$s",
+                    e.toString(), getPackageName(), mRemoteDevice.getName()));
+            mParser.handleTestRunFailed(e.toString());
+            throw e;
+        }
+    }
+
+    @Override
+    public void cancel() {
+        if (mParser != null) {
+            mParser.cancel();
+        }
+    }
+
+    /**
+     * Returns the full instrumentation command line syntax for the provided
+     * instrumentation arguments. Returns an empty string if no arguments were
+     * specified.
+     */
+    private String getArgsCommand() {
+        StringBuilder commandBuilder = new StringBuilder();
+        for (Entry<String, String> argPair : mArgMap.entrySet()) {
+            final String argCmd = String.format(" -e %1$s %2$s", argPair.getKey(),
+                    argPair.getValue());
+            commandBuilder.append(argCmd);
+        }
+        return commandBuilder.toString();
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java
new file mode 100644
index 0000000..a7a6ccc
--- /dev/null
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRunner.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tradefed.testtype;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.cts.tradefed.targetprep.SettingsToggler;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.IRemoteAndroidTestRunner.TestSize;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.ITestInvocationListener;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.testtype.IDeviceTest;
+import com.android.tradefed.testtype.IRemoteTest;
+import com.android.tradefed.util.StringEscapeUtils;
+
+import java.io.FileNotFoundException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Running the print tests requires modification of secure settings. Secure
+ * settings cannot be changed from device CTS tests since system signature
+ * permission is required. Such settings can be modified by the shell user,
+ * so a host side test driver is used for enabling these services, running
+ * the tests, and disabling the services.
+ */
+public class PrintTestRunner implements IBuildReceiver, IRemoteTest, IDeviceTest  {
+
+    private static final String PRINT_TEST_AND_SERVICES_APP_NAME =
+            "CtsPrintTestCases.apk";
+
+    private static final String PRINT_TESTS_PACKAGE_NAME =
+            "com.android.cts.print";
+
+    private static final String FIRST_PRINT_SERVICE_NAME =
+            "android.print.cts.services.FirstPrintService";
+
+    private static final String SECOND_PRINT_SERVICE_NAME =
+            "android.print.cts.services.SecondPrintService";
+
+    private static final String SHELL_USER_FOLDER = "data/local/tmp";
+
+    private static final String PRINT_INSTRUMENT_JAR = "CtsPrintInstrument.jar";
+
+    private static final String PRINT_INSTRUMENT_SCRIPT = "print-instrument";
+
+    private ITestDevice mDevice;
+
+    private CtsBuildHelper mCtsBuild;
+
+    private String mPackageName;
+    private String mRunnerName = "android.test.InstrumentationTestRunner";
+    private String mTestClassName;
+    private String mTestMethodName;
+    private String mTestPackageName;
+    private int mTestTimeout = 10 * 60 * 1000;  // 10 minutes
+    private String mTestSize;
+    private String mRunName = null;
+    private Map<String, String> mInstrArgMap = new HashMap<String, String>();
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    public void setDevice(ITestDevice device) {
+        mDevice = device;
+    }
+
+    @Override
+    public ITestDevice getDevice() {
+        return mDevice;
+    }
+
+    public void setPackageName(String packageName) {
+        mPackageName = packageName;
+    }
+
+    public void setRunnerName(String runnerName) {
+        mRunnerName = runnerName;
+    }
+
+    public void setClassName(String testClassName) {
+        mTestClassName = testClassName;
+    }
+
+    public void setMethodName(String testMethodName) {
+        mTestMethodName = StringEscapeUtils.escapeShell(testMethodName);
+    }
+
+    public void setTestPackageName(String testPackageName) {
+        mTestPackageName = testPackageName;
+    }
+
+    public void setTestSize(String size) {
+        mTestSize = size;
+    }
+
+    public void setRunName(String runName) {
+        mRunName = runName;
+    }
+
+    @Override
+    public void run(final ITestInvocationListener listener) throws DeviceNotAvailableException {
+        installShellProgramAndScriptFiles();
+        installTestsAndServicesApk();
+        enablePrintServices();
+        doRunTests(listener);
+        disablePrintServices();
+        uninstallTestsAndServicesApk();
+        uninstallShellProgramAndScriptFiles();
+    }
+
+    private void doRunTests(ITestInvocationListener listener)
+            throws DeviceNotAvailableException {
+        if (mPackageName == null) {
+            throw new IllegalArgumentException("package name has not been set");
+        }
+        if (mDevice == null) {
+            throw new IllegalArgumentException("Device has not been set");
+        }
+
+        IRemoteAndroidTestRunner runner =  new PrintTestRemoteTestRunner(mPackageName,
+                mRunnerName, mDevice.getIDevice());
+
+        if (mTestClassName != null) {
+            if (mTestMethodName != null) {
+                runner.setMethodName(mTestClassName, mTestMethodName);
+            } else {
+                runner.setClassName(mTestClassName);
+            }
+        } else if (mTestPackageName != null) {
+            runner.setTestPackageName(mTestPackageName);
+        }
+        if (mTestSize != null) {
+            runner.setTestSize(TestSize.getTestSize(mTestSize));
+        }
+        runner.setMaxTimeToOutputResponse(mTestTimeout, TimeUnit.MILLISECONDS);
+        if (mRunName != null) {
+            runner.setRunName(mRunName);
+        }
+        for (Map.Entry<String, String> argEntry : mInstrArgMap.entrySet()) {
+            runner.addInstrumentationArg(argEntry.getKey(), argEntry.getValue());
+        }
+
+        mDevice.runInstrumentationTests(runner, listener);
+    }
+
+    private void installShellProgramAndScriptFiles() throws DeviceNotAvailableException {
+        installFile(PRINT_INSTRUMENT_JAR);
+        installFile(PRINT_INSTRUMENT_SCRIPT);
+    }
+
+    private void installFile(String fileName) throws DeviceNotAvailableException {
+        try {
+            final boolean success = getDevice().pushFile(mCtsBuild.getTestApp(
+                    fileName), SHELL_USER_FOLDER + "/" + fileName);
+            if (!success) {
+                throw new IllegalArgumentException("Failed to install "
+                        + fileName + " on " + getDevice().getSerialNumber());
+           }
+        } catch (FileNotFoundException fnfe) {
+            throw new IllegalArgumentException("Cannot find file: " + fileName);
+        }
+    }
+
+    private void uninstallShellProgramAndScriptFiles() throws DeviceNotAvailableException {
+        getDevice().executeShellCommand("rm " + SHELL_USER_FOLDER + "/"
+                + PRINT_INSTRUMENT_JAR);
+        getDevice().executeShellCommand("rm " + SHELL_USER_FOLDER + "/"
+                + PRINT_INSTRUMENT_SCRIPT);
+    }
+
+    private void installTestsAndServicesApk() throws DeviceNotAvailableException {
+        try {
+            String installCode = getDevice().installPackage(mCtsBuild.getTestApp(
+                    PRINT_TEST_AND_SERVICES_APP_NAME), true);
+            if (installCode != null) {
+                throw new IllegalArgumentException("Failed to install "
+                        + PRINT_TEST_AND_SERVICES_APP_NAME + " on " + getDevice().getSerialNumber()
+                        + ". Reason: " + installCode);
+           }
+        } catch (FileNotFoundException fnfe) {
+            throw new IllegalArgumentException("Cannot find file: "
+                    + PRINT_TEST_AND_SERVICES_APP_NAME);
+        }
+    }
+
+    private void uninstallTestsAndServicesApk() throws DeviceNotAvailableException {
+        getDevice().uninstallPackage(PRINT_TESTS_PACKAGE_NAME);
+    }
+
+    private void enablePrintServices() throws DeviceNotAvailableException {
+        String enabledServicesValue = PRINT_TESTS_PACKAGE_NAME + "/" + FIRST_PRINT_SERVICE_NAME
+                + ":" + PRINT_TESTS_PACKAGE_NAME + "/" + SECOND_PRINT_SERVICE_NAME;
+        SettingsToggler.setSecureString(getDevice(), "enabled_print_services",
+                enabledServicesValue);
+    }
+
+    private void disablePrintServices() throws DeviceNotAvailableException {
+        SettingsToggler.setSecureString(getDevice(), "enabled_print_services", "");
+    }
+}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
index 053b265..448f067 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestFilter.java
@@ -25,6 +25,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.regex.Pattern;
 
 /**
  * Filter for {@link TestIdentifier}s.
@@ -34,7 +35,7 @@
     private final Set<String> mExcludedClasses;
     private final Set<TestIdentifier> mExcludedTests;
     private String mIncludedClass = null;
-    private String mIncludedMethod = null;
+    private Pattern mIncludedMethod = null;
 
     /**
      * Creates a {@link TestFilter}
@@ -87,7 +88,9 @@
      */
     public void setTestInclusion(String className, String method) {
         mIncludedClass = className;
-        mIncludedMethod = method;
+        if (method != null) {
+            mIncludedMethod = Pattern.compile(method);
+        }
     }
 
     /**
@@ -103,7 +106,7 @@
                 // skip
                 continue;
             }
-            if (mIncludedMethod != null && !test.getTestName().equals(mIncludedMethod)) {
+            if (mIncludedMethod != null && !mIncludedMethod.matcher(test.getTestName()).matches()) {
                 // skip
                 continue;
             }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
index 8ab5d18..994da0b 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageDef.java
@@ -47,9 +47,11 @@
     public static final String WRAPPED_NATIVE_TEST = "wrappednative";
     public static final String VM_HOST_TEST = "vmHostTest";
     public static final String ACCESSIBILITY_TEST =
-        "com.android.cts.tradefed.testtype.AccessibilityTestRunner";
+            "com.android.cts.tradefed.testtype.AccessibilityTestRunner";
     public static final String ACCESSIBILITY_SERVICE_TEST =
-        "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+            "com.android.cts.tradefed.testtype.AccessibilityServiceTestRunner";
+    public static final String PRINT_TEST =
+            "com.android.cts.tradefed.testtype.PrintTestRunner";
     public static final String DISPLAY_TEST =
             "com.android.cts.tradefed.testtype.DisplayTestRunner";
     public static final String UIAUTOMATOR_TEST = "uiAutomator";
@@ -61,7 +63,6 @@
     private String mAppNameSpace = null;
     private String mName = null;
     private String mRunner = null;
-    private boolean mIsVMHostTest = false;
     private String mTestType = null;
     private String mJarPath = null;
     private boolean mIsSignatureTest = false;
@@ -230,6 +231,9 @@
         } else if (ACCESSIBILITY_TEST.equals(mTestType)) {
             AccessibilityTestRunner test = new AccessibilityTestRunner();
             return setInstrumentationTest(test, testCaseDir);
+        } else if (PRINT_TEST.equals(mTestType)) {
+            PrintTestRunner test = new PrintTestRunner();
+            return setPrintTest(test, testCaseDir);
         } else if (ACCESSIBILITY_SERVICE_TEST.equals(mTestType)) {
             AccessibilityServiceTestRunner test = new AccessibilityServiceTestRunner();
             return setInstrumentationTest(test, testCaseDir);
@@ -270,6 +274,18 @@
         }
     }
 
+    private PrintTestRunner setPrintTest(PrintTestRunner printTest,
+            File testCaseDir) {
+        printTest.setRunName(getUri());
+        printTest.setPackageName(mAppNameSpace);
+        printTest.setRunnerName(mRunner);
+        printTest.setTestPackageName(mTestPackageName);
+        printTest.setClassName(mClassName);
+        printTest.setMethodName(mMethodName);
+        mDigest = generateDigest(testCaseDir, String.format("%s.apk", mName));
+        return printTest;
+    }
+
     /**
      * Populates given {@link InstrumentationApkTest} with data from the package xml.
      *
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index fe13e00..4efc8bd 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -324,21 +324,11 @@
         return getAnnotation(testClass, testName, KNOWN_FAILURE) != null;
     }
 
-    private static boolean isBrokenTest(final Class<? extends TestCase> testClass,
-            final String testName)  {
-        return getAnnotation(testClass, testName, BROKEN_TEST) != null;
-    }
-
     private static boolean isSuppressed(final Class<? extends TestCase> testClass,
             final String testName)  {
         return getAnnotation(testClass, testName, SUPPRESSED_TEST) != null;
     }
 
-    private static boolean hasSideEffects(final Class<? extends TestCase> testClass,
-            final String testName) {
-        return getAnnotation(testClass, testName, SIDE_EFFECT) != null;
-    }
-
     private static String getAnnotation(final Class<? extends TestCase> testClass,
             final String testName, final String annotationName) {
         try {
@@ -413,15 +403,9 @@
         if (isKnownFailure(test, testName)) {
             System.out.println("ignoring known failure: " + test + "#" + testName);
             return;
-        } else if (isBrokenTest(test, testName)) {
-            System.out.println("ignoring broken test: " + test + "#" + testName);
-            return;
         } else if (isSuppressed(test, testName)) {
             System.out.println("ignoring suppressed test: " + test + "#" + testName);
             return;
-        } else if (hasSideEffects(test, testName)) {
-            System.out.println("ignoring test with side effects: " + test + "#" + testName);
-            return;
         } else if (VogarUtils.isVogarKnownFailure(expectations,
                                                   testClassName,
                                                   testName)) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 0731b49..607d2e5 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -66,8 +66,6 @@
 public class DescriptionGenerator extends Doclet {
     static final String HOST_CONTROLLER = "dalvik.annotation.HostController";
     static final String KNOWN_FAILURE = "dalvik.annotation.KnownFailure";
-    static final String BROKEN_TEST = "dalvik.annotation.BrokenTest";
-    static final String SIDE_EFFECT = "dalvik.annotation.SideEffect";
     static final String SUPPRESSED_TEST = "android.test.suitebuilder.annotation.Suppress";
     static final String CTS_EXPECTATION_DIR = "cts/tests/expectations";
 
@@ -545,8 +543,6 @@
                         controller = getAnnotationDescription(cAnnot);
                     } else if (atype.toString().equals(KNOWN_FAILURE)) {
                         knownFailure = getAnnotationDescription(cAnnot);
-                    } else if (atype.toString().equals(BROKEN_TEST)) {
-                        isBroken = true;
                     } else if (atype.toString().equals(SUPPRESSED_TEST)) {
                         isSuppressed = true;
                     }
diff --git a/tools/vm-tests-tf/etc/starttests b/tools/vm-tests-tf/etc/starttests
new file mode 100755
index 0000000..0c8721b
--- /dev/null
+++ b/tools/vm-tests-tf/etc/starttests
@@ -0,0 +1,296 @@
+#!/bin/bash
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set up prog to be the path of this script, including following symlinks,
+# and set up progdir to be the fully-qualified pathname of its directory.
+
+prog="$0"
+while [ -h "${prog}" ]; do
+    newProg=`/bin/ls -ld "${prog}"`
+    newProg=`expr "${newProg}" : ".* -> \(.*\)$"`
+    if expr "x${newProg}" : 'x/' >/dev/null; then
+        prog="${newProg}"
+    else
+        progdir=`dirname "${prog}"`
+        prog="${progdir}/${newProg}"
+    fi
+done
+oldwd=`pwd`
+progdir=`dirname "${prog}"`
+cd "${progdir}"
+progdir=`pwd`
+prog="${progdir}"/`basename "${prog}"`
+cd "${oldwd}"
+
+libdir=`dirname $progdir`/framework
+
+javaOpts=""
+while expr "x$1" : 'x-J' >/dev/null; do
+    opt=`expr "$1" : '-J\(.*\)'`
+    javaOpts="${javaOpts} -${opt}"
+    shift
+done
+
+
+#######################################################################
+# Original content of invocation script follows. Uses values cleverly
+# deduced by the above code.
+#######################################################################
+
+selection=$1
+interpreter="fast"
+if [ "$selection" = "--portable" ]; then
+    selection=$2;
+    interpreter="portable"
+fi
+
+dalviktest=$ANDROID_BUILD_TOP/out/host/common/obj/JAVA_LIBRARIES/vm-tests-tf_intermediates
+dalviktestdir=$dalviktest/tests
+dexcore=$dalviktest/tests/dot/junit/dexcore.jar
+scriptdata=$dalviktestdir/data/scriptdata
+report=$dalviktest/report.html
+curdate=`date`
+curmode=""
+datadir=/tmp/${USER}
+base=$OUT
+framework=$base/system/framework
+export ANDROID_PRINTF_LOG=tag
+export ANDROID_LOG_TAGS='*:s' # was: jdwp:i dalvikvm:i dalvikvmi:i'
+export ANDROID_DATA=$datadir
+export ANDROID_ROOT=$base/system
+export LD_LIBRARY_PATH=$base/system/lib
+export DYLD_LIBRARY_PATH=$base/system/lib
+debug_opts="-Xcheck:jni -Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=n"
+exe=$base/system/bin/dalvikvm
+bpath=$framework/core.jar
+
+echo "--------------------------------------------------"
+echo "Dalvik VM Test Suite"
+echo "Version 1.0"
+echo "Copyright (c) 2008 The Android Open Source Project"
+echo ""
+
+if [ "$selection" = "--help" ]; then
+    echo "Usage: vm-tests [--help|--portable] [<mnemonic>]"
+    echo ""
+    echo "    --help      prints this help message"
+    echo "    --portable  uses the portable interpreter;"
+    echo "                default is the fast one"
+    echo ""
+    echo "    <mnemonic>  specifies the instruction to test;"
+    echo "                default is to run all tests"
+    echo ""
+    exit 1;
+fi
+
+rm -rf --preserve-root $datadir/dalvik-cache
+mkdir -p $datadir
+mkdir -p $datadir/dalvik-cache
+
+if [ "$TARGET_SIMULATOR" = "true" ]; then
+    echo "Simulator mode, $interpreter interpreter";
+    curmode="simulator"
+else
+    echo "Emulator mode, $interpreter interpreter";
+    curmode="emulator"
+fi
+
+echo ""
+
+pre_report="<html><head><style>
+table tr.ok { background:#a0ffa0; }
+table tr.nok { background:#ffa0a0; }
+table tr.wok { background:#ffffa0; }
+table tr.lok { background:#aaaaff; }
+</style></head>
+<body>
+<h1>Dalvik VM test suite results</h1>
+Generated $curdate (using the $curmode)
+<p>
+<table width='100%'>
+<tr><td>Status</td><td>Target</td><td>Category</td><td>Details</td></tr>"
+post_report="</body></html>"
+
+rm -f $report
+echo $pre_report > $report
+
+# ----------- running each opcode test ------------
+
+export jpassedcnt=0
+export jfailedcnt=0
+export jvfefailedcnt=0
+export jwarningcnt=0
+export jallcnt=0
+export jcolumns=0
+
+# TODO unhack
+if [ "$TARGET_SIMULATOR" = "true" ]; then
+    echo -n ""
+else
+    adb push $dexcore /data/local/tmp/dexcore.jar >> /dev/null 2>&1
+fi
+
+function classnameToJar()
+{
+    echo $1 | sed -e 's#\.#/#g;s#$#.jar#'
+}
+
+while read -u 3 myline;
+do
+    # dot.junit.opcodes.add_double.Main_testB1;dot.junit.opcodes.add_double.d.T_add_double_1 ;opcode add_double;test B #1 (border edge case)
+    # ->
+    # mainclass: dot.junit.opcodes.add_double.Main_testB1
+    # testcasedir: opcodes/add_double
+    # testname: testB1 ->
+    # dir dot/junit/opcodes/add_double/testB1
+
+    # e.g dot.junit.opcodes.add_double.Main_testB1
+    mainclass=`echo $myline | cut -d";" -f1`
+    # e.g dot.junit.opcodes.add_double.d.T_add_double_1, space sep. >=1 entries
+    deps=`echo $myline | cut -d";" -f2`
+
+    jtitle=`echo $myline | cut -d";" -f3`
+    jcomment=`echo $myline | cut -d";" -f4`
+    details=`echo $myline | cut -d";" -f5`
+
+    if [ "$selection" == "" ] || [ "$jtitle" == "$selection" ]; then
+
+        (( jallcnt += 1 ))
+
+        cd $dalviktestdir
+        rm -f $datadir/dalvikout
+        # write dalvik output to file
+        echo -n "mk_b:" > $datadir/dalvikout
+
+        if [ "$TARGET_SIMULATOR" = "true" ]; then
+            classpath=`classnameToJar ${mainclass}`
+            for dep in ${deps}; do
+                depJar=`classnameToJar ${dep}`
+                classpath=${classpath}:${depJar}
+            done
+            $valgrind $exe -Xint:$interpreter -Xmx512M -Xss32K -Xbootclasspath:$bpath $debug_opts \
+                -classpath $dexcore:$classpath $mainclass >> $datadir/dalvikout 2>&1
+
+            RESULTCODE=$?
+            if [ ${RESULTCODE} -ne 0 ]; then
+                echo "Dalvik VM failed, result=${RESULTCODE}" >> $datadir/dalvikout 2>&1
+            fi
+        else
+            classpath="/data/local/tmp/dexcore.jar"
+            deps=${deps}" "${mainclass}
+            pushedjars=""
+            for dep in ${deps}; do
+                depJar=`classnameToJar ${dep}`
+                depFileName=`basename ${depJar}`
+                deviceFileName=/data/local/tmp/${depFileName}
+                adb push ${depJar} ${deviceFileName} &> /dev/null
+                classpath=${classpath}:${deviceFileName}
+                pushedjars=${pushedjars}" "${deviceFileName}
+            done
+
+            adb shell dalvikvm -Djava.io.tmpdir=/data/local/tmp \
+                -classpath $classpath $mainclass >> $datadir/dalvikout 2>&1 && \
+                echo -n dvmpassed: >> $datadir/dalvikout 2>&1
+
+            for jar in ${pushedjars}; do
+                adb shell rm ${jar} &> /dev/null
+            done
+        fi
+
+        echo -n "mk_s:" >> $datadir/dalvikout
+        # Verify tmpout only contains mkdxc_start;mkdxc_stop -> no system.out/err
+        # because of exception. If ok -> green report line else red report with info
+        # between mkdxc_start and stop
+        vmresult=`cat $datadir/dalvikout`
+
+        if [[ ("$vmresult" == "mk_b:mk_s:") || ("$vmresult" == "mk_b:dvmpassed:mk_s:") ]]; then
+            (( jpassedcnt += 1 ))
+            echo -n "<tr class=\"ok\"><td>Success</td><td>$jtitle</td>" >> $report
+            echo "<td>$jcomment</td><td>$details</td></tr>" >> $report
+            echo -n "."
+        else
+            vmres=`cat $datadir/dalvikout | sed -e 's/mk_b://;s/mk_s://'`
+            vmres="$details<br><pre>$vmres</pre>"
+
+            stacktraces=`echo $vmresult | grep "java\.lang\." | grep -c "at dot\.junit\."`
+            if [[ $stacktraces > 0 ]]; then
+                jtype=`echo "$mainclass" | sed -e 's/.*_test\([^0-9]*\)[0-9].*/\1/' `
+                if [ "$jtype" == "VFE" ]; then
+                    (( jvfefailedcnt += 1 ))
+                    echo -n "V"
+                else
+                    (( jfailedcnt += 1 ))
+                    echo -n "F"
+                fi
+
+                echo "<tr class=\"nok\"><td>Failure</td><td>$jtitle</td><td>" >> $report
+                echo "$jcomment</td><td>$vmres</td></tr>" >> $report
+            else
+                (( jwarningcnt += 1 ))
+                echo "<tr class=\"wok\"><td>Failure</td><td>$jtitle</td><td>" >> $report
+                echo "$jcomment</td><td>(No stacktrace, but errors on console)" >> $report
+                echo "<br>$vmres</td></tr>" >> $report
+                echo -n "C"
+            fi
+        fi
+
+        (( jcolumns += 1 ))
+        if [ ${jcolumns} -eq 40 ]; then
+            echo ""
+            (( jcolumns = 0 ))
+        fi
+
+    fi
+# Use fd nr 3 to avoid subshelling via cat since this looses all
+# variables(and thus also the counters we are interested in).
+done 3<$scriptdata
+
+echo "</table>" >> $report
+let jallcalccnt=$jpassedcnt+$jfailedcnt+$jvfefailedcnt+$jwarningcnt
+if [ $jallcalccnt -ne $jallcnt ]; then
+    echo "<br>error: green & red != total , $jallcalccnt -ne $jallcnt" >> $report
+    exit 1;
+fi
+
+echo $post_report >> $report
+
+echo "<br>Tests run: ${jallcnt}" >> $report
+echo "<br>Functional failures: ${jfailedcnt}" >> $report
+echo "<br>Verifier failures: ${jvfefailedcnt}" >> $report
+echo "<br>Console errors: ${jwarningcnt}" >> $report
+
+echo $post_report >> $report
+
+if [[ jcolumns -ne 0 ]]; then
+    echo ""
+fi
+
+echo ""
+
+if [[ jallcnt -eq jpassedcnt ]]; then
+    echo "OK (${jpassedcnt} tests)"
+else
+    echo "FAILURES!!!"
+    echo ""
+    echo "Tests run          : ${jallcnt}"
+    echo "Functional failures: ${jfailedcnt}"
+    echo "Verifier failures  : ${jvfefailedcnt}"
+    echo "Console errors     : ${jwarningcnt}"
+fi
+
+echo ""
+echo "Please see complete report in ${report}"
+echo "--------------------------------------------------"
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
index 02d509a..8068732 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual/d/T_invoke_virtual_12.d
@@ -25,6 +25,7 @@
 .end method
 
 .method public test(Ljava/lang/String;)V
+.limit regs 2
     return-void
 .end method
 
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
index 9b63ef8..93df0f5 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_virtual_range/d/T_invoke_virtual_range_12.d
@@ -25,6 +25,7 @@
 .end method
 
 .method public test(Ljava/lang/String;)V
+.limit regs 2
     return-void
 .end method
 
diff --git a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
index 9d4bbed..d18ff4f 100644
--- a/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
+++ b/tools/vm-tests-tf/src/util/build/BuildDalvikSuite.java
@@ -260,6 +260,7 @@
     private void handleTests() throws IOException {
         System.out.println("collected " + testMethodsCnt + " test methods in " +
                 testClassCnt + " junit test classes");
+        String datafileContent = "";
         Set<BuildStep> targets = new TreeSet<BuildStep>();
 
         javacHostJunitBuildStep = new JavacBuildStep(HOSTJUNIT_CLASSES_OUTPUT_FOLDER, CLASS_PATH);
@@ -336,6 +337,86 @@
                 targets.add(dexBuildStep);
                 // }
 
+
+                // prepare the entry in the data file for the bash script.
+                // e.g.
+                // main class to execute; opcode/constraint; test purpose
+                // dxc.junit.opcodes.aaload.Main_testN1;aaload;normal case test
+                // (#1)
+
+                char ca = method.charAt("test".length()); // either N,B,E,
+                // or V (VFE)
+                String comment;
+                switch (ca) {
+                case 'N':
+                    comment = "Normal #" + method.substring(5);
+                    break;
+                case 'B':
+                    comment = "Boundary #" + method.substring(5);
+                    break;
+                case 'E':
+                    comment = "Exception #" + method.substring(5);
+                    break;
+                case 'V':
+                    comment = "Verifier #" + method.substring(7);
+                    break;
+                default:
+                    throw new RuntimeException("unknown test abbreviation:"
+                            + method + " for " + fqcn);
+                }
+
+                String line = pName + ".Main_" + method + ";";
+                for (String className : dependentTestClassNames) {
+                    line += className + " ";
+                }
+
+
+                // test description
+                String[] pparts = pName.split("\\.");
+                // detail e.g. add_double
+                String detail = pparts[pparts.length-1];
+                // type := opcode | verify
+                String type = pparts[pparts.length-2];
+
+                String description;
+                if ("format".equals(type)) {
+                    description = "format";
+                } else if ("opcodes".equals(type)) {
+                    // Beautify name, so it matches the actual mnemonic
+                    detail = detail.replaceAll("_", "-");
+                    detail = detail.replace("-from16", "/from16");
+                    detail = detail.replace("-high16", "/high16");
+                    detail = detail.replace("-lit8", "/lit8");
+                    detail = detail.replace("-lit16", "/lit16");
+                    detail = detail.replace("-4", "/4");
+                    detail = detail.replace("-16", "/16");
+                    detail = detail.replace("-32", "/32");
+                    detail = detail.replace("-jumbo", "/jumbo");
+                    detail = detail.replace("-range", "/range");
+                    detail = detail.replace("-2addr", "/2addr");
+
+                    // Unescape reserved words
+                    detail = detail.replace("opc-", "");
+
+                    description = detail;
+                } else if ("verify".equals(type)) {
+                    description = "verifier";
+                } else {
+                    description = type + " " + detail;
+                }
+
+                String details = (md.title != null ? md.title : "");
+                if (md.constraint != null) {
+                    details = " Constraint " + md.constraint + ", " + details;
+                }
+                if (details.length() != 0) {
+                    details = details.substring(0, 1).toUpperCase()
+                            + details.substring(1);
+                }
+
+                line += ";" + description + ";" + comment + ";" + details;
+
+                datafileContent += line + "\n";
                 generateBuildStepFor(pName, method, dependentTestClassNames,
                         targets);
             }
@@ -346,6 +427,10 @@
         // write latest HOSTJUNIT generated file.
         flushHostJunitFile();
 
+        File scriptDataDir = new File(OUTPUT_FOLDER + "/data/");
+        scriptDataDir.mkdirs();
+        writeToFile(new File(scriptDataDir, "scriptdata"), datafileContent);
+
         if (!javacHostJunitBuildStep.build()) {
             System.out.println("main javac cts-host-hostjunit-classes build step failed");
             System.exit(1);