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">-> (%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} µ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} µ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