Merge "Add a required feature to Projection/Presentation tests and exclude tests not meant for Leanback where test_required_features cannot be applied." into lmp-dev
diff --git a/CtsBuild.mk b/CtsBuild.mk
index 86ecde8..dbe93c7 100644
--- a/CtsBuild.mk
+++ b/CtsBuild.mk
@@ -47,7 +47,7 @@
 endef
 
 define cts-get-native-paths
-	$(foreach exe,$(1),$(call intermediates-dir-for,EXECUTABLES,$(exe))/$(exe))
+	$(foreach exe,$(1),$(call intermediates-dir-for,EXECUTABLES,$(exe),,,$(3))/$(exe)$(2))
 endef
 
 define cts-get-package-paths
@@ -61,3 +61,7 @@
 define cts-get-executable-paths
 	$(foreach executable,$(1),$(CTS_TESTCASES_OUT)/$(executable))
 endef
+
+define cts-get-deqp-test-xmls
+	$(foreach api,$(1),$(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(api).xml)
+endef
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index ce67d37..721b9d4 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -69,6 +69,7 @@
     CtsDeviceTaskswitchingControl \
     CtsDeviceUi \
     CtsIntentReceiverApp \
+    CtsIntentSenderApp \
     CtsManagedProfileApp \
     CtsMonkeyApp \
     CtsMonkeyApp2 \
@@ -121,6 +122,7 @@
     CtsGraphicsTestCases \
     CtsGraphics2TestCases \
     CtsHardwareTestCases \
+    CtsJobSchedulerDeviceTestCases \
     CtsJniTestCases \
     CtsKeystoreTestCases \
     CtsLocationTestCases \
@@ -177,13 +179,16 @@
     CtsSecurityHostTestCases \
     CtsUsbTests
 
-# Native test executables that need to have associated test XMLs.
-cts_native_exes := \
+# List of native tests. For 32 bit targets, assumes that there will be
+# one test executable, and it will end in 32. For 64 bit targets, assumes
+# that there will be two executables, one that ends in 32 for the 32
+# bit executable and one that ends in 64 for the 64 bit executable.
+cts_native_tests := \
     NativeMediaTest_SL \
     NativeMediaTest_XA \
 
 ifeq ($(HOST_OS)-$(HOST_ARCH),$(filter $(HOST_OS)-$(HOST_ARCH),linux-x86 linux-x86_64))
-cts_native_exes += bionic-unit-tests-cts
+cts_native_tests += bionic-unit-tests-cts
 endif
 
 cts_ui_tests := \
@@ -199,25 +204,38 @@
 cts_target_junit_tests := \
     CtsJdwp
 
+cts_deqp_test_apis := \
+    gles3 \
+    gles31
+
 # All the files that will end up under the repository/testcases
 # directory of the final CTS distribution.
 CTS_TEST_CASES := $(call cts-get-lib-paths,$(cts_host_libraries)) \
     $(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_target_junit_tests)) \
     $(call cts-get-executable-paths,$(cts_device_executables))
 
+# NOTE: If compiling on a 64 bit target, TARGET_2ND_ARCH will be non-empty
+# and will cause the function to expand to the secondary arch object
+# directory. If compiling on a 32 bit target, TARGET_2ND_ARCH will be
+# empty and will cause the function to expand to the primary arch object
+# directory.
+CTS_TEST_CASES += $(call cts-get-native-paths,$(cts_native_tests),32,$(TARGET_2ND_ARCH))
+
+ifeq ($(TARGET_IS_64_BIT),true)
+CTS_TEST_CASES += $(call cts-get-native-paths,$(cts_native_tests),64)
+endif
+
 # All the XMLs that will end up under the repository/testcases
 # and that need to be created before making the final CTS distribution.
 CTS_TEST_XMLS := $(call cts-get-test-xmls,$(cts_host_libraries)) \
     $(call cts-get-test-xmls,$(cts_test_packages)) \
-    $(call cts-get-test-xmls,$(cts_native_exes)) \
+    $(call cts-get-test-xmls,$(cts_native_tests)) \
     $(call cts-get-test-xmls,$(cts_target_junit_tests)) \
     $(call cts-get-test-xmls,$(cts_ui_tests)) \
-    external/deqp/android/cts/com.drawelements.deqp.gles3.xml \
-    external/deqp/android/cts/com.drawelements.deqp.gles31.xml
+    $(call cts-get-deqp-test-xmls,$(cts_deqp_test_apis))
 
 # The following files will be placed in the tools directory of the CTS distribution
 CTS_TOOLS_LIST :=
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 88386ec..34bc230 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -21,6 +21,8 @@
 
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
 
+LOCAL_MULTILIB := both
+
 LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
 
 LOCAL_STATIC_JAVA_LIBRARIES := cts-sensors-tests ctstestrunner
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 1204396..106611c 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -48,6 +48,7 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <uses-permission android:name="android.permission.READ_CONTACTS"/>
     <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
 
     <!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
@@ -1279,6 +1280,33 @@
             </intent-filter>
         </receiver>
 
+        <activity android:name=".jobscheduler.IdleConstraintTestActivity" android:label="@string/js_idle_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_jobscheduler" />
+        </activity>
+
+        <activity android:name=".jobscheduler.ChargingConstraintTestActivity" android:label="@string/js_charging_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_jobscheduler" />
+        </activity>
+
+        <activity android:name=".jobscheduler.ConnectivityConstraintTestActivity" android:label="@string/js_connectivity_test">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.cts.intent.category.MANUAL_TEST" />
+            </intent-filter>
+            <meta-data android:name="test_category" android:value="@string/test_category_jobscheduler" />
+        </activity>
+
+        <service android:name=".jobscheduler.MockJobService"
+            android:permission="android.permission.BIND_JOB_SERVICE"/>
+
     </application>
 
 </manifest>
diff --git a/apps/CtsVerifier/res/layout/js_charging.xml b/apps/CtsVerifier/res/layout/js_charging.xml
new file mode 100644
index 0000000..4c0e552
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/js_charging.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/js_test_description"
+        android:layout_margin="@dimen/js_padding"/>
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/js_padding"
+        android:text="@string/js_charging_description_1"
+        android:textStyle="bold"/>
+    <Button
+        android:id="@+id/js_charging_start_test_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/js_start_test_text"
+        android:onClick="startTest"
+        android:enabled="false"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/charging_off_test_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_charging_off_test"
+            android:textSize="16dp"/>
+    </LinearLayout>
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="@dimen/js_padding"
+        android:text="@string/js_charging_description_2"
+        android:textStyle="bold"/>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/charging_on_test_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_charging_on_test"
+            android:textSize="16dp"/>
+    </LinearLayout>
+    <include layout="@layout/pass_fail_buttons" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/js_connectivity.xml b/apps/CtsVerifier/res/layout/js_connectivity.xml
new file mode 100644
index 0000000..5208c18
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/js_connectivity.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/js_test_description"
+        android:layout_margin="@dimen/js_padding"/>
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/js_connectivity_description_1"
+        android:layout_margin="@dimen/js_padding"
+        android:textStyle="bold"/>
+
+    <Button
+        android:id="@+id/js_connectivity_start_test_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/js_start_test_text"
+        android:onClick="startTest"
+        android:enabled="false"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/connectivity_off_test_unmetered_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_unmetered_connectivity_test"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/connectivity_off_test_any_connectivity_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_any_connectivity_test"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/connectivity_off_test_no_connectivity_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_no_connectivity_test"
+            android:textSize="16dp"/>
+    </LinearLayout>
+
+    <include layout="@layout/pass_fail_buttons" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/js_idle.xml b/apps/CtsVerifier/res/layout/js_idle.xml
new file mode 100644
index 0000000..90e55ec
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/js_idle.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical" android:layout_width="match_parent"
+    android:layout_height="match_parent">
+
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/js_test_description"
+        android:layout_margin="@dimen/js_padding"/>
+    <TextView
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/js_idle_description_1"
+        android:layout_margin="@dimen/js_padding"
+        android:textStyle="bold"/>
+
+    <Button
+        android:id="@+id/js_idle_start_test_button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:text="@string/js_start_test_text"
+        android:onClick="startTest"
+        android:enabled="false"/>
+
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/idle_off_test_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_idle_item_idle_off"
+            android:textSize="16dp"/>
+    </LinearLayout>
+    <LinearLayout
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginTop="@dimen/js_padding"
+        android:layout_marginBottom="@dimen/js_padding">
+        <ImageView
+            android:id="@+id/idle_on_test_image"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/fs_indeterminate"
+            android:layout_marginRight="@dimen/js_padding"/>
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/js_idle_item_idle_on"
+            android:textSize="16dp"/>
+    </LinearLayout>
+    <include layout="@layout/pass_fail_buttons" />
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pa_main.xml b/apps/CtsVerifier/res/layout/pa_main.xml
index b6b8e4b..76cb7d4 100644
--- a/apps/CtsVerifier/res/layout/pa_main.xml
+++ b/apps/CtsVerifier/res/layout/pa_main.xml
@@ -17,11 +17,15 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent" >
 
+    <include
+        android:id="@+id/pass_fail_buttons"
+        android:layout_gravity="top"
+        layout="@layout/pass_fail_buttons" />
+
     <TextureView
         android:id="@+id/texture_view"
         android:layout_width="match_parent"
-        android:layout_height="match_parent" />
-
-     <include layout="@layout/pass_fail_buttons" />
+        android:layout_height="match_parent"
+        android:layout_below="@id/pass_fail_buttons" />
 
 </RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/dimens.xml b/apps/CtsVerifier/res/values/dimens.xml
index b1367f7..8df5b35 100644
--- a/apps/CtsVerifier/res/values/dimens.xml
+++ b/apps/CtsVerifier/res/values/dimens.xml
@@ -36,4 +36,6 @@
     <dimen name="snsr_view_padding_left">8dp</dimen>
     <dimen name="snsr_view_padding_right">8dp</dimen>
 
+    <dimen name="js_padding">10dp</dimen>
+
 </resources>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 7364340..fa86474 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -35,6 +35,7 @@
     <string name="test_category_streaming">Streaming</string>
     <string name="test_category_features">Features</string>
     <string name="test_category_deskclock">Clock</string>
+    <string name="test_category_jobscheduler">Job Scheduler</string>
     <string name="test_category_other">Other</string>
     <string name="clear">Clear</string>
     <string name="test_results_cleared">Test results cleared.</string>
@@ -469,7 +470,7 @@
     <string name="snsr_test_pass">PASS</string>
     <string name="snsr_test_skipped">SKIPPED</string>
     <string name="snsr_test_fail">FAIL</string>
-    <string name="snsr_executing_test">\nExecuting test case \'%1$s\'..\n</string>
+    <string name="snsr_execution_time">Test execution time %1$s sec</string>
 
     <!-- Strings to interact with users in Sensor Tests -->
     <string name="snsr_test_play_sound">A sound will be played once the verification is complete...</string>
@@ -479,10 +480,10 @@
     <string name="snsr_keep_device_rotating_clockwise">Once the test begins, you will have to keep rotating the device clockwise.</string>
     <string name="snsr_wait_for_user">Press \'Next\' to continue.</string>
     <string name="snsr_wait_to_begin">Press \'Next\' to begin.</string>
-    <string name="snsr_wait_to_complete">Press \'Next\' to complete.</string>
     <string name="snsr_on_complete_return">After completing the task, go back to this test.</string>
     <string name="snsr_movement_expected">Movement was expected during the test. Found=%1$b.</string>
-    <string name="snsr_sensor_feature_deactivation">Additionally, turn off any other features installed in the device, that register for sensors. Once you are done, you can continue the test.</string>
+    <string name="snsr_sensor_feature_deactivation">Turn off any special features installed in the
+        device that register for sensors. Once you are done, you can begin the test.</string>
     <string name="snsr_setting_mode_request">You will be redirected to set \'%1$s\' to: %2$s.</string>
     <string name="snsr_setting_mode_set">\'%1$s\' set to: %2$s.</string>
     <string name="snsr_setting_mode_not_set">\'%1$s\' not set to: %2$s.</string>
@@ -491,7 +492,7 @@
     <string name="snsr_setting_auto_rotate_screen_mode">Auto-rotate screen</string>
     <string name="snsr_setting_keep_screen_on">Stay awake</string>
     <string name="snsr_setting_location_mode">Location</string>
-    <string name="snsr_setting_auto_screen_off_mode">Display Sleep</string>
+    <string name="snsr_setting_ambient_display">Ambient Display</string>
     <string name="snsr_pass_on_error">Pass Anyway</string>
     <string name="snsr_run_automated_tests">The screen will be turned off to execute the tests,
         when tests complete, the device will vibrate and the screen will be turned back on.</string>
@@ -526,7 +527,6 @@
         facing the ceiling. Read the instructions for each scenario, before you perform the
         test.</string>
     <string name="snsr_gyro_device_static">Leave the device static.</string>
-    <string name="snsr_gyro_rotate_clockwise">Rotate the device clockwise.</string>
     <string name="snsr_gyro_rotate_device">Once you begin the test, you will need to rotate the
         device 360deg (one time) in the direction show by the animation, then place it back on the
         flat surface.</string>
@@ -550,20 +550,15 @@
     <string name="snsr_mag_measurement">-&gt; (%1$.2f, %2$.2f, %3$.2f) : %4$.2f uT</string>
 
     <!-- Sensor Value Accuracy -->
-    <string name="snsr_val_acc_test">Sensor Value Accuracy Tests</string>
     <string name="snsr_rot_vec_test">Rotation Vector Accuracy Test</string>
-    <string name="snsr_collected_events_length">Sensor(%2$s). Collected events expected to be greater than zero. Found=%1$d.</string>
     <string name="snsr_event_length">Sensor(%3$s). Event values expected to have size=%1$d. Found=%2$d.</string>
-    <string name="snsr_event_length_positive">Sensor(%2$s). Event values expected to have size > 0. Found=%1$d</string>
     <string name="snsr_event_value">Sensor(%3$s). Event value[0] expected to be of value=%1$f. Found=%2$f.</string>
     <string name="snsr_event_time">Sensor(%5$s). Event timestamp expected to be synchronized with SystemClock.elapsedRealtimeNanos(). Event received at=%1$d. Event timestamp=%2$d. Delta=%3$d. Threshold=%4$d.</string>
-    <string name="snsr_event_time_positive">Sensor(%2$s). Event timestamp expected to positive (> 0). Found=%1$d.</string>
 
     <!-- Sensor Batching -->
     <string name="snsr_batch_test">Sensor Batching Tests</string>
     <string name="snsr_batching_walking_needed">Once the test begins, you will have to take the
         device in your hand and walk.</string>
-    <string name="snsr_batching_fifo_count">FifoReservedEventCount=%1$d. Expected to be at most FifoMaxEventCount=%2$d.</string>
 
     <!-- Sensor Synchronization -->
     <string name="snsr_synch_test">Sensor Synchronization Test</string>
@@ -1271,4 +1266,28 @@
     <string name="device_owner_negative_test">Device owner negative test</string>
     <string name="device_owner_negative_test_info">Device owner provisioning should only work on new or factory reset devices. Please click on the "Start provisioning" button and verify that you get a warning dialog telling you that the device is already set up. If that is the case, this test has passed.</string>
     <string name="start_device_owner_provisioning_button">Start provisioning</string>
+
+    <!-- Strings for JobScheduler Tests -->
+    <string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
+
+    <string name="js_idle_test">Idle Mode Constraints</string>
+    <string name="js_start_test_text">Start test</string>
+    <string name="js_idle_instructions">Verify the behaviour of the JobScheduler API for when the device is in idle mode. Simply follow the on-screen instructions.</string>
+    <string name="js_idle_description_1">Turn the screen off and then back on in order to begin.</string>
+    <string name="js_idle_item_idle_off">Idle job does not execute when device is not idle.</string>
+    <string name="js_idle_item_idle_on">Idle job does execute when device is forced into idle.</string>
+
+    <string name="js_charging_test">Charging Constraints</string>
+    <string name="js_charging_instructions">Verify the behaviour of the JobScheduler API for when the device is on power and unplugged from power. Simply follow the on-screen instructions.</string>
+    <string name="js_charging_description_1">Unplug the phone in order to begin.</string>
+    <string name="js_charging_off_test">Device not charging will not execute a job with a charging constraint.</string>
+    <string name="js_charging_on_test">Device when charging will execute a job with a charging constraint.</string>
+    <string name="js_charging_description_2">After the above test has passed, plug the device back in to continue. If the above failed, you can simply fail this test.</string>
+
+    <string name="js_connectivity_test">Connectivity Constraints</string>
+    <string name="js_connectivity_instructions">Verify the behaviour of the JobScheduler API for when the device has no access to data connectivity. Simply follow the on-screen instructions.</string>
+    <string name="js_connectivity_description_1">Disable WiFi and Cellular data to begin.</string>
+    <string name="js_unmetered_connectivity_test">Device with no connectivity will not execute a job with an unmetered connectivity constraint.</string>
+    <string name="js_any_connectivity_test">Device with no connectivity will not execute a job with an unmetered connectivity constraint.</string>
+    <string name="js_no_connectivity_test">Device with no connectivity will still execute a job with no connectivity constraints.</string>
 </resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java
new file mode 100644
index 0000000..2a94ace
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ChargingConstraintTestActivity.java
@@ -0,0 +1,184 @@
+package com.android.cts.verifier.jobscheduler;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import com.android.cts.verifier.R;
+
+/**
+ *  This activity runs the following tests:
+ *     - Ask the tester to unplug the phone, and verify that jobs with charging constraints will
+ *      not run.
+ *     - Ask the tester to ensure the phone is plugged in, and verify that jobs with charging
+ *      constraints are run.
+ */
+@TargetApi(21)
+public class ChargingConstraintTestActivity extends ConstraintTestActivity {
+
+    private static final int ON_CHARGING_JOB_ID =
+            ChargingConstraintTestActivity.class.hashCode() + 0;
+    private static final int OFF_CHARGING_JOB_ID =
+            ChargingConstraintTestActivity.class.hashCode() + 1;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set up the UI.
+        setContentView(R.layout.js_charging);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.js_charging_test, R.string.js_charging_instructions, -1);
+        mStartButton = (Button) findViewById(R.id.js_charging_start_test_button);
+
+        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
+
+        // Register receiver for connected/disconnected power events.
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_POWER_CONNECTED);
+        intentFilter.addAction(Intent.ACTION_POWER_DISCONNECTED);
+
+        registerReceiver(mChargingChangedReceiver, intentFilter);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mChargingChangedReceiver);
+    }
+
+    @Override
+    public void startTestImpl() {
+        new TestDeviceUnpluggedConstraint().execute();
+    }
+
+    private BroadcastReceiver mChargingChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_POWER_DISCONNECTED.equals(intent.getAction())) {
+                mDeviceUnpluggedTestPassed = false;
+                mStartButton.setEnabled(true);
+            } else if (Intent.ACTION_POWER_CONNECTED.equals(intent.getAction())) {
+                mStartButton.setEnabled(false);
+                if (mDeviceUnpluggedTestPassed) {
+                    continueTest();
+                }
+            }
+        }
+    };
+
+    /** Simple state boolean we use to determine whether to continue with the second test. */
+    private boolean mDeviceUnpluggedTestPassed = false;
+
+    /**
+     * After the first test has passed, and preconditions are met, this will kick off the second
+     * test.
+     * See {@link #startTest(android.view.View)}.
+     */
+    private void continueTest() {
+        new TestDevicePluggedInConstraint().execute();
+    }
+
+    /**
+     * Test blocks and can't be run on the main thread.
+     */
+    private void testChargingConstraintFails_notCharging() {
+        mTestEnvironment.setUp();
+
+        mTestEnvironment.setExpectedExecutions(0);
+        JobInfo runOnCharge = new JobInfo.Builder(OFF_CHARGING_JOB_ID, mMockComponent)
+                .setRequiresCharging(true)
+                .build();
+        mJobScheduler.schedule(runOnCharge);
+
+        // Send intent to kick off any jobs. This will be a no-op as the device is not plugged in;
+        // the JobScheduler tracks charging state independently.
+        sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitTimeout();
+        } catch (InterruptedException e) {
+            testPassed = false;
+        }
+        mDeviceUnpluggedTestPassed = testPassed;
+        runOnUiThread(new ChargingConstraintTestResultRunner(OFF_CHARGING_JOB_ID, testPassed));
+    }
+
+    /**
+     * Test blocks and can't be run on the main thread.
+     */
+    private void testChargingConstraintExecutes_onCharging() {
+        mTestEnvironment.setUp();
+
+        JobInfo delayConstraintAndUnexpiredDeadline =
+                new JobInfo.Builder(ON_CHARGING_JOB_ID, mMockComponent)
+                        .setRequiresCharging(true)
+                        .build();
+
+        mTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(delayConstraintAndUnexpiredDeadline);
+
+        // Force the JobScheduler to consider any jobs that have charging constraints.
+        sendBroadcast(EXPEDITE_STABLE_CHARGING);
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitExecution();
+        } catch (InterruptedException e) {
+            testPassed = false;
+        }
+        runOnUiThread(new ChargingConstraintTestResultRunner(ON_CHARGING_JOB_ID, testPassed));
+    }
+
+    /** Run test for when the <bold>device is not connected to power.</bold>. */
+    private class TestDeviceUnpluggedConstraint extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... voids) {
+            testChargingConstraintFails_notCharging();
+
+            // Do not call notifyTestCompleted here, as we're still waiting for the user to put
+            // the device back on charge to continue with TestDevicePluggedInConstraint.
+            return null;
+        }
+    }
+
+    /** Run test for when the <bold>device is connected to power.</bold> */
+    private class TestDevicePluggedInConstraint extends AsyncTask<Void, Void, Void> {
+        @Override
+        protected Void doInBackground(Void... voids) {
+            testChargingConstraintExecutes_onCharging();
+
+            notifyTestCompleted();
+            return null;
+        }
+    }
+
+    private class ChargingConstraintTestResultRunner extends TestResultRunner {
+        ChargingConstraintTestResultRunner(int jobId, boolean testPassed) {
+            super(jobId, testPassed);
+        }
+
+        @Override
+        public void run() {
+            ImageView view;
+            if (mJobId == OFF_CHARGING_JOB_ID) {
+                view = (ImageView) findViewById(R.id.charging_off_test_image);
+            } else if (mJobId == ON_CHARGING_JOB_ID) {
+                view = (ImageView) findViewById(R.id.charging_on_test_image);
+            } else {
+                noteInvalidTest();
+                return;
+            }
+            view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
new file mode 100644
index 0000000..e97539d
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
@@ -0,0 +1,184 @@
+package com.android.cts.verifier.jobscheduler;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.ImageView;
+
+import com.android.cts.verifier.R;
+
+/**
+ * The majority of the connectivity constraints are done in the device-side test app
+ * android.jobscheduler.cts.deviceside. However a manual tester is required to completely turn off
+ * connectivity on the device in order to verify that jobs with connectivity constraints will not
+ * run in the absence of an internet connection.
+ */
+@TargetApi(21)
+public class ConnectivityConstraintTestActivity extends ConstraintTestActivity {
+    private static final String TAG = "ConnectivityConstraintTestActivity";
+    private static final int ANY_CONNECTIVITY_JOB_ID =
+            ConnectivityConstraintTestActivity.class.hashCode() + 0;
+    private static final int UNMETERED_CONNECTIVITY_JOB_ID =
+            ConnectivityConstraintTestActivity.class.hashCode() + 1;
+    private static final int NO_CONNECTIVITY_JOB_ID =
+            ConnectivityConstraintTestActivity.class.hashCode() + 2;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set up the UI.
+        setContentView(R.layout.js_connectivity);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.js_connectivity_test, R.string.js_connectivity_instructions, -1);
+        mStartButton = (Button) findViewById(R.id.js_connectivity_start_test_button);
+
+        // Disable test start if there is data connectivity.
+        mStartButton.setEnabled(isDataUnavailable());
+        // Register receiver to listen for connectivity changes.
+        IntentFilter intentFilter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+        registerReceiver(mConnectivityChangedReceiver, intentFilter);
+
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mConnectivityChangedReceiver);
+    }
+
+    @Override
+    protected void startTestImpl() {
+        new TestConnectivityConstraint().execute();
+    }
+
+    /** Ensure that there's no connectivity before we allow the test to start. */
+    BroadcastReceiver mConnectivityChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            Log.d(TAG, "received: " + intent);
+            String extras = "";
+            for (String name : intent.getExtras().keySet()) {
+                extras += " |" + name + " " + intent.getExtras().get(name) + "|";
+
+            }
+            Log.d(TAG, "extras: " + extras);
+            if (ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) {
+                // Only enable the test when we know there is no connectivity.
+                mStartButton.setEnabled(isDataUnavailable());
+            }
+        }
+    };
+
+    private void testUnmeteredConstraintFails_noConnectivity() {
+        testConnectivityConstraintFailsImpl(
+                JobInfo.NETWORK_TYPE_UNMETERED, UNMETERED_CONNECTIVITY_JOB_ID);
+    }
+
+    private void testAnyConnectivityConstraintFails_noConnectivity() {
+        testConnectivityConstraintFailsImpl(JobInfo.NETWORK_TYPE_ANY, ANY_CONNECTIVITY_JOB_ID);
+    }
+
+    private void testNoConnectivityConstraintExecutes_noConnectivity() {
+        JobInfo testJob = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
+                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
+                .setOverrideDeadline(100000L)  // Will not expire.
+                .build();
+
+        mTestEnvironment.setUp();
+        mTestEnvironment.setExpectedExecutions(1);
+
+        mJobScheduler.schedule(testJob);
+
+        // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
+        sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitExecution();
+        } catch (InterruptedException e) {
+            testPassed = false;
+        }
+        runOnUiThread(
+                new ConnectivityConstraintTestResultRunner(NO_CONNECTIVITY_JOB_ID, testPassed));
+    }
+
+    private void testConnectivityConstraintFailsImpl(int requiredNetworkType, int jobId) {
+        // Use arguments provided to construct job with required connectivity constraint.
+        JobInfo testJob = new JobInfo.Builder(jobId, mMockComponent)
+                .setRequiredNetworkType(requiredNetworkType)
+                .build();
+
+        mTestEnvironment.setUp();
+        mTestEnvironment.setExpectedExecutions(0);
+
+        mJobScheduler.schedule(testJob);
+
+        // Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
+        sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitTimeout();
+        } catch (InterruptedException e) {
+            testPassed = false;
+        }
+        runOnUiThread(
+                new ConnectivityConstraintTestResultRunner(jobId, testPassed));
+    }
+
+    /** Query the active network connection and return if there is no data connection. */
+    private boolean isDataUnavailable() {
+        final ConnectivityManager cm =
+                (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+        final NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
+
+        return (activeNetwork == null) ||
+                !activeNetwork.isConnectedOrConnecting();
+    }
+
+    private class TestConnectivityConstraint extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... voids) {
+            testUnmeteredConstraintFails_noConnectivity();
+            testAnyConnectivityConstraintFails_noConnectivity();
+            testNoConnectivityConstraintExecutes_noConnectivity();
+
+            notifyTestCompleted();
+            return null;
+        }
+    }
+
+    private class ConnectivityConstraintTestResultRunner extends TestResultRunner {
+        ConnectivityConstraintTestResultRunner(int jobId, boolean testPassed) {
+            super(jobId, testPassed);
+        }
+
+        @Override
+        public void run() {
+            ImageView view;
+            if (mJobId == ANY_CONNECTIVITY_JOB_ID) {
+                view = (ImageView) findViewById(R.id.connectivity_off_test_any_connectivity_image);
+            } else if (mJobId == UNMETERED_CONNECTIVITY_JOB_ID) {
+                view = (ImageView) findViewById(R.id.connectivity_off_test_unmetered_image);
+            } else if (mJobId == NO_CONNECTIVITY_JOB_ID) {
+                view = (ImageView) findViewById(R.id.connectivity_off_test_no_connectivity_image);
+            } else {
+                noteInvalidTest();
+                return;
+            }
+            view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error);
+        }
+    }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java
new file mode 100644
index 0000000..da0862a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConstraintTestActivity.java
@@ -0,0 +1,115 @@
+package com.android.cts.verifier.jobscheduler;
+
+import android.annotation.TargetApi;
+import android.app.job.JobScheduler;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Button;
+import android.widget.Toast;
+
+import com.android.cts.verifier.PassFailButtons;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@TargetApi(21)
+public abstract class ConstraintTestActivity extends PassFailButtons.Activity {
+    /**
+     * Intent we use to force the job scheduler to consider any ready jobs that otherwise it may
+     * have decided to be lazy about.
+     */
+    protected static final Intent EXPEDITE_STABLE_CHARGING =
+            new Intent("com.android.server.task.controllers.BatteryController.ACTION_CHARGING_STABLE");
+
+    protected ComponentName mMockComponent;
+
+    protected MockJobService.TestEnvironment mTestEnvironment;
+    protected JobScheduler mJobScheduler;
+
+    /** Avoid cases where user might press "start test" more than once. */
+    private boolean mTestInProgress;
+    /**
+     * Starts the test - set up by subclass, which also controls the logic for how/when the test
+     * can be started.
+     */
+    protected Button mStartButton;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mJobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        mMockComponent = new ComponentName(this, MockJobService.class);
+        mTestEnvironment = MockJobService.TestEnvironment.getTestEnvironment();
+    }
+
+    /** OnClickListener for the "Start Test" ({@link #mStartButton}) button */
+    public final void startTest(View v) {
+        if (mTestInProgress) {
+            Toast toast =
+                    Toast.makeText(
+                            ConstraintTestActivity.this,
+                            "Test already in progress",
+                            Toast.LENGTH_SHORT);
+            toast.show();
+            return;
+        } else {
+            mTestInProgress = true;
+            startTestImpl();
+        }
+    }
+
+    /** Called by subclasses to allow the user to rerun the test if necessary. */
+    protected final void notifyTestCompleted() {
+        mTestInProgress = false;
+    }
+
+    /** Implemented by subclasses to determine logic for running the test. */
+    protected abstract void startTestImpl();
+
+    /**
+     * Broadcast the provided intent, and register a receiver to notify us after the broadcast has
+     * been processed.
+     * This function will block until the broadcast comes back, and <bold>cannot</bold> be called
+     * on the main thread.
+     * @return True if we received the callback, false if not.
+     */
+    protected boolean sendBroadcastAndBlockForResult(Intent intent) {
+        final CountDownLatch latch = new CountDownLatch(1);
+        sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                latch.countDown();
+            }
+        }, null, -1, null, null);
+        try {
+            return latch.await(5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            return false;
+        }
+    }
+
+    /** Extended by test activities to report results of a test. */
+    protected abstract class TestResultRunner implements Runnable {
+        final int mJobId;
+        final boolean mTestPassed;
+
+        TestResultRunner(int jobId, boolean testPassed) {
+            mJobId = jobId;
+            mTestPassed = testPassed;
+        }
+        protected void noteInvalidTest() {
+            final Toast toast =
+                    Toast.makeText(
+                            ConstraintTestActivity.this,
+                            "Invalid result returned from test thread: job=" + mJobId + ", res="
+                                    + mTestPassed,
+                            Toast.LENGTH_SHORT);
+            toast.show();
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
new file mode 100644
index 0000000..a8bd993
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/IdleConstraintTestActivity.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.jobscheduler;
+
+import com.android.cts.verifier.R;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.ImageView;
+
+/**
+ *  Idle constraints:
+ *      The framework doesn't support turning idle mode off. Use the manual tester to ensure that
+ *      the device is not in idle mode (by turning the screen off and then back on) before running
+ *      the tests.
+ */
+@TargetApi(21)
+public class IdleConstraintTestActivity extends ConstraintTestActivity {
+    private static final String TAG = "IdleModeTestActivity";
+    /**
+     * It takes >1hr for idle mode to be triggered. We'll use this secret broadcast to force the
+     * scheduler into idle. It's not a protected broadcast so that's alright.
+     */
+    private static final String ACTION_EXPEDITE_IDLE_MODE =
+            "com.android.server.task.controllers.IdleController.ACTION_TRIGGER_IDLE";
+
+    /**
+     * Id for the job that we schedule when the device is not in idle mode. This job is expected
+     * to not execute. Executing means that the verifier test should fail.
+     */
+    private static final int IDLE_OFF_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 0;
+    /**
+     * Id for the job that we schedule when the device *is* in idle mode. This job is expected to
+     * execute. Not executing means that the verifier test should fail.
+     */
+    private static final int IDLE_ON_JOB_ID = IdleConstraintTestActivity.class.hashCode() + 1;
+
+    /**
+     * Listens for idle mode off/on events, namely {@link #ACTION_EXPEDITE_IDLE_MODE} and
+     * {@link Intent#ACTION_SCREEN_ON}.
+     * On ACTION_EXPEDITE_IDLE_MODE, we will disable the {@link #mStartButton}, and on
+     * ACTION_SCREEN_ON we enable it. This is to avoid the start button being clicked when the
+     * device is in idle mode.
+     */
+    private BroadcastReceiver mIdleChangedReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (Intent.ACTION_SCREEN_ON.equals(intent.getAction())) {
+                mStartButton.setEnabled(true);
+            } else if (ACTION_EXPEDITE_IDLE_MODE.equals(intent.getAction())) {
+                mStartButton.setEnabled(false);
+            } else {
+                Log.e(TAG, "Invalid broadcast received, was expecting SCREEN_ON");
+            }
+        }
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        // Set up the UI.
+        setContentView(R.layout.js_idle);
+        setPassFailButtonClickListeners();
+        setInfoResources(R.string.js_idle_test, R.string.js_idle_instructions, -1);
+        mStartButton = (Button) findViewById(R.id.js_idle_start_test_button);
+
+        // Register receiver for idle off/on events.
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
+        intentFilter.addAction(ACTION_EXPEDITE_IDLE_MODE);
+
+        registerReceiver(mIdleChangedReceiver, intentFilter);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        unregisterReceiver(mIdleChangedReceiver);
+    }
+
+    @Override
+    protected void startTestImpl() {
+        new TestIdleModeTask().execute();
+    }
+
+    /** Background task that will run the actual test. */
+    private class TestIdleModeTask extends AsyncTask<Void, Void, Void> {
+
+        @Override
+        protected Void doInBackground(Void... voids) {
+            testIdleConstraintFails_notIdle();
+
+
+            // Send the {@link #ACTION_EXPEDITE_IDLE_MODE} broadcast as an ordered broadcast, this
+            // function will block until all receivers have processed the broadcast.
+            if (!sendBroadcastAndBlockForResult(new Intent(ACTION_EXPEDITE_IDLE_MODE))) {
+                // Fail the test if the broadcast wasn't processed.
+                runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, false));
+            }
+
+            testIdleConstraintExecutes_onIdle();
+
+            notifyTestCompleted();
+            return null;
+        }
+
+    }
+
+    /**
+     * The user has just pressed the "Start Test" button, so we know that the device can't be idle.
+     * Schedule a job with an idle constraint and verify that it doesn't execute.
+     */
+    private void testIdleConstraintFails_notIdle() {
+        mTestEnvironment.setUp();
+        mJobScheduler.cancelAll();
+
+        mTestEnvironment.setExpectedExecutions(0);
+
+        mJobScheduler.schedule(
+                new JobInfo.Builder(IDLE_OFF_JOB_ID, mMockComponent)
+                        .setRequiresDeviceIdle(true)
+                        .build());
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitTimeout();
+        } catch (InterruptedException e) {
+            // We'll just indicate that it failed, not why.
+            testPassed = false;
+        }
+        runOnUiThread(new IdleTestResultRunner(IDLE_OFF_JOB_ID, testPassed));
+    }
+
+    private void testIdleConstraintExecutes_onIdle() {
+        mTestEnvironment.setUp();
+        mJobScheduler.cancelAll();
+
+        mTestEnvironment.setExpectedExecutions(1);
+
+        mJobScheduler.schedule(
+                new JobInfo.Builder(IDLE_ON_JOB_ID, mMockComponent)
+                .setRequiresDeviceIdle(true)
+                .build());
+
+        boolean testPassed;
+        try {
+            testPassed = mTestEnvironment.awaitExecution();
+        } catch (InterruptedException e) {
+            // We'll just indicate that it failed, not why.
+            testPassed = false;
+        }
+        runOnUiThread(new IdleTestResultRunner(IDLE_ON_JOB_ID, testPassed));
+    }
+
+    /**
+     * Runnable to update the UI with the outcome of the test. This class only runs two tests, so
+     * the argument passed into the constructor will indicate which of the tests we are reporting
+     * for.
+     */
+    protected class IdleTestResultRunner extends TestResultRunner {
+
+        IdleTestResultRunner(int jobId, boolean testPassed) {
+            super(jobId, testPassed);
+        }
+
+        @Override
+        public void run() {
+            ImageView view;
+            if (mJobId == IDLE_OFF_JOB_ID) {
+                view = (ImageView) findViewById(R.id.idle_off_test_image);
+            } else if (mJobId == IDLE_ON_JOB_ID) {
+                view = (ImageView) findViewById(R.id.idle_on_test_image);
+            } else {
+                noteInvalidTest();
+                return;
+            }
+            view.setImageResource(mTestPassed ? R.drawable.fs_good : R.drawable.fs_error);
+        }
+    }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/MockJobService.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/MockJobService.java
new file mode 100644
index 0000000..9595a6a
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/MockJobService.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.jobscheduler;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
+ * class is configured through the static
+ * {@link TestEnvironment}.
+ */
+@TargetApi(21)
+public class MockJobService extends JobService {
+    private static final String TAG = "MockJobService";
+
+    /** Wait this long before timing out the test. */
+    private static final long DEFAULT_TIMEOUT_MILLIS = 5000L; // 5 seconds.
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.e(TAG, "Created test service.");
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log.i(TAG, "Test job executing: " + params.getJobId());
+
+        TestEnvironment.getTestEnvironment().notifyExecution(params.getJobId());
+        return false;  // No work to do.
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+
+    /**
+     * Configures the expected behaviour for each test. This object is shared across consecutive
+     * tests, so to clear state each test is responsible for calling
+     * {@link TestEnvironment#setUp()}.
+     */
+    public static final class TestEnvironment {
+
+        private static TestEnvironment kTestEnvironment;
+        public static final int INVALID_JOB_ID = -1;
+
+        private CountDownLatch mLatch;
+        private int mExecutedJobId;
+
+        public static TestEnvironment getTestEnvironment() {
+            if (kTestEnvironment == null) {
+                kTestEnvironment = new TestEnvironment();
+            }
+            return kTestEnvironment;
+        }
+
+        /**
+         * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
+         * job on this service.
+         */
+        public boolean awaitExecution() throws InterruptedException {
+            final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            return executed;
+        }
+
+        /**
+         * Block the test thread, expecting to timeout but still listening to ensure that no jobs
+         * land in the interim.
+         * @return True if the latch timed out waiting on an execution.
+         */
+        public boolean awaitTimeout() throws InterruptedException {
+            return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        }
+
+        private void notifyExecution(int jobId) {
+            Log.d(TAG, "Job executed:" + jobId);
+            mExecutedJobId = jobId;
+            mLatch.countDown();
+        }
+
+        public void setExpectedExecutions(int numExecutions) {
+            // For no executions expected, set count to 1 so we can still block for the timeout.
+            if (numExecutions == 0) {
+                mLatch = new CountDownLatch(1);
+            } else {
+                mLatch = new CountDownLatch(numExecutions);
+            }
+        }
+
+        /** Called in each testCase#setup */
+        public void setUp() {
+            mLatch = null;
+            mExecutedJobId = INVALID_JOB_ID;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
index f992618..510a03b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/offscreen/ProjectionOffscreenActivity.java
@@ -26,6 +26,9 @@
 import android.graphics.PixelFormat;
 import android.media.Image;
 import android.media.ImageReader;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -68,7 +71,7 @@
     protected TestStatus mTestStatus = TestStatus.RUNNING;
 
     private final Runnable sendKeyEventRunnable = new Runnable() {
-            @Override
+        @Override
         public void run() {
             try {
                 mService.onKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_DOWN));
@@ -79,15 +82,28 @@
         }
     };
 
+    private final Runnable playNotificationRunnable = new Runnable() {
+
+        @Override
+        public void run() {
+            Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+            Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification);
+            r.play();
+        }
+    };
+
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
 
     @Override
     public void onReceive(Context context, Intent intent) {
         if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
-            new Handler(Looper.getMainLooper()).postDelayed(
+            Handler handler = new Handler(Looper.getMainLooper());
+            handler.postDelayed(
                     sendKeyEventRunnable, DELAYED_RUNNABLE_TIME);
             mStatusView.setText("Running test...");
             mTimeScreenTurnedOff = SystemClock.uptimeMillis();
+            // Notify user its safe to turn screen back on after 5s + fudge factor
+            handler.postDelayed(playNotificationRunnable, TIME_SCREEN_OFF + 500);
         } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
             if (SystemClock.uptimeMillis() - mTimeScreenTurnedOff < TIME_SCREEN_OFF) {
                 mStatusView.setText("ERROR: Turned on screen too early");
@@ -121,8 +137,9 @@
                     filter.addAction(Intent.ACTION_SCREEN_ON);
 
                     registerReceiver(mReceiver, filter);
-                    mStatusView.setText(
-                            "Please turn off your screen and turn it back on after 5 seconds");
+                    mStatusView.setText("Please turn off your screen and turn it back on after " +
+                            "5 seconds. A sound will be played when it is safe to turn the " +
+                            "screen back on");
                 }
 
             });
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 4ba38a9..6f0a7aa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -19,15 +19,12 @@
 import com.android.cts.verifier.R;
 import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
 
-import android.content.Context;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
 import android.hardware.cts.helpers.TestSensorEnvironment;
 import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
 import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
 import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
-import android.os.Bundle;
-import android.os.PowerManager;
 
 import java.util.concurrent.TimeUnit;
 
@@ -51,27 +48,9 @@
     // such events to generate
     private static final int REPORT_LATENCY_25_SEC = 25;
 
-    private PowerManager.WakeLock mWakeLock;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-    }
-
-    @Override
-    protected void activitySetUp() throws InterruptedException {
-        PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "BatchingTests");
-        mWakeLock.acquire();
-    }
-
-    @Override
-    protected void activityCleanUp() throws InterruptedException {
-        mWakeLock.release();
-    }
-
     // TODO: refactor to discover all available sensors of each type and dynamically generate test
     // cases for all of them
+    @SuppressWarnings("unused")
     public String testStepCounter_batching() throws Throwable {
         return runBatchTest(
                 Sensor.TYPE_STEP_COUNTER,
@@ -79,6 +58,7 @@
                 R.string.snsr_batching_walking_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testStepCounter_flush() throws Throwable {
         return runFlushTest(
                 Sensor.TYPE_STEP_COUNTER,
@@ -86,6 +66,7 @@
                 R.string.snsr_batching_walking_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testStepDetector_batching() throws Throwable {
         return  runBatchTest(
                 Sensor.TYPE_STEP_DETECTOR,
@@ -93,6 +74,7 @@
                 R.string.snsr_batching_walking_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testStepDetector_flush() throws Throwable {
         return  runFlushTest(
                 Sensor.TYPE_STEP_DETECTOR,
@@ -100,6 +82,7 @@
                 R.string.snsr_batching_walking_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testProximity_batching() throws Throwable {
         return runBatchTest(
                 Sensor.TYPE_PROXIMITY,
@@ -107,6 +90,7 @@
                 R.string.snsr_interaction_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testProximity_flush() throws Throwable {
         return runFlushTest(
                 Sensor.TYPE_PROXIMITY,
@@ -114,6 +98,7 @@
                 R.string.snsr_interaction_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testLight_batching() throws Throwable {
         return runBatchTest(
                 Sensor.TYPE_LIGHT,
@@ -121,6 +106,7 @@
                 R.string.snsr_interaction_needed);
     }
 
+    @SuppressWarnings("unused")
     public String testLight_flush() throws Throwable {
         return runFlushTest(
                 Sensor.TYPE_LIGHT,
@@ -164,7 +150,7 @@
         return executeTest(operation);
     }
 
-    private String executeTest(VerifiableSensorOperation operation) {
+    private String executeTest(VerifiableSensorOperation operation) throws InterruptedException {
         operation.addDefaultVerifications();
         operation.setLogEvents(true);
         operation.execute();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index ac63780..4b2a7f4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -51,7 +51,7 @@
     }
 
     @Override
-    protected void activitySetUp() {
+    protected void activitySetUp() throws InterruptedException {
         getTestLogger().logInstructions(R.string.snsr_gyro_device_placement);
         waitForUserToContinue();
         initializeGlSurfaceView(mRenderer);
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 bd18a95..553147b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -46,7 +46,7 @@
     }
 
     @Override
-    public void activitySetUp() {
+    public void activitySetUp() throws InterruptedException {
         calibrateMagnetometer();
     }
 
@@ -165,7 +165,7 @@
     /**
      * A routine to help operators calibrate the magnetometer.
      */
-    private void calibrateMagnetometer() {
+    private void calibrateMagnetometer() throws InterruptedException {
         SensorEventListener2 listener = new SensorEventListener2() {
             @Override
             public void onSensorChanged(SensorEvent event) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RotationVectorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RotationVectorTestActivity.java
index cd94128..6b804dd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RotationVectorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/RotationVectorTestActivity.java
@@ -87,7 +87,7 @@
         // TODO: take reference value automatically when device is 'still'
         clearText();
         appendText(R.string.snsr_rotation_vector_set_reference);
-        waitForUser();
+        waitForUserToContinue();
 
         clearText();
         for (int i = 0; i < MAX_SENSORS_AVAILABLE; ++i) {
@@ -104,7 +104,7 @@
         // TODO: take final value automatically when device becomes 'still' at the end
         clearText();
         appendText(R.string.snsr_rotation_vector_set_final);
-        waitForUser();
+        waitForUserToContinue();
 
         clearText();
         closeGlSurfaceView();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java
index cfc4db0..8370d3e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorPowerTestActivity.java
@@ -34,11 +34,10 @@
         super(SensorPowerTestActivity.class);
     }
 
-
     @Override
-    public void waitForUserAcknowledgement(final String message) {
+    public void waitForUserAcknowledgement(final String message) throws InterruptedException {
         appendText(message);
-        waitForUser();
+        waitForUserToContinue();
     }
 
     @Override
@@ -63,7 +62,7 @@
     }
 
     @Override
-    protected void activitySetUp() {
+    protected void activitySetUp() throws InterruptedException {
         mScreenManipulator = new SensorTestScreenManipulator(getApplicationContext());
         mScreenManipulator.initialize(this);
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorSynchronizationTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorSynchronizationTestActivity.java
index 1dd5984..683430c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorSynchronizationTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SensorSynchronizationTestActivity.java
@@ -7,7 +7,6 @@
 
 import android.annotation.TargetApi;
 import android.content.Context;
-import android.graphics.Color;
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener;
@@ -137,9 +136,9 @@
     public String testCrossSensorSynchronization() throws Throwable {
         appendText("This test provides a rough indication of cross-sensor timestamp synchronization.");
         appendText("Hold device still in hand and click 'Next'");
-        waitForUser();
+        waitForUserToBegin();
         clearText();
-        appendText("Quickly twist device upside-down and back", Color.GREEN);
+        appendText("Quickly twist device upside-down and back");
 
         startDataCollection();
         Thread.sleep(DATA_COLLECTION_TIME_IN_MS);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index a84a045..faba445 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -59,6 +59,8 @@
     /**
      * Test cases.
      */
+
+    @SuppressWarnings("unused")
     public String testTrigger() throws Throwable {
         return runTest(
                 R.string.snsr_significant_motion_test_trigger,
@@ -67,6 +69,7 @@
                 false /* vibrate */);
     }
 
+    @SuppressWarnings("unused")
     public String testNotTriggerAfterCancel() throws Throwable {
         return runTest(
                 R.string.snsr_significant_motion_test_cancel,
@@ -78,6 +81,7 @@
     /**
      * Verifies that Significant Motion is not trigger by the vibrator motion.
      */
+    @SuppressWarnings("unused")
     public String testVibratorDoesNotTrigger() throws Throwable {
      return runTest(
              R.string.snsr_significant_motion_test_vibration,
@@ -90,6 +94,7 @@
      * Verifies that the natural motion of keeping the device in hand does not change the location.
      * It ensures that Significant Motion will not trigger in that scenario.
      */
+    @SuppressWarnings("unused")
     public String testInHandDoesNotTrigger() throws Throwable {
         return runTest(
                 R.string.snsr_significant_motion_test_in_hand,
@@ -98,6 +103,7 @@
                 false /* vibrate */);
     }
 
+    @SuppressWarnings("unused")
     public String testSittingDoesNotTrigger() throws Throwable {
         return runTest(
                 R.string.snsr_significant_motion_test_sitting,
@@ -106,6 +112,7 @@
                 false /* vibrate */);
     }
 
+    @SuppressWarnings("unused")
     public String testTriggerDeactivation() throws Throwable {
         SensorTestLogger logger = getTestLogger();
         logger.logInstructions(R.string.snsr_significant_motion_test_deactivation);
@@ -167,6 +174,7 @@
             }
         } finally {
             mSensorManager.cancelTriggerSensor(verifier, mSensorSignificantMotion);
+            playSound();
         }
         return result;
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
index 76d12d9..98368c6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
@@ -33,7 +33,6 @@
 import android.os.SystemClock;
 import android.view.MotionEvent;
 import android.view.View;
-import android.widget.ScrollView;
 
 import java.util.ArrayList;
 import java.util.List;
@@ -86,8 +85,7 @@
                     "Sensors Step Counter/Detector are not supported.");
         }
 
-        ScrollView scrollView = (ScrollView) findViewById(R.id.log_scroll_view);
-        scrollView.setOnTouchListener(new View.OnTouchListener() {
+        setLogScrollViewListener(new View.OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 // during movement of the device, the ScrollView will detect user taps as attempts
@@ -95,14 +93,27 @@
                 // to overcome the fact that a ScrollView cannot be disabled from scrolling, we
                 // listen for ACTION_UP events instead of click events in the child layout
                 long elapsedTime = SystemClock.elapsedRealtimeNanos();
-                if (event.getAction() == MotionEvent.ACTION_UP) {
+                if (event.getAction() != MotionEvent.ACTION_UP) {
+                    return false;
+                }
+
+                try {
                     logUserReportedStep(elapsedTime);
+                } catch (InterruptedException e) {
+                    // we cannot propagate the exception in the main thread, so we just catch and
+                    // restore the status, we don't need to log as we are terminating anyways
+                    Thread.currentThread().interrupt();
                 }
                 return false;
             }
         });
     }
 
+    @Override
+    protected void activityCleanUp() {
+        setLogScrollViewListener(null /* listener */);
+    }
+
     public String testWalking() throws Throwable {
         return runTest(
                 R.string.snsr_step_counter_test_walking,
@@ -323,11 +334,10 @@
         // TODO: with delayed assertions check events of other types are tracked
     }
 
-    private void logUserReportedStep(long timestamp) {
+    private void logUserReportedStep(long timestamp) throws InterruptedException {
         if (!mCheckForMotion) {
             return;
         }
-
         playSound();
         mTimestampsUserReported.add(timestamp);
         getTestLogger().logMessage(R.string.snsr_step_reported, timestamp);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
index d1c06cb..0bf9636 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/BaseSensorTestActivity.java
@@ -28,13 +28,14 @@
 import android.content.Context;
 import android.content.Intent;
 import android.hardware.cts.helpers.ActivityResultMultiplexedLatch;
-import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 import android.media.MediaPlayer;
 import android.opengl.GLSurfaceView;
 import android.os.Bundle;
+import android.os.SystemClock;
 import android.os.Vibrator;
 import android.provider.Settings;
 import android.text.TextUtils;
+import android.text.format.DateUtils;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -42,8 +43,11 @@
 import android.widget.ScrollView;
 import android.widget.TextView;
 
-import java.security.InvalidParameterException;
-import java.util.concurrent.Semaphore;
+import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
 
 /**
  * A base Activity that is used to build different methods to execute tests inside CtsVerifier.
@@ -76,10 +80,11 @@
     private final int mLayoutId;
     private final SensorFeaturesDeactivator mSensorFeaturesDeactivator;
 
-    private final Semaphore mSemaphore = new Semaphore(0);
+    private final ExecutorService mExecutorService = Executors.newSingleThreadExecutor();
     private final SensorTestLogger mTestLogger = new SensorTestLogger();
     private final ActivityResultMultiplexedLatch mActivityResultMultiplexedLatch =
             new ActivityResultMultiplexedLatch();
+    private final ArrayList<CountDownLatch> mWaitForUserLatches = new ArrayList<CountDownLatch>();
 
     private ScrollView mLogScrollView;
     private LinearLayout mLogLayout;
@@ -128,7 +133,13 @@
         mGLSurfaceView = (GLSurfaceView) findViewById(R.id.gl_surface_view);
 
         updateNextButton(false /*enabled*/);
-        new Thread(this).start();
+        mExecutorService.execute(this);
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        mExecutorService.shutdownNow();
     }
 
     @Override
@@ -149,7 +160,12 @@
 
     @Override
     public void onClick(View target) {
-        mSemaphore.release();
+        synchronized (mWaitForUserLatches) {
+            for (CountDownLatch latch : mWaitForUserLatches) {
+                latch.countDown();
+            }
+            mWaitForUserLatches.clear();
+        }
     }
 
     @Override
@@ -167,40 +183,36 @@
      */
     @Override
     public void run() {
+        long startTimeNs = SystemClock.elapsedRealtimeNanos();
         String testName = getTestClassName();
 
-        // guarantee the proper clean up of tests based on the operations that successfully ran
-        SensorTestDetails testDetails = deactivateSensorFeatures();
-        if (testDetails.getResultCode() == SensorTestDetails.ResultCode.PASS) {
-            // sensor features
-            testDetails = executeActivitySetUp();
-            if (testDetails.getResultCode() == SensorTestDetails.ResultCode.PASS) {
-                // activity set up
-                // TODO: implement execution filters:
-                //      - execute all tests and report results officially
-                //      - execute single test or failed tests only
-                testDetails = executeTests();
-                try {
-                    activityCleanUp();
-                } catch (Throwable e) {
-                    testDetails = new SensorTestDetails(
-                            testName,
-                            SensorTestDetails.ResultCode.FAIL,
-                            "[ActivityCleanUp] " + e.getMessage());
-                }
-                // end activity set up
-            }
-            try {
-                mSensorFeaturesDeactivator.requestToRestoreFeatures();
-            } catch (Throwable e) {
-                testDetails = new SensorTestDetails(
-                        testName,
-                        SensorTestDetails.ResultCode.FAIL,
-                        "[RestoreSensorFeatures] " + e.getMessage());
-            }
-            // end sensor features
+        SensorTestDetails testDetails;
+        try {
+            mSensorFeaturesDeactivator.requestDeactivationOfFeatures();
+            testDetails = new SensorTestDetails(testName, SensorTestDetails.ResultCode.PASS);
+        } catch (Throwable e) {
+            testDetails = new SensorTestDetails(testName, "DeactivateSensorFeatures", e);
         }
+
+        SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+        if (resultCode == SensorTestDetails.ResultCode.SKIPPED) {
+            // this is an invalid state at this point of the test setup
+            throw new IllegalStateException("Deactivation of features cannot skip the test.");
+        }
+        if (resultCode == SensorTestDetails.ResultCode.PASS) {
+            testDetails = executeActivityTests(testName);
+        }
+
+        // we consider all remaining states at this point, because we could have been half way
+        // deactivating features
+        try {
+            mSensorFeaturesDeactivator.requestToRestoreFeatures();
+        } catch (Throwable e) {
+            testDetails = new SensorTestDetails(testName, "RestoreSensorFeatures", e);
+        }
+
         mTestLogger.logTestDetails(testDetails);
+        mTestLogger.logExecutionTime(startTimeNs);
 
         // because we cannot enforce test failures in several devices, set the test UI so the
         // operator can report the result of the test
@@ -210,6 +222,9 @@
     /**
      * A general set up routine. It executes only once before the first test case.
      *
+     * NOTE: implementers must be aware of the interrupted status of the worker thread, and let
+     * {@link InterruptedException} propagate.
+     *
      * @throws Throwable An exception that denotes the failure of set up. No tests will be executed.
      */
     protected void activitySetUp() throws Throwable {}
@@ -218,6 +233,11 @@
      * A general clean up routine. It executes upon successful execution of {@link #activitySetUp()}
      * and after all the test cases.
      *
+     * NOTE: implementers must be aware of the interrupted status of the worker thread, and handle
+     * it in two cases:
+     * - let {@link InterruptedException} propagate
+     * - if it is invoked with the interrupted status, prevent from showing any UI
+
      * @throws Throwable An exception that will be logged and ignored, for ease of implementation
      *                   by subclasses.
      */
@@ -229,7 +249,7 @@
      *
      * @return A {@link SensorTestDetails} object containing information about the executed tests.
      */
-    protected abstract SensorTestDetails executeTests();
+    protected abstract SensorTestDetails executeTests() throws InterruptedException;
 
     @Override
     public SensorTestLogger getTestLogger() {
@@ -237,11 +257,6 @@
     }
 
     @Deprecated
-    protected void appendText(String text, int textColor) {
-        appendText(text);
-    }
-
-    @Deprecated
     protected void appendText(int resId) {
         mTestLogger.logInstructions(resId);
     }
@@ -268,21 +283,22 @@
      *
      * @param waitMessageResId The action requested to the operator.
      */
-    protected void waitForUser(int waitMessageResId) {
+    protected void waitForUser(int waitMessageResId) throws InterruptedException {
+        CountDownLatch latch = new CountDownLatch(1);
+        synchronized (mWaitForUserLatches) {
+            mWaitForUserLatches.add(latch);
+        }
+
         mTestLogger.logInstructions(waitMessageResId);
         updateNextButton(true);
-        try {
-            mSemaphore.acquire();
-        } catch (InterruptedException e)  {
-            Log.e(LOG_TAG, "Error on waitForUser", e);
-        }
+        latch.await();
         updateNextButton(false);
     }
 
     /**
      * Waits for the operator to acknowledge to begin execution.
      */
-    protected void waitForUserToBegin() {
+    protected void waitForUserToBegin() throws InterruptedException {
         waitForUser(R.string.snsr_wait_to_begin);
     }
 
@@ -290,20 +306,15 @@
      * {@inheritDoc}
      */
     @Override
-    public void waitForUserToContinue() {
+    public void waitForUserToContinue() throws InterruptedException {
         waitForUser(R.string.snsr_wait_for_user);
     }
 
-    @Deprecated
-    protected void waitForUser() {
-        waitForUserToContinue();
-    }
-
     /**
      * {@inheritDoc}
      */
     @Override
-    public int executeActivity(String action) {
+    public int executeActivity(String action) throws InterruptedException {
         return executeActivity(new Intent(action));
     }
 
@@ -311,7 +322,7 @@
      * {@inheritDoc}
      */
     @Override
-    public int executeActivity(Intent intent) {
+    public int executeActivity(Intent intent) throws InterruptedException {
         ActivityResultMultiplexedLatch.Latch latch = mActivityResultMultiplexedLatch.bindThread();
         startActivityForResult(intent, latch.getRequestCode());
         return latch.await();
@@ -352,18 +363,15 @@
     /**
      * Plays a (default) sound as a notification for the operator.
      */
-    protected void playSound() {
+    protected void playSound() throws InterruptedException {
         MediaPlayer player = MediaPlayer.create(this, Settings.System.DEFAULT_NOTIFICATION_URI);
         if (player == null) {
             Log.e(LOG_TAG, "MediaPlayer unavailable.");
             return;
         }
-
         player.start();
         try {
             Thread.sleep(500);
-        } catch(InterruptedException e) {
-            Log.d(LOG_TAG, "Error on playSound", e);
         } finally {
             player.stop();
         }
@@ -411,10 +419,16 @@
         return mTestClass.getName();
     }
 
+    protected void setLogScrollViewListener(View.OnTouchListener listener) {
+        mLogScrollView.setOnTouchListener(listener);
+    }
+
     private void setTestResult(SensorTestDetails testDetails) {
+        // the name here, must be the Activity's name because it is what CtsVerifier expects
+        String name = super.getClass().getName();
         String summary = mTestLogger.getOverallSummary();
-        String name = testDetails.getName();
-        switch(testDetails.getResultCode()) {
+        SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+        switch(resultCode) {
             case SKIPPED:
                 TestResult.setPassedResult(this, name, summary);
                 break;
@@ -424,48 +438,50 @@
             case FAIL:
                 TestResult.setFailedResult(this, name, summary);
                 break;
+            case INTERRUPTED:
+                // do not set a result, just return so the test can complete
+                break;
+            default:
+                throw new IllegalStateException("Unknown ResultCode: " + resultCode);
         }
     }
 
-    private SensorTestDetails deactivateSensorFeatures() {
-        String testName = getTestClassName();
-        try {
-            mSensorFeaturesDeactivator.requestDeactivationOfFeatures();
-        } catch (Throwable e) {
-            return new SensorTestDetails(
-                    testName,
-                    SensorTestDetails.ResultCode.FAIL,
-                    "[DeactivateSensorFeatures] " + e.getMessage());
-        }
-        return new SensorTestDetails(
-                testName,
-                SensorTestDetails.ResultCode.PASS,
-                null /* summary */);
-    }
-
-    private SensorTestDetails executeActivitySetUp() {
-        String testName = getTestClassName();
+    private SensorTestDetails executeActivityTests(String testName) {
+        SensorTestDetails testDetails;
         try {
             activitySetUp();
-        } catch (SensorTestStateNotSupportedException e) {
-            return new SensorTestDetails(
-                    testName,
-                    SensorTestDetails.ResultCode.SKIPPED,
-                    e.getMessage());
+            testDetails = new SensorTestDetails(testName, SensorTestDetails.ResultCode.PASS);
         } catch (Throwable e) {
-            return new SensorTestDetails(
-                    testName,
-                    SensorTestDetails.ResultCode.FAIL,
-                    "[ActivitySetUp] " + e.getMessage());
+            testDetails = new SensorTestDetails(testName, "ActivitySetUp", e);
         }
-        return new SensorTestDetails(
-                testName,
-                SensorTestDetails.ResultCode.PASS,
-                null /* summary */);
+
+        SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+        if (resultCode == SensorTestDetails.ResultCode.PASS) {
+            // TODO: implement execution filters:
+            //      - execute all tests and report results officially
+            //      - execute single test or failed tests only
+            try {
+                testDetails = executeTests();
+            } catch (Throwable e) {
+                // we catch and continue because we have to guarantee a proper clean-up sequence
+                testDetails = new SensorTestDetails(testName, "TestExecution", e);
+            }
+        }
+
+        // clean-up executes for all states, even on SKIPPED and INTERRUPTED there might be some
+        // intermediate state that needs to be taken care of
+        try {
+            activityCleanUp();
+        } catch (Throwable e) {
+            testDetails = new SensorTestDetails(testName, "ActivityCleanUp", e);
+        }
+
+        return testDetails;
     }
 
     private void promptUserToSetResult(SensorTestDetails testDetails) {
-        if (testDetails.getResultCode() == SensorTestDetails.ResultCode.FAIL) {
+        SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+        if (resultCode == SensorTestDetails.ResultCode.FAIL) {
             mTestLogger.logInstructions(R.string.snsr_test_complete_with_errors);
             enableTestResultButton(
                     mPassButton,
@@ -475,7 +491,7 @@
                     mFailButton,
                     R.string.fail_button_text,
                     testDetails.cloneAndChangeResultCode(SensorTestDetails.ResultCode.FAIL));
-        } else {
+        } else if (resultCode != SensorTestDetails.ResultCode.INTERRUPTED) {
             mTestLogger.logInstructions(R.string.snsr_test_complete);
             enableTestResultButton(
                     mPassButton,
@@ -548,7 +564,8 @@
         public void logTestDetails(SensorTestDetails testDetails) {
             String name = testDetails.getName();
             String summary = testDetails.getSummary();
-            switch (testDetails.getResultCode()) {
+            SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+            switch (resultCode) {
                 case SKIPPED:
                     logTestSkip(name, summary);
                     break;
@@ -558,9 +575,11 @@
                 case FAIL:
                     logTestFail(name, summary);
                     break;
+                case INTERRUPTED:
+                    // do nothing, the test was interrupted so do we
+                    break;
                 default:
-                    throw new InvalidParameterException(
-                            "Invalid SensorTestDetails.ResultCode: " + testDetails.getResultCode());
+                    throw new IllegalStateException("Unknown ResultCode: " + resultCode);
             }
         }
 
@@ -589,6 +608,17 @@
             return mOverallSummaryBuilder.toString();
         }
 
+        void logExecutionTime(long startTimeNs) {
+            if (Thread.currentThread().isInterrupted()) {
+                return;
+            }
+            long executionTimeNs = SystemClock.elapsedRealtimeNanos() - startTimeNs;
+            long executionTimeSec = TimeUnit.NANOSECONDS.toSeconds(executionTimeNs);
+            // TODO: find a way to format times with nanosecond accuracy and longer than 24hrs
+            String formattedElapsedTime = DateUtils.formatElapsedTime(executionTimeSec);
+            logMessage(R.string.snsr_execution_time, formattedElapsedTime);
+        }
+
         private void logTestEnd(int textViewResId, String testSummary) {
             TextAppender textAppender = new TextAppender(textViewResId);
             textAppender.setText(testSummary);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
index 3ef7e21..2ba74e3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/ISensorTestStateContainer.java
@@ -33,7 +33,7 @@
     /**
      * Waits for the operator to acknowledge to continue execution.
      */
-    void waitForUserToContinue();
+    void waitForUserToContinue() throws InterruptedException;
 
     /**
      * @param resId The resource Id to extract.
@@ -55,7 +55,7 @@
      * @param action The action to start the Activity.
      * @return The Activity's result code.
      */
-    int executeActivity(String action);
+    int executeActivity(String action) throws InterruptedException;
 
     /**
      * Starts an Activity and blocks until it completes, then it returns its result back to the
@@ -64,7 +64,7 @@
      * @param intent The intent to start the Activity.
      * @return The Activity's result code.
      */
-    int executeActivity(Intent intent);
+    int executeActivity(Intent intent) throws InterruptedException;
 
     /**
      * @return The {@link ContentResolver} associated with the test.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestActivity.java
index 35bff24..16c5fcd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestActivity.java
@@ -37,7 +37,6 @@
 import org.junit.runners.model.InitializationError;
 import org.junit.runners.model.RunnerBuilder;
 
-import android.app.KeyguardManager;
 import android.content.Context;
 import android.hardware.cts.SensorTestCase;
 import android.os.PowerManager;
@@ -67,11 +66,11 @@
     }
 
     @Override
-    protected void activitySetUp() {
-        mScreenManipulator = new SensorTestScreenManipulator(getApplicationContext());
-        mScreenManipulator.initialize(this);
+    protected void activitySetUp() throws InterruptedException {
         PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
         mWakeLock =  powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "SensorCtsTests");
+        mScreenManipulator = new SensorTestScreenManipulator(getApplicationContext());
+        mScreenManipulator.initialize(this);
 
         SensorTestLogger logger = getTestLogger();
         logger.logInstructions(R.string.snsr_no_interaction);
@@ -93,7 +92,9 @@
             }
         });
         mScreenManipulator.turnScreenOn();
-        mWakeLock.release();
+        if (mWakeLock.isHeld()) {
+            mWakeLock.release();
+        }
     }
 
     @Override
@@ -101,6 +102,7 @@
         super.onDestroy();
         if (mScreenManipulator != null) {
             mScreenManipulator.releaseScreenOn();
+            mScreenManipulator.close();
         }
     }
 
@@ -154,7 +156,7 @@
             return new JUnit38ClassRunner(sensorTestSuite);
         }
 
-        private boolean hasSuiteMethod(Class testClass) {
+        private boolean hasSuiteMethod(Class<?> testClass) {
             try {
                 testClass.getMethod("suite");
                 return true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
index 851d405..5bbaaf7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
@@ -39,6 +39,8 @@
     private final Context mContext;
     private final TestResult mWrappedTestResult;
 
+    private volatile boolean mInterrupted;
+
     public SensorCtsTestResult(Context context, TestResult testResult) {
         mContext = context;
         mWrappedTestResult = testResult;
@@ -96,12 +98,23 @@
 
     @Override
     public void runProtected(Test test, Protectable protectable) {
-        mWrappedTestResult.runProtected(test, protectable);
+        try {
+            protectable.protect();
+        } catch (AssertionFailedError e) {
+            addFailure(test, e);
+        } catch (ThreadDeath e) {
+            throw e;
+        } catch (InterruptedException e) {
+            mInterrupted = true;
+            addError(test, e);
+        } catch (Throwable e) {
+            addError(test, e);
+        }
     }
 
     @Override
     public boolean shouldStop() {
-        return mWrappedTestResult.shouldStop();
+        return mInterrupted || mWrappedTestResult.shouldStop();
     }
 
     @Override
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
index 09753cc..a88abd0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
@@ -19,8 +19,6 @@
 
 import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
 
-import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
-
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
@@ -47,29 +45,16 @@
     }
 
     /**
-     * {@inheritDoc}
-     */
-    protected SensorCtsVerifierTestActivity(
-            Class<? extends SensorCtsVerifierTestActivity> testClass,
-            int layoutId) {
-        super(testClass, layoutId);
-    }
-
-    /**
      * Executes Semi-automated Sensor tests.
      * Execution is driven by this class, and allows discovery of tests using reflection.
      */
     @Override
-    protected SensorTestDetails executeTests() {
+    protected SensorTestDetails executeTests() throws InterruptedException {
         // TODO: use reporting to log individual test results
-        StringBuilder overallTestResults = new StringBuilder();
         for (Method testMethod : findTestMethods()) {
             SensorTestDetails testDetails = executeTest(testMethod);
             getTestLogger().logTestDetails(testDetails);
-            overallTestResults.append(testDetails.toString());
-            overallTestResults.append("\n");
         }
-
         return new SensorTestDetails(
                 getApplicationContext(),
                 getTestClassName(),
@@ -91,34 +76,40 @@
         return testMethods;
     }
 
-    private SensorTestDetails executeTest(Method testMethod) {
+    private SensorTestDetails executeTest(Method testMethod) throws InterruptedException {
         String testMethodName = testMethod.getName();
         String testName = String.format("%s#%s", getTestClassName(), testMethodName);
-        String testSummary;
-        SensorTestDetails.ResultCode testResultCode;
 
+        SensorTestDetails testDetails;
         try {
-            getTestLogger().logTestStart(testMethod.getName());
-            testSummary = (String) testMethod.invoke(this);
-            testResultCode = SensorTestDetails.ResultCode.PASS;
-            ++mTestPassedCounter;
+            getTestLogger().logTestStart(testMethodName);
+            String testSummary = (String) testMethod.invoke(this);
+            testDetails =
+                    new SensorTestDetails(testName, SensorTestDetails.ResultCode.PASS, testSummary);
         } catch (InvocationTargetException e) {
             // get the inner exception, because we use reflection APIs to execute the test
-            Throwable cause = e.getCause();
-            testSummary = cause.getMessage();
-            if (cause instanceof SensorTestStateNotSupportedException) {
-                testResultCode = SensorTestDetails.ResultCode.SKIPPED;
-                ++mTestSkippedCounter;
-            } else {
-                testResultCode = SensorTestDetails.ResultCode.FAIL;
-                ++mTestFailedCounter;
-            }
+            testDetails = new SensorTestDetails(testName, "TestExecution", e.getCause());
         } catch (Throwable e) {
-            testSummary = e.getMessage();
-            testResultCode = SensorTestDetails.ResultCode.FAIL;
-            ++mTestFailedCounter;
+            testDetails = new SensorTestDetails(testName, "TestInfrastructure", e);
         }
 
-        return new SensorTestDetails(testName, testResultCode, testSummary);
+        SensorTestDetails.ResultCode resultCode = testDetails.getResultCode();
+        switch(resultCode) {
+            case PASS:
+                ++mTestPassedCounter;
+                break;
+            case SKIPPED:
+                ++mTestSkippedCounter;
+                break;
+            case INTERRUPTED:
+                throw new InterruptedException();
+            case FAIL:
+                ++mTestFailedCounter;
+                break;
+            default:
+                throw new IllegalStateException("Unknown ResultCode: " + resultCode);
+        }
+
+        return testDetails;
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
index 0041aec..ed2fea3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/PowerTestHostLink.java
@@ -53,7 +53,7 @@
     public interface HostToDeviceInterface {
         void logTestResult(SensorTestDetails testDetails);
         void raiseError(String testName, String message) throws Exception;
-        void waitForUserAcknowledgement(String message);
+        void waitForUserAcknowledgement(String message) throws InterruptedException;
         void logText(String text);
         void turnScreenOff();
     }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
index d69d343..36559bd 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorFeaturesDeactivator.java
@@ -23,6 +23,8 @@
 import android.os.Build;
 import android.provider.Settings;
 
+import java.lang.reflect.Field;
+
 /**
  * A helper class that provides a mechanism to:
  * - prompt users to activate/deactivate features that are known to register for sensor data.
@@ -32,13 +34,12 @@
  */
 public class SensorFeaturesDeactivator {
 
-    private boolean mInitialStateCaptured;
-
     private final ISensorTestStateContainer mStateContainer;
 
     private final SensorSettingContainer mAirplaneMode = new AirplaneModeSettingContainer();
     private final SensorSettingContainer mScreenBrightnessMode =
             new ScreenBrightnessModeSettingContainer();
+    private final SensorSettingContainer mAmbientDisplayMode = new AmbientDisplaySettingContainer();
     private final SensorSettingContainer mAutoRotateScreenMode =
             new AutoRotateScreenModeSettingContainer();
     private final SensorSettingContainer mKeepScreenOnMode = new KeepScreenOnModeSettingContainer();
@@ -48,11 +49,12 @@
         mStateContainer = stateContainer;
     }
 
-    public synchronized void requestDeactivationOfFeatures() {
+    public synchronized void requestDeactivationOfFeatures() throws InterruptedException {
         captureInitialState();
 
         mAirplaneMode.requestToSetMode(mStateContainer, true);
         mScreenBrightnessMode.requestToSetMode(mStateContainer, false);
+        mAmbientDisplayMode.requestToSetMode(mStateContainer, false);
         mAutoRotateScreenMode.requestToSetMode(mStateContainer, false);
         mKeepScreenOnMode.requestToSetMode(mStateContainer, false);
         mLocationMode.requestToSetMode(mStateContainer, false);
@@ -63,34 +65,28 @@
         mStateContainer.waitForUserToContinue();
     }
 
-    public synchronized void requestToRestoreFeatures() {
-        if (!isInitialStateCaptured()) {
+    public synchronized void requestToRestoreFeatures() throws InterruptedException {
+        if (Thread.currentThread().isInterrupted()) {
+            // TODO: in the future, if the thread is interrupted, we might need to serialize the
+            //       intermediate state we acquired so we can restore when we have a chance
             return;
         }
 
         mAirplaneMode.requestToResetMode(mStateContainer);
         mScreenBrightnessMode.requestToResetMode(mStateContainer);
+        mAmbientDisplayMode.requestToResetMode(mStateContainer);
         mAutoRotateScreenMode.requestToResetMode(mStateContainer);
         mKeepScreenOnMode.requestToResetMode(mStateContainer);
         mLocationMode.requestToResetMode(mStateContainer);
     }
 
     private void captureInitialState() {
-        if (mInitialStateCaptured) {
-            return;
-        }
-
         mAirplaneMode.captureInitialState();
         mScreenBrightnessMode.captureInitialState();
+        mAmbientDisplayMode.captureInitialState();
         mAutoRotateScreenMode.captureInitialState();
         mLocationMode.captureInitialState();
         mKeepScreenOnMode.captureInitialState();
-
-        mInitialStateCaptured = true;
-    }
-
-    private boolean isInitialStateCaptured() {
-        return mInitialStateCaptured;
     }
 
     private class AirplaneModeSettingContainer extends SensorSettingContainer {
@@ -99,13 +95,15 @@
         }
 
         @Override
-        protected int getSettingMode() {
+        protected int getSettingMode(int defaultValue) {
             ContentResolver contentResolver = mStateContainer.getContentResolver();
             // Settings.System.AIRPLANE_MODE_ON is deprecated in API 17
             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
-                return Settings.System.getInt(contentResolver, Settings.System.AIRPLANE_MODE_ON, 0);
+                return Settings.System
+                        .getInt(contentResolver, Settings.System.AIRPLANE_MODE_ON, defaultValue);
             } else {
-                return Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, 0);
+                return Settings.Global
+                        .getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON, defaultValue);
             }
         }
     }
@@ -116,11 +114,42 @@
         }
 
         @Override
-        public int getSettingMode() {
+        public int getSettingMode(int defaultValue) {
             return Settings.System.getInt(
                     mStateContainer.getContentResolver(),
                     Settings.System.SCREEN_BRIGHTNESS_MODE,
-                    Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+                    defaultValue);
+        }
+    }
+
+    private class AmbientDisplaySettingContainer extends SensorSettingContainer {
+        public AmbientDisplaySettingContainer() {
+            super(Settings.ACTION_DISPLAY_SETTINGS, R.string.snsr_setting_ambient_display);
+        }
+
+        @Override
+        protected int getSettingMode(int defaultValue) {
+            // TODO: replace the use of reflection with Settings.Secure.DOZE_ENABLED when the
+            //       static field is not hidden anymore
+            Class<?> secureSettingsClass = Settings.Secure.class;
+            Field dozeEnabledField;
+            try {
+                dozeEnabledField = secureSettingsClass.getField("DOZE_ENABLED");
+            } catch (NoSuchFieldException e) {
+                return defaultValue;
+            }
+
+            String settingName;
+            try {
+                settingName = (String) dozeEnabledField.get(null /* obj */);
+            } catch (IllegalAccessException e) {
+                return defaultValue;
+            }
+
+            return Settings.Secure.getInt(
+                    mStateContainer.getContentResolver(),
+                    settingName,
+                    defaultValue);
         }
     }
 
@@ -131,11 +160,11 @@
         }
 
         @Override
-        protected int getSettingMode() {
+        protected int getSettingMode(int defaultValue) {
             return Settings.System.getInt(
                     mStateContainer.getContentResolver(),
                     Settings.System.ACCELEROMETER_ROTATION,
-                    0 /* default */);
+                    defaultValue);
         }
     }
 
@@ -146,11 +175,11 @@
         }
 
         @Override
-        protected int getSettingMode() {
+        protected int getSettingMode(int defaultValue) {
             return Settings.Global.getInt(
                     mStateContainer.getContentResolver(),
                     Settings.Global.STAY_ON_WHILE_PLUGGED_IN,
-                    0);
+                    defaultValue);
         }
     }
 
@@ -160,11 +189,11 @@
         }
 
         @Override
-        protected int getSettingMode() {
+        protected int getSettingMode(int defaultValue) {
             return Settings.Secure.getInt(
                     mStateContainer.getContentResolver(),
                     Settings.Secure.LOCATION_MODE,
-                    Settings.Secure.LOCATION_MODE_OFF);
+                    defaultValue);
         }
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
index 9a0d7e5..2d44d8d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorSettingContainer.java
@@ -24,12 +24,18 @@
  * A helper class for {@link SensorFeaturesDeactivator}. It abstracts the responsibility of handling
  * device settings that affect sensors.
  *
- * This class is not thread safe. It is meant to be used only by {@link SensorFeaturesDeactivator}.
+ * This class is meant to be used only by {@link SensorFeaturesDeactivator}.
+ * To keep things simple, this class synchronizes access to its internal state on public methods.
+ * This approach is fine, because there is no need for concurrent access.
  */
 abstract class SensorSettingContainer {
+    private static final int DEFAULT_SETTING_VALUE = -1;
+
     private final String mAction;
     private final int mSettingNameResId;
 
+    private boolean mInitialized;
+    private boolean mSettingAvailable;
     private boolean mCapturedModeOn;
 
     public SensorSettingContainer(String action, int settingNameResId) {
@@ -37,13 +43,21 @@
         mSettingNameResId = settingNameResId;
     }
 
-    public void captureInitialState() {
+    public synchronized void captureInitialState() {
+        if (mInitialized) {
+            return;
+        }
+        mSettingAvailable = getSettingMode(DEFAULT_SETTING_VALUE) != DEFAULT_SETTING_VALUE;
         mCapturedModeOn = getCurrentSettingMode();
+        mInitialized = true;
     }
 
     public synchronized void requestToSetMode(
             ISensorTestStateContainer stateContainer,
-            boolean modeOn) {
+            boolean modeOn) throws InterruptedException {
+        if (!isSettingAvailable()) {
+            return;
+        }
         trySetMode(stateContainer, modeOn);
         if (getCurrentSettingMode() != modeOn) {
             String message = stateContainer.getString(
@@ -54,11 +68,16 @@
         }
     }
 
-    public synchronized void requestToResetMode(ISensorTestStateContainer stateContainer) {
+    public synchronized void requestToResetMode(ISensorTestStateContainer stateContainer)
+            throws InterruptedException {
+        if (!isSettingAvailable()) {
+            return;
+        }
         trySetMode(stateContainer, mCapturedModeOn);
     }
 
-    private void trySetMode(ISensorTestStateContainer stateContainer, boolean modeOn) {
+    private void trySetMode(ISensorTestStateContainer stateContainer, boolean modeOn)
+            throws InterruptedException {
         BaseSensorTestActivity.SensorTestLogger logger = stateContainer.getTestLogger();
         String settingName = getSettingName(stateContainer);
         if (getCurrentSettingMode() == modeOn) {
@@ -73,12 +92,20 @@
     }
 
     private boolean getCurrentSettingMode() {
-        return getSettingMode() != 0;
+        return getSettingMode(DEFAULT_SETTING_VALUE) != 0;
     }
 
     private String getSettingName(ISensorTestStateContainer stateContainer) {
         return stateContainer.getString(mSettingNameResId);
     }
 
-    protected abstract int getSettingMode();
+    private boolean isSettingAvailable() {
+        if (!mInitialized) {
+            throw new IllegalStateException(
+                    "Object must be initialized first by invoking #captureInitialState.");
+        }
+        return mSettingAvailable;
+    }
+
+    protected abstract int getSettingMode(int defaultValue);
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorTestScreenManipulator.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorTestScreenManipulator.java
index 0263975..835ff56 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorTestScreenManipulator.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/helpers/SensorTestScreenManipulator.java
@@ -85,7 +85,8 @@
      * NOTE: Initialization will bring up an Activity to let the user activate the Device Admin,
      * this method will block until the user completes the operation.
      */
-    public synchronized void initialize(ISensorTestStateContainer stateContainer) {
+    public synchronized void initialize(ISensorTestStateContainer stateContainer)
+            throws InterruptedException {
         if (!isDeviceAdminInitialized()) {
             Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
             intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mComponentName);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
index dcf6c4a..c88187c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/reporting/SensorTestDetails.java
@@ -22,6 +22,7 @@
 import org.junit.runner.Result;
 
 import android.content.Context;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
 
 /**
  * A class that holds the result of a Sensor test execution.
@@ -34,7 +35,12 @@
     public enum ResultCode {
         SKIPPED,
         PASS,
-        FAIL
+        FAIL,
+        INTERRUPTED
+    }
+
+    public SensorTestDetails(String name, ResultCode resultCode) {
+        this(name, resultCode, null /* summary */);
     }
 
     public SensorTestDetails(String name, ResultCode resultCode, String summary) {
@@ -69,6 +75,21 @@
                 result.getFailureCount());
     }
 
+    public SensorTestDetails(String name, String tag, Throwable cause) {
+        ResultCode resultCode = ResultCode.FAIL;
+        if (cause instanceof InterruptedException) {
+            resultCode = ResultCode.INTERRUPTED;
+            // the interrupted status must be restored, so other routines can consume it
+            Thread.currentThread().interrupt();
+        } else if (cause instanceof SensorTestStateNotSupportedException) {
+            resultCode = ResultCode.SKIPPED;
+        }
+
+        mName = name;
+        mResultCode = resultCode;
+        mSummary = String.format("[%s] %s", tag, cause.getMessage());
+    }
+
     public String getName() {
         return mName;
     }
diff --git a/build/config.mk b/build/config.mk
index e127e90..931220b 100644
--- a/build/config.mk
+++ b/build/config.mk
@@ -18,3 +18,4 @@
 BUILD_CTS_HOST_JAVA_LIBRARY := cts/build/test_host_java_library.mk
 BUILD_CTS_TARGET_JAVA_LIBRARY := cts/build/test_target_java_library.mk
 BUILD_CTS_UI_JAVA_LIBRARY := cts/build/test_uiautomator.mk
+BUILD_CTS_DEQP_PACKAGE := cts/build/test_deqp_package.mk
diff --git a/build/deqp_dummy_test_list b/build/deqp_dummy_test_list
new file mode 100644
index 0000000..6bf22aa
--- /dev/null
+++ b/build/deqp_dummy_test_list
@@ -0,0 +1,3 @@
+suite:TestSuite
+case:TEST_CASE_NAME
+test:TEST_NAME
diff --git a/build/test_deqp_package.mk b/build/test_deqp_package.mk
new file mode 100644
index 0000000..b07876d
--- /dev/null
+++ b/build/test_deqp_package.mk
@@ -0,0 +1,44 @@
+# 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.
+
+#
+# Builds dEQP test description XMLs needed by CTS.
+#
+
+CTS_DEQP_CONFIG_PATH := $(call my-dir)
+
+cts_library_xml := $(CTS_TESTCASES_OUT)/com.drawelements.deqp.$(DEQP_API).xml
+
+$(cts_library_xml): MUSTPASS_XML_FILE := external/deqp/android/cts/com.drawelements.deqp.$(DEQP_API).xml
+$(cts_library_xml): PRIVATE_TEST_NAME := $(DEQP_TEST_NAME)
+$(cts_library_xml): PRIVATE_TEST_PACKAGE := com.drawelements.deqp.$(DEQP_API)
+$(cts_library_xml): PRIVATE_DUMMY_CASELIST := $(CTS_DEQP_CONFIG_PATH)/deqp_dummy_test_list
+$(cts_library_xml): $(call intermediates-dir-for,APPS,$(LOCAL_PACKAGE_NAME))/package.apk external/deqp/android/cts/com.drawelements.deqp.$(DEQP_API).xml $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_XML_GENERATOR)
+	$(hide) echo Generating test description for $(PRIVATE_TEST_NAME)
+	$(hide) mkdir -p $(CTS_TESTCASES_OUT)
+	
+# Query build ABIs by routing a dummy test list through xml generator and parse result
+	$(hide) $(eval supported_abi_attr := $(shell $(CTS_XML_GENERATOR) -t dummyTest \
+										-n dummyName \
+										-p invalid.dummy \
+										-e $(CTS_EXPECTATIONS) \
+										-b $(CTS_UNSUPPORTED_ABIS) \
+										-a $(CTS_TARGET_ARCH) \
+										< $(PRIVATE_DUMMY_CASELIST) \
+										| grep --only-matching -e " abis=\"[^\"]*\""))
+	
+# Patch xml caselist with supported abi
+	$(hide) $(SED_EXTENDED) -e 's:^<Test (.*)/>$$:<Test \1 $(supported_abi_attr) />:' \
+				< $(MUSTPASS_XML_FILE) \
+				> $@
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 83187c7..fe8f9ee 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -30,6 +30,7 @@
 import android.support.test.uiautomator.UiSelector;
 import android.test.InstrumentationTestCase;
 import android.test.MoreAsserts;
+import android.text.format.DateUtils;
 
 import com.android.cts.documentclient.MyActivity.Result;
 
@@ -47,6 +48,8 @@
     private UiDevice mDevice;
     private MyActivity mActivity;
 
+    private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
+
     @Override
     public void setUp() throws Exception {
         super.setUp();
@@ -54,6 +57,7 @@
         mDevice = UiDevice.getInstance(getInstrumentation());
         mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
                 MyActivity.class, null);
+        mDevice.waitForIdle();
     }
 
     @Override
@@ -78,7 +82,8 @@
         mActivity.startActivityForResult(intent, 42);
 
         // Ensure that we see both of our roots
-        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).exists());
+        mDevice.waitForIdle();
+        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
         assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
         assertFalse("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
 
@@ -86,8 +91,6 @@
         mDevice.waitForIdle();
         new UiObject(new UiSelector().text("CtsLocal")).click();
 
-        // make sure drawer is expanded?
-
         mDevice.waitForIdle();
         new UiObject(new UiSelector().text("FILE1")).click();
 
@@ -148,6 +151,7 @@
         mDevice.waitForIdle();
         new UiObject(new UiSelector().text("FILE1")).click();
 
+        mDevice.waitForIdle();
         new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
                 .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
 
@@ -237,16 +241,15 @@
         intent.setType("*/*");
         mActivity.startActivityForResult(intent, 42);
 
-        mDevice.waitForIdle();
-
         // Look around, we should be able to see both DocumentsProviders and
         // other GET_CONTENT sources.
-        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).exists());
+        mDevice.waitForIdle();
+        assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
         assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
         assertTrue("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
 
-        new UiObject(new UiSelector().text("CtsGetContent")).click();
         mDevice.waitForIdle();
+        new UiObject(new UiSelector().text("CtsGetContent")).click();
 
         final Result result = mActivity.getResult();
         assertEquals("ReSuLt", result.data.getAction());
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index bc3acaf..8b25f4b 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -30,7 +30,7 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -53,7 +53,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
@@ -76,7 +76,7 @@
 LOCAL_PACKAGE_SPLITS := v7
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
 
 LOCAL_PROGUARD_ENABLED := disabled
 LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index 9965f60..e93f6c3 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -24,7 +24,7 @@
 LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
 
 LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
 
 LOCAL_MODULE_TAGS := tests
 
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index 17dc3f1..59f0752 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -22,12 +22,11 @@
 import android.util.Log;
 
 import java.io.BufferedReader;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.IOException;
 import java.io.OutputStreamWriter;
 
-
 /**
  * Class to receive intents sent across profile boundaries, and read/write to content uri specified
  * in these intents to test cross-profile content uris.
diff --git a/hostsidetests/devicepolicy/app/IntentSender/Android.mk b/hostsidetests/devicepolicy/app/IntentSender/Android.mk
new file mode 100644
index 0000000..e45ec31
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/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_PACKAGE_NAME := CtsIntentSenderApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/IntentSender/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentSender/AndroidManifest.xml
new file mode 100644
index 0000000..070ef40
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/AndroidManifest.xml
@@ -0,0 +1,59 @@
+<?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.intent.sender">
+
+    <uses-sdk android:minSdkVersion="19" />
+
+    <permission
+        android:name="com.android.cts.intent.sender.permission.SAMPLE"
+        android:label="Sample Permission" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name=".IntentSenderActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+            </intent-filter>
+        </activity>
+
+        <provider
+            android:name="android.support.v4.content.FileProvider"
+            android:authorities="com.android.cts.intent.sender.fileprovider"
+            android:grantUriPermissions="true"
+            android:exported="false">
+            <meta-data
+                android:name="android.support.FILE_PROVIDER_PATHS"
+                android:resource="@xml/filepaths" />
+        </provider>
+
+        <provider
+            android:name=".BasicContentProvider"
+            android:authorities="com.android.cts.intent.sender.provider"
+            android:grantUriPermissions="true"
+            android:exported="true"
+            android:permission="com.android.cts.intent.sender.permission.SAMPLE" />
+
+    </application>
+
+    <instrumentation
+        android:name="android.test.InstrumentationTestRunner"
+        android:targetPackage="com.android.cts.intent.sender"
+        android:label="Intent Sender CTS Tests" />
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/filepaths.xml b/hostsidetests/devicepolicy/app/IntentSender/res/xml/filepaths.xml
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/res/xml/filepaths.xml
rename to hostsidetests/devicepolicy/app/IntentSender/res/xml/filepaths.xml
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/BasicContentProvider.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/BasicContentProvider.java
similarity index 96%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/BasicContentProvider.java
rename to hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/BasicContentProvider.java
index f91d404..183ab9f 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/BasicContentProvider.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/BasicContentProvider.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.cts.managedprofile.crossprofilecontent;
+package com.android.cts.intent.sender;
 
 import android.content.ContentProvider;
 import android.content.ContentValues;
@@ -69,4 +69,3 @@
                 new File("/dev/null"), ParcelFileDescriptor.MODE_READ_ONLY);
     }
 }
-
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
new file mode 100644
index 0000000..00fa6b7
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
@@ -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.
+ */
+
+package com.android.cts.intent.sender;
+
+import android.app.Activity;
+import android.content.Intent;
+
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+public class IntentSenderActivity extends Activity {
+
+    private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+
+    public static class Result {
+        public final int resultCode;
+        public final Intent data;
+
+        public Result(int resultCode, Intent data) {
+            this.resultCode = resultCode;
+            this.data = data;
+        }
+    }
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+        if (resultCode == Activity.RESULT_OK) {
+            try {
+                mResult.offer(new Result(resultCode, data), 5, TimeUnit.SECONDS);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    public Intent getResult(Intent intent) throws Exception {
+        startActivityForResult(intent, 42);
+        final Result result = mResult.poll(30, TimeUnit.SECONDS);
+        return (result != null) ? result.data : null;
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
similarity index 76%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java
rename to hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
index 85e7d1b..47de0da 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/CrossProfileContentTest.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
@@ -13,30 +13,26 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package com.android.cts.managedprofile.crossprofilecontent;
 
-import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+package com.android.cts.intent.sender;
 
-import android.app.admin.DevicePolicyManager;
 import android.content.ClipData;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.Uri;
 import android.support.v4.content.FileProvider;
-import android.test.ActivityInstrumentationTestCase2;
+import android.test.InstrumentationTestCase;
 import android.util.Log;
 
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileWriter;
+import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.io.IOException;
 
-public class CrossProfileContentTest extends
-        ActivityInstrumentationTestCase2<IntentSenderActivity> {
+public class IntentSenderTest extends InstrumentationTestCase {
 
     private static final String MESSAGE = "Sample Message";
 
@@ -49,35 +45,20 @@
 
     private static final String TAG = "CrossProfileContentTest";
 
-    private static final String BASIC_CONTENT_PROVIDER_AUTHORITY =
-            "com.android.cts.managedprofile.basiccontentProvider";
-
-
-    private DevicePolicyManager mDpm;
-
     private Context mContext;
-
-    public CrossProfileContentTest() {
-        super(IntentSenderActivity.class);
-    }
+    private IntentSenderActivity mActivity;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
         mContext = getInstrumentation().getTargetContext();
-        mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        IntentFilter intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_READ_FROM_URI);
-        intentFilter.addAction(ACTION_WRITE_TO_URI);
-        intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
-        mDpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
-                DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+        mActivity = launchActivity(mContext.getPackageName(), IntentSenderActivity.class, null);
     }
 
     @Override
-    protected void tearDown() throws Exception {
-        mDpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
+    public void tearDown() throws Exception {
         super.tearDown();
+        mActivity.finish();
     }
 
     /**
@@ -85,14 +66,15 @@
      * This intent will have, in the ClipData, a uri whose associated file stores a message.
      * The receiver will read the message from the uri, and put it inside the result intent.
      */
-    public void testReceiverCanRead() {
+    public void testReceiverCanRead() throws Exception {
         Uri uri = getUriWithTextInFile("reading_test", MESSAGE);
         assertTrue(uri != null);
         Intent intent = new Intent(ACTION_READ_FROM_URI);
         intent.setClipData(ClipData.newRawUri("", uri));
         intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        Intent result = getActivity().getResultForIntent(intent);
-        assertTrue(result != null);
+
+        final Intent result = mActivity.getResult(intent);
+        assertNotNull(result);
         assertEquals(MESSAGE, result.getStringExtra("extra_response"));
     }
 
@@ -102,7 +84,7 @@
      * The receiver will read the message from the extra, and write it to the uri in
      * the ClipData.
      */
-    public void testReceiverCanWrite() {
+    public void testReceiverCanWrite() throws Exception {
         // It's the receiver of the intent that should write to the uri, not us. So, for now, we
         // write an empty string.
         Uri uri = getUriWithTextInFile("writing_test", "");
@@ -112,19 +94,21 @@
         intent.putExtra("extra_message", MESSAGE);
         intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                 | Intent.FLAG_GRANT_READ_URI_PERMISSION);
-        getActivity().getResultForIntent(intent);
+
+        mActivity.getResult(intent);
         assertEquals(MESSAGE, getFirstLineFromUri(uri));
     }
 
-    public void testPersistablePermission() {
+    public void testPersistablePermission() throws Exception {
         Uri uri = getUriWithTextInFile("persistable_test", MESSAGE);
         grantPersistableReadPermission(uri);
 
         // Now checking if the receiver can read this uri, without re-granting the read permission.
         Intent intent = new Intent(ACTION_READ_FROM_URI);
         intent.setClipData(ClipData.newRawUri("", uri));
-        Intent result = getActivity().getResultForIntent(intent);
-        assertTrue(result != null);
+
+        final Intent result = mActivity.getResult(intent);
+        assertNotNull(result);
         assertEquals(MESSAGE, result.getStringExtra("extra_response"));
     }
 
@@ -138,7 +122,7 @@
      * uriNotGranted), to enforce that even if an app has permission to one uri of a
      * ContentProvider, it still cannot access a uri it does not have access to.
      */
-    public void testAppPermissionsDontWorkAcrossProfiles() {
+    public void testAppPermissionsDontWorkAcrossProfiles() throws Exception {
         // The FileProvider does not allow to use app permissions. So we need to use another
         // ContentProvider.
         Uri uriGranted = getBasicContentProviderUri("uri_granted");
@@ -152,19 +136,41 @@
         Intent notGrant = new Intent(ACTION_READ_FROM_URI);
         notGrant.setClipData(ClipData.newRawUri("", uriNotGranted));
 
-        Intent result = getActivity().getResultForIntent(notGrant);
-        assertTrue(result != null);
+        final Intent result = mActivity.getResult(notGrant);
+        assertNotNull(result);
         // The receiver did not have permission to read the uri. So it should have caught a security
         // exception.
         assertTrue(result.getBooleanExtra("extra_caught_security_exception", false));
     }
 
-    private void grantPersistableReadPermission(Uri uri) {
+    /**
+     * Ensure that sender is only able to send data that it has access to.
+     */
+    public void testSecurity() throws Exception {
+        // Pick a URI that neither of us have access to; it doens't matter if
+        // its missing, since we expect a SE before a FNFE.
+        final Uri uri = Uri.parse("content://media/external/images/media/10240");
+        final Intent intent = new Intent(ACTION_READ_FROM_URI);
+        intent.setClipData(ClipData.newRawUri("", uri));
+        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+
+        // We're expecting to run into a security exception
+        final Intent result = mActivity.getResult(intent);
+        if (result == null) {
+            // This is fine; probably of a SecurityException when off in the
+            // system somewhere.
+        } else {
+            // But if we somehow came through, make sure they threw.
+            assertTrue(result.getBooleanExtra("extra_caught_security_exception", false));
+        }
+    }
+
+    private void grantPersistableReadPermission(Uri uri) throws Exception {
         Intent grantPersistable = new Intent(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
         grantPersistable.setClipData(ClipData.newRawUri("", uri));
         grantPersistable.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
                 | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
-        getActivity().getResultForIntent(grantPersistable);
+        mActivity.getResult(grantPersistable);
     }
 
     private Uri getBasicContentProviderUri(String path) {
@@ -172,7 +178,7 @@
         // granting these uris to other apps, or these apps from trying to access these uris.
         return new Uri.Builder()
                 .scheme(ContentResolver.SCHEME_CONTENT)
-                .authority(BASIC_CONTENT_PROVIDER_AUTHORITY)
+                .authority("com.android.cts.intent.sender.provider")
                 .path(path)
                 .build();
     }
@@ -191,7 +197,7 @@
             Log.e(TAG, "Could not create file " + filename + " with text " + text);
             return null;
         }
-        return FileProvider.getUriForFile(mContext, "com.android.cts.managedprofile.fileprovider",
+        return FileProvider.getUriForFile(mContext, "com.android.cts.intent.sender.fileprovider",
                 file);
     }
 
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 56b3671..008ed38 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -19,9 +19,6 @@
 
     <uses-sdk android:minSdkVersion="20"/>
 
-    <permission android:name="com.android.cts.managedprofile.permission.SAMPLE"
-                android:label="Sample Permission"/>
-
     <application>
         <uses-library android:name="android.test.runner" />
         <receiver
@@ -65,11 +62,6 @@
                 <action android:name="com.android.cts.managedprofile.ACTION_TEST_ALL_ACTIVITY" />
             </intent-filter>
         </activity>
-        <activity android:name=".crossprofilecontent.IntentSenderActivity">
-            <intent-filter>
-                <action android:name="android.intent.action.MAIN"/>
-            </intent-filter>
-        </activity>
         <activity android:name=".UserRestrictionActivity" >
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
@@ -77,22 +69,6 @@
             </intent-filter>
         </activity>
         <activity android:name=".TestActivity" />
-        <provider
-            android:name="android.support.v4.content.FileProvider"
-            android:authorities="com.android.cts.managedprofile.fileprovider"
-            android:grantUriPermissions="true"
-            android:exported="false">
-            <meta-data
-                android:name="android.support.FILE_PROVIDER_PATHS"
-                android:resource="@xml/filepaths" />
-        </provider>
-        <provider
-            android:name="com.android.cts.managedprofile.crossprofilecontent.BasicContentProvider"
-            android:authorities="com.android.cts.managedprofile.basiccontentProvider"
-            android:grantUriPermissions="true"
-            android:exported="true"
-            android:permission="com.android.cts.managedprofile.permission.SAMPLE"
-            />
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/AllUsersActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/AllUsersActivity.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/AllUsersActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/AllUsersActivity.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ComponentDisablingActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ComponentDisablingActivity.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ComponentDisablingActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ComponentDisablingActivity.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
new file mode 100644
index 0000000..9615991
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
@@ -0,0 +1,65 @@
+/*
+ * 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.managedprofile;
+
+import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.IntentFilter;
+import android.test.AndroidTestCase;
+
+public class CrossProfileUtils extends AndroidTestCase {
+    private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+
+    private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+
+    private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
+            "com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
+
+    public void addParentCanAccessManagedFilters() {
+        removeAllFilters();
+
+        final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_READ_FROM_URI);
+        intentFilter.addAction(ACTION_WRITE_TO_URI);
+        intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
+        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
+                DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
+    }
+
+    public void addManagedCanAccessParentFilters() {
+        removeAllFilters();
+
+        final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(ACTION_READ_FROM_URI);
+        intentFilter.addAction(ACTION_WRITE_TO_URI);
+        intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
+        dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
+                DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+    }
+
+    public void removeAllFilters() {
+        final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        dpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
+    }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ManagedProfileActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileActivity.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ManagedProfileActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileActivity.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ManagedProfileTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/ManagedProfileTest.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ManagedProfileTest.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserActivity.java
similarity index 99%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserActivity.java
index 35f70be..b0e84ae 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserActivity.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserActivity.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.cts.managedprofile;
 
 import android.app.Activity;
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserFilterSetterActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserFilterSetterActivity.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserFilterSetterActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserFilterSetterActivity.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserTest.java
similarity index 99%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserTest.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserTest.java
index 7098d9e..40ff6c5 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/PrimaryUserTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserTest.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package com.android.cts.managedprofile;
 
 import android.app.admin.DevicePolicyManager;
@@ -55,7 +56,7 @@
     public void testAddCrossProfileIntentFilter_all() {
         assertEquals(2, mPackageManager.queryIntentActivities(
                 new Intent(AllUsersActivity.ACTION), /* flags = */ 0).size());
-        
+
         // If we used startActivity(), the user would have a disambiguation dialog presented which
         // requires human intervention, so we won't be testing like that
     }
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/TestActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/TestActivity.java
similarity index 100%
rename from hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofileintentfilters/TestActivity.java
rename to hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/TestActivity.java
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java
deleted file mode 100644
index e4c8ddf..0000000
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/crossprofilecontent/IntentSenderActivity.java
+++ /dev/null
@@ -1,52 +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 com.android.cts.managedprofile.crossprofilecontent;
-
-import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.util.Log;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-
-public class IntentSenderActivity extends Activity {
-
-    private CountDownLatch mLatch;
-
-    private static final int WAIT_FOR_RESPONSE_TIMEOUT_SECONDS = 5;
-
-    private Intent mResult;
-
-    Intent getResultForIntent(Intent intent) {
-        mLatch = new CountDownLatch(1);
-        mResult = null;
-        startActivityForResult(intent, 0);
-        try {
-            mLatch.await(WAIT_FOR_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
-        } catch (InterruptedException e) {
-        }
-        return mResult;
-    }
-
-    @Override
-    protected void onActivityResult(int requestCode, int resultCode, Intent result) {
-        if (resultCode == Activity.RESULT_OK) {
-            mResult = result;
-        }
-        mLatch.countDown();
-    }
-}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 15c7725..544ddff 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.devicepolicy;
 
 import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.cts.util.AbiUtils;
 import com.android.ddmlib.Log.LogLevel;
 import com.android.ddmlib.testrunner.InstrumentationResultParser;
 import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
@@ -26,11 +27,13 @@
 import com.android.ddmlib.testrunner.TestRunResult;
 import com.android.tradefed.build.IBuildInfo;
 import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
 import com.android.tradefed.log.LogUtil.CLog;
 import com.android.tradefed.result.CollectingTestListener;
 import com.android.tradefed.testtype.DeviceTestCase;
 import com.android.tradefed.testtype.IBuildReceiver;
 
+import java.io.File;
 import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.HashSet;
@@ -74,6 +77,21 @@
                 installResult);
     }
 
+    protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
+            DeviceNotAvailableException {
+        final ITestDevice device = getDevice();
+
+        final File apk = mCtsBuild.getTestApp(appFileName);
+        final String remotePath = "/data/local/tmp/" + apk.getName();
+        if (!device.pushFile(apk, remotePath)) {
+            throw new IllegalStateException("Failed to push " + apk);
+        }
+
+        final String result = device.executeShellCommand(
+                "pm install --user " + userId + " " + remotePath);
+        assertTrue(result, result.contains("\nSuccess"));
+    }
+
     /** Initializes the user with the given id. This is required so that apps can run on it. */
     protected void startUser(int userId) throws DeviceNotAvailableException {
         String command = "am start-user " + userId;
@@ -135,7 +153,17 @@
     protected boolean runDeviceTestsAsUser(
             String pkgName, @Nullable String testClassName, int userId)
             throws DeviceNotAvailableException {
-        return runDeviceTests(pkgName, testClassName, null /*testMethodName*/, userId);
+        return runDeviceTestsAsUser(pkgName, testClassName, null, userId);
+    }
+
+    /** Returns true if the specified tests passed. Tests are run as given user. */
+    protected boolean runDeviceTestsAsUser(
+            String pkgName, @Nullable String testClassName, String testMethodName, int userId)
+            throws DeviceNotAvailableException {
+        if (testClassName.startsWith(".")) {
+            testClassName = pkgName + testClassName;
+        }
+        return runDeviceTests(pkgName, testClassName, testMethodName, userId);
     }
 
     private boolean runDeviceTests(String pkgName, @Nullable String testClassName,
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 88a3b70..6ece85c 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -28,6 +28,9 @@
     private static final String MANAGED_PROFILE_PKG = "com.android.cts.managedprofile";
     private static final String MANAGED_PROFILE_APK = "CtsManagedProfileApp.apk";
 
+    private static final String INTENT_SENDER_PKG = "com.android.cts.intent.sender";
+    private static final String INTENT_SENDER_APK = "CtsIntentSenderApp.apk";
+
     private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
     private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
 
@@ -110,16 +113,30 @@
         if (!mHasFeature) {
             return;
         }
+
         try {
-            installApp(INTENT_RECEIVER_APK);
+            getDevice().uninstallPackage(INTENT_SENDER_PKG);
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+            installAppAsUser(INTENT_SENDER_APK, 0);
+            installAppAsUser(INTENT_RECEIVER_APK, mUserId);
 
-            String command = "pm uninstall --user " + mUserId + " " + INTENT_RECEIVER_PKG;
-            CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": "
-                    + getDevice().executeShellCommand(command));
+            // Test from parent to managed
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                    "addManagedCanAccessParentFilters", mUserId));
+            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", 0));
 
-            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
-                    MANAGED_PROFILE_PKG + ".crossprofilecontent.CrossProfileContentTest", mUserId));
+            getDevice().uninstallPackage(INTENT_SENDER_PKG);
+            getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+            installAppAsUser(INTENT_SENDER_APK, mUserId);
+            installAppAsUser(INTENT_RECEIVER_APK, 0);
+
+            // Test from managed to parent
+            assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+                    "addParentCanAccessManagedFilters", mUserId));
+            assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", mUserId));
+
         } finally {
+            getDevice().uninstallPackage(INTENT_SENDER_PKG);
             getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
         }
     }
diff --git a/hostsidetests/theme/assets/21/400dpi.zip b/hostsidetests/theme/assets/21/400dpi.zip
index 17efbbe..6d62e5b 100644
--- a/hostsidetests/theme/assets/21/400dpi.zip
+++ b/hostsidetests/theme/assets/21/400dpi.zip
Binary files differ
diff --git a/libs/commonutil/Android.mk b/libs/commonutil/Android.mk
new file mode 100644
index 0000000..9c131b0
--- /dev/null
+++ b/libs/commonutil/Android.mk
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+include $(call all-subdir-makefiles)
+
+# ======================================================
+# Build a static host library for the AbiUtils
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := src/com/android/cts/util/AbiUtils.java
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ctsabiutilslib
+include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/libs/commonutil/src/com/android/cts/util/AbiUtils.java b/libs/commonutil/src/com/android/cts/util/AbiUtils.java
index 4a34c6f..6f47d52 100644
--- a/libs/commonutil/src/com/android/cts/util/AbiUtils.java
+++ b/libs/commonutil/src/com/android/cts/util/AbiUtils.java
@@ -163,4 +163,22 @@
     public static String getBitness(String name) {
         return ABIS_32BIT.contains(name) ? "32" : "64";
     }
+
+    /**
+     * @param abilistString A comma separated string containing abis.
+     * @return A List of Strings containing valid ABIs.
+     */
+    public static Set<String> parseAbiList(String unsupportedAbiDescription) {
+        Set<String> abiSet = new HashSet<>();
+        String[] descSegments = unsupportedAbiDescription.split(":");
+        if (descSegments.length == 2) {
+            for (String abi : descSegments[1].split(",")) {
+                String trimmedAbi = abi.trim();
+                if (isAbiSupportedByCts(trimmedAbi)) {
+                    abiSet.add(trimmedAbi);
+                }
+            }
+        }
+        return abiSet;
+    }
 }
diff --git a/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java b/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
index 997f730..d74ddb2 100644
--- a/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
+++ b/suite/cts/deviceTests/browserbench/src/com/android/cts/browser/BrowserBenchTest.java
@@ -17,6 +17,7 @@
 package com.android.cts.browser;
 
 import android.content.Intent;
+import android.content.pm.PackageManager;
 import android.cts.util.WatchDog;
 import android.net.Uri;
 import android.provider.Browser;
@@ -70,10 +71,12 @@
     private volatile int mRunIndex;
     /** stores results for each runs. last entry will be the final score. */
     private LinkedHashMap<String, double[]> mResultsMap;
+    private PackageManager mPackageManager;
 
     @Override
     protected void setUp() throws Exception {
         super.setUp();
+        mPackageManager = getInstrumentation().getContext().getPackageManager();
         mWebServer = new CtsTestServer(getContext()) {
             @Override
             protected HttpResponse onPost(HttpRequest request) throws Exception {
@@ -124,6 +127,10 @@
 
     @TimeoutReq(minutes = 60)
     public void testOctane() throws InterruptedException {
+        if (!isBrowserSupported()) {
+            Log.i(TAG, "Skipping test for device with no supported browser");
+            return;
+        }
         String url = mWebServer.getAssetUrl(OCTANE_START_FILE) + "?auto=1";
         final int kRepeat = 5;
         doTest(url, ResultType.LOWER_BETTER, ResultUnit.MS,
@@ -167,4 +174,13 @@
             numberToProcess++;
         }
     }
+
+    /**
+     * @return true iff this device is has a working browser.
+     */
+    private boolean isBrowserSupported() {
+        return !(mPackageManager.hasSystemFeature("android.hardware.type.television")
+                 || mPackageManager.hasSystemFeature("android.software.leanback")
+                 || mPackageManager.hasSystemFeature("android.hardware.type.watch"));
+    }
 }
diff --git a/tests/JobScheduler/Android.mk b/tests/JobScheduler/Android.mk
new file mode 100755
index 0000000..499abde
--- /dev/null
+++ b/tests/JobScheduler/Android.mk
@@ -0,0 +1,34 @@
+# 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)
+
+# 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)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_PACKAGE_NAME := CtsJobSchedulerDeviceTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/tests/JobScheduler/AndroidManifest.xml b/tests/JobScheduler/AndroidManifest.xml
new file mode 100755
index 0000000..17cf399
--- /dev/null
+++ b/tests/JobScheduler/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.jobscheduler.cts.deviceside">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <service android:name="android.jobscheduler.MockJobService"
+            android:permission="android.permission.BIND_JOB_SERVICE" />
+    </application>
+
+    <!--  self-instrumenting test package. -->
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:label="JobScheduler device-side tests"
+        android:targetPackage="android.jobscheduler.cts.deviceside" >
+        <meta-data
+            android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
new file mode 100644
index 0000000..a0177e2
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -0,0 +1,118 @@
+/*
+ * 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.jobscheduler;
+
+import android.annotation.TargetApi;
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Handles callback from the framework {@link android.app.job.JobScheduler}. The behaviour of this
+ * class is configured through the static
+ * {@link TestEnvironment}.
+ */
+@TargetApi(21)
+public class MockJobService extends JobService {
+    private static final String TAG = "MockJobService";
+
+    /** Wait this long before timing out the test. */
+    private static final long DEFAULT_TIMEOUT_MILLIS = 5000L; // 5 seconds.
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.e(TAG, "Created test service.");
+    }
+
+    @Override
+    public boolean onStartJob(JobParameters params) {
+        Log.i(TAG, "Test job executing: " + params.getJobId());
+
+        TestEnvironment.getTestEnvironment().notifyExecution(params.getJobId());
+        return false;  // No work to do.
+    }
+
+    @Override
+    public boolean onStopJob(JobParameters params) {
+        return false;
+    }
+
+    /**
+     * Configures the expected behaviour for each test. This object is shared across consecutive
+     * tests, so to clear state each test is responsible for calling
+     * {@link TestEnvironment#setUp()}.
+     */
+    public static final class TestEnvironment {
+
+        private static TestEnvironment kTestEnvironment;
+        public static final int INVALID_JOB_ID = -1;
+
+        private CountDownLatch mLatch;
+        private int mExecutedJobId;
+
+        public static TestEnvironment getTestEnvironment() {
+            if (kTestEnvironment == null) {
+                kTestEnvironment = new TestEnvironment();
+            }
+            return kTestEnvironment;
+        }
+
+        /**
+         * Block the test thread, waiting on the JobScheduler to execute some previously scheduled
+         * job on this service.
+         */
+        public boolean awaitExecution() throws InterruptedException {
+            final boolean executed = mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+            return executed;
+        }
+
+        /**
+         * Block the test thread, expecting to timeout but still listening to ensure that no jobs
+         * land in the interim.
+         * @return True if the latch timed out waiting on an execution.
+         */
+        public boolean awaitTimeout() throws InterruptedException {
+            return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        }
+
+        private void notifyExecution(int jobId) {
+            Log.d(TAG, "Job executed:" + jobId);
+            mExecutedJobId = jobId;
+            mLatch.countDown();
+        }
+
+        public void setExpectedExecutions(int numExecutions) {
+            // For no executions expected, set count to 1 so we can still block for the timeout.
+            if (numExecutions == 0) {
+                mLatch = new CountDownLatch(1);
+            } else {
+                mLatch = new CountDownLatch(numExecutions);
+            }
+        }
+
+        /** Called in each testCase#setup */
+        public void setUp() {
+            mLatch = null;
+            mExecutedJobId = INVALID_JOB_ID;
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
new file mode 100644
index 0000000..a83f7a9
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.jobscheduler.cts;
+
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Schedules jobs with the {@link android.app.job.JobScheduler} that have network connectivity
+ * constraints.
+ * Requires manipulating the {@link android.net.wifi.WifiManager} to ensure an unmetered network.
+ * Similarly, requires that the phone be connected to a wifi hotspot, or else the test will fail.
+ */
+@TargetApi(21)
+public class ConnectivityConstraintTest extends ConstraintTest {
+    private static final String TAG = "ConnectivityConstraintTest";
+
+    /** Unique identifier for the job scheduled by this suite of tests. */
+    public static final int CONNECTIVITY_JOB_ID = ConnectivityConstraintTest.class.hashCode();
+
+    private WifiManager mWifiManager;
+    private ConnectivityManager mCm;
+
+    /** Whether the device running these tests supports WiFi. */
+    private boolean mHasWifi;
+    /** Whether the device running these tests supports telephony. */
+    private boolean mHasTelephony;
+
+    private JobInfo.Builder mBuilder;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mWifiManager = (WifiManager) getContext().getSystemService(Context.WIFI_SERVICE);
+        mCm =
+                (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+
+        PackageManager packageManager = mContext.getPackageManager();
+        mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI);
+        mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
+        mBuilder =
+                new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent);
+    }
+
+    // --------------------------------------------------------------------------------------------
+    // Positives - schedule jobs under conditions that require them to pass.
+    // --------------------------------------------------------------------------------------------
+
+    /**
+     * Schedule a job that requires a WiFi connection, and assert that it executes when the device
+     * is connected to WiFi. This will fail if a wifi connection is unavailable.
+     */
+    public void testUnmeteredConstraintExecutes_withWifi() throws Exception {
+        if (!mHasWifi) {
+            Log.d(TAG, "Skipping test that requires the device be WiFi enabled.");
+            return;
+        }
+        connectToWiFi();
+
+        kTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(
+                mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
+                        .build());
+
+        sendExpediteStableChargingBroadcast();
+
+        assertTrue("Job with unmetered constraint did not fire on WiFi.",
+                kTestEnvironment.awaitExecution());
+    }
+
+    /**
+     * Schedule a job with a connectivity constraint, and ensure that it executes on WiFi.
+     */
+    public void testConnectivityConstraintExecutes_withWifi() throws Exception {
+        if (!mHasWifi) {
+            Log.d(TAG, "Skipping test that requires the device be WiFi enabled.");
+            return;
+        }
+        connectToWiFi();
+
+        kTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(
+                mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                        .build());
+
+        sendExpediteStableChargingBroadcast();
+
+        assertTrue("Job with connectivity constraint did not fire on WiFi.",
+                kTestEnvironment.awaitExecution());
+    }
+
+    /**
+     * Schedule a job with a connectivity constraint, and ensure that it executes on on a mobile
+     * data connection.
+     */
+    public void testConnectivityConstraintExecutes_withMobile() throws Exception {
+        if (!checkDeviceSupportsMobileData()) {
+            return;
+        }
+        disconnectWifiToConnectToMobile();
+
+        kTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(
+                mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
+                        .build());
+
+        sendExpediteStableChargingBroadcast();
+
+        assertTrue("Job with connectivity constraint did not fire on mobile.",
+                kTestEnvironment.awaitExecution());
+    }
+
+    // --------------------------------------------------------------------------------------------
+    // Negatives - schedule jobs under conditions that require that they fail.
+    // --------------------------------------------------------------------------------------------
+
+    /**
+     * Schedule a job that requires a WiFi connection, and assert that it fails when the device is
+     * connected to a cellular provider.
+     * This test assumes that if the device supports a mobile data connection, then this connection
+     * will be available.
+     */
+    public void testUnmeteredConstraintFails_withMobile() throws Exception {
+        if (!checkDeviceSupportsMobileData()) {
+            return;
+        }
+        disconnectWifiToConnectToMobile();
+
+        kTestEnvironment.setExpectedExecutions(0);
+        mJobScheduler.schedule(
+                mBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
+                        .build());
+        sendExpediteStableChargingBroadcast();
+
+        assertTrue("Job requiring unmetered connectivity still executed on mobile.",
+                kTestEnvironment.awaitTimeout());
+    }
+
+    /**
+     * Determine whether the device running these CTS tests should be subject to tests involving
+     * mobile data.
+     * @return True if this device will support a mobile data connection.
+     */
+    private boolean checkDeviceSupportsMobileData() {
+        if (!mHasTelephony) {
+            Log.d(TAG, "Skipping test that requires telephony features, not supported by this" +
+                    " device");
+            return false;
+        }
+        if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+            Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Ensure WiFi is enabled, and block until we've verified that we are in fact connected.
+     * Taken from {@link android.net.http.cts.ApacheHttpClientTest}.
+     */
+    private void connectToWiFi() throws InterruptedException {
+        if (!mWifiManager.isWifiEnabled()) {
+            ConnectivityActionReceiver receiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI,
+                            NetworkInfo.State.CONNECTED);
+            IntentFilter filter = new IntentFilter();
+            filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+            mContext.registerReceiver(receiver, filter);
+
+            assertTrue(mWifiManager.setWifiEnabled(true));
+            assertTrue("Wifi must be configured to connect to an access point for this test.",
+                    receiver.waitForStateChange());
+
+            mContext.unregisterReceiver(receiver);
+        }
+    }
+
+    private void disconnectWifiToConnectToMobile() throws InterruptedException {
+        if (mHasWifi && mWifiManager.isWifiEnabled()) {
+            ConnectivityActionReceiver connectMobileReceiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_MOBILE,
+                            NetworkInfo.State.CONNECTED);
+            ConnectivityActionReceiver disconnectWifiReceiver =
+                    new ConnectivityActionReceiver(ConnectivityManager.TYPE_WIFI,
+                            NetworkInfo.State.DISCONNECTED);
+            IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
+            mContext.registerReceiver(connectMobileReceiver, filter);
+            mContext.registerReceiver(disconnectWifiReceiver, filter);
+
+            assertTrue(mWifiManager.setWifiEnabled(false));
+            assertTrue("Failure disconnecting from WiFi.",
+                    disconnectWifiReceiver.waitForStateChange());
+            assertTrue("Device must have access to a metered network for this test.",
+                    connectMobileReceiver.waitForStateChange());
+
+            mContext.unregisterReceiver(connectMobileReceiver);
+            mContext.unregisterReceiver(disconnectWifiReceiver);
+        }
+    }
+
+    /** Capture the last connectivity change's network type and state. */
+    private class ConnectivityActionReceiver extends BroadcastReceiver {
+
+        private final CountDownLatch mReceiveLatch = new CountDownLatch(1);
+
+        private final int mNetworkType;
+
+        private final NetworkInfo.State mExpectedState;
+
+        ConnectivityActionReceiver(int networkType, NetworkInfo.State expectedState) {
+            mNetworkType = networkType;
+            mExpectedState = expectedState;
+        }
+
+        public void onReceive(Context context, Intent intent) {
+            // Dealing with a connectivity changed event for this network type.
+            final int networkTypeChanged =
+                    intent.getIntExtra(ConnectivityManager.EXTRA_NETWORK_TYPE, -1);
+            if (networkTypeChanged == -1) {
+                Log.e(TAG, "No network type provided in intent");
+                return;
+            }
+
+            if (networkTypeChanged != mNetworkType) {
+                // Only track changes for the connectivity event that we are interested in.
+                return;
+            }
+            // Pull out the NetworkState object that we're interested in. Necessary because
+            // the ConnectivityManager will filter on uid for background connectivity.
+            NetworkInfo[] allNetworkInfo = mCm.getAllNetworkInfo();
+            NetworkInfo networkInfo = null;
+            for (int i=0; i<allNetworkInfo.length; i++) {
+                NetworkInfo ni = allNetworkInfo[i];
+                if (ni.getType() == mNetworkType) {
+                    networkInfo =  ni;
+                    break;
+                }
+            }
+            if (networkInfo == null) {
+                Log.e(TAG, "Could not find correct network type.");
+                return;
+            }
+
+            NetworkInfo.State networkState = networkInfo.getState();
+            Log.i(TAG, "Network type: " + mNetworkType + " State: " + networkState);
+            if (networkState == mExpectedState) {
+                mReceiveLatch.countDown();
+            }
+        }
+
+        public boolean waitForStateChange() throws InterruptedException {
+            return mReceiveLatch.await(30, TimeUnit.SECONDS) || hasExpectedState();
+        }
+
+        private boolean hasExpectedState() {
+            return mExpectedState == mCm.getNetworkInfo(mNetworkType).getState();
+        }
+    }
+
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
new file mode 100644
index 0000000..b9a498f
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConstraintTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.jobscheduler.cts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.jobscheduler.MockJobService;
+import android.test.AndroidTestCase;
+
+/**
+ * Common functionality from which the other test case classes derive.
+ */
+@TargetApi(21)
+public abstract class ConstraintTest extends AndroidTestCase {
+    /** Force the scheduler to consider the device to be on stable charging. */
+    private static final Intent EXPEDITE_STABLE_CHARGING =
+            new Intent("com.android.server.task.controllers.BatteryController.ACTION_CHARGING_STABLE");
+
+    /** Environment that notifies of JobScheduler callbacks. */
+    static MockJobService.TestEnvironment kTestEnvironment =
+            MockJobService.TestEnvironment.getTestEnvironment();
+    /** Handle for the service which receives the execution callbacks from the JobScheduler. */
+    static ComponentName kJobServiceComponent;
+    JobScheduler mJobScheduler;
+
+    @Override
+    public void setUp() throws Exception {
+        kTestEnvironment.setUp();
+        kJobServiceComponent = new ComponentName(getContext(), MockJobService.class);
+        mJobScheduler = (JobScheduler) getContext().getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        mJobScheduler.cancelAll();
+    }
+
+    /**
+     * The scheduler will usually only flush its queue of unexpired jobs when the device is
+     * considered to be on stable power - that is, plugged in for a period of 2 minutes.
+     * Rather than wait for this to happen, we cheat and send this broadcast instead.
+     */
+    protected void sendExpediteStableChargingBroadcast() {
+        getContext().sendBroadcast(EXPEDITE_STABLE_CHARGING);
+    }
+}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
new file mode 100644
index 0000000..36f44ef
--- /dev/null
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.jobscheduler.cts;
+
+import android.annotation.TargetApi;
+import android.app.job.JobInfo;
+
+/**
+ * Schedules jobs with various timing constraints and ensures that they are executed when
+ * appropriate.
+ */
+@TargetApi(21)
+public class TimingConstraintsTest extends ConstraintTest {
+    private static final int TIMING_JOB_ID = TimingConstraintsTest.class.hashCode() + 0;
+    private static final int CANCEL_JOB_ID = TimingConstraintsTest.class.hashCode() + 1;
+
+    public void testScheduleOnce() throws Exception {
+        JobInfo oneTimeJob = new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
+                        .setOverrideDeadline(1000)  // 1 secs
+                        .build();
+
+        kTestEnvironment.setExpectedExecutions(1);
+        mJobScheduler.schedule(oneTimeJob);
+        final boolean executed = kTestEnvironment.awaitExecution();
+        assertTrue("Timed out waiting for override deadline.", executed);
+    }
+
+    public void testSchedulePeriodic() throws Exception {
+        JobInfo periodicJob =
+                new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
+                        .setPeriodic(1000L)  // 1 second period.
+                        .build();
+
+        kTestEnvironment.setExpectedExecutions(3);
+        mJobScheduler.schedule(periodicJob);
+        final boolean countedDown = kTestEnvironment.awaitExecution();
+        assertTrue("Timed out waiting for periodic jobs to execute", countedDown);
+    }
+
+    public void testCancel() throws Exception {
+        JobInfo cancelJob = new JobInfo.Builder(CANCEL_JOB_ID, kJobServiceComponent)
+                .setOverrideDeadline(2000L)
+                .build();
+
+        kTestEnvironment.setExpectedExecutions(0);
+        mJobScheduler.schedule(cancelJob);
+        // Now cancel it.
+        mJobScheduler.cancel(CANCEL_JOB_ID);
+        assertTrue("Cancel failed: job executed when it shouldn't have.",
+                kTestEnvironment.awaitTimeout());
+    }
+}
\ No newline at end of file
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 8adf345..d931855 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -1,10 +1,11 @@
 [
 {
-  description: "signature test stil needs more work",
+  description: "tests a fragile by nature as they rely on hardcoded behavior",
   names: [
-    "android.signature.cts.SignatureTest#testSignature"
+    "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverText",
+    "android.accessibilityservice.cts.AccessibilityTextTraversalTest#testActionNextAndPreviousAtGranularityPageOverTextExtend"
   ],
-  bug: 17894722
+  bug: 17595050
 },
 {
   description: "Not all jdwp features are currently supported. These tests will fail",
@@ -37,6 +38,26 @@
   bug: 16720689
 },
 {
+  description: "test can only run properly on a user build device when the bug is resolved",
+  names: [
+    "android.appwidget.cts.AppWidgetTest#testAppWidgetProviderCallbacks",
+    "android.appwidget.cts.AppWidgetTest#testBindAppWidget",
+    "android.appwidget.cts.AppWidgetTest#testCollectionWidgets",
+    "android.appwidget.cts.AppWidgetTest#testDeleteHost",
+    "android.appwidget.cts.AppWidgetTest#testDeleteHosts",
+    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetIds",
+    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetInfo",
+    "android.appwidget.cts.AppWidgetTest#testGetAppWidgetOptions",
+    "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetId",
+    "android.appwidget.cts.AppWidgetTest#testPartiallyUpdateAppWidgetViaWidgetIds",
+    "android.appwidget.cts.AppWidgetTest#testTwoAppWidgetProviderCallbacks",
+    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaComponentName",
+    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetId",
+    "android.appwidget.cts.AppWidgetTest#testUpdateAppWidgetViaWidgetIds"
+  ],
+  bug: 17993121
+},
+{
   description: "A few WebGL tests are known to fail in WebView",
   names: [
     "android.webgl.cts.WebGLTest#test_conformance_extensions_oes_texture_float_with_video_html",
@@ -58,14 +79,18 @@
   description: "permissions for the API previously used in the test has changed, making it impossible to pass",
   names: [
     "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingFast",
-    "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingSlow",
-    "android.openglperf.cts.GlVboPerfTest#testVboVsNonVboPerfGeometry0",
-    "android.openglperf.cts.GlVboPerfTest#testVboVsNonVboPerfGeometry1",
-    "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers"
+    "android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingSlow"
   ],
   bug: 17394321
 },
 {
+  description: "unexpected failures",
+  names: [
+    "android.openglperf.cts.GlVboPerfTest#testVboWithVaryingIndexBufferNumbers"
+  ],
+  bug: 18091590
+},
+{
   description: "these tests require a good test scene, so they fail if run in random conditions",
   names: [
     "android.hardware.camera2.cts.AllocationTest#testBlackWhite",
@@ -74,6 +99,13 @@
   bug: 17530117
 },
 {
+  description: "this test removes the stay-awake option, causing the screen to turn off during the execution of subsequent tests",
+  names: [
+    "android.admin.cts.DevicePolicyManagerTest#testMaximumTimeToLock"
+  ],
+  bug: 18002490
+},
+{
   description: "these tests locks the screen with an emtpy password or swipe-to-unlock, blocking subsequent test to dismiss keyguard",
   names: [
     "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_something",
@@ -322,5 +354,12 @@
     "android.media.cts.ImageReaderDecoderTest#testHwAVCDecode360pForFlexibleYuv"
   ],
   bug: 17144778
+},
+{
+  description: "Roboto font tests are not yet known good on all devices",
+  names: [
+    "android.uirendering.cts.testclasses.FontRenderingTests"
+  ],
+  bug: 17109280
 }
 ]
diff --git a/tests/expectations/unsupportedabis.txt b/tests/expectations/unsupportedabis.txt
index 520d750..817179b 100644
--- a/tests/expectations/unsupportedabis.txt
+++ b/tests/expectations/unsupportedabis.txt
@@ -2,6 +2,14 @@
 {
   description: "Tests not supporting: arm64-v8a, x86_64, mips64",
   names: [
+    "android.bionic.malloc#pvalloc_overflow",
+    "android.bionic.malloc#pvalloc_std",
+    "android.bionic.malloc#valloc_overflow",
+    "android.bionic.malloc#valloc_std",
+    "android.renderscriptlegacy.cts.LeakTest",
+    "android.renderscriptlegacy.cts.RSBase",
+    "android.renderscriptlegacy.cts.RSBaseCompute",
+    "android.renderscriptlegacy.cts.VersionTest",
     "android.sample.cts.SampleDeviceResultTest",
     "android.sample.cts.SampleDeviceTest",
     "android.sample.cts.SampleHostResultTest",
diff --git a/tests/signature/src/android/signature/cts/JDiffClassDescription.java b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
index 7ea9911..7e36c1c 100644
--- a/tests/signature/src/android/signature/cts/JDiffClassDescription.java
+++ b/tests/signature/src/android/signature/cts/JDiffClassDescription.java
@@ -26,8 +26,10 @@
 import java.lang.reflect.TypeVariable;
 import java.lang.reflect.WildcardType;
 import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -53,6 +55,8 @@
 
     @SuppressWarnings("unchecked")
     private Class<?> mClass;
+    // A map of field name to field of the fields contained in {@code mClass}
+    private Map<String, Field> mClassFieldMap;
 
     private String mPackageName;
     private String mShortClassName;
@@ -437,9 +441,12 @@
     public void checkSignatureCompliance() {
         checkClassCompliance();
         if (mClass != null) {
+            mClassFieldMap = buildFieldMap(mClass);
             checkFieldsCompliance();
             checkConstructorCompliance();
             checkMethodCompliance();
+        } else {
+            mClassFieldMap = null;
         }
     }
 
@@ -751,14 +758,8 @@
      * @param field the field description to find
      * @return the reflected field, or null if not found.
      */
-    private Field findMatchingField(JDiffField field){
-        Field[] fields = mClass.getDeclaredFields();
-        for (Field f : fields) {
-            if (f.getName().equals(field.mName)) {
-                return f;
-            }
-        }
-        return null;
+    private Field findMatchingField(JDiffField field) {
+        return mClassFieldMap.get(field.mName);
     }
 
     /**
@@ -1191,6 +1192,31 @@
         return paramType.replace("<? extends java.lang.Object>", "<?>");
     }
 
+    /**
+     * Scan a class (an its entire inheritance chain) for fields.
+     *
+     * @return a {@link Map} of fieldName to {@link Field}
+     */
+    private static Map<String, Field> buildFieldMap(Class testClass) {
+        Map<String, Field> fieldMap = new HashMap<String, Field>();
+        // Scan the superclass
+        if (testClass.getSuperclass() != null) {
+            fieldMap.putAll(buildFieldMap(testClass.getSuperclass()));
+        }
+
+        // Scan the interfaces
+        for (Class interfaceClass : testClass.getInterfaces()) {
+            fieldMap.putAll(buildFieldMap(interfaceClass));
+        }
+
+        // Check the fields in the test class
+        for (Field field : testClass.getDeclaredFields()) {
+            fieldMap.put(field.getName(), field);
+        }
+
+        return fieldMap;
+    }
+
     private static void loge(String message, Exception exception) {
         System.err.println(String.format("%s: %s", message, exception));
     }
diff --git a/tests/signature/src/android/signature/cts/SignatureTest.java b/tests/signature/src/android/signature/cts/SignatureTest.java
index e3099a9..85b77f8 100644
--- a/tests/signature/src/android/signature/cts/SignatureTest.java
+++ b/tests/signature/src/android/signature/cts/SignatureTest.java
@@ -69,8 +69,6 @@
     private static final String ATTRIBUTE_TYPE = "type";
     private static final String ATTRIBUTE_RETURN = "return";
 
-    private static ArrayList<String> mDebugArray = new ArrayList<String>();
-
     private HashSet<String> mKeyTagSet;
     private TestResultObserver mResultObserver;
 
@@ -85,6 +83,8 @@
             mErrorString.append(type.toString().toLowerCase());
             mErrorString.append(":\t");
             mErrorString.append(name);
+            mErrorString.append("\tError: ");
+            mErrorString.append(errorMessage);
         }
     }
 
@@ -109,7 +109,7 @@
         logd(String.format("Class: %s", rClass.toString()));
         Field[] fs = rClass.getFields();
         for (Field f : fs) {
-            logd(String.format("Field: %s", fs.toString()));
+            logd(String.format("Field: %s", f.toString()));
             try {
                 start(r.getXml(f.getInt(rClass)));
             } catch (Exception e) {
@@ -142,11 +142,12 @@
      * Signature test entry point.
      */
     private void start(XmlPullParser parser) throws XmlPullParserException, IOException {
-        logd(String.format("Parser: %s", parser.getName()));
-        logd(String.format("Parser: %s", parser.getNamespace()));
-        logd(String.format("Parser: %s", parser.getLineNumber()));
-        logd(String.format("Parser: %s", parser.getColumnNumber()));
-        logd(String.format("Parser: %s", parser.getPositionDescription()));
+        logd(String.format("Name: %s", parser.getName()));
+        logd(String.format("Text: %s", parser.getText()));
+        logd(String.format("Namespace: %s", parser.getNamespace()));
+        logd(String.format("Line Number: %s", parser.getLineNumber()));
+        logd(String.format("Column Number: %s", parser.getColumnNumber()));
+        logd(String.format("Position Description: %s", parser.getPositionDescription()));
         JDiffClassDescription currentClass = null;
         String currentPackage = "";
         JDiffMethod currentMethod = null;
@@ -204,7 +205,16 @@
                 currentClass.addField(field);
             } else {
                 throw new RuntimeException(
-                        "unknow tag exception:" + tagname);
+                        "unknown tag exception:" + tagname);
+            }
+            if (currentPackage != null) {
+                logd(String.format("currentPackage: %s", currentPackage));
+            }
+            if (currentClass != null) {
+                logd(String.format("currentClass: %s", currentClass.toSignatureString()));
+            }
+            if (currentMethod != null) {
+                logd(String.format("currentMethod: %s", currentMethod.toSignatureString()));
             }
         }
     }
diff --git a/tests/tests/usage/Android.mk b/tests/tests/app.usage/Android.mk
similarity index 100%
rename from tests/tests/usage/Android.mk
rename to tests/tests/app.usage/Android.mk
diff --git a/tests/tests/usage/AndroidManifest.xml b/tests/tests/app.usage/AndroidManifest.xml
similarity index 100%
rename from tests/tests/usage/AndroidManifest.xml
rename to tests/tests/app.usage/AndroidManifest.xml
diff --git a/tests/tests/usage/src/android/app/usage/cts/Activities.java b/tests/tests/app.usage/src/android/app/usage/cts/Activities.java
similarity index 100%
rename from tests/tests/usage/src/android/app/usage/cts/Activities.java
rename to tests/tests/app.usage/src/android/app/usage/cts/Activities.java
diff --git a/tests/tests/usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
similarity index 100%
rename from tests/tests/usage/src/android/app/usage/cts/UsageStatsTest.java
rename to tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
diff --git a/tests/tests/bionic/Android.mk b/tests/tests/bionic/Android.mk
index 1a048c6..e1afd50 100644
--- a/tests/tests/bionic/Android.mk
+++ b/tests/tests/bionic/Android.mk
@@ -8,6 +8,9 @@
 LOCAL_MODULE := $(test_executable)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
 LOCAL_ADDITION_DEPENDENCIES := \
     $(LOCAL_PATH)/Android.mk \
@@ -31,6 +34,10 @@
 
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := $(list_executable)
+LOCAL_MULTILIB := both
+# Use the 32 bit list executable since it will include some 32 bit only tests.
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
 LOCAL_ADDITION_DEPENDENCIES := \
     $(LOCAL_PATH)/Android.mk \
diff --git a/tests/tests/deqp/Android.mk b/tests/tests/deqp/Android.mk
new file mode 100644
index 0000000..d8a4dda
--- /dev/null
+++ b/tests/tests/deqp/Android.mk
@@ -0,0 +1,26 @@
+# 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.
+
+# Dummy target to make dEQP test list generation consistent with other tests.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# All APIs share the same package
+LOCAL_PACKAGE_NAME := com.drawelements.deqp
+
+include $(LOCAL_PATH)/deqp_gles2.mk
+include $(LOCAL_PATH)/deqp_gles3.mk
+include $(LOCAL_PATH)/deqp_gles31.mk
diff --git a/tests/tests/deqp/deqp_gles2.mk b/tests/tests/deqp/deqp_gles2.mk
new file mode 100644
index 0000000..3cd60da
--- /dev/null
+++ b/tests/tests/deqp/deqp_gles2.mk
@@ -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.
+
+# Dummy target to make dEQP GLES2 test list generation consistent with other
+# tests.
+
+DEQP_API := gles2
+DEQP_TEST_NAME := dEQP-GLES2
+
+include $(BUILD_CTS_DEQP_PACKAGE)
diff --git a/tests/tests/deqp/deqp_gles3.mk b/tests/tests/deqp/deqp_gles3.mk
new file mode 100644
index 0000000..07dfdae
--- /dev/null
+++ b/tests/tests/deqp/deqp_gles3.mk
@@ -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.
+
+# Dummy target to make dEQP GLES3 test list generation consistent with other
+# tests.
+
+DEQP_API := gles3
+DEQP_TEST_NAME := dEQP-GLES3
+
+include $(BUILD_CTS_DEQP_PACKAGE)
diff --git a/tests/tests/deqp/deqp_gles31.mk b/tests/tests/deqp/deqp_gles31.mk
new file mode 100644
index 0000000..7e45413
--- /dev/null
+++ b/tests/tests/deqp/deqp_gles31.mk
@@ -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.
+
+# Dummy target to make dEQP GLES31 test list generation consistent with other
+# tests.
+
+DEQP_API := gles31
+DEQP_TEST_NAME := dEQP-GLES31
+
+include $(BUILD_CTS_DEQP_PACKAGE)
diff --git a/tests/tests/graphics/res/drawable/vector_icon_delete.xml b/tests/tests/graphics/res/drawable/vector_icon_delete.xml
index 7b8f2aa..8d9c21c 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_delete.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_delete.xml
@@ -24,6 +24,6 @@
 
     <path
         android:fillColor="#FF000000"
-        android:pathData="M6.0,19.0c0.0,1.104 896e-3,2.0 2.0,2.0l8.0,0.0c1.104,0.0 2.0-896e-3 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" />
+        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" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/tests/graphics/res/drawable/vector_icon_heart.xml b/tests/tests/graphics/res/drawable/vector_icon_heart.xml
index ad991c9..ff55fe5 100644
--- a/tests/tests/graphics/res/drawable/vector_icon_heart.xml
+++ b/tests/tests/graphics/res/drawable/vector_icon_heart.xml
@@ -24,6 +24,6 @@
 
     <path
         android:fillColor="#FF000000"
-        android:pathData="M16.0,5.0c-1.955.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" />
+        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" />
 
 </vector>
\ No newline at end of file
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index ea15c19..669de2d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -78,7 +78,7 @@
     private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
     private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
     private static final int SLOWMO_SLOW_FACTOR = 4;
-    private static final int MAX_NUM_FRAME_DROP_ALLOWED = 3;
+    private static final int MAX_NUM_FRAME_DROP_ALLOWED = 4;
     private List<Size> mSupportedVideoSizes;
     private Surface mRecordingSurface;
     private MediaRecorder mMediaRecorder;
@@ -548,6 +548,8 @@
      */
     private void videoSnapshotTestByCamera(boolean burstTest)
             throws Exception {
+        final int NUM_SINGLE_SHOT_TEST = 5;
+        final int FRAMEDROP_TOLERANCE = 8;
         for (int profileId : mCamcorderProfileList) {
             int cameraId = Integer.valueOf(mCamera.getId());
             if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
@@ -603,70 +605,89 @@
                         + videoSz.toString() + ".mp4";
             }
 
-            prepareRecordingWithProfile(profile);
+            int numTestIterations = burstTest ? 1 : NUM_SINGLE_SHOT_TEST;
+            int totalDroppedFrames = 0;
 
-            // prepare video snapshot
-            SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
-            SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
-            CaptureRequest.Builder videoSnapshotRequestBuilder =
-                    mCamera.createCaptureRequest((mStaticInfo.isHardwareLevelLegacy()) ?
-                            CameraDevice.TEMPLATE_RECORD : CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
+            for (int numTested = 0; numTested < numTestIterations; numTested++) {
+                prepareRecordingWithProfile(profile);
 
-            // prepare preview surface by using video size.
-            updatePreviewSurfaceWithVideoSize(videoSz);
+                // prepare video snapshot
+                SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+                SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+                CaptureRequest.Builder videoSnapshotRequestBuilder =
+                        mCamera.createCaptureRequest((mStaticInfo.isHardwareLevelLegacy()) ?
+                                CameraDevice.TEMPLATE_RECORD :
+                                CameraDevice.TEMPLATE_VIDEO_SNAPSHOT);
 
-            prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
+                // prepare preview surface by using video size.
+                updatePreviewSurfaceWithVideoSize(videoSz);
 
-            // Start recording
-            startRecording(/* useMediaRecorder */true, resultListener);
-            long startTime = SystemClock.elapsedRealtime();
+                prepareVideoSnapshot(videoSnapshotRequestBuilder, imageListener);
+                CaptureRequest request = videoSnapshotRequestBuilder.build();
 
-            // Record certain duration.
-            SystemClock.sleep(RECORDING_DURATION_MS / 2);
+                // Start recording
+                startRecording(/* useMediaRecorder */true, resultListener);
+                long startTime = SystemClock.elapsedRealtime();
 
-            // take a video snapshot
-            CaptureRequest request = videoSnapshotRequestBuilder.build();
-            if (burstTest) {
-                List<CaptureRequest> requests =
-                        new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
-                for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
-                    requests.add(request);
+                // Record certain duration.
+                SystemClock.sleep(RECORDING_DURATION_MS / 2);
+
+                // take video snapshot
+                if (burstTest) {
+                    List<CaptureRequest> requests =
+                            new ArrayList<CaptureRequest>(BURST_VIDEO_SNAPSHOT_NUM);
+                    for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
+                        requests.add(request);
+                    }
+                    mSession.captureBurst(requests, resultListener, mHandler);
+                } else {
+                    mSession.capture(request, resultListener, mHandler);
                 }
-                mSession.captureBurst(requests, resultListener, mHandler);
-            } else {
-                mSession.capture(request, resultListener, mHandler);
-            }
 
-            // make sure recording is still going after video snapshot
-            SystemClock.sleep(RECORDING_DURATION_MS / 2);
+                // make sure recording is still going after video snapshot
+                SystemClock.sleep(RECORDING_DURATION_MS / 2);
 
-            // Stop recording and preview
-            stopRecording(/* useMediaRecorder */true);
-            int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+                // Stop recording and preview
+                stopRecording(/* useMediaRecorder */true);
+                int duration = (int) (SystemClock.elapsedRealtime() - startTime);
 
-            // Validation recorded video
-            validateRecording(videoSz, duration);
+                // Validation recorded video
+                validateRecording(videoSz, duration);
 
-            if (burstTest) {
-                for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
+                if (burstTest) {
+                    for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
+                        Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+                        validateVideoSnapshotCapture(image, videoSnapshotSz);
+                        image.close();
+                    }
+                } else {
+                    // validate video snapshot image
                     Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
                     validateVideoSnapshotCapture(image, videoSnapshotSz);
+
+                    // validate if there is framedrop around video snapshot
+                    totalDroppedFrames +=  validateFrameDropAroundVideoSnapshot(
+                            resultListener, image.getTimestamp());
+
+                    //TODO: validate jittering. Should move to PTS
+                    //validateJittering(resultListener);
+
                     image.close();
                 }
-            } else {
-                // validate video snapshot image
-                Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
-                validateVideoSnapshotCapture(image, videoSnapshotSz);
-
-                // validate if there is framedrop around video snapshot
-                validateFrameDropAroundVideoSnapshot(resultListener, image.getTimestamp());
-
-                //TODO: validate jittering. Should move to PTS
-                //validateJittering(resultListener);
-
-                image.close();
             }
 
+            if (!burstTest) {
+                Log.w(TAG, String.format("Camera %d Video size %s: Number of dropped frames " +
+                        "detected in %d trials is %d frames.", cameraId, videoSz.toString(),
+                        numTestIterations, totalDroppedFrames));
+                mCollector.expectLessOrEqual(
+                        String.format(
+                                "Camera %d Video size %s: Number of dropped frames %d must not"
+                                + " be larger than %d",
+                                cameraId, videoSz.toString(), totalDroppedFrames,
+                                FRAMEDROP_TOLERANCE),
+                        FRAMEDROP_TOLERANCE, totalDroppedFrames);
+            }
             closeImageReader();
         }
     }
@@ -867,8 +888,9 @@
     /**
      * Validate if video snapshot causes frame drop.
      * Here frame drop is defined as frame duration >= 2 * expected frame duration.
+     * Return the estimated number of frames dropped during video snapshot
      */
-    private void validateFrameDropAroundVideoSnapshot(
+    private int validateFrameDropAroundVideoSnapshot(
             SimpleCaptureCallback resultListener, long imageTimeStamp) {
         int expectedDurationMs = 1000 / mVideoFrameRate;
         CaptureResult prevResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
@@ -883,6 +905,7 @@
                         resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
                 long nextTS = getValueNotNull(nextResult, CaptureResult.SENSOR_TIMESTAMP);
                 int durationMs = (int) (currentTS - prevTS) / 1000000;
+                int totalFramesDropped = 0;
 
                 // Snapshots in legacy mode pause the preview briefly.  Skip the duration
                 // requirements for legacy mode unless this is fixed.
@@ -925,8 +948,18 @@
                                 durationMs, expectedDurationMs
                         ));
                     }
+
+                    int totalDurationMs = (int) (nextTS - prevTS) / 1000000;
+                    // Rounding and minus 2 for the expected 2 frames interval
+                    totalFramesDropped =
+                            (totalDurationMs + expectedDurationMs / 2) /expectedDurationMs - 2;
+                    if (totalFramesDropped < 0) {
+                        Log.w(TAG, "totalFrameDropped is " + totalFramesDropped +
+                                ". Video frame rate might be too fast.");
+                    }
+                    totalFramesDropped = Math.max(0, totalFramesDropped);
                 }
-                return;
+                return totalFramesDropped;
             }
             prevTS = currentTS;
         }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ActivityResultMultiplexedLatch.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ActivityResultMultiplexedLatch.java
index bfc59c4..093a659 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/ActivityResultMultiplexedLatch.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ActivityResultMultiplexedLatch.java
@@ -16,9 +16,6 @@
 
 package android.hardware.cts.helpers;
 
-import android.app.Activity;
-import android.util.Log;
-
 import java.util.HashMap;
 import java.util.concurrent.CountDownLatch;
 
@@ -53,13 +50,8 @@
          *
          * @return The result code of the Activity executed.
          */
-        public int await() {
-            try {
-                mEntry.latch.await();
-            } catch (InterruptedException e) {
-                Log.e(TAG, "Error waiting for Activity result.", e);
-                return Activity.RESULT_CANCELED;
-            }
+        public int await() throws InterruptedException {
+            mEntry.latch.await();
             return mEntry.resultCode;
         }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
index c11b29f..ca7d133 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
@@ -64,7 +64,7 @@
      * </p>
      */
     @Override
-    public void waitForEvents(int eventCount) {
+    public void waitForEvents(int eventCount) throws InterruptedException {
         clearEvents();
         super.waitForEvents(eventCount);
     }
@@ -76,7 +76,7 @@
      * </p>
      */
     @Override
-    public void waitForEvents(long duration, TimeUnit timeUnit) {
+    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
         clearEvents();
         super.waitForEvents(duration, timeUnit);
     }
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 1f1b290..a79e5b1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -26,7 +26,7 @@
 /**
  * Set of static helper methods for CTS tests.
  */
-//TODO: Refactor this class into several more well defined helper classes
+//TODO: Refactor this class into several more well defined helper classes, look at StatisticsUtils
 public class SensorCtsHelper {
 
     private static final long NANOS_PER_MILLI = 1000000;
@@ -143,13 +143,9 @@
     /**
      * Helper method to sleep for a given duration.
      */
-    public static void sleep(long duration, TimeUnit timeUnit) {
+    public static void sleep(long duration, TimeUnit timeUnit) throws InterruptedException {
         long durationNs = TimeUnit.NANOSECONDS.convert(duration, timeUnit);
-        try {
-            Thread.sleep(durationNs / NANOS_PER_MILLI, (int) (durationNs % NANOS_PER_MILLI));
-        } catch (InterruptedException e) {
-            // Ignore
-        }
+        Thread.sleep(durationNs / NANOS_PER_MILLI, (int) (durationNs % NANOS_PER_MILLI));
     }
 
     /**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 4505633..9b3a5e4 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -21,6 +21,7 @@
 import android.hardware.Sensor;
 import android.hardware.SensorEvent;
 import android.hardware.SensorEventListener2;
+import android.os.SystemClock;
 import android.util.Log;
 
 import java.util.Arrays;
@@ -87,30 +88,20 @@
      */
     @Override
     public void onSensorChanged(SensorEvent event) {
+        mListener.onSensorChanged(event);
+        if (mLogEvents) {
+            Log.v(LOG_TAG, String.format(
+                    "Sensor %d: sensor_timestamp=%dns, received_timestamp=%dns, values=%s",
+                    mEnvironment.getSensor().getType(),
+                    event.timestamp,
+                    SystemClock.elapsedRealtimeNanos(),
+                    Arrays.toString(event.values)));
+        }
+
         CountDownLatch eventLatch = mEventLatch;
         if(eventLatch != null) {
             eventLatch.countDown();
         }
-        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",
-                    mEnvironment.getSensor().getType(),
-                    event.timestamp,
-                    System.nanoTime(),
-                    Arrays.toString(event.values)));
-        }
     }
 
     /**
@@ -139,17 +130,14 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForFlushComplete() {
+    public void waitForFlushComplete() throws InterruptedException {
         CountDownLatch latch = mFlushLatch;
-        try {
-            if(latch != null) {
-                Assert.assertTrue(
-                        SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
-                        latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
-            }
-        } catch(InterruptedException e) {
-            // Ignore
+        if(latch == null) {
+            return;
         }
+        Assert.assertTrue(
+                SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
+                latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
     }
 
     /**
@@ -157,15 +145,14 @@
      *
      * @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} &micro;s
      */
-    public void waitForEvents(int eventCount) {
+    public void waitForEvents(int eventCount) throws InterruptedException {
         mEventLatch = new CountDownLatch(eventCount);
         try {
             int rateUs = mEnvironment.getExpectedSamplingPeriodUs();
             // Timeout is 2 * event count * expected period + batch timeout + default wait
-            long timeoutUs = ((2 * eventCount * rateUs)
+            long timeoutUs = (2 * eventCount * rateUs)
                     + mEnvironment.getMaxReportLatencyUs()
-                    + EVENT_TIMEOUT_US);
-
+                    + EVENT_TIMEOUT_US;
             String message = SensorCtsHelper.formatAssertionMessage(
                     "WaitForEvents",
                     mEnvironment,
@@ -173,8 +160,6 @@
                     eventCount,
                     eventCount - mEventLatch.getCount());
             Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
-        } catch(InterruptedException e) {
-            // Ignore
         } finally {
             mEventLatch = null;
         }
@@ -183,7 +168,7 @@
     /**
      * Collect {@link TestSensorEvent} for a specific duration.
      */
-    public void waitForEvents(long duration, TimeUnit timeUnit) {
+    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
         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
index d72a2ce..dc40ff4 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -120,24 +120,22 @@
     /**
      * Wait for a specific number of events.
      */
-    public void waitForEvents(int eventCount) {
+    public void waitForEvents(int eventCount) throws InterruptedException {
         if (mTestSensorEventListener == null) {
             Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
-
         mTestSensorEventListener.waitForEvents(eventCount);
     }
 
     /**
      * Wait for a specific duration.
      */
-    public void waitForEvents(long duration, TimeUnit timeUnit) {
+    public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
         if (mTestSensorEventListener == null) {
             Log.w(LOG_TAG, "No listener registered, returning.");
             return;
         }
-
         mTestSensorEventListener.waitForEvents(duration, timeUnit);
     }
 
@@ -168,7 +166,6 @@
         if (mTestSensorEventListener == null) {
             return;
         }
-
         mTestSensorEventListener.waitForFlushComplete();
     }
 
@@ -185,7 +182,6 @@
         if (mTestSensorEventListener == null) {
             return;
         }
-
         startFlush();
         waitForFlushCompleted();
     }
@@ -193,12 +189,12 @@
     /**
      * Register a listener, wait for a specific number of events, and then unregister the listener.
      */
-    public void runSensor(TestSensorEventListener listener, int eventCount) {
+    public void runSensor(TestSensorEventListener listener, int eventCount)
+            throws InterruptedException {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
             return;
         }
-
         try {
             registerListener(listener);
             waitForEvents(eventCount);
@@ -210,12 +206,12 @@
     /**
      * Register a listener, wait for a specific duration, and then unregister the listener.
      */
-    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit) {
+    public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit)
+            throws InterruptedException {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
             return;
         }
-
         try {
             registerListener(listener);
             waitForEvents(duration, timeUnit);
@@ -231,7 +227,7 @@
     public void runSensorAndFlush(
             TestSensorEventListener listener,
             long duration,
-            TimeUnit timeUnit) {
+            TimeUnit timeUnit) throws InterruptedException {
         if (mTestSensorEventListener != null) {
             Log.w(LOG_TAG, "Listener already registered, returning.");
             return;
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
index 95f1248..88e4954 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -72,7 +72,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() {
+    public void execute() throws InterruptedException {
         // Start alarm
         IntentFilter intentFilter = new IntentFilter(ACTION);
         BroadcastReceiver receiver = new BroadcastReceiver() {
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
index bf43189..b4d1f23 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -50,8 +50,8 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() {
-        sleep(mDelay, mTimeUnit);
+    public void execute() throws InterruptedException {
+        SensorCtsHelper.sleep(mDelay, mTimeUnit);
         mOperation.execute();
     }
 
@@ -70,11 +70,4 @@
     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/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
index 4ae56ea..62a4e9e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
@@ -38,8 +38,12 @@
     /**
      * Executes the sensor operation.  This may throw {@link RuntimeException}s such as
      * {@link AssertionError}s.
+     *
+     * NOTE: the operation is expected to handle interruption by:
+     * - cleaning up on {@link InterruptedException}
+     * - propagating the exception down the stack
      */
-    public void execute();
+    public void execute() throws InterruptedException;
 
     /**
      * Get the stats for the operation.
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
index 4cca428..5a4466c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -16,15 +16,21 @@
 
 package android.hardware.cts.helpers.sensoroperations;
 
-import android.hardware.cts.helpers.SensorStats;
-import android.util.Log;
-
 import junit.framework.Assert;
 
+import android.hardware.cts.helpers.SensorStats;
+import android.os.SystemClock;
+
 import java.util.ArrayList;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
@@ -34,9 +40,6 @@
 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;
@@ -44,6 +47,7 @@
     /**
      * Constructor for the {@link ParallelSensorOperation} without a timeout.
      */
+    // TODO: sensor tests must always provide a timeout to prevent tests from running forever
     public ParallelSensorOperation() {
         mTimeout = null;
         mTimeUnit = null;
@@ -77,57 +81,67 @@
      * 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);
-        }
+    public void execute() throws InterruptedException {
+        int operationsCount = mOperations.size();
+        ThreadPoolExecutor executor = new ThreadPoolExecutor(
+                operationsCount,
+                operationsCount,
+                1 /* keepAliveTime */,
+                TimeUnit.SECONDS,
+                new LinkedBlockingQueue<Runnable>());
+        executor.allowCoreThreadTimeOut(true);
+        executor.prestartAllCoreThreads();
 
-        List<OperationThread> threadPool = new ArrayList<OperationThread>(mOperations.size());
+        ArrayList<Future<ISensorOperation>> futures = new ArrayList<Future<ISensorOperation>>();
         for (final ISensorOperation operation : mOperations) {
-            OperationThread thread = new OperationThread(operation);
-            thread.start();
-            threadPool.add(thread);
+            Future<ISensorOperation> future = executor.submit(new Callable<ISensorOperation>() {
+                @Override
+                public ISensorOperation call() throws Exception {
+                    operation.execute();
+                    return operation;
+                }
+            });
+            futures.add(future);
         }
 
-        List<Integer> timeoutIndices = new ArrayList<Integer>();
-        List<OperationExceptionInfo> exceptions = new ArrayList<OperationExceptionInfo>();
-        Throwable earliestException = null;
-        Long earliestExceptionTime = null;
+        Long executionTimeNs = null;
+        if (mTimeout != null) {
+            executionTimeNs = SystemClock.elapsedRealtimeNanos()
+                    + TimeUnit.NANOSECONDS.convert(mTimeout, mTimeUnit);
+        }
 
-        for (int i = 0; i < threadPool.size(); i++) {
-            OperationThread thread = threadPool.get(i);
-            join(thread, timeoutTimeNs);
-            if (thread.isAlive()) {
+        boolean hasAssertionErrors = false;
+        ArrayList<Integer> timeoutIndices = new ArrayList<Integer>();
+        ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
+        for (int i = 0; i < operationsCount; ++i) {
+            Future<ISensorOperation> future = futures.get(i);
+            try {
+                ISensorOperation operation = getFutureResult(future, executionTimeNs);
+                addSensorStats(STATS_TAG, i, operation.getStats());
+            } catch (ExecutionException e) {
+                // extract the exception thrown by the worker thread
+                Throwable cause = e.getCause();
+                hasAssertionErrors |= (cause instanceof AssertionError);
+                exceptions.add(e.getCause());
+                addSensorStats(STATS_TAG, i, mOperations.get(i).getStats());
+            } catch (TimeoutException e) {
+                // we log, but we also need to interrupt the operation to terminate cleanly
                 timeoutIndices.add(i);
-                thread.interrupt();
+                future.cancel(true /* mayInterruptIfRunning */);
+            } catch (InterruptedException e) {
+                // clean-up after ourselves by interrupting all the worker threads, and propagate
+                // the interruption status, so we stop the outer loop as well
+                executor.shutdownNow();
+                throw e;
             }
-
-            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;
+        String summary = getSummaryMessage(exceptions, timeoutIndices);
+        if (hasAssertionErrors) {
+            getStats().addValue(SensorStats.ERROR, summary);
+        }
+        if (!exceptions.isEmpty() || !timeoutIndices.isEmpty()) {
+            Assert.fail(summary);
         }
     }
 
@@ -144,114 +158,39 @@
     }
 
     /**
-     * Helper method that joins a thread at a given time in the future.
+     * Helper method that waits for a {@link Future} to complete, and returns its result.
      */
-    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");
+    private ISensorOperation getFutureResult(Future<ISensorOperation> future, Long timeoutNs)
+            throws ExecutionException, TimeoutException, InterruptedException {
+        if (timeoutNs == null) {
+            return future.get();
         }
+        // cap timeout to 1ns so that join doesn't block indefinitely
+        long waitTimeNs = Math.max(timeoutNs - SystemClock.elapsedRealtimeNanos(), 1);
+        return future.get(waitTimeNs, TimeUnit.NANOSECONDS);
     }
 
     /**
-     * Helper method for joining the exception messages used in assertions.
+     * Helper method for joining the exception and timeout messages used in assertions.
      */
-    private String getExceptionMessage(List<OperationExceptionInfo> exceptions,
-            List<Integer> timeoutIndices) {
+    private String getSummaryMessage(List<Throwable> 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;
+        for (Throwable exception : exceptions) {
+            sb.append(exception.toString()).append(", ");
         }
 
-        @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;
+        if (!timeoutIndices.isEmpty()) {
+            sb.append("Operation");
+            if (timeoutIndices.size() != 1) {
+                sb.append("s");
             }
+            sb.append(" [");
+            for (Integer index : timeoutIndices) {
+                sb.append(index).append(", ");
+            }
+            sb.append("] timed out");
         }
 
-        public ISensorOperation getSensorOperation() {
-            return mOperation;
-        }
-
-        public Throwable getException() {
-            return mException;
-        }
-
-        public Long getExceptionTime() {
-            return mExceptionTime;
-        }
+        return sb.toString();
     }
 }
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
index 5e023e5..3d682fe 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -48,7 +48,7 @@
      * in one iterations, it is thrown and all subsequent iterations will not run.
      */
     @Override
-    public void execute() {
+    public void execute() throws InterruptedException {
         for(int i = 0; i < mIterations; ++i) {
             ISensorOperation operation = mOperation.clone();
             try {
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
index 15b6978..bc48725 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -35,7 +35,7 @@
      * Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
      * rely on this operation.
      */
-    public void testFakeSensorOperation() {
+    public void testFakeSensorOperation() throws InterruptedException {
         final int opDurationMs = 100;
 
         ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
@@ -60,7 +60,7 @@
     /**
      * Test that the {@link DelaySensorOperation} functions correctly.
      */
-    public void testDelaySensorOperation() {
+    public void testDelaySensorOperation() throws InterruptedException {
         final int opDurationMs = 500;
         final int subOpDurationMs = 100;
 
@@ -77,7 +77,7 @@
     /**
      * Test that the {@link ParallelSensorOperation} functions correctly.
      */
-    public void testParallelSensorOperation() {
+    public void testParallelSensorOperation() throws InterruptedException {
         final int subOpCount = 100;
         final int subOpDurationMs = 500;
 
@@ -118,7 +118,7 @@
      * Test that the {@link ParallelSensorOperation} functions correctly if there is a failure in
      * a child operation.
      */
-    public void testParallelSensorOperation_fail() {
+    public void testParallelSensorOperation_fail() throws InterruptedException {
         final int subOpCount = 100;
 
         ParallelSensorOperation op = new ParallelSensorOperation();
@@ -158,7 +158,7 @@
      * Test that the {@link ParallelSensorOperation} functions correctly if a child exceeds the
      * timeout.
      */
-    public void testParallelSensorOperation_timeout() {
+    public void testParallelSensorOperation_timeout() throws InterruptedException {
         final int subOpCount = 100;
 
         ParallelSensorOperation op = new ParallelSensorOperation(1, TimeUnit.SECONDS);
@@ -192,7 +192,7 @@
     /**
      * Test that the {@link RepeatingSensorOperation} functions correctly.
      */
-    public void testRepeatingSensorOperation() {
+    public void testRepeatingSensorOperation() throws InterruptedException {
         final int iterations = 10;
         final int subOpDurationMs = 100;
 
@@ -219,7 +219,7 @@
      * Test that the {@link RepeatingSensorOperation} functions correctly if there is a failure in
      * a child operation.
      */
-    public void testRepeatingSensorOperation_fail() {
+    public void testRepeatingSensorOperation_fail() throws InterruptedException {
         final int iterations = 100;
         final int failCount = 75;
 
@@ -277,7 +277,7 @@
     /**
      * Test that the {@link SequentialSensorOperation} functions correctly.
      */
-    public void testSequentialSensorOperation() {
+    public void testSequentialSensorOperation() throws InterruptedException {
         final int subOpCount = 10;
         final int subOpDurationMs = 100;
 
@@ -308,7 +308,7 @@
      * Test that the {@link SequentialSensorOperation} functions correctly if there is a failure in
      * a child operation.
      */
-    public void testSequentialSensorOperation_fail() {
+    public void testSequentialSensorOperation_fail() throws InterruptedException {
         final int subOpCount = 100;
         final int failCount = 75;
 
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
index 050a8f6..2ed0ca6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -48,7 +48,7 @@
      * in one operation, it is thrown and all subsequent operations will not run.
      */
     @Override
-    public void execute() {
+    public void execute() throws InterruptedException {
         for (int i = 0; i < mOperations.size(); i++) {
             ISensorOperation operation = mOperations.get(i);
             try {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
index 1fb6bef..d5aa4b9 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
@@ -53,7 +53,7 @@
      * {@inheritDoc}
      */
     @Override
-    protected void doExecute(TestSensorEventListener listener) {
+    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
         mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
     }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 6c3851e..695e1a7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -76,7 +76,7 @@
      * {@inheritDoc}
      */
     @Override
-    protected void doExecute(TestSensorEventListener listener) {
+    protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
         if (mEventCount != null) {
             mSensorManager.runSensor(listener, mEventCount);
         } else {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
index c635a75..57018eb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
@@ -94,7 +94,7 @@
      * Collect the specified number of events from the sensor and run all enabled verifications.
      */
     @Override
-    public void execute() {
+    public void execute() throws InterruptedException {
         getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
 
         ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
@@ -131,7 +131,7 @@
     /**
      * Execute operations in a {@link TestSensorManager}.
      */
-    protected abstract void doExecute(TestSensorEventListener listener);
+    protected abstract void doExecute(TestSensorEventListener listener) throws InterruptedException;
 
     /**
      * Clone the subclass operation.
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
index 73da9c9..b500ea7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -59,7 +59,7 @@
      * {@inheritDoc}
      */
     @Override
-    public void execute() {
+    public void execute() throws InterruptedException {
         PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
         WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
 
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index f330e8a..3765809 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -227,15 +227,18 @@
     @UiThreadTest
     public void testGpsStatusListener() {
         try {
-            mManager.addGpsStatusListener(new MockGpsStatusListener());
-            fail("Should have failed to add a gps status listener");
+            // .addGpsStatusListener returns true if the listener added successfully
+            if (mManager.addGpsStatusListener(new MockGpsStatusListener())) {
+                fail("Should have failed to add a gps status listener");
+            }
         } catch (SecurityException e) {
             // expected
         }
 
         try {
-            mManager.addGpsStatusListener(null);
-            fail("Should have failed to add a gps status listener");
+            if (mManager.addGpsStatusListener(null)) {
+                fail("Should have failed to add null as a gps status listener");
+            }
         } catch (SecurityException e) {
             // expected
         }
diff --git a/tests/tests/media/src/android/media/cts/AudioEffectTest.java b/tests/tests/media/src/android/media/cts/AudioEffectTest.java
index 7ff78d8..1c96abd 100644
--- a/tests/tests/media/src/android/media/cts/AudioEffectTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioEffectTest.java
@@ -35,10 +35,10 @@
 import android.util.Log;
 import java.util.UUID;
 
-public class AudioEffectTest extends AndroidTestCase {
+public class AudioEffectTest extends PostProcTestBase {
 
     private String TAG = "AudioEffectTest";
-    private final static int MIN_NUMBER_EFFECTS = 5;
+    private final static int MIN_NUMBER_EFFECTS = 1;
     // allow +/- 5% tolerance between set and get delays
     private final static float DELAY_TOLERANCE = 1.05f;
     // allow +/- 5% tolerance between set and get ratios
@@ -50,17 +50,9 @@
 
     private AudioEffect mEffect = null;
     private AudioEffect mEffect2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
     private MediaPlayer mMediaPlayer = null;
     private int mError = 0;
 
-    private final Object mLock = new Object();
-
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -71,9 +63,11 @@
     // 0 - static methods
     //----------------------------------
 
-    //Test case 0.0: test queryEffects() and platfrom at least provides Equalizer, Bass Boost,
-    // Virtualizer, Environmental reverb and Preset reverb effects
+    //Test case 0.0: test queryEffects() and platfrom at least provides an Equalizer
     public void test0_0QueryEffects() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
 
         AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
 
@@ -81,29 +75,14 @@
                 (desc.length >= MIN_NUMBER_EFFECTS));
 
         boolean hasEQ = false;
-        boolean hasBassBoost = false;
-        boolean hasVirtualizer = false;
-        boolean hasEnvReverb = false;
-        boolean hasPresetReverb = false;
 
         for (int i = 0; i < desc.length; i++) {
             if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)) {
                 hasEQ = true;
-            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)) {
-                hasBassBoost = true;
-            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
-                hasVirtualizer = true;
-            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)) {
-                hasEnvReverb = true;
-            } else if (desc[i].type.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)) {
-                hasPresetReverb = true;
+                break;
             }
         }
         assertTrue("test0_0QueryEffects: equalizer not found", hasEQ);
-        assertTrue("test0_0QueryEffects: bass boost not found", hasBassBoost);
-        assertTrue("test0_0QueryEffects: virtualizer not found", hasVirtualizer);
-        assertTrue("test0_0QueryEffects: environmental reverb not found", hasEnvReverb);
-        assertTrue("test0_0QueryEffects: preset reverb not found", hasPresetReverb);
     }
 
     //-----------------------------------------------------------------
@@ -129,45 +108,10 @@
         return ar;
     }
 
-    //Test case 1.0: test constructor from effect type and get effect ID
-    public void test1_0ConstructorFromType() throws Exception {
-        AudioEffect.Descriptor[] desc = AudioEffect.queryEffects();
-        assertTrue("no effects found", (desc.length != 0));
-        for (int i = 0; i < desc.length; i++) {
-            if (!desc[i].type.equals(AudioEffect.EFFECT_TYPE_NULL)) {
-                try {
-                    int sessionId;
-                    AudioRecord ar = null;
-                    if (AudioEffect.EFFECT_PRE_PROCESSING.equals(desc[i].connectMode)) {
-                        ar = getAudioRecord();
-                        sessionId = ar.getAudioSessionId();
-                    } else {
-                        sessionId = 0;
-                    }
-                    AudioEffect effect = new AudioEffect(desc[i].type,
-                            AudioEffect.EFFECT_TYPE_NULL,
-                            0,
-                            sessionId);
+//    // Test case 1.0: test constructor from effect type and get effect ID
+//    public void test1_0ConstructorFromType() ...
+//    Note: This test was removed because it used hidden api's.
 
-                    assertNotNull("could not create AudioEffect", effect);
-                    try {
-                        assertTrue("invalid effect ID", (effect.getId() != 0));
-                    } catch (IllegalStateException e) {
-                        fail("AudioEffect not initialized");
-                    } finally {
-                        effect.release();
-                        if (ar != null) {
-                            ar.release();
-                        }
-                    }
-                } catch (IllegalArgumentException e) {
-                    fail("Effect not found: "+desc[i].name);
-                } catch (UnsupportedOperationException e) {
-                    fail("Effect library not loaded");
-                }
-            }
-        }
-    }
 
 //    //Test case 1.1: test constructor from effect uuid
 //    public void test1_1ConstructorFromUuid() ...
@@ -175,28 +119,15 @@
 //     1. will fail in devices that offload effects
 //     2. it used hidden api's.
 
-    //Test case 1.2: test constructor failure from unknown type
-    public void test1_2ConstructorUnknownType() throws Exception {
-
-        try {
-            AudioEffect effect = new AudioEffect(UUID.randomUUID(),
-                    AudioEffect.EFFECT_TYPE_NULL,
-                    0,
-                    0);
-            fail("could create random AudioEffect");
-            if (effect != null) {
-                effect.release();
-            }
-        } catch (IllegalArgumentException e) {
-
-        } catch (UnsupportedOperationException e) {
-            fail("Effect library not loaded");
-        }
-    }
+//    //Test case 1.2: test constructor failure from unknown type
+//    public void test1_2ConstructorUnknownType() ...
+//    Note: This test was removed because it used hidden api's.
 
     //Test case 1.3: test getEnabled() failure when called on released effect
     public void test1_3GetEnabledAfterRelease() throws Exception {
-
+        if (!hasAudioOutput()) {
+            return;
+        }
         try {
             AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
                     AudioEffect.EFFECT_TYPE_NULL,
@@ -219,6 +150,9 @@
 
     //Test case 1.4: test contructor on mediaPlayer audio session
     public void test1_4InsertOnMediaPlayer() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         MediaPlayer mp = new MediaPlayer();
         assertNotNull("could not create mediaplayer", mp);
         AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.testmp3);
@@ -238,6 +172,9 @@
 
     //Test case 1.5: test auxiliary effect attachement on MediaPlayer
     public void test1_5AuxiliaryOnMediaPlayer() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createMediaPlayerLooper();
@@ -270,6 +207,9 @@
 
     //Test case 1.6: test auxiliary effect attachement failure before setDatasource
     public void test1_6AuxiliaryOnMediaPlayerFailure() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createMediaPlayerLooper();
@@ -301,6 +241,9 @@
 
     //Test case 1.7: test auxiliary effect attachement on AudioTrack
     public void test1_7AuxiliaryOnAudioTrack() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         AudioTrack track = null;
         getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
         try {
@@ -342,7 +285,9 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
-
+        if (!hasAudioOutput()) {
+            return;
+        }
         try {
             AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
                     AudioEffect.EFFECT_TYPE_NULL,
@@ -370,7 +315,9 @@
 
     //Test case 2.1: test setEnabled() throws exception after release
     public void test2_1SetEnabledAfterRelease() throws Exception {
-
+        if (!hasAudioOutput()) {
+            return;
+        }
         try {
             AudioEffect effect = new AudioEffect(AudioEffect.EFFECT_TYPE_EQUALIZER,
                     AudioEffect.EFFECT_TYPE_NULL,
@@ -397,6 +344,9 @@
 
     //Test case 3.0: test setParameter(byte[], byte[]) / getParameter(byte[], byte[])
     public void test3_0SetParameterByteArrayByteArray() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
         try {
             byte[] param = mEffect.intToByteArray(PresetReverb.PARAM_PRESET);
@@ -428,6 +378,9 @@
 
     //Test case 3.1: test setParameter(int, int) / getParameter(int, int[])
     public void test3_1SetParameterIntInt() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
         try {
             int param = EnvironmentalReverb.PARAM_DECAY_TIME;
@@ -459,6 +412,9 @@
 
     //Test case 3.2: test setParameter(int, short) / getParameter(int, short[])
     public void test3_2SetParameterIntShort() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
         try {
             int param = PresetReverb.PARAM_PRESET;
@@ -488,6 +444,9 @@
 
     //Test case 3.3: test setParameter(int, byte[]) / getParameter(int, byte[])
     public void test3_3SetParameterIntByteArray() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
         try {
             int param = EnvironmentalReverb.PARAM_DECAY_TIME;
@@ -521,6 +480,9 @@
 
     //Test case 3.4: test setParameter(int[], int[]) / getParameter(int[], int[])
     public void test3_4SetParameterIntArrayIntArray() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
         try {
             int[] param = new int[1];
@@ -555,6 +517,9 @@
     //Test case 3.5: test setParameter(int[], short[]) / getParameter(int[], short[])
 
     public void test3_5SetParameterIntArrayShortArray() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
         try {
             int[] param = new int[1];
@@ -586,6 +551,9 @@
 
     //Test case 3.6: test setParameter(int[], byte[]) / getParameter(int[], byte[])
     public void test3_6SetParameterIntArrayByteArray() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_ENV_REVERB, 0);
         try {
             int[] param = new int[1];
@@ -620,6 +588,9 @@
 
     //Test case 3.7: test setParameter() throws exception after release()
     public void test3_7SetParameterAfterRelease() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         AudioEffect effect = null;
         try {
             effect = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
@@ -645,6 +616,9 @@
 
     //Test case 3.8: test getParameter() throws exception after release()
     public void test3_8GetParameterAfterRelease() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         AudioEffect effect = null;
         try {
             effect = new AudioEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB,
@@ -675,6 +649,9 @@
 
     //Test case 4.0: test control passed to higher priority client
     public void test4_0setEnabledLowerPriority() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         AudioEffect effect1 = null;
         AudioEffect effect2 = null;
         try {
@@ -712,6 +689,9 @@
 
     //Test case 4.1: test control passed to higher priority client
     public void test4_1setParameterLowerPriority() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         AudioEffect effect1 = null;
         AudioEffect effect2 = null;
         try {
@@ -761,7 +741,9 @@
 
     //Test case 4.2: test control status listener
     public void test4_2ControlStatusListener() throws Exception {
-
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -784,7 +766,9 @@
 
     //Test case 4.3: test enable status listener
     public void test4_3EnableStatusListener() throws Exception {
-
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -811,6 +795,9 @@
 
     //Test case 4.4: test parameter changed listener
     public void test4_4ParameterChangedListener() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
@@ -844,6 +831,9 @@
 
     //Test case 5.0: test command method
     public void test5_0Command() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getEffect(AudioEffect.EFFECT_TYPE_PRESET_REVERB, 0);
         try {
             byte[] cmd = new byte[0];
diff --git a/tests/tests/media/src/android/media/cts/AudioPreProcessingTest.java b/tests/tests/media/src/android/media/cts/AudioPreProcessingTest.java
index e993695..de5b698 100644
--- a/tests/tests/media/src/android/media/cts/AudioPreProcessingTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioPreProcessingTest.java
@@ -18,6 +18,7 @@
 
 import com.android.cts.media.R;
 
+import android.content.pm.PackageManager;
 import android.media.AudioFormat;
 import android.media.AudioRecord;
 import android.media.audiofx.AcousticEchoCanceler;
@@ -47,6 +48,10 @@
 
     //Test case 1.1: test NS creation and release
     public void test1_1NsCreateAndRelease() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         AudioRecord ar = getAudioRecord();
         assertNotNull("could not create AudioRecord", ar);
 
@@ -67,6 +72,10 @@
 
     //Test case 1.2: test setEnabled() and getEnabled()
     public void test1_2NsSetEnabledGetEnabled() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         if (!NoiseSuppressor.isAvailable()) {
             return;
         }
@@ -100,6 +109,10 @@
 
     //Test case 2.1: test AEC creation and release
     public void test2_1AecCreateAndRelease() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         AudioRecord ar = getAudioRecord();
         assertNotNull("could not create AudioRecord", ar);
 
@@ -120,6 +133,10 @@
 
     //Test case 2.2: test AEC setEnabled() and getEnabled()
     public void test2_2AecSetEnabledGetEnabled() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         if (!AcousticEchoCanceler.isAvailable()) {
             return;
         }
@@ -153,6 +170,10 @@
 
     //Test case 3.1: test AGC creation and release
     public void test3_1AgcCreateAndRelease() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         AudioRecord ar = getAudioRecord();
         assertNotNull("could not create AudioRecord", ar);
 
@@ -173,6 +194,10 @@
 
     //Test case 3.2: test AGC setEnabled() and getEnabled()
     public void test3_2AgcSetEnabledGetEnabled() throws Exception {
+        if (!hasMicrophone()) {
+            return;
+        }
+
         if (!AutomaticGainControl.isAvailable()) {
             return;
         }
@@ -199,6 +224,10 @@
     //-----------------------------------------------------------------
     // private methods
     //----------------------------------
+    private boolean hasMicrophone() {
+        return getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_MICROPHONE);
+    }
 
     private AudioRecord getAudioRecord() {
         AudioRecord ar = null;
diff --git a/tests/tests/media/src/android/media/cts/BassBoostTest.java b/tests/tests/media/src/android/media/cts/BassBoostTest.java
index 9dcb4e9..c9bffd9 100644
--- a/tests/tests/media/src/android/media/cts/BassBoostTest.java
+++ b/tests/tests/media/src/android/media/cts/BassBoostTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class BassBoostTest extends AndroidTestCase {
+public class BassBoostTest extends PostProcTestBase {
 
     private String TAG = "BassBoostTest";
     private final static short TEST_STRENGTH = 500;
@@ -34,13 +34,6 @@
 
     private BassBoost mBassBoost = null;
     private BassBoost mBassBoost2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -53,10 +46,12 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         BassBoost eq = null;
         try {
             eq = new BassBoost(0, 0);
-            assertNotNull("could not create BassBoost", eq);
             try {
                 assertTrue("invalid effect ID", (eq.getId() != 0));
             } catch (IllegalStateException e) {
@@ -81,6 +76,9 @@
 
     //Test case 1.0: test strength
     public void test1_0Strength() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         getBassBoost(0);
         try {
             if (mBassBoost.getStrengthSupported()) {
@@ -110,6 +108,9 @@
 
     //Test case 1.1: test properties
     public void test1_1Properties() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         getBassBoost(0);
         try {
             BassBoost.Settings settings = mBassBoost.getProperties();
@@ -144,6 +145,9 @@
 
     //Test case 1.2: test setStrength() throws exception after release
     public void test1_2SetStrengthAfterRelease() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         getBassBoost(0);
         mBassBoost.release();
         try {
@@ -162,6 +166,9 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         getBassBoost(0);
         try {
             mBassBoost.setEnabled(true);
@@ -178,6 +185,9 @@
 
     //Test case 2.1: test setEnabled() throws exception after release
     public void test2_1SetEnabledAfterRelease() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         getBassBoost(0);
         mBassBoost.release();
         try {
@@ -196,6 +206,9 @@
 
     //Test case 3.0: test control status listener
     public void test3_0ControlStatusListener() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -218,6 +231,9 @@
 
     //Test case 3.1: test enable status listener
     public void test3_1EnableStatusListener() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -242,6 +258,9 @@
 
     //Test case 3.2: test parameter changed listener
     public void test3_2ParameterChangedListener() throws Exception {
+        if (!isBassBoostAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
@@ -413,5 +432,4 @@
             mBassBoost2 = null;
         }
     }
-
 }
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/EnvReverbTest.java b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
index edfef96..e2e9b6d 100644
--- a/tests/tests/media/src/android/media/cts/EnvReverbTest.java
+++ b/tests/tests/media/src/android/media/cts/EnvReverbTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class EnvReverbTest extends AndroidTestCase {
+public class EnvReverbTest extends PostProcTestBase {
 
     private String TAG = "EnvReverbTest";
     private final static int MILLIBEL_TOLERANCE = 100;            // +/-1dB
@@ -34,13 +34,6 @@
 
     private EnvironmentalReverb mReverb = null;
     private EnvironmentalReverb mReverb2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -53,10 +46,12 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         EnvironmentalReverb envReverb = null;
          try {
             envReverb = new EnvironmentalReverb(0, 0);
-            assertNotNull("could not create EnvironmentalReverb", envReverb);
             try {
                 assertTrue("invalid effect ID", (envReverb.getId() != 0));
             } catch (IllegalStateException e) {
@@ -80,6 +75,9 @@
 
     //Test case 1.0: test room level and room HF level
     public void test1_0Room() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             short level = mReverb.getRoomLevel();
@@ -111,6 +109,9 @@
 
     //Test case 1.1: test decay time and ratio
     public void test1_1Decay() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             int time = mReverb.getDecayTime();
@@ -142,6 +143,9 @@
 
     //Test case 1.2: test reverb level and delay
     public void test1_2Reverb() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             short level = mReverb.getReverbLevel();
@@ -175,6 +179,9 @@
 
     //Test case 1.3: test early reflections level and delay
     public void test1_3Reflections() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
 // FIXME:uncomment actual test when early reflections are implemented in the reverb
@@ -213,6 +220,9 @@
 
     //Test case 1.4: test diffusion and density
     public void test1_4DiffusionAndDensity() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             short ratio = mReverb.getDiffusion();
@@ -244,6 +254,9 @@
 
     //Test case 1.5: test properties
     public void test1_5Properties() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             EnvironmentalReverb.Settings settings = mReverb.getProperties();
@@ -273,6 +286,9 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             mReverb.setEnabled(true);
@@ -306,6 +322,9 @@
 
     //Test case 3.0: test control status listener
     public void test3_0ControlStatusListener() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -328,6 +347,9 @@
 
     //Test case 3.1: test enable status listener
     public void test3_1EnableStatusListener() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
          synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -352,6 +374,9 @@
 
     //Test case 3.2: test parameter changed listener
     public void test3_2ParameterChangedListener() throws Exception {
+        if (!isEnvReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
@@ -523,5 +548,4 @@
             mReverb2 = null;
         }
     }
-
 }
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/EqualizerTest.java b/tests/tests/media/src/android/media/cts/EqualizerTest.java
index 3bc0d8e..15f0c73 100644
--- a/tests/tests/media/src/android/media/cts/EqualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/EqualizerTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class EqualizerTest extends AndroidTestCase {
+public class EqualizerTest extends PostProcTestBase {
 
     private String TAG = "EqualizerTest";
     private final static int MIN_NUMBER_OF_BANDS = 4;
@@ -37,13 +37,6 @@
 
     private Equalizer mEqualizer = null;
     private Equalizer mEqualizer2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -56,10 +49,12 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         Equalizer eq = null;
         try {
             eq = new Equalizer(0, 0);
-            assertNotNull("could not create Equalizer", eq);
             try {
                 assertTrue("invalid effect ID", (eq.getId() != 0));
             } catch (IllegalStateException e) {
@@ -83,6 +78,9 @@
 
     //Test case 1.0: test setBandLevel() and getBandLevel()
     public void test1_0BandLevel() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         try {
             short numBands = mEqualizer.getNumberOfBands();
@@ -112,6 +110,9 @@
 
     //Test case 1.1: test band frequency
     public void test1_1BandFrequency() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         try {
             short band = mEqualizer.getBand(TEST_FREQUENCY_MILLIHERTZ);
@@ -137,6 +138,9 @@
 
     //Test case 1.2: test presets
     public void test1_2Presets() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         try {
             short numPresets = mEqualizer.getNumberOfPresets();
@@ -162,6 +166,9 @@
 
     //Test case 1.3: test properties
     public void test1_3Properties() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         try {
             Equalizer.Settings settings = mEqualizer.getProperties();
@@ -193,7 +200,9 @@
 
     //Test case 1.4: test setBandLevel() throws exception after release
     public void test1_4SetBandLevelAfterRelease() throws Exception {
-
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         mEqualizer.release();
         try {
@@ -211,6 +220,9 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         try {
             mEqualizer.setEnabled(true);
@@ -227,7 +239,9 @@
 
     //Test case 2.1: test setEnabled() throws exception after release
     public void test2_1SetEnabledAfterRelease() throws Exception {
-
+        if (!hasAudioOutput()) {
+            return;
+        }
         getEqualizer(0);
         mEqualizer.release();
         try {
@@ -245,6 +259,9 @@
 
     //Test case 3.0: test control status listener
     public void test3_0ControlStatusListener() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -267,6 +284,9 @@
 
     //Test case 3.1: test enable status listener
     public void test3_1EnableStatusListener() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -291,6 +311,9 @@
 
     //Test case 3.2: test parameter changed listener
     public void test3_2ParameterChangedListener() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
@@ -460,5 +483,4 @@
             mEqualizer2 = null;
         }
     }
-
 }
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index d8a20e9..865780e 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -16,24 +16,38 @@
 
 package android.media.cts;
 
-
+import android.content.pm.PackageManager;
 import android.media.MediaCodec;
 import android.media.MediaCodecInfo;
 import android.media.MediaCodecInfo.CodecProfileLevel;
 import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.AudioCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecInfo.EncoderCapabilities;
 import android.media.MediaCodecList;
+import android.media.MediaFormat;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
 import java.io.File;
 import java.io.IOException;
-import java.util.List;
 import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
 
 public class MediaCodecListTest extends AndroidTestCase {
 
     private static final String TAG = "MediaCodecListTest";
     private static final String MEDIA_CODEC_XML_FILE = "/etc/media_codecs.xml";
+    private final MediaCodecList mRegularCodecs =
+            new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+    private final MediaCodecList mAllCodecs =
+            new MediaCodecList(MediaCodecList.ALL_CODECS);
+    private final MediaCodecInfo[] mRegularInfos =
+            mRegularCodecs.getCodecInfos();
+    private final MediaCodecInfo[] mAllInfos =
+            mAllCodecs.getCodecInfos();
 
     class CodecType {
         CodecType(String type, boolean isEncoder) {
@@ -55,55 +69,120 @@
         assertTrue("/etc/media_codecs.xml does not exist", file.exists());
     }
 
-    // Each component advertised by MediaCodecList should at least be
-    // instantiate-able.
-    public void testComponentInstantiation() throws IOException {
-        Log.d(TAG, "testComponentInstantiation");
+    private MediaCodecInfo[] getLegacyInfos() {
+        Log.d(TAG, "getLegacyInfos");
 
         int codecCount = MediaCodecList.getCodecCount();
-        for (int i = 0; i < codecCount; ++i) {
-            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+        MediaCodecInfo[] res = new MediaCodecInfo[codecCount];
 
-            Log.d(TAG, (i + 1) + ": " + info.getName());
+        for (int i = 0; i < codecCount; ++i) {
+            res[i] = MediaCodecList.getCodecInfoAt(i);
+        }
+        return res;
+    }
+
+    public void assertEqualsOrSuperset(Set big, Set tiny, boolean superset) {
+        if (!superset) {
+            assertEquals(big, tiny);
+        } else {
+            assertTrue(big.containsAll(tiny));
+        }
+    }
+
+    private static <T> Set<T> asSet(T[] array) {
+        Set<T> s = new HashSet<T>();
+        for (T el : array) {
+            s.add(el);
+        }
+        return s;
+    }
+
+    private static Set<Integer> asSet(int[] array) {
+        Set<Integer> s = new HashSet<Integer>();
+        for (int el : array) {
+            s.add(el);
+        }
+        return s;
+    }
+
+    public void assertEqualsOrSuperset(
+            CodecCapabilities big, CodecCapabilities tiny, boolean superset) {
+        // ordering of enumerations may differ
+        assertEqualsOrSuperset(asSet(big.colorFormats), asSet(tiny.colorFormats), superset);
+        assertEqualsOrSuperset(asSet(big.profileLevels), asSet(tiny.profileLevels), superset);
+        AudioCapabilities bigAudCaps = big.getAudioCapabilities();
+        VideoCapabilities bigVidCaps = big.getVideoCapabilities();
+        EncoderCapabilities bigEncCaps = big.getEncoderCapabilities();
+        AudioCapabilities tinyAudCaps = tiny.getAudioCapabilities();
+        VideoCapabilities tinyVidCaps = tiny.getVideoCapabilities();
+        EncoderCapabilities tinyEncCaps = tiny.getEncoderCapabilities();
+        assertEquals(bigAudCaps != null, tinyAudCaps != null);
+        assertEquals(bigAudCaps != null, tinyAudCaps != null);
+        assertEquals(bigAudCaps != null, tinyAudCaps != null);
+    }
+
+    public void assertEqualsOrSuperset(
+            MediaCodecInfo big, MediaCodecInfo tiny, boolean superset) {
+        assertEquals(big.getName(), tiny.getName());
+        assertEquals(big.isEncoder(), tiny.isEncoder());
+        assertEqualsOrSuperset(
+                asSet(big.getSupportedTypes()), asSet(tiny.getSupportedTypes()), superset);
+        for (String type : big.getSupportedTypes()) {
+            assertEqualsOrSuperset(
+                    big.getCapabilitiesForType(type),
+                    tiny.getCapabilitiesForType(type),
+                    superset);
+        }
+    }
+
+    public void assertSuperset(MediaCodecInfo big, MediaCodecInfo tiny) {
+        assertEqualsOrSuperset(big, tiny, true /* superset */);
+    }
+
+    public void assertEquals(MediaCodecInfo big, MediaCodecInfo tiny) {
+        assertEqualsOrSuperset(big, tiny, false /* superset */);
+    }
+
+    // Each component advertised by MediaCodecList should at least be
+    // instantiable.
+    private void testComponentInstantiation(MediaCodecInfo[] infos) throws IOException {
+        for (MediaCodecInfo info : infos) {
+            Log.d(TAG, "codec: " + info.getName());
             Log.d(TAG, "  isEncoder = " + info.isEncoder());
 
-            if (!info.getName().startsWith("OMX.")) {
-                // Unfortunately for legacy reasons, "AACEncoder", a
-                // non OMX component had to be in this list for the video
-                // editor code to work... but it cannot actually be instantiated
-                // using MediaCodec.
-                Log.d(TAG, "  skipping...");
-                continue;
-            }
-
             MediaCodec codec = MediaCodec.createByCodecName(info.getName());
 
+            assertEquals(codec.getName(), info.getName());
+
+            assertEquals(codec.getCodecInfo(), info);
+
             codec.release();
             codec = null;
         }
     }
 
+    public void testRegularComponentInstantiation() throws IOException {
+        Log.d(TAG, "testRegularComponentInstantiation");
+        testComponentInstantiation(mRegularInfos);
+    }
+
+    public void testAllComponentInstantiation() throws IOException {
+        Log.d(TAG, "testAllComponentInstantiation");
+        testComponentInstantiation(mAllInfos);
+    }
+
+    public void testLegacyComponentInstantiation() throws IOException {
+        Log.d(TAG, "testLegacyComponentInstantiation");
+        testComponentInstantiation(getLegacyInfos());
+    }
+
     // For each type advertised by any of the components we should be able
     // to get capabilities.
-    public void testGetCapabilities() {
-        Log.d(TAG, "testGetCapabilities");
-
-        int codecCount = MediaCodecList.getCodecCount();
-        for (int i = 0; i < codecCount; ++i) {
-            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
-
-            Log.d(TAG, (i + 1) + ": " + info.getName());
+    private void testGetCapabilities(MediaCodecInfo[] infos) {
+        for (MediaCodecInfo info : infos) {
+            Log.d(TAG, "codec: " + info.getName());
             Log.d(TAG, "  isEncoder = " + info.isEncoder());
 
-            if (!info.getName().startsWith("OMX.")) {
-                // Unfortunately for legacy reasons, "AACEncoder", a
-                // non OMX component had to be in this list for the video
-                // editor code to work... but it cannot actually be instantiated
-                // using MediaCodec.
-                Log.d(TAG, "  skipping...");
-                continue;
-            }
-
             String[] types = info.getSupportedTypes();
             for (int j = 0; j < types.length; ++j) {
                 Log.d(TAG, "calling getCapabilitiesForType " + types[j]);
@@ -112,14 +191,71 @@
         }
     }
 
+    public void testGetRegularCapabilities() {
+        Log.d(TAG, "testGetRegularCapabilities");
+        testGetCapabilities(mRegularInfos);
+    }
+
+    public void testGetAllCapabilities() {
+        Log.d(TAG, "testGetAllCapabilities");
+        testGetCapabilities(mAllInfos);
+    }
+
+    public void testGetLegacyCapabilities() {
+        Log.d(TAG, "testGetLegacyCapabilities");
+        testGetCapabilities(getLegacyInfos());
+    }
+
+    public void testLegacyMediaCodecListIsSameAsRegular() {
+        // regular codecs should be equivalent to legacy codecs, including
+        // codec ordering
+        MediaCodecInfo[] legacyInfos = getLegacyInfos();
+        assertEquals(legacyInfos.length, mRegularInfos.length);
+        for (int i = 0; i < legacyInfos.length; ++i) {
+            assertEquals(legacyInfos[i], mRegularInfos[i]);
+        }
+    }
+
+    public void testRegularMediaCodecListIsASubsetOfAll() {
+        Log.d(TAG, "testRegularMediaCodecListIsASubsetOfAll");
+        // regular codecs should be a subsequence of all codecs, including
+        // codec ordering
+        int ix = 0;
+        for (MediaCodecInfo info : mAllInfos) {
+            if (ix == mRegularInfos.length) {
+                break;
+            }
+            if (!mRegularInfos[ix].getName().equals(info.getName())) {
+                Log.d(TAG, "skipping non-regular codec " + info.getName());
+                continue;
+            }
+            Log.d(TAG, "checking codec " + info.getName());
+            assertSuperset(info, mRegularInfos[ix]);
+            ++ix;
+        }
+        assertEquals(
+                "some regular codecs are not listed in all codecs", ix, mRegularInfos.length);
+    }
+
     public void testRequiredMediaCodecList() {
         List<CodecType> requiredList = getRequiredCodecTypes();
         List<CodecType> supportedList = getSupportedCodecTypes();
         assertTrue(areRequiredCodecTypesSupported(requiredList, supportedList));
     }
 
+    private boolean hasCamera() {
+        PackageManager pm = getContext().getPackageManager();
+        return pm.hasSystemFeature(pm.FEATURE_CAMERA_FRONT) ||
+                pm.hasSystemFeature(pm.FEATURE_CAMERA);
+    }
+
     // H263 baseline profile must be supported
     public void testIsH263BaselineProfileSupported() {
+        if (!hasCamera()) {
+            Log.d(TAG, "not required without camera");
+            return;
+        }
+
         int profile = CodecProfileLevel.H263ProfileBaseline;
         assertTrue(checkProfileSupported("video/3gpp", false, profile));
         assertTrue(checkProfileSupported("video/3gpp", true, profile));
@@ -140,6 +276,11 @@
 
     // MPEG4 simple profile must be supported
     public void testIsM4VSimpleProfileSupported() {
+        if (!hasCamera()) {
+            Log.d(TAG, "not required without camera");
+            return;
+        }
+
         int profile = CodecProfileLevel.MPEG4ProfileSimple;
         assertTrue(checkProfileSupported("video/mp4v-es", false, profile));
 
@@ -151,26 +292,24 @@
      * Find whether the given codec is supported
      */
     private boolean checkProfileSupported(
-        String codecName, boolean isEncoder, int profile) {
+            String mime, boolean isEncoder, int profile) {
+        return profileIsListed(mime, isEncoder, profile) &&
+                codecCanBeFound(mime, isEncoder);
+    }
 
-        boolean isSupported = false;
+    private boolean profileIsListed(
+        String mime, boolean isEncoder, int profile) {
 
-        int codecCount = MediaCodecList.getCodecCount();
-        for (int i = 0; i < codecCount; ++i) {
-
-            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
-            String[] types = info.getSupportedTypes();
-
+        for (MediaCodecInfo info : mRegularInfos) {
             if (isEncoder != info.isEncoder()) {
                 continue;
             }
 
-            for (int j = 0; j < types.length; ++j) {
-                if (types[j].compareTo(codecName) == 0) {
-                    CodecCapabilities cap = info.getCapabilitiesForType(types[j]);
-                    CodecProfileLevel[] profileLevels = cap.profileLevels;
-                    for (int k = 0; k < profileLevels.length; ++k) {
-                        if (profileLevels[k].profile == profile) {
+            for (String type : info.getSupportedTypes()) {
+                if (type.equalsIgnoreCase(mime)) {
+                    CodecCapabilities cap = info.getCapabilitiesForType(type);
+                    for (CodecProfileLevel pl : cap.profileLevels) {
+                        if (pl.profile == profile) {
                             return true;
                         }
                     }
@@ -180,6 +319,16 @@
         return false;
     }
 
+    // Find whether the given codec can be found using MediaCodecList.find methods.
+    private boolean codecCanBeFound(String mime, boolean isEncoder) {
+        // implicit assumption that QVGA video is always valid.
+        MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
+        String codecName = isEncoder
+                ? mRegularCodecs.findEncoderForFormat(format)
+                : mRegularCodecs.findDecoderForFormat(format);
+        return codecName != null;
+    }
+
     /*
      * Find whether all required media codec types are supported
      */
@@ -206,11 +355,8 @@
      * Find all the media codec types are supported.
      */
     private List<CodecType> getSupportedCodecTypes() {
-        int codecCount = MediaCodecList.getCodecCount();
-        assertTrue("Unexpected media codec count", codecCount > 0);
-        List<CodecType> supportedList = new ArrayList<CodecType>(codecCount);
-        for (int i = 0; i < codecCount; ++i) {
-            MediaCodecInfo info = MediaCodecList.getCodecInfoAt(i);
+        List<CodecType> supportedList = new ArrayList<CodecType>();
+        for (MediaCodecInfo info : mRegularInfos) {
             String[] types = info.getSupportedTypes();
             assertTrue("Unexpected number of supported types", types.length > 0);
             boolean isEncoder = info.isEncoder();
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 6726728..f72e3a0 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -402,8 +402,7 @@
             inputSurface.makeCurrent();
             encoder.start();
 
-            int totalBuffers = encoder.getInputBuffers().length +
-                    encoder.getOutputBuffers().length;
+            int totalBuffers = encoder.getOutputBuffers().length;
             if (VERBOSE) Log.d(TAG, "Total buffers: " + totalBuffers);
 
             // Submit several frames quickly, without draining the encoder output, to try to
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 2815d81..78ba149 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -593,14 +593,16 @@
         }
         byte [] vizdata = new byte[size];
         Visualizer vis = new Visualizer(session);
-        assertTrue(vis.setCaptureSize(vizdata.length) == Visualizer.SUCCESS);
-        assertTrue(vis.setEnabled(true) == Visualizer.SUCCESS);
         AudioManager am = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
         int oldRingerMode = am.getRingerMode();
         am.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
         int oldvolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
         am.setStreamVolume(AudioManager.STREAM_MUSIC, 1, 0);
         try {
+            assertEquals("setCaptureSize failed",
+                    Visualizer.SUCCESS, vis.setCaptureSize(vizdata.length));
+            assertEquals("setEnabled failed", Visualizer.SUCCESS, vis.setEnabled(true));
+
             mp1.setNextMediaPlayer(mp2);
             mp1.start();
             assertTrue(mp1.isPlaying());
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
new file mode 100644
index 0000000..b034763
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.pm.PackageManager;
+import android.media.audiofx.AudioEffect;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+
+public class PostProcTestBase extends AndroidTestCase {
+    protected int mSession = -1;
+    protected boolean mHasControl = false;
+    protected boolean mIsEnabled = false;
+    protected boolean mInitialized = false;
+    protected Looper mLooper = null;
+    protected final Object mLock = new Object();
+    protected int mChangedParameter = -1;
+
+    protected boolean hasAudioOutput() {
+        return getContext().getPackageManager().hasSystemFeature(
+                PackageManager.FEATURE_AUDIO_OUTPUT);
+    }
+
+    protected boolean isBassBoostAvailable() {
+        if (!hasAudioOutput()) {
+            return false;
+        }
+        return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_BASS_BOOST);
+    }
+
+    protected boolean isVirtualizerAvailable() {
+        if (!hasAudioOutput()) {
+            return false;
+        }
+        return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_VIRTUALIZER);
+    }
+
+    protected boolean isPresetReverbAvailable() {
+        if (!hasAudioOutput()) {
+            return false;
+        }
+        return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_PRESET_REVERB);
+    }
+
+    protected boolean isEnvReverbAvailable() {
+        if (!hasAudioOutput()) {
+            return false;
+        }
+        return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_ENV_REVERB);
+    }
+}
diff --git a/tests/tests/media/src/android/media/cts/PresetReverbTest.java b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
index d69256c..ba6d08c 100644
--- a/tests/tests/media/src/android/media/cts/PresetReverbTest.java
+++ b/tests/tests/media/src/android/media/cts/PresetReverbTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class PresetReverbTest extends AndroidTestCase {
+public class PresetReverbTest extends PostProcTestBase {
 
     private String TAG = "PresetReverbTest";
     private final static short FIRST_PRESET = PresetReverb.PRESET_NONE;
@@ -33,13 +33,6 @@
 
     private PresetReverb mReverb = null;
     private PresetReverb mReverb2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -52,10 +45,12 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         PresetReverb reverb = null;
         try {
             reverb = new PresetReverb(0, 0);
-            assertNotNull("could not create PresetReverb", reverb);
             try {
                 assertTrue("invalid effect ID", (reverb.getId() != 0));
             } catch (IllegalStateException e) {
@@ -78,6 +73,9 @@
 
     //Test case 1.0: test presets
     public void test1_0Presets() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             for (short preset = FIRST_PRESET;
@@ -99,6 +97,9 @@
 
     //Test case 1.1: test properties
     public void test1_1Properties() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             PresetReverb.Settings settings = mReverb.getProperties();
@@ -127,6 +128,9 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         try {
             mReverb.setEnabled(true);
@@ -142,6 +146,9 @@
 
     //Test case 2.1: test setEnabled() throws exception after release
     public void test2_1SetEnabledAfterRelease() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         getReverb(0);
         mReverb.release();
         try {
@@ -159,6 +166,9 @@
 
     //Test case 3.0: test control status listener
     public void test3_0ControlStatusListener() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
          synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -181,6 +191,9 @@
 
     //Test case 3.1: test enable status listener
     public void test3_1EnableStatusListener() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
          synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -205,6 +218,9 @@
 
     //Test case 3.2: test parameter changed listener
     public void test3_2ParameterChangedListener() throws Exception {
+        if (!isPresetReverbAvailable()) {
+            return;
+        }
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
@@ -377,5 +393,4 @@
             mReverb2 = null;
         }
     }
-
 }
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
index 3d18c47..51adf1d 100644
--- a/tests/tests/media/src/android/media/cts/VirtualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class VirtualizerTest extends AndroidTestCase {
+public class VirtualizerTest extends PostProcTestBase {
 
     private String TAG = "VirtualizerTest";
     private final static short TEST_STRENGTH = 500;
@@ -34,13 +34,6 @@
 
     private Virtualizer mVirtualizer = null;
     private Virtualizer mVirtualizer2 = null;
-    private int mSession = -1;
-    private boolean mHasControl = false;
-    private boolean mIsEnabled = false;
-    private int mChangedParameter = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private ListenerThread mEffectListenerLooper = null;
 
     //-----------------------------------------------------------------
@@ -53,10 +46,13 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         Virtualizer eq = null;
         try {
             eq = new Virtualizer(0, 0);
-            assertNotNull(" could not create Virtualizer", eq);
             try {
                 assertTrue(" invalid effect ID", (eq.getId() != 0));
             } catch (IllegalStateException e) {
@@ -80,6 +76,10 @@
 
     //Test case 1.0: test strength
     public void test1_0Strength() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         getVirtualizer(0);
         try {
             if (mVirtualizer.getStrengthSupported()) {
@@ -108,6 +108,10 @@
 
     //Test case 1.1: test properties
     public void test1_1Properties() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         getVirtualizer(0);
         try {
             Virtualizer.Settings settings = mVirtualizer.getProperties();
@@ -141,6 +145,10 @@
 
     //Test case 1.2: test setStrength() throws exception after release
     public void test1_2SetStrengthAfterRelease() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         getVirtualizer(0);
         mVirtualizer.release();
         try {
@@ -158,6 +166,10 @@
 
     //Test case 2.0: test setEnabled() and getEnabled() in valid state
     public void test2_0SetEnabledGetEnabled() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         getVirtualizer(0);
         try {
             mVirtualizer.setEnabled(true);
@@ -173,6 +185,10 @@
 
     //Test case 2.1: test setEnabled() throws exception after release
     public void test2_1SetEnabledAfterRelease() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         getVirtualizer(0);
         mVirtualizer.release();
         try {
@@ -190,6 +206,10 @@
 
     //Test case 3.0: test control status listener
     public void test3_0ControlStatusListener() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         synchronized(mLock) {
             mHasControl = true;
             mInitialized = false;
@@ -212,6 +232,10 @@
 
     //Test case 3.1: test enable status listener
     public void test3_1EnableStatusListener() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, true, false);
@@ -236,6 +260,10 @@
 
     //Test case 3.2: test parameter changed listener
     public void test3_2ParameterChangedListener() throws Exception {
+        if (!isVirtualizerAvailable()) {
+            return;
+        }
+
         synchronized(mLock) {
             mInitialized = false;
             createListenerLooper(false, false, true);
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index e2a9fdd..8c91e9b 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -24,7 +24,7 @@
 import android.test.AndroidTestCase;
 import android.util.Log;
 
-public class VisualizerTest extends AndroidTestCase {
+public class VisualizerTest extends PostProcTestBase {
 
     private String TAG = "VisualizerTest";
     private final static int MIN_CAPTURE_RATE_MAX = 10000; // 10Hz
@@ -33,10 +33,6 @@
     private final static int MAX_LOOPER_WAIT_COUNT = 10;
 
     private Visualizer mVisualizer = null;
-    private int mSession = -1;
-    private boolean mInitialized = false;
-    private Looper mLooper = null;
-    private final Object mLock = new Object();
     private byte[] mWaveform = null;
     private byte[] mFft = null;
     private boolean mCaptureWaveform = false;
@@ -53,10 +49,12 @@
 
     //Test case 0.0: test constructor and release
     public void test0_0ConstructorAndRelease() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         Visualizer visualizer = null;
-         try {
+        try {
             visualizer = new Visualizer(0);
-            assertNotNull("could not create Visualizer", visualizer);
         } catch (IllegalArgumentException e) {
             fail("Visualizer not found");
         } catch (UnsupportedOperationException e) {
@@ -75,6 +73,9 @@
 
     //Test case 1.0: capture rates
     public void test1_0CaptureRates() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getVisualizer(0);
         try {
             int captureRate = mVisualizer.getMaxCaptureRate();
@@ -94,6 +95,9 @@
 
     //Test case 1.1: test capture size
     public void test1_1CaptureSize() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         getVisualizer(0);
         try {
             int[] range = mVisualizer.getCaptureSizeRange();
@@ -124,6 +128,9 @@
 
     //Test case 2.0: test cature in polling mode
     public void test2_0PollingCapture() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         try {
             getVisualizer(0);
             mVisualizer.setEnabled(true);
@@ -151,6 +158,9 @@
 
     //Test case 2.1: test capture with listener
     public void test2_1ListenerCapture() throws Exception {
+        if (!hasAudioOutput()) {
+            return;
+        }
         try {
             getVisualizer(0);
             synchronized(mLock) {
diff --git a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
index 5e38842..586b9ef 100644
--- a/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
+++ b/tests/tests/media/src/android/media/cts/Vp8CodecTestBase.java
@@ -335,11 +335,11 @@
     }
 
     /**
-     * Converts (interleaves) YUV420 planar to NV12 (if hw) or NV21 (if sw).
+     * Converts (interleaves) YUV420 planar to NV12.
      * Assumes packed, macroblock-aligned frame with no cropping
-     * (visible/coded row length == stride).  Swap U/V if |sw|.
+     * (visible/coded row length == stride).
      */
-    private static byte[] YUV420ToNV(int width, int height, byte[] yuv, boolean sw) {
+    private static byte[] YUV420ToNV(int width, int height, byte[] yuv) {
         byte[] nv = new byte[yuv.length];
         // Y plane we just copy.
         System.arraycopy(yuv, 0, nv, 0, width * height);
@@ -348,17 +348,9 @@
         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++];
-            }
+        for (int i = 0; i < width * height / 4; i++) {
+            nv[nv_offset++] = yuv[u_offset++];
+            nv[nv_offset++] = yuv[v_offset++];
         }
         return nv;
     }
@@ -788,7 +780,7 @@
             // Convert YUV420 to NV12 if necessary
             if (mProperties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
                 return YUV420ToNV(mStreamParams.frameWidth, mStreamParams.frameHeight,
-                        mSrcFrame, mProperties.isGoogleSwCodec());
+                        mSrcFrame);
             } else {
                 return mSrcFrame;
             }
@@ -1390,7 +1382,7 @@
                     // Convert YUV420 to NV12 if necessary
                     if (properties.colorFormat != CodecCapabilities.COLOR_FormatYUV420Planar) {
                         srcFrame = YUV420ToNV(streamParams.frameWidth, streamParams.frameHeight,
-                                srcFrame, properties.isGoogleSwCodec());
+                                srcFrame);
                     }
                 }
 
@@ -1652,8 +1644,7 @@
                     // 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());
+                        srcFrame[i] = YUV420ToNV(params.frameWidth, params.frameHeight, srcFrame[i]);
                     }
                 }
 
diff --git a/tests/tests/nativemedia/sl/Android.mk b/tests/tests/nativemedia/sl/Android.mk
index 5b34b3d..48b816c 100644
--- a/tests/tests/nativemedia/sl/Android.mk
+++ b/tests/tests/nativemedia/sl/Android.mk
@@ -10,6 +10,9 @@
 LOCAL_MODULE := $(test_executable)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
 LOCAL_C_INCLUDES := \
     bionic \
diff --git a/tests/tests/nativemedia/xa/Android.mk b/tests/tests/nativemedia/xa/Android.mk
index 6995bc0..ace315a 100644
--- a/tests/tests/nativemedia/xa/Android.mk
+++ b/tests/tests/nativemedia/xa/Android.mk
@@ -10,6 +10,9 @@
 LOCAL_MODULE:= $(test_executable)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest
+LOCAL_MULTILIB := both
+LOCAL_MODULE_STEM_32 := $(LOCAL_MODULE)32
+LOCAL_MODULE_STEM_64 := $(LOCAL_MODULE)64
 
 LOCAL_C_INCLUDES := \
     bionic \
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
index 7faea64..d8df064 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiManagerTest.java
@@ -28,6 +28,7 @@
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.TxPacketCountListener;
 import android.net.wifi.WifiManager.WifiLock;
+import android.os.SystemClock;
 import android.test.AndroidTestCase;
 import android.util.Log;
 
@@ -36,6 +37,7 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 
 public class WifiManagerTest extends AndroidTestCase {
@@ -46,7 +48,7 @@
     private WifiManager mWifiManager;
     private WifiLock mWifiLock;
     private static MySync mMySync;
-    private List<ScanResult> mScanResult = null;
+    private List<ScanResult> mScanResults = null;
     private NetworkInfo mNetworkInfo;
 
     // Please refer to WifiManager
@@ -66,6 +68,10 @@
     private static final int TIMEOUT_MSEC = 6000;
     private static final int WAIT_MSEC = 60;
     private static final int DURATION = 10000;
+    private static final int WIFI_SCAN_TEST_INTERVAL_MILLIS = 60 * 1000;
+    private static final int WIFI_SCAN_TEST_CACHE_DELAY_MILLIS = 3 * 60 * 1000;
+    private static final int WIFI_SCAN_TEST_ITERATIONS = 5;
+
     private IntentFilter mIntentFilter;
     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
         @Override
@@ -74,9 +80,9 @@
             if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
                 synchronized (mMySync) {
                     if (mWifiManager.getScanResults() != null) {
-                        mScanResult = mWifiManager.getScanResults();
+                        mScanResults = mWifiManager.getScanResults();
                         mMySync.expectedState = STATE_SCAN_RESULTS_AVAILABLE;
-                        mScanResult = mWifiManager.getScanResults();
+                        mScanResults = mWifiManager.getScanResults();
                         mMySync.notifyAll();
                     }
                 }
@@ -261,6 +267,46 @@
     }
 
     /**
+     * Test WiFi scan timestamp - fails when WiFi scan timestamps are inconsistent with
+     * {@link SystemClock#elapsedRealtime()} on device.<p>
+     * To run this test in cts-tradefed:
+     * run cts --class android.net.wifi.cts.WifiManagerTest --method testWifiScanTimestamp
+     */
+    public void testWifiScanTimestamp() throws Exception {
+        if (!WifiFeature.isWifiSupported(getContext())) {
+            Log.d(TAG, "Skipping test as WiFi is not supported");
+            return;
+        }
+        if (!mWifiManager.isWifiEnabled()) {
+            setWifiEnabled(true);
+        }
+        // Scan multiple times to make sure scan timestamps increase with device timestamp.
+        for (int i = 0; i < WIFI_SCAN_TEST_ITERATIONS; ++i) {
+            startScan();
+            // Make sure at least one AP is found.
+            assertFalse("empty scan results!", mScanResults.isEmpty());
+            long nowMillis = SystemClock.elapsedRealtime();
+            // Keep track of how many APs are fresh in one scan.
+            int numFreshAps = 0;
+            for (ScanResult result : mScanResults) {
+                long scanTimeMillis = TimeUnit.MICROSECONDS.toMillis(result.timestamp);
+                if (Math.abs(nowMillis - scanTimeMillis)  < WIFI_SCAN_TEST_CACHE_DELAY_MILLIS) {
+                    numFreshAps++;
+                }
+            }
+            // At least half of the APs in the scan should be fresh.
+            int numTotalAps = mScanResults.size();
+            String msg = "Stale AP count: " + (numTotalAps - numFreshAps) + ", fresh AP count: "
+                    + numFreshAps;
+            assertTrue(msg, numFreshAps * 2 >= mScanResults.size());
+            if (i < WIFI_SCAN_TEST_ITERATIONS - 1) {
+                // Wait before running next iteration.
+                Thread.sleep(WIFI_SCAN_TEST_INTERVAL_MILLIS);
+            }
+        }
+    }
+
+    /**
      * test point of wifiManager NetWork:
      * 1.add NetWork
      * 2.update NetWork
diff --git a/tests/tests/os/src/android/os/cts/BuildTest.java b/tests/tests/os/src/android/os/cts/BuildTest.java
index 08db484..26b07f1 100644
--- a/tests/tests/os/src/android/os/cts/BuildTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildTest.java
@@ -18,8 +18,13 @@
 
 
 import android.os.Build;
+import android.os.SystemProperties;
+
+import dalvik.system.VMRuntime;
 
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
 import java.util.Scanner;
 import java.util.regex.Pattern;
 
@@ -27,59 +32,76 @@
 
 public class BuildTest extends TestCase {
 
-    private static final String RO_PRODUCT_CPU_ABI = "ro.product.cpu.abi";
-
-    private static final String RO_PRODUCT_CPU_ABI2 = "ro.product.cpu.abi2";
+    private static final String RO_PRODUCT_CPU_ABILIST = "ro.product.cpu.abilist";
+    private static final String RO_PRODUCT_CPU_ABILIST32 = "ro.product.cpu.abilist32";
+    private static final String RO_PRODUCT_CPU_ABILIST64 = "ro.product.cpu.abilist64";
 
     /** Tests that check the values of {@link Build#CPU_ABI} and {@link Build#CPU_ABI2}. */
-    public void testCpuAbi() throws IOException {
-        if (CpuFeatures.isArmCpu()) {
-            assertArmCpuAbiConstants();
+    public void testCpuAbi() throws Exception {
+        testCpuAbiCommon();
+        if (VMRuntime.getRuntime().is64Bit()) {
+            testCpuAbi64();
+        } else {
+            testCpuAbi32();
         }
     }
 
-    private void assertArmCpuAbiConstants() throws IOException {
-        if (CpuFeatures.isArm7Compatible()) {
-            String cpuAbi = getProperty(RO_PRODUCT_CPU_ABI);
-            String cpuAbi2 = getProperty(RO_PRODUCT_CPU_ABI2);
-            //if CPU_ABI is armv7, CPU_ABI2 is either of {armeabi, NULL}
-            if (cpuAbi.equals(CpuFeatures.ARMEABI_V7)) {
-                String message = "CPU is ARM v7 compatible, so "
-                    + RO_PRODUCT_CPU_ABI  + " must be set to " + CpuFeatures.ARMEABI_V7 + " and "
-                    + RO_PRODUCT_CPU_ABI2 + " must be set to " + CpuFeatures.ARMEABI + " or NULL";
-                assertEquals(message, CpuFeatures.ARMEABI_V7, Build.CPU_ABI);
-                if (cpuAbi2.equals(CpuFeatures.ARMEABI)){
-                    assertEquals(message, cpuAbi2, Build.CPU_ABI2);
-                } else {
-                    assertNoPropertySet(message, RO_PRODUCT_CPU_ABI2);
-                    assertEquals(message, Build.UNKNOWN, Build.CPU_ABI2);
-                }
-            }
-            //if CPU_ABI is x86, then CPU_ABI2 is either of {armeabi, armv7, NULL}
-            else if (cpuAbi.equals(CpuFeatures.X86ABI)) {
-                String message = "CPU is x86 but ARM v7 compatible, so "
-                    + RO_PRODUCT_CPU_ABI  + " must be set to " + CpuFeatures.X86ABI + " and "
-                    + RO_PRODUCT_CPU_ABI2 + " must be set to " + CpuFeatures.ARMEABI + " or "
-                    + CpuFeatures.ARMEABI_V7 + " or NULL";
-                assertEquals(message, CpuFeatures.X86ABI, Build.CPU_ABI);
-                if (cpuAbi2.equals(CpuFeatures.ARMEABI_V7) || cpuAbi2.equals(CpuFeatures.ARMEABI))
-                    assertEquals(message, cpuAbi2, Build.CPU_ABI2);
-                else {
-                    assertNoPropertySet(message, RO_PRODUCT_CPU_ABI2);
-                    assertEquals(message, Build.UNKNOWN, Build.CPU_ABI2);
-                }
-            }
+    private void testCpuAbiCommon() throws Exception {
+        // The build property must match Build.SUPPORTED_ABIS exactly.
+        final String[] abiListProperty = getStringList(RO_PRODUCT_CPU_ABILIST);
+        assertEquals(Arrays.toString(abiListProperty), Arrays.toString(Build.SUPPORTED_ABIS));
+
+        List<String> abiList = Arrays.asList(abiListProperty);
+
+        // Every device must support at least one 32 bit ABI.
+        assertTrue(Build.SUPPORTED_32_BIT_ABIS.length > 0);
+
+        // Every supported 32 bit ABI must be present in Build.SUPPORTED_ABIS.
+        for (String abi : Build.SUPPORTED_32_BIT_ABIS) {
+            assertTrue(abiList.contains(abi));
+            assertFalse(VMRuntime.is64BitAbi(abi));
         }
-        else {
-            String message = "CPU is not ARM v7 compatible. "
-                    + RO_PRODUCT_CPU_ABI  + " must be set to " + CpuFeatures.ARMEABI + " and "
-                    + RO_PRODUCT_CPU_ABI2 + " must not be set.";
-            assertProperty(message, RO_PRODUCT_CPU_ABI, CpuFeatures.ARMEABI);
-            assertNoPropertySet(message, RO_PRODUCT_CPU_ABI2);
-            assertEquals(message, CpuFeatures.ARMEABI, Build.CPU_ABI);
-            assertEquals(message, Build.UNKNOWN, Build.CPU_ABI2);
+
+        // Every supported 64 bit ABI must be present in Build.SUPPORTED_ABIS.
+        for (String abi : Build.SUPPORTED_64_BIT_ABIS) {
+            assertTrue(abiList.contains(abi));
+            assertTrue(VMRuntime.is64BitAbi(abi));
+        }
+
+        // Build.CPU_ABI and Build.CPU_ABI2 must be present in Build.SUPPORTED_ABIS.
+        assertTrue(abiList.contains(Build.CPU_ABI));
+        if (!Build.CPU_ABI2.isEmpty()) {
+            assertTrue(abiList.contains(Build.CPU_ABI2));
         }
     }
+
+    private void testCpuAbi32() throws Exception {
+        List<String> abi32 = Arrays.asList(Build.SUPPORTED_32_BIT_ABIS);
+        assertTrue(abi32.contains(Build.CPU_ABI));
+
+        if (!Build.CPU_ABI2.isEmpty()) {
+            assertTrue(abi32.contains(Build.CPU_ABI2));
+        }
+    }
+
+    private void testCpuAbi64() {
+        List<String> abi64 = Arrays.asList(Build.SUPPORTED_64_BIT_ABIS);
+        assertTrue(abi64.contains(Build.CPU_ABI));
+
+        if (!Build.CPU_ABI2.isEmpty()) {
+            assertTrue(abi64.contains(Build.CPU_ABI2));
+        }
+    }
+
+    private String[] getStringList(String property) throws IOException {
+        String value = getProperty(property);
+        if (value.isEmpty()) {
+            return new String[0];
+        } else {
+            return value.split(",");
+        }
+    }
+
     /**
      * @param property name passed to getprop
      */
diff --git a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
index 225c623..ee1b027 100644
--- a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
+++ b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
@@ -337,39 +337,50 @@
                     continue;
                 }
 
-                // Get the context via attr/current
-                String context = new Scanner(new File(f, "attr/current")).next();
-                context = context.trim();
-
-                // Get the vSize, item #23 from the stat file
-                String x = new Scanner(new File(f, "stat")).nextLine();
-                long vSize = getVsizeFromStat(x);
-
-                StringBuilder sb = new StringBuilder();
-                Scanner tmp = new Scanner(new File(f, "cmdline"));
-
-                // Java's scanner tends to return oddly when handling
-                // long binary blobs. Probably some caching optimization.
-                while (tmp.hasNext()) {
-                    sb.append(tmp.next().replace('\0', ' '));
+                try {
+                    ProcessDetails p = getProcessDetails(pid, f);
+                    ArrayList<ProcessDetails> l = map.get(p.label);
+                    if (l == null) {
+                        l = new ArrayList<ProcessDetails>();
+                        map.put(p.label, l);
+                    }
+                    l.add(p);
+                } catch (FileNotFoundException e) {
+                    // sometimes processes go away while the test is running.
+                    // Don't freak out if this happens
                 }
-
-                // At this point we build up a valid proctitle, then split
-                // on whitespace to get the left portion. Which is either
-                // package name or process executable path. This avoids
-                // the comm 16 char width limitation and is limited to PAGE_SIZE
-                String cmdline = sb.toString().trim();
-                cmdline = cmdline.split("\\s+")[0];
-
-                ProcessDetails p = new ProcessDetails(cmdline, context, vSize, pid);
-                ArrayList<ProcessDetails> l = map.get(context);
-                if (l == null) {
-                    l = new ArrayList<ProcessDetails>();
-                    map.put(context, l);
-                }
-                l.add(p);
             }
             return map;
         }
+
+        private static ProcessDetails getProcessDetails(int pid, File f) throws FileNotFoundException {
+            // Get the context via attr/current
+            String context = new Scanner(new File(f, "attr/current")).next();
+            context = context.trim();
+
+            // Get the vSize, item #23 from the stat file
+            String x = new Scanner(new File(f, "stat")).nextLine();
+            long vSize = getVsizeFromStat(x);
+
+            StringBuilder sb = new StringBuilder();
+            Scanner tmp = new Scanner(new File(f, "cmdline"));
+
+            // Java's scanner tends to return oddly when handling
+            // long binary blobs. Probably some caching optimization.
+            while (tmp.hasNext()) {
+                sb.append(tmp.next().replace('\0', ' '));
+            }
+            tmp.close();
+
+            // At this point we build up a valid proctitle, then split
+            // on whitespace to get the left portion. Which is either
+            // package name or process executable path. This avoids
+            // the comm 16 char width limitation and is limited to PAGE_SIZE
+            String cmdline = sb.toString().trim();
+            cmdline = cmdline.split("\\s+")[0];
+
+            return new ProcessDetails(cmdline, context, vSize, pid);
+        }
+
     }
 }
diff --git a/tests/tests/uirendering/res/drawable-nodpi/black1.png b/tests/tests/uirendering/res/drawable-nodpi/black1.png
new file mode 100644
index 0000000..3487ced
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/black1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png
new file mode 100644
index 0000000..8fd3b50
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/blackitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/bold1.png b/tests/tests/uirendering/res/drawable-nodpi/bold1.png
new file mode 100644
index 0000000..199cccc
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/bold1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png
new file mode 100644
index 0000000..985635e
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/bolditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensed1.png b/tests/tests/uirendering/res/drawable-nodpi/condensed1.png
new file mode 100644
index 0000000..6889a3a
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensed1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png
new file mode 100644
index 0000000..9554dee
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedbold1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png
new file mode 100644
index 0000000..0483355
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedbolditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png
new file mode 100644
index 0000000..6584147
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condenseditalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png
new file mode 100644
index 0000000..49d01ac
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedlight1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png
new file mode 100644
index 0000000..6fe4a76
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/condensedlightitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/hello1.png b/tests/tests/uirendering/res/drawable-nodpi/hello1.png
new file mode 100644
index 0000000..7a4be5a
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/hello1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/italic1.png b/tests/tests/uirendering/res/drawable-nodpi/italic1.png
new file mode 100644
index 0000000..a5f9ef2
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/italic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/light1.png b/tests/tests/uirendering/res/drawable-nodpi/light1.png
new file mode 100644
index 0000000..dfa59da
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/light1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png
new file mode 100644
index 0000000..283ddc4
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/lightitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/medium1.png b/tests/tests/uirendering/res/drawable-nodpi/medium1.png
new file mode 100644
index 0000000..e615186
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/medium1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png
new file mode 100644
index 0000000..3e15fc8
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/mediumitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/thin1.png b/tests/tests/uirendering/res/drawable-nodpi/thin1.png
new file mode 100644
index 0000000..9637262
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/thin1.png
Binary files differ
diff --git a/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png b/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png
new file mode 100644
index 0000000..0afbb9a
--- /dev/null
+++ b/tests/tests/uirendering/res/drawable-nodpi/thinitalic1.png
Binary files differ
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java
new file mode 100644
index 0000000..e7ed7ac
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/FontRenderingTests.java
@@ -0,0 +1,223 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Typeface;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.BitmapComparer;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.GoldenImageVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+
+import com.android.cts.uirendering.R;
+
+public class FontRenderingTests extends ActivityTestBase {
+    // Threshold is barely loose enough for differences between sw and hw renderers
+    static double MSSIM_THRESHOLD = 0.91;
+
+    private final BitmapComparer mFuzzyComparer = new MSSIMComparer(MSSIM_THRESHOLD);
+
+    // Representative characters including some from Unicode 7
+    private final String mTestString1 = "Hamburg \u20bd";
+    private final String mTestString2 = "\u20b9\u0186\u0254\u1e24\u1e43";
+
+    private void fontTestBody(final Typeface typeface, int id) {
+        Bitmap goldenBitmap = BitmapFactory.decodeResource(getActivity().getResources(), id);
+        createTest()
+                .addCanvasClient(new CanvasClient() {
+                    @Override
+                    public void draw(Canvas canvas, int width, int height) {
+                        Paint p = new Paint();
+                        p.setAntiAlias(true);
+                        p.setColor(Color.BLACK);
+                        p.setTextSize(30);
+                        p.setTypeface(typeface);
+                        canvas.drawText(mTestString1, 10, 60, p);
+                        canvas.drawText(mTestString2, 10, 100, p);
+                    }
+                })
+                .runWithVerifier(new GoldenImageVerifier(goldenBitmap, mFuzzyComparer));
+    }
+
+    @SmallTest
+    public void testDefaultFont() {
+        Typeface tf = Typeface.create("sans-serif", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.hello1);
+    }
+
+    @SmallTest
+    public void testBoldFont() {
+        Typeface tf = Typeface.create("sans-serif", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.bold1);
+    }
+
+    @SmallTest
+    public void testItalicFont() {
+        Typeface tf = Typeface.create("sans-serif", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.italic1);
+    }
+
+    @SmallTest
+    public void testBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.bolditalic1);
+    }
+
+    @SmallTest
+    public void testMediumFont() {
+        Typeface tf = Typeface.create("sans-serif-medium", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.medium1);
+    }
+
+    @SmallTest
+    public void testMediumBoldFont() {
+        // bold attribute on medium base font = black
+        Typeface tf = Typeface.create("sans-serif-medium", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.black1);
+    }
+
+    @SmallTest
+    public void testMediumItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-medium", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.mediumitalic1);
+    }
+
+    @SmallTest
+    public void testMediumBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-medium", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.blackitalic1);
+    }
+
+    @SmallTest
+    public void testLightFont() {
+        Typeface tf = Typeface.create("sans-serif-light", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.light1);
+    }
+
+    @SmallTest
+    public void testLightBoldFont() {
+        // bold attribute on light base font = medium
+        Typeface tf = Typeface.create("sans-serif-light", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.medium1);
+    }
+
+    @SmallTest
+    public void testLightItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-light", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.lightitalic1);
+    }
+
+    @SmallTest
+    public void testLightBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-light", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.mediumitalic1);
+    }
+
+    @SmallTest
+    public void testThinFont() {
+        Typeface tf = Typeface.create("sans-serif-thin", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.thin1);
+    }
+
+    @SmallTest
+    public void testThinBoldFont() {
+        // bold attribute on thin base font = normal
+        Typeface tf = Typeface.create("sans-serif-thin", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.hello1);
+    }
+
+    @SmallTest
+    public void testThinItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-thin", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.thinitalic1);
+    }
+
+    @SmallTest
+    public void testThinBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-thin", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.italic1);
+    }
+
+    @SmallTest
+    public void testBlackFont() {
+        Typeface tf = Typeface.create("sans-serif-black", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.black1);
+    }
+
+    @SmallTest
+    public void testBlackBoldFont() {
+        // bold attribute on black base font = black
+        Typeface tf = Typeface.create("sans-serif-black", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.black1);
+    }
+
+    @SmallTest
+    public void testBlackItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-black", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.blackitalic1);
+    }
+
+    @SmallTest
+    public void testBlackBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-black", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.blackitalic1);
+    }
+
+    /* condensed fonts */
+
+    @SmallTest
+    public void testCondensedFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.condensed1);
+    }
+
+    @SmallTest
+    public void testCondensedBoldFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed", Typeface.BOLD);
+        fontTestBody(tf, R.drawable.condensedbold1);
+    }
+
+    @SmallTest
+    public void testCondensedItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.condenseditalic1);
+    }
+
+    @SmallTest
+    public void testCondensedBoldItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed", Typeface.BOLD | Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.condensedbolditalic1);
+    }
+
+    @SmallTest
+    public void testCondensedLightFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed-light", Typeface.NORMAL);
+        fontTestBody(tf, R.drawable.condensedlight1);
+    }
+
+    @SmallTest
+    public void testCondensedLightItalicFont() {
+        Typeface tf = Typeface.create("sans-serif-condensed-light", Typeface.ITALIC);
+        fontTestBody(tf, R.drawable.condensedlightitalic1);
+    }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 9f9aa41..052b251 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -185,7 +185,8 @@
                 TEST_WIDTH, TEST_HEIGHT);
         boolean success = bitmapVerifier.verify(mSoftwareArray, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT);
         if (!success) {
-            BitmapDumper.dumpBitmap(bitmap, getName(), this.getClass().getSimpleName());
+            Bitmap croppedBitmap = Bitmap.createBitmap(bitmap, 0, 0, TEST_WIDTH, TEST_HEIGHT);
+            BitmapDumper.dumpBitmap(croppedBitmap, getName(), this.getClass().getSimpleName());
             BitmapDumper.dumpBitmap(bitmapVerifier.getDifferenceBitmap(), getName() + "_verifier",
                     this.getClass().getSimpleName());
         }
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
index be680d9..41e255b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
@@ -84,9 +84,10 @@
         int[] visualizerArray = differenceVisualizer.getDifferences(idealArray, testedArray);
         visualizerBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
         visualizerBitmap.setPixels(visualizerArray, 0, width, 0, 0, width, height);
+        Bitmap croppedBitmap = Bitmap.createBitmap(testedBitmap, 0, 0, width, height);
 
         saveFile(className, testName, IDEAL_RENDERING_FILE_NAME, idealBitmap);
-        saveFile(className, testName, TESTED_RENDERING_FILE_NAME, testedBitmap);
+        saveFile(className, testName, TESTED_RENDERING_FILE_NAME, croppedBitmap);
         saveFile(className, testName, VISUALIZER_RENDERING_FILE_NAME, visualizerBitmap);
     }
 
diff --git a/tests/tests/widget/res/layout/popupwindow.xml b/tests/tests/widget/res/layout/popupwindow.xml
index 2508115..f93f965 100644
--- a/tests/tests/widget/res/layout/popupwindow.xml
+++ b/tests/tests/widget/res/layout/popupwindow.xml
@@ -14,37 +14,36 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:orientation="vertical">
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
 
-    <TextView android:id="@+id/anchor_upper"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/text_view_hint" />
+    <View android:id="@+id/anchor_upper"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:background="#f00" />
 
-    <LinearLayout android:layout_width="match_parent"
-                android:layout_height="0dp"
-                android:layout_weight="1">
+    <View android:id="@+id/anchor_lower"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_alignParentBottom="true"
+        android:layout_centerHorizontal="true"
+        android:background="#0f0" />
 
-        <TextView android:id="@+id/anchor_middle_left"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/text_view_hint"
-            android:layout_weight="1"/>
+    <View android:id="@+id/anchor_middle_left"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_alignParentLeft="true"
+        android:layout_centerVertical="true"
+        android:background="#00f" />
 
-        <TextView android:id="@+id/anchor_middle_right"
-            android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:text="@string/text_view_hint"
-            android:layout_weight="1"/>
+    <View android:id="@+id/anchor_middle_right"
+        android:layout_width="10dp"
+        android:layout_height="10dp"
+        android:layout_alignParentRight="true"
+        android:layout_centerVertical="true"
+        android:background="#ff0" />
 
-    </LinearLayout>
-
-    <TextView android:id="@+id/anchor_lower"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:text="@string/text_view_hint" />
-
-</LinearLayout>
+</RelativeLayout>
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 4a14d2b..e1742c8 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -25,6 +25,7 @@
 import android.graphics.Color;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.os.Debug;
 import android.os.SystemClock;
 import android.test.ActivityInstrumentationTestCase2;
 import android.test.UiThreadTest;
@@ -580,7 +581,8 @@
         assertEquals(50, mPopupWindow.getHeight());
 
         mPopupWindow.getContentView().getLocationOnScreen(viewXY);
-        // the position should be changed
+
+        // The popup should appear below and to right with an offset.
         assertEquals(anchorXY[0] + 20 + viewInWindowOff[0], viewXY[0]);
         assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]);
 
@@ -597,14 +599,15 @@
         assertEquals(50, mPopupWindow.getHeight());
 
         mPopupWindow.getContentView().getLocationOnScreen(viewXY);
-        // the position should be changed
+
+        // The popup should appear below and to right with an offset.
         assertEquals(anchorXY[0] + 10 + viewInWindowOff[0], viewXY[0]);
         assertEquals(anchorXY[1] + anchorView.getHeight() + 50 + viewInWindowOff[1], viewXY[1]);
 
-        final View anthoterView = mActivity.findViewById(R.id.anchor_middle_right);
+        final View anotherView = mActivity.findViewById(R.id.anchor_middle_left);
         mInstrumentation.runOnMainSync(new Runnable() {
             public void run() {
-                mPopupWindow.update(anthoterView, 0, 0, 60, 60);
+                mPopupWindow.update(anotherView, 0, 0, 60, 60);
             }
         });
         mInstrumentation.waitForIdleSync();
@@ -614,11 +617,12 @@
         assertEquals(60, mPopupWindow.getHeight());
 
         int[] newXY = new int[2];
-        anthoterView.getLocationOnScreen(newXY);
+        anotherView.getLocationOnScreen(newXY);
         mPopupWindow.getContentView().getLocationOnScreen(viewXY);
-        // the position should be changed
+
+        // The popup should appear below and to the right.
         assertEquals(newXY[0] + viewInWindowOff[0], viewXY[0]);
-        assertEquals(newXY[1] + anthoterView.getHeight() + viewInWindowOff[1], viewXY[1]);
+        assertEquals(newXY[1] + anotherView.getHeight() + viewInWindowOff[1], viewXY[1]);
 
         dismissPopup();
     }
@@ -815,8 +819,7 @@
     }
 
     private View createPopupContent() {
-        TextView popupView = new TextView(mActivity);
-        popupView.setText("Popup");
+        View popupView = new View(mActivity);
         popupView.setLayoutParams(new ViewGroup.LayoutParams(50, 50));
         popupView.setBackgroundColor(Color.WHITE);
 
diff --git a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
index 1680cae..833bf69 100644
--- a/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
+++ b/tools/cts-xml-generator/src/com/android/cts/xmlgenerator/XmlGenerator.java
@@ -205,7 +205,8 @@
             String className = nameCollector.toString();
             nameCollector.append('#').append(test.getName());
             writer.append("<Test name=\"").append(test.getName()).append("\"");
-            String abis = getSupportedAbis(mUnsupportedAbis, mArchitecture, className).toString();
+            String abis = getSupportedAbis(mUnsupportedAbis, mArchitecture,
+                    className, nameCollector.toString()).toString();
             writer.append(" abis=\"" + abis.substring(1, abis.length() - 1) + "\"");
             if (isKnownFailure(mKnownFailures, nameCollector.toString())) {
                 writer.append(" expectation=\"failure\"");
@@ -232,23 +233,36 @@
 
     // Returns the list of ABIs supported by this TestCase on this architecture.
     public static Set<String> getSupportedAbis(ExpectationStore expectationStore,
-            String architecture, String className) {
+            String architecture, String className, String testName) {
         Set<String> supportedAbis = AbiUtils.getAbisForArch(architecture);
-        Expectation e = (expectationStore == null) ? null : expectationStore.get(className);
-        if (e != null && !e.getDescription().isEmpty()) {
-            // Description should be written in the form "blah blah: abi1, abi2..."
-            String description = e.getDescription().split(":")[1];
-            String[] unsupportedAbis = description.split(",");
-            for (String a : unsupportedAbis) {
-                String abi = a.trim();
-                if (!AbiUtils.isAbiSupportedByCts(abi)) {
-                    throw new RuntimeException(
-                            String.format("Unrecognised ABI %s in %s", abi, e.getDescription()));
-                }
-                supportedAbis.remove(abi);
-            }
+        if (expectationStore == null) {
+            return supportedAbis;
         }
+
+        removeUnsupportedAbis(expectationStore.get(className), supportedAbis);
+        removeUnsupportedAbis(expectationStore.get(testName), supportedAbis);
         return supportedAbis;
     }
 
+    public static void removeUnsupportedAbis(Expectation expectation, Set<String> supportedAbis) {
+        if (expectation == null) {
+            return;
+        }
+
+        String description = expectation.getDescription();
+        if (description.isEmpty()) {
+            return;
+        }
+
+        String[] unsupportedAbis = description.split(":")[1].split(",");
+        for (String a : unsupportedAbis) {
+            String abi = a.trim();
+            if (!AbiUtils.isAbiSupportedByCts(abi)) {
+                throw new RuntimeException(
+                        String.format("Unrecognised ABI %s in %s", abi, description));
+            }
+            supportedAbis.remove(abi);
+        }
+    }
+
 }
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
index 61b4b43..0940355 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildHelper.java
@@ -33,7 +33,7 @@
     private final String mSuiteName = "CTS";
     /** The root location of the extracted CTS package */
     private final File mRootDir;
-    /** the {@link CTS_DIR_NAME} directory */
+    /** the {@link #CTS_DIR_NAME} directory */
     private final File mCtsDir;
 
     /**
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 e8d6e71..08e9a0b 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
@@ -33,7 +33,6 @@
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.result.LogFileSaver;
 import com.android.tradefed.result.TestSummary;
-import com.android.tradefed.util.AbiFormatter;
 import com.android.tradefed.util.FileUtil;
 import com.android.tradefed.util.StreamUtil;
 
@@ -223,8 +222,11 @@
 
     @Override
     public void testRunStarted(String id, int numTests) {
-        mCurrentPkgResult = mResults.getOrCreatePackage(id);
         mIsDeviceInfoRun = DeviceInfoCollector.IDS.contains(id);
+        if (!mIsDeviceInfoRun) {
+            mCurrentPkgResult = mResults.getOrCreatePackage(id);
+            mCurrentPkgResult.setDeviceSerial(mDeviceSerial);
+        }
     }
 
     /**
@@ -232,7 +234,9 @@
      */
     @Override
     public void testStarted(TestIdentifier test) {
-        mCurrentPkgResult.insertTest(test);
+        if (!mIsDeviceInfoRun) {
+            mCurrentPkgResult.insertTest(test);
+        }
     }
 
     /**
@@ -240,7 +244,9 @@
      */
     @Override
     public void testFailed(TestIdentifier test, String trace) {
-        mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        if (!mIsDeviceInfoRun) {
+            mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        }
     }
 
     /**
@@ -249,7 +255,9 @@
     @Override
     public void testAssumptionFailure(TestIdentifier test, String trace) {
         // TODO: do something different here?
-        mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        if (!mIsDeviceInfoRun) {
+            mCurrentPkgResult.reportTestFailure(test, CtsTestStatus.FAIL, trace);
+        }
     }
 
     /**
@@ -265,7 +273,9 @@
      */
     @Override
     public void testEnded(TestIdentifier test, Map<String, String> testMetrics) {
-        mCurrentPkgResult.reportTestEnded(test, testMetrics);
+        if (!mIsDeviceInfoRun) {
+            mCurrentPkgResult.reportTestEnded(test, testMetrics);
+        }
     }
 
     /**
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 6d617ea..a16e1c4 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
@@ -37,6 +37,7 @@
 import com.android.tradefed.result.InputStreamSource;
 import com.android.tradefed.result.LogDataType;
 import com.android.tradefed.result.ResultForwarder;
+import com.android.tradefed.testtype.IAbi;
 import com.android.tradefed.testtype.IBuildReceiver;
 import com.android.tradefed.testtype.IDeviceTest;
 import com.android.tradefed.testtype.IRemoteTest;
@@ -209,11 +210,18 @@
         }
 
         /**
-         * Return the test run name that should be used for the TestPackage
+         * @return the test run name that should be used for the TestPackage.
          */
         String getTestRunName() {
             return mPackageDef.getId();
         }
+
+        /**
+         * @return the ABI on which the test will run.
+         */
+        IAbi getAbi() {
+            return mPackageDef.getAbi();
+        }
     }
 
     /**
@@ -444,11 +452,18 @@
             listener = logcatListener;
         }
 
+        // Only run test packages on ABIs which this device supports.
+        Set<String> abis = getAbis();
+        if (abis == null || abis.isEmpty()) {
+            throw new IllegalArgumentException("could not get device's ABIs");
+        }
+        List<TestPackage> packages = filterTestPackagesByAbi(mRemainingTestPkgs, abis);
+
         // collect and install the prerequisiteApks first, to save time when multiple test
         // packages are using the same prerequisite apk (I'm looking at you, CtsTestStubs!)
-        Collection<String> prerequisiteApks = getPrerequisiteApks(mRemainingTestPkgs);
-        Collection<String> uninstallPackages = getPrerequisitePackageNames(mRemainingTestPkgs);
-        List<ResultFilter> filters = new ArrayList<ResultFilter>(mRemainingTestPkgs.size());
+        Collection<String> prerequisiteApks = getPrerequisiteApks(packages);
+        Collection<String> uninstallPackages = getPrerequisitePackageNames(packages);
+        List<ResultFilter> filters = new ArrayList<ResultFilter>(packages.size());
 
         try {
             installPrerequisiteApks(prerequisiteApks);
@@ -456,14 +471,14 @@
             // always collect the device info, even for resumed runs, since test will likely be
             // running on a different device
             collectDeviceInfo(getDevice(), mCtsBuild, listener);
-            if (mRemainingTestPkgs.size() > 1 && !mDisableReboot) {
+            if (packages.size() > 1 && !mDisableReboot) {
                 Log.i(LOG_TAG, "Initial reboot for multiple packages");
                 rebootDevice();
             }
             mPrevRebootTime = System.currentTimeMillis();
 
-            while (!mRemainingTestPkgs.isEmpty()) {
-                TestPackage knownTests = mRemainingTestPkgs.get(0);
+            while (!packages.isEmpty()) {
+                TestPackage knownTests = packages.get(0);
                 ResultFilter filter = new ResultFilter(listener, knownTests);
                 filters.add(filter);
 
@@ -480,9 +495,9 @@
 
                 forwardPackageDetails(knownTests.getPackageDef(), listener);
                 test.run(filter);
-                mRemainingTestPkgs.remove(0);
-                if (mRemainingTestPkgs.size() > 0) {
-                    rebootIfNecessary(knownTests, mRemainingTestPkgs.get(0));
+                packages.remove(0);
+                if (packages.size() > 0) {
+                    rebootIfNecessary(knownTests, packages.get(0));
                     // remove artifacts like status bar from the previous test.
                     // But this cannot dismiss dialog popped-up.
                     changeToHomeScreen();
@@ -513,6 +528,22 @@
         }
     }
 
+    /**
+     * @param packages The package list to filter
+     * @param abis The ABIs to test on
+     * @return A list of {@link TestPackage} which test one of the given ABIs
+     */
+    private static List<TestPackage> filterTestPackagesByAbi(
+            List<TestPackage> packages, Set<String> abis){
+        List<TestPackage> testPackages = new LinkedList<>();
+        for (TestPackage test : packages) {
+            if (abis.contains(test.getAbi().getName())) {
+                testPackages.add(test);
+            }
+        }
+        return testPackages;
+    }
+
     private void rebootIfNecessary(TestPackage testFinished, TestPackage testToRun)
             throws DeviceNotAvailableException {
         // If there comes spurious failure like INJECT_EVENTS for a package,
@@ -579,17 +610,12 @@
     }
     /**
      * Build the list of test packages to run
-     * @throws DeviceNotAvailableException
      */
-    private List<TestPackage> buildTestsToRun() throws DeviceNotAvailableException {
-        Set<String> abis = getAbis();
-        if (abis == null || abis.isEmpty()) {
-            throw new IllegalArgumentException("could not get device's ABIs");
-        }
+    private List<TestPackage> buildTestsToRun() {
         List<TestPackage> testPkgList = new LinkedList<TestPackage>();
         try {
-            ITestPackageRepo testRepo = createTestCaseRepo(abis);
-            Collection<ITestPackageDef> testPkgDefs = getTestPackagesToRun(testRepo, abis);
+            ITestPackageRepo testRepo = createTestCaseRepo();
+            Collection<ITestPackageDef> testPkgDefs = getTestPackagesToRun(testRepo);
             for (ITestPackageDef testPkgDef : testPkgDefs) {
                 addTestPackage(testPkgList, testPkgDef);
             }
@@ -628,14 +654,14 @@
      * @throws FileNotFoundException
      * @throws ConfigurationException
      */
-    private Collection<ITestPackageDef> getTestPackagesToRun(ITestPackageRepo testRepo,
-            Set<String> abis) throws ParseException, FileNotFoundException, ConfigurationException {
+    private Collection<ITestPackageDef> getTestPackagesToRun(ITestPackageRepo testRepo)
+            throws ParseException, FileNotFoundException, ConfigurationException {
         // use LinkedHashSet to have predictable iteration order
         Set<ITestPackageDef> testPkgDefs = new LinkedHashSet<ITestPackageDef>();
         if (mPlanName != null) {
             Log.i(LOG_TAG, String.format("Executing CTS test plan %s", mPlanName));
             File ctsPlanFile = mCtsBuild.getTestPlanFile(mPlanName);
-            ITestPlan plan = createPlan(mPlanName, abis);
+            ITestPlan plan = createPlan(mPlanName);
             plan.parse(createXmlStream(ctsPlanFile));
             for (String id : plan.getTestIds()) {
                 if (!mExcludedPackageNames.contains(AbiUtils.parseId(id)[1])) {
@@ -681,7 +707,7 @@
             String uniquePlanName = Long.toString(System.currentTimeMillis());
             PlanCreator planCreator = new PlanCreator(uniquePlanName, mContinueSessionId,
                     CtsTestStatus.NOT_EXECUTED);
-            ITestPlan plan = createPlan(planCreator, abis);
+            ITestPlan plan = createPlan(planCreator);
             for (String id : plan.getTestIds()) {
                 if (!mExcludedPackageNames.contains(AbiUtils.parseId(id)[1])) {
                     ITestPackageDef testPackageDef = testRepo.getTestPackage(id);
@@ -762,15 +788,26 @@
      */
     private void installPrerequisiteApks(Collection<String> prerequisiteApks)
             throws DeviceNotAvailableException {
+        Log.logAndDisplay(LogLevel.INFO, LOG_TAG, "Installing prerequisites");
+        Set<String> supportedAbiSet = getAbis();
         for (String apkName : prerequisiteApks) {
             try {
                 File apkFile = mCtsBuild.getTestApp(apkName);
-                String errorCode = null;
-                String abi = AbiFormatter.getDefaultAbi(getDevice(), mForceAbi);
-                String[] options = {AbiUtils.createAbiFlag(abi)};
-                errorCode = getDevice().installPackage(apkFile, true, options);
-                if (errorCode != null) {
-                    CLog.e("Failed to install %s. Reason: %s", apkName, errorCode);
+                // As a workaround for multi arch support, try to install the APK
+                // for all device supported ABIs. This will generate warning messages
+                // until the above FIXME is resolved.
+                int installFailCount = 0;
+                for (String abi : supportedAbiSet) {
+                    String[] options = {AbiUtils.createAbiFlag(abi)};
+                    String errorCode = getDevice().installPackage(apkFile, true, options);
+                    if (errorCode != null) {
+                        installFailCount++;
+                        CLog.w("Failed to install %s. Reason: %s", apkName, errorCode);
+                    }
+
+                }
+                if (installFailCount >= supportedAbiSet.size()) {
+                    CLog.e("Failed to install %s. See warning messages.", apkName);
                 }
             } catch (FileNotFoundException e) {
                 CLog.e("Could not find test apk %s", apkName);
@@ -799,12 +836,7 @@
             return null;
         }
         checkFields();
-        List<TestPackage> allTests = null;
-        try {
-            allTests = buildTestsToRun();
-        } catch (DeviceNotAvailableException e) {
-            e.printStackTrace();
-        }
+        List<TestPackage> allTests = buildTestsToRun();
 
         if (allTests == null || allTests.size() <= 1) {
             Log.w(LOG_TAG, "no tests to shard!");
@@ -851,8 +883,9 @@
      * <p/>
      * Exposed for unit testing
      */
-    ITestPackageRepo createTestCaseRepo(Set<String> abis) {
-        return new TestPackageRepo(mCtsBuild.getTestCasesDir(), abis, mIncludeKnownFailures);
+    ITestPackageRepo createTestCaseRepo() {
+        return new TestPackageRepo(mCtsBuild.getTestCasesDir(), AbiUtils.getAbisSupportedByCts(),
+                mIncludeKnownFailures);
     }
 
     /**
@@ -860,8 +893,8 @@
      * <p/>
      * Exposed for unit testing
      */
-    ITestPlan createPlan(String planName, Set<String> abis) {
-        return new TestPlan(planName, abis);
+    ITestPlan createPlan(String planName) {
+        return new TestPlan(planName, AbiUtils.getAbisSupportedByCts());
     }
 
     /**
@@ -889,9 +922,9 @@
      * Exposed for unit testing
      * @throws ConfigurationException
      */
-    ITestPlan createPlan(PlanCreator planCreator, Set<String> abis)
+    ITestPlan createPlan(PlanCreator planCreator)
             throws ConfigurationException {
-        return planCreator.createDerivedPlan(mCtsBuild, abis);
+        return planCreator.createDerivedPlan(mCtsBuild, AbiUtils.getAbisSupportedByCts());
     }
 
     /**
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
index 9bfe151..6c2ed65 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/GeeTest.java
@@ -49,9 +49,9 @@
     private CtsBuildHelper mCtsBuild;
     private ITestDevice mDevice;
     private IAbi mAbi;
+    private String mExeName;
 
     private final String mPackageName;
-    private final String mExeName;
 
     public GeeTest(String packageName, String exeName) {
         mPackageName = packageName;
@@ -63,6 +63,7 @@
      */
     public void setAbi(IAbi abi) {
         mAbi = abi;
+        mExeName += mAbi.getBitness();
     }
 
     @Override
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
index ce91a8a..c1f168d 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/TestPackageRepo.java
@@ -89,7 +89,7 @@
                     if (def.getTests().size() > 0) {
                         mTestMap.get(abi).put(name, def);
                     } else {
-                        Log.i(LOG_TAG, String.format("No tests in %s for %s, skipping",
+                        Log.d(LOG_TAG, String.format("No tests in %s for %s, skipping",
                                 name, abi));
                     }
                 }
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
index 48f1ba5..b74e26c 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/result/CtsXmlResultReporterTest.java
@@ -173,6 +173,21 @@
         assertTrue(output.contains(failureTag));
     }
 
+    public void testDeviceSetup() {
+        Map<String, String> emptyMap = Collections.emptyMap();
+        final TestIdentifier testId = new TestIdentifier("android.tests.devicesetup", "TestDeviceSetup");
+        mResultReporter.invocationStarted(mMockBuild);
+        mResultReporter.testRunStarted(AbiUtils.createId(UnitTests.ABI.getName(), testId.getClassName()), 1);
+        mResultReporter.testStarted(testId);
+        mResultReporter.testEnded(testId, emptyMap);
+        mResultReporter.testRunEnded(3, emptyMap);
+        mResultReporter.invocationEnded(1);
+        String output = getOutput();
+        // TODO: consider doing xml based compare
+        final String deviceSetupTag = "appPackageName=\"android.tests.devicesetup\"";
+        assertFalse(output, output.contains(deviceSetupTag));
+    }
+
     /**
      * Gets the output produced, stripping it of extraneous whitespace characters.
      */
diff --git a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
index 42c2ed1..9915309 100644
--- a/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
+++ b/tools/tradefed-host/tests/src/com/android/cts/tradefed/testtype/CtsTestTest.java
@@ -85,17 +85,17 @@
 
         mCtsTest = new CtsTest() {
             @Override
-            ITestPackageRepo createTestCaseRepo(Set<String> abis) {
+            ITestPackageRepo createTestCaseRepo() {
                 return mMockRepo;
             }
 
             @Override
-            ITestPlan createPlan(String planName, Set<String> abis) {
+            ITestPlan createPlan(String planName) {
                 return mMockPlan;
             }
 
             @Override
-            ITestPlan createPlan(PlanCreator planCreator, Set<String> abis) {
+            ITestPlan createPlan(PlanCreator planCreator) {
                 return mMockPlan;
             }
 
diff --git a/tools/utils/Android.mk b/tools/utils/Android.mk
index 36081ca..0ba5cf4 100644
--- a/tools/utils/Android.mk
+++ b/tools/utils/Android.mk
@@ -25,6 +25,6 @@
 LOCAL_CLASSPATH := $(HOST_JDK_TOOLS_JAR)
 
 LOCAL_JAVA_LIBRARIES := junit
-LOCAL_STATIC_JAVA_LIBRARIES := vogarexpectlib
+LOCAL_STATIC_JAVA_LIBRARIES := ctsabiutilslib vogarexpectlib
 
 include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index 367fb93..ed74824 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -13,6 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+import com.android.cts.util.AbiUtils;
 
 import org.junit.runner.RunWith;
 import org.w3c.dom.Document;
@@ -20,6 +21,7 @@
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import vogar.Expectation;
 import vogar.ExpectationStore;
 
 import java.io.BufferedReader;
@@ -61,9 +63,9 @@
     private static final String TEST_TYPE = "LOCAL_TEST_TYPE :";
 
     public static void main(String[] args) {
-        if (args.length < 4 || args.length > 6) {
+        if (args.length < 5 || args.length > 7) {
             System.err.println("usage: CollectAllTests <output-file> <manifest-file> <jar-file> "
-                               + "<java-package> [expectation-dir [makefile-file]]");
+                               + "<java-package> <architecture> [expectation-dir [makefile-file]]");
             if (args.length != 0) {
                 System.err.println("received:");
                 for (String arg : args) {
@@ -86,8 +88,14 @@
                 return;
             }
         }
-        String libcoreExpectationDir = (args.length > 4) ? args[4] : null;
-        String androidMakeFile = (args.length > 5) ? args[5] : null;
+        String architecture = args[4];
+        if (architecture == null || architecture.equals("")) {
+            System.err.println("Invalid architecture");
+            System.exit(1);
+            return;
+        }
+        String libcoreExpectationDir = (args.length > 5) ? args[5] : null;
+        String androidMakeFile = (args.length > 6) ? args[6] : null;
 
         final TestType testType = TestType.getTestType(androidMakeFile);
 
@@ -207,7 +215,7 @@
 
                 try {
                     klass.getConstructor(new Class<?>[] { String.class } );
-                    addToTests(expectations, testCases, klass);
+                    addToTests(expectations, architecture, testCases, klass);
                     continue;
                 } catch (NoSuchMethodException e) {
                 } catch (SecurityException e) {
@@ -218,7 +226,7 @@
 
                 try {
                     klass.getConstructor(new Class<?>[0]);
-                    addToTests(expectations, testCases, klass);
+                    addToTests(expectations, architecture, testCases, klass);
                     continue;
                 } catch (NoSuchMethodException e) {
                 } catch (SecurityException e) {
@@ -356,6 +364,7 @@
     }
 
     private static void addToTests(ExpectationStore[] expectations,
+                                   String architecture,
                                    Map<String,TestClass> testCases,
                                    Class<?> testClass) {
         Set<String> testNames = new HashSet<String>();
@@ -386,11 +395,12 @@
             }
 
             testNames.add(testName);
-            addToTests(expectations, testCases, testClass, testName);
+            addToTests(expectations, architecture, testCases, testClass, testName);
         }
     }
 
     private static void addToTests(ExpectationStore[] expectations,
+                                   String architecture,
                                    Map<String,TestClass> testCases,
                                    Class<?> test,
                                    String testName) {
@@ -412,6 +422,10 @@
             return;
         }
 
+        Set<String> supportedAbis = VogarUtils.extractSupportedAbis(architecture,
+                                                                    expectations,
+                                                                    testClassName,
+                                                                    testName);
         TestClass testClass;
         if (testCases.containsKey(testClassName)) {
             testClass = testCases.get(testClassName);
@@ -420,7 +434,8 @@
             testCases.put(testClassName, testClass);
         }
 
-        testClass.mCases.add(new TestMethod(testName, "", "", knownFailure, false, false));
+        testClass.mCases.add(new TestMethod(testName, "", "", supportedAbis,
+              knownFailure, false, false));
     }
 
     private static boolean isJunit3Test(Class<?> klass) {
diff --git a/tools/utils/DescriptionGenerator.java b/tools/utils/DescriptionGenerator.java
index 607d2e5..09e1118 100644
--- a/tools/utils/DescriptionGenerator.java
+++ b/tools/utils/DescriptionGenerator.java
@@ -22,6 +22,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.Set;
 
 import javax.xml.parsers.DocumentBuilderFactory;
 import javax.xml.parsers.ParserConfigurationException;
@@ -38,6 +39,7 @@
 import org.w3c.dom.NodeList;
 
 import vogar.ExpectationStore;
+import vogar.Expectation;
 
 import com.sun.javadoc.AnnotationDesc;
 import com.sun.javadoc.AnnotationTypeDoc;
@@ -82,11 +84,13 @@
     static final String ATTRIBUTE_VALUE_FRAMEWORK = "Android 1.0";
 
     static final String ATTRIBUTE_NAME = "name";
+    static final String ATTRIBUTE_ABIS = "abis";
     static final String ATTRIBUTE_HOST_CONTROLLER = "HostController";
 
     static final String XML_OUTPUT_PATH = "./description.xml";
 
     static final String OUTPUT_PATH_OPTION = "-o";
+    static final String ARCHITECTURE_OPTION = "-a";
 
     /**
      * Start to parse the classes passed in by javadoc, and generate
@@ -103,12 +107,21 @@
         }
 
         String outputPath = XML_OUTPUT_PATH;
+        String architecture = null;
         String[][] options = root.options();
         for (String[] option : options) {
-            if (option.length == 2 && option[0].equals(OUTPUT_PATH_OPTION)) {
-                outputPath = option[1];
+            if (option.length == 2) {
+                if (option[0].equals(OUTPUT_PATH_OPTION)) {
+                    outputPath = option[1];
+                } else if (option[0].equals(ARCHITECTURE_OPTION)) {
+                    architecture = option[1];
+                }
             }
         }
+        if (architecture == null || architecture.equals("")) {
+            Log.e("Missing architecture!", null);
+            return false;
+        }
 
         XMLGenerator xmlGenerator = null;
         try {
@@ -128,7 +141,7 @@
 
         for (ClassDoc clazz : classes) {
             if ((!clazz.isAbstract()) && (isValidJUnitTestCase(clazz))) {
-                xmlGenerator.addTestClass(new TestClass(clazz, ctsExpectationStore));
+                xmlGenerator.addTestClass(new TestClass(clazz, ctsExpectationStore, architecture));
             }
         }
 
@@ -420,6 +433,8 @@
                     Node caseNode = elem.appendChild(mDoc.createElement(TAG_TEST));
 
                     setAttribute(caseNode, ATTRIBUTE_NAME, caze.mName);
+                    String abis = caze.mAbis.toString();
+                    setAttribute(caseNode, ATTRIBUTE_ABIS, abis.substring(1, abis.length() - 1));
                     if ((caze.mController != null) && (caze.mController.length() != 0)) {
                         setAttribute(caseNode, ATTRIBUTE_HOST_CONTROLLER, caze.mController);
                     }
@@ -509,9 +524,9 @@
          *
          * @param clazz The specified ClassDoc.
          */
-        TestClass(ClassDoc clazz, ExpectationStore expectationStore) {
+        TestClass(ClassDoc clazz, ExpectationStore expectationStore, String architecture) {
             mName = clazz.toString();
-            mCases = getTestMethods(expectationStore, clazz);
+            mCases = getTestMethods(expectationStore, architecture, clazz);
         }
 
         /**
@@ -520,7 +535,8 @@
          * @param clazz The specified ClassDoc.
          * @return A collection of TestMethod.
          */
-        Collection<TestMethod> getTestMethods(ExpectationStore expectationStore, ClassDoc clazz) {
+        Collection<TestMethod> getTestMethods(ExpectationStore expectationStore,
+                String architecture, ClassDoc clazz) {
             Collection<MethodDoc> methods = getAllMethods(clazz);
 
             ArrayList<TestMethod> cases = new ArrayList<TestMethod>();
@@ -553,8 +569,13 @@
                 }
 
                 if (name.startsWith("test")) {
-                    cases.add(new TestMethod(name, method.commentText(), controller, knownFailure,
-                            isBroken, isSuppressed));
+                    Expectation expectation = expectationStore.get(
+                            VogarUtils.buildFullTestName(clazz.toString(), name));
+                    Set<String> supportedAbis =
+                            VogarUtils.extractSupportedAbis(architecture, expectation);
+                    cases.add(new TestMethod(
+                            name, method.commentText(), controller, supportedAbis,
+                                    knownFailure, isBroken, isSuppressed));
                 }
             }
 
@@ -610,6 +631,7 @@
         String mName;
         String mDescription;
         String mController;
+        Set<String> mAbis;
         String mKnownFailure;
         boolean mIsBroken;
         boolean mIsSuppressed;
@@ -621,11 +643,12 @@
          * @param description The description of the test case.
          * @param knownFailure The reason of known failure.
          */
-        TestMethod(String name, String description, String controller, String knownFailure,
-                boolean isBroken, boolean isSuppressed) {
+        TestMethod(String name, String description, String controller, Set<String> abis,
+                String knownFailure, boolean isBroken, boolean isSuppressed) {
             mName = name;
             mDescription = description;
             mController = controller;
+            mAbis = abis;
             mKnownFailure = knownFailure;
             mIsBroken = isBroken;
             mIsSuppressed = isSuppressed;
diff --git a/tools/utils/VogarUtils.java b/tools/utils/VogarUtils.java
index c7070a5..5e8b944 100644
--- a/tools/utils/VogarUtils.java
+++ b/tools/utils/VogarUtils.java
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+import com.android.cts.util.AbiUtils;
+
 import vogar.Expectation;
 import vogar.ExpectationStore;
 import vogar.ModeId;
@@ -38,12 +40,26 @@
         return false;
     }
 
+    /**
+     * @return true iff the class/name is found in the vogar known failure list and it is not
+     * a known failure that is a result of an unsupported abi.
+     */
     public static boolean isVogarKnownFailure(ExpectationStore expectationStore,
             final String testClassName,
             final String testMethodName) {
-        String fullTestName = String.format("%s#%s", testClassName, testMethodName);
-        return expectationStore != null
-                && expectationStore.get(fullTestName) != Expectation.SUCCESS;
+        if (expectationStore == null) {
+            return false;
+        }
+        String fullTestName = buildFullTestName(testClassName, testMethodName);
+        Expectation expectation = expectationStore.get(fullTestName);
+        if (expectation == Expectation.SUCCESS) {
+            return false;
+        }
+
+        String description = expectation.getDescription();
+        boolean foundAbi = AbiUtils.parseAbiList(description).size() > 0;
+
+        return expectation != Expectation.SUCCESS && !foundAbi;
     }
 
     public static ExpectationStore provideExpectationStore(String dir) throws IOException {
@@ -67,4 +83,49 @@
         }
         return expectSet;
     }
+
+    /** @return the test name in the form of com.android.myclass.TestClass#testMyMethod */
+    public static String buildFullTestName(String testClass, String testMethodName) {
+        return String.format("%s#%s", testClass, testMethodName);
+    }
+
+    /**
+     * This method looks in the description field of the Vogar entry for the ABI_LIST_MARKER
+     * and returns the list of abis found there.
+     *
+     * @return The Set of supported abis parsed from the {@code expectation}'s description.
+     */
+    public static Set<String> extractSupportedAbis(String architecture, Expectation expectation) {
+        Set<String> supportedAbiSet = AbiUtils.getAbisForArch(architecture);
+        if (expectation == null || expectation.getDescription().isEmpty()) {
+            // Include all abis since there was no limitation found in the description
+            return supportedAbiSet;
+        }
+
+        // Remove any abis that are not supported for the test.
+        supportedAbiSet.removeAll(AbiUtils.parseAbiList(expectation.getDescription()));
+
+        return supportedAbiSet;
+    }
+
+    /**
+     * Determine the correct set of ABIs for the given className/testName.
+     *
+     * @return the set of ABIs that can be expected to pass for the given combination of
+     * {@code architecture}, {@code className} and {@code testName}.
+     */
+    public static Set<String> extractSupportedAbis(String architecture,
+                                                   ExpectationStore[] expectationStores,
+                                                   String className,
+                                                   String testName) {
+
+        String fullTestName = VogarUtils.buildFullTestName(className, testName);
+        Set<String> supportedAbiSet = AbiUtils.getAbisForArch(architecture);
+        for (ExpectationStore expectationStore : expectationStores) {
+            Expectation expectation = expectationStore.get(fullTestName);
+            supportedAbiSet.retainAll(extractSupportedAbis(architecture, expectation));
+        }
+
+        return supportedAbiSet;
+    }
 }
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 4a1b2f3..9d0a5ff 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -158,7 +158,6 @@
     # CTS Stable plan
     plan = tools.TestPlan(packages)
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-stable')
@@ -167,7 +166,6 @@
     plan = tools.TestPlan(packages)
     plan.Exclude('.*')
     plan.Include(r'com\.android\.cts\.browserbench')
-    plan.Include(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.Include(package+'$')
       plan.IncludeTests(package, test_list)
@@ -183,7 +181,6 @@
     for package, test_list in small_tests.iteritems():
       plan.Include(package+'$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-kitkat-small')
@@ -194,7 +191,6 @@
     for package, test_list in medium_tests.iteritems():
       plan.Include(package+'$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-kitkat-medium')
@@ -204,7 +200,6 @@
     plan.Exclude('.*')
     plan.Include(r'android\.hardware$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-hardware')
@@ -213,8 +208,8 @@
     plan = tools.TestPlan(packages)
     plan.Exclude('.*')
     plan.Include(r'android\.media$')
+    plan.Include(r'android\.view$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-media')
@@ -224,7 +219,6 @@
     plan.Exclude('.*')
     plan.Include(r'android\.mediastress$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-mediastress')
@@ -235,7 +229,6 @@
     for package, test_list in new_test_packages.iteritems():
       plan.Include(package+'$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-l-tests')
@@ -250,9 +243,9 @@
       plan.Exclude(package+'$')
     plan.Exclude(r'android\.hardware$')
     plan.Exclude(r'android\.media$')
+    plan.Exclude(r'android\.view$')
     plan.Exclude(r'android\.mediastress$')
     plan.Exclude(r'com\.android\.cts\.browserbench')
-    plan.Exclude(r'com\.android\.cts\.filesystemperf\.RandomRWTest$')
     for package, test_list in flaky_tests.iteritems():
       plan.ExcludeTests(package, test_list)
     self.__WritePlan(plan, 'CTS-staging')
@@ -261,6 +254,12 @@
     plan.Exclude('.*')
     plan.Include(r'android\.core\.tests\.libcore\.')
     plan.Include(r'android\.jdwp')
+    for package, test_list in small_tests.iteritems():
+      plan.Exclude(package+'$')
+    for package, test_list in medium_tests.iteritems():
+      plan.Exclude(package+'$')
+    for package, tests_list in new_test_packages.iteritems():
+      plan.Exclude(package+'$')
     self.__WritePlan(plan, 'CTS-ART')
 
     plan = tools.TestPlan(packages)
@@ -345,7 +344,6 @@
       'android.theme' : [],
       'android.usb' : [],
       'android.util' : [],
-      'android.view' : [],
       'com.android.cts.dram' : [],
       'com.android.cts.filesystemperf' : [],
       'com.android.cts.jank' : [],
@@ -359,7 +357,6 @@
 def BuildCtsVettedNewPackagesList():
   """ Construct a defaultdict that maps package names that is vetted for L. """
   return {
-      'android.appwidget' : [],
       'android.core.tests.libcore.package.harmony_annotation' : [],
       'android.core.tests.libcore.package.harmony_beans' : [],
       'android.core.tests.libcore.package.harmony_java_io' : [],
@@ -368,6 +365,7 @@
       'android.core.tests.libcore.package.harmony_java_net' : [],
       'android.core.tests.libcore.package.harmony_java_nio' : [],
       'android.core.tests.libcore.package.harmony_java_util' : [],
+      'android.core.tests.libcore.package.harmony_java_text' : [],
       'android.core.tests.libcore.package.harmony_javax_security' : [],
       'android.core.tests.libcore.package.harmony_logging' : [],
       'android.core.tests.libcore.package.harmony_prefs' : [],
@@ -384,11 +382,12 @@
       'android.tv' : [],
       'android.uiautomation' : [],
       'android.uirendering' : [],
-      'android.webgl' : []}
+      'android.webgl' : [],
+      'com.drawelements.deqp.gles31' : []}
 
 def BuildCtsFlakyTestList():
   """ Construct a defaultdict that maps package name to a list of tests
-      that are known to be flaky. """
+      that are known to be flaky in the lab or not passing on userdebug builds. """
   return {
       'android.app' : [
           'cts.ActivityManagerTest#testIsRunningInTestHarness',],
@@ -423,7 +422,11 @@
           'cts.SELinuxDomainTest#testSuDomain',
           'cts.SELinuxHostTest#testAllEnforcing',],
       'android.webkit' : [
-          'cts.WebViewClientTest#testOnUnhandledKeyEvent',]}
+          'cts.WebViewClientTest#testOnUnhandledKeyEvent',],
+      'com.android.cts.filesystemperf' : [
+          'RandomRWTest#testRandomRead',
+          'RandomRWTest#testRandomUpdate',],
+      '' : []}
 
 def LogGenerateDescription(name):
   print 'Generating test description for package %s' % name