Merge "Switch from racy notify()/wait() to using a proper semaphore." into lmp-dev
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index c227665..4b75be2 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -21,7 +21,7 @@
LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := cts-sensors-tests
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index b67bdf8..c7563b7 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -34,6 +34,7 @@
<uses-permission android:name="android.permission.FULLSCREEN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
+ <uses-permission android:name="android.permission.VIBRATE" />
<uses-feature android:name="android.hardware.camera.front"
android:required="false" />
<uses-feature android:name="android.hardware.camera.autofocus"
@@ -43,7 +44,6 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
-
<uses-feature android:name="android.hardware.usb.accessory" />
<!-- Needed by the Audio Quality Verifier to store the sound samples that will be mailed. -->
@@ -364,6 +364,17 @@
<meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
</activity>
+ <activity android:name=".bluetooth.BleScannerHardwareScanFilterActivity"
+ android:label="@string/ble_scanner_scan_filter_name"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <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/bt_le" />
+ <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
+ </activity>
+
<activity android:name=".bluetooth.BleAdvertiserTestActivity"
android:label="@string/ble_advertiser_test_name"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -397,6 +408,17 @@
<meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
</activity>
+ <activity android:name=".bluetooth.BleAdvertiserHardwareScanFilterActivity"
+ android:label="@string/ble_advertiser_scan_filter_name"
+ android:configChanges="keyboardHidden|orientation|screenSize">
+ <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/bt_le" />
+ <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleAdvertiserTestActivity" />
+ </activity>
+
<activity android:name=".suid.SuidFilesActivity"
android:label="@string/suid_files"
android:configChanges="keyboardHidden|orientation|screenSize">
@@ -927,6 +949,32 @@
<meta-data android:name="test_category" android:value="@string/test_category_deskclock" />
</activity>
+ <activity
+ android:name="com.android.cts.verifier.sensors.StepCounterTestActivity"
+ android:label="@string/snsr_step_counter_test"
+ android:screenOrientation="nosensor" >
+ <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_sensors" />
+ </activity>
+
+ <activity
+ android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity"
+ android:label="@string/snsr_significant_motion_test"
+ android:screenOrientation="nosensor" >
+ <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_sensors" />
+ </activity>
+
<receiver android:name=".widget.WidgetCtsProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -941,6 +989,55 @@
android:permission="android.permission.BIND_REMOTEVIEWS"
android:exported="false" />
+ <activity android:name=".projection.cube.ProjectionCubeActivity"
+ android:label="@string/pca_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_projection" />
+ </activity>
+
+ <activity android:name=".projection.widgets.ProjectionWidgetActivity"
+ android:label="@string/pwa_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_projection" />
+ </activity>
+
+ <activity android:name=".projection.list.ProjectionListActivity"
+ android:label="@string/pla_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_projection" />
+ </activity>
+
+ <activity android:name=".projection.video.ProjectionVideoActivity"
+ android:label="@string/pva_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_projection" />
+ </activity>
+
+ <activity android:name=".projection.touch.ProjectionTouchActivity"
+ android:label="@string/pta_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_projection" />
+ </activity>
+
+ <service android:name=".projection.ProjectionService"
+ android:label="@string/projection_service_name"
+ android:process=":projectionservice" />
+
</application>
</manifest>
diff --git a/apps/CtsVerifier/proguard.flags b/apps/CtsVerifier/proguard.flags
index 7e6587a..2dfa185 100644
--- a/apps/CtsVerifier/proguard.flags
+++ b/apps/CtsVerifier/proguard.flags
@@ -6,6 +6,11 @@
private <fields>;
}
+# ensure we keep public sensor test methods, these are needed at runtime
+-keepclassmembers class * extends com.android.cts.verifier.sensors.BaseSensorTestActivity {
+ public <methods>;
+}
+
-keepclasseswithmembers class * extends com.android.cts.verifier.location.LocationModeTestActivity
-dontwarn android.hardware.Sensor
diff --git a/apps/CtsVerifier/res/layout/ble_advertiser_hardware_scan_filter.xml b/apps/CtsVerifier/res/layout/ble_advertiser_hardware_scan_filter.xml
new file mode 100644
index 0000000..1496f81
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_advertiser_hardware_scan_filter.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dip"
+ >
+
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"
+ >
+ <TextView android:text="@string/ble_advertiser_scannable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <Button android:id="@+id/ble_advertiser_scannable_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_advertiser_start"
+ />
+ <Button android:id="@+id/ble_advertiser_scannable_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_advertiser_stop"
+ />
+ </LinearLayout>
+ <TextView android:text="@string/ble_advertiser_unscannable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <Button android:id="@+id/ble_advertiser_unscannable_start"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_advertiser_start"
+ />
+ <Button android:id="@+id/ble_advertiser_unscannable_stop"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_advertiser_stop"
+ />
+ </LinearLayout>
+ </LinearLayout>
+
+ <include android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ layout="@layout/pass_fail_buttons"
+ />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml b/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml
index ad45c52..37a7bbc 100644
--- a/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml
+++ b/apps/CtsVerifier/res/layout/ble_advertiser_power_level.xml
@@ -28,12 +28,12 @@
<Button android:id="@+id/ble_power_level_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/ble_power_level_start"
+ android:text="@string/ble_advertiser_start"
/>
<Button android:id="@+id/ble_power_level_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/ble_power_level_stop"
+ android:text="@string/ble_advertiser_stop"
/>
</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_privacy_mac.xml b/apps/CtsVerifier/res/layout/ble_privacy_mac.xml
index 2609654..e67c61a 100644
--- a/apps/CtsVerifier/res/layout/ble_privacy_mac.xml
+++ b/apps/CtsVerifier/res/layout/ble_privacy_mac.xml
@@ -28,12 +28,12 @@
<Button android:id="@+id/ble_privacy_mac_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/ble_privacy_mac_start"
+ android:text="@string/ble_advertiser_start"
/>
<Button android:id="@+id/ble_privacy_mac_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:text="@string/ble_privacy_mac_stop"
+ android:text="@string/ble_advertiser_stop"
/>
</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_scanner_hardware_scan_filter.xml b/apps/CtsVerifier/res/layout/ble_scanner_hardware_scan_filter.xml
new file mode 100644
index 0000000..4f5de53
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/ble_scanner_hardware_scan_filter.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2013 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="10dip"
+ >
+ <LinearLayout android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <LinearLayout android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ >
+ <Button android:id="@+id/ble_scan_with_filter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_scan_with_filter"
+ />
+ <Button android:id="@+id/ble_scan_without_filter"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/ble_scan_without_filter"
+ />
+ </LinearLayout>
+ <ListView android:id="@+id/ble_scan_result_list"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+ </ListView>
+ </LinearLayout>
+ <include android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ layout="@layout/pass_fail_buttons"
+ />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pa_main.xml b/apps/CtsVerifier/res/layout/pa_main.xml
new file mode 100644
index 0000000..b6b8e4b
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pa_main.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextureView
+ android:id="@+id/texture_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pca_cubes.xml b/apps/CtsVerifier/res/layout/pca_cubes.xml
new file mode 100644
index 0000000..25869b9
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pca_cubes.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true" >
+
+ <android.opengl.GLSurfaceView
+ android:id="@+id/cube_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pla_list.xml b/apps/CtsVerifier/res/layout/pla_list.xml
new file mode 100644
index 0000000..0973136
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pla_list.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <ListView
+ android:id="@+id/pla_list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" >
+ </ListView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pta_touch.xml b/apps/CtsVerifier/res/layout/pta_touch.xml
new file mode 100644
index 0000000..8cf6e89
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pta_touch.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <com.android.cts.verifier.projection.touch.TouchPointView
+ android:id="@+id/multi_touch_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/pva_video.xml b/apps/CtsVerifier/res/layout/pva_video.xml
new file mode 100644
index 0000000..a62c833
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pva_video.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <VideoView
+ android:id="@+id/video_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pwa_buttons.xml b/apps/CtsVerifier/res/layout/pwa_buttons.xml
new file mode 100644
index 0000000..fc2d10f
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pwa_buttons.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:gravity="bottom"
+ android:orientation="vertical" >
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pwa_button" />
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pwa_button" />
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pwa_button" />
+
+ <Button
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/pwa_button" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/pwa_widgets.xml b/apps/CtsVerifier/res/layout/pwa_widgets.xml
new file mode 100644
index 0000000..4bfcec6
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/pwa_widgets.xml
@@ -0,0 +1,73 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <TextureView
+ android:id="@+id/texture_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <include layout="@layout/pass_fail_buttons" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal" >
+
+ <Button
+ android:id="@+id/up_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/pwa_button_up" />
+
+ <Button
+ android:id="@+id/down_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/pwa_button_down" />
+
+ <Button
+ android:id="@+id/left_button"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:text="@string/pwa_button_left" />
+
+ <Button
+ android:id="@+id/right_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/pwa_button_right" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+</FrameLayout>
diff --git a/apps/CtsVerifier/res/raw/test_video.mp4 b/apps/CtsVerifier/res/raw/test_video.mp4
new file mode 100644
index 0000000..ab95ac0
--- /dev/null
+++ b/apps/CtsVerifier/res/raw/test_video.mp4
Binary files differ
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1fb7ecb..849698f 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -231,14 +231,16 @@
<string name="ble_advertiser_test_name">BLE Advertiser Test</string>
<string name="ble_advertiser_test_info">The BLE test must be done simultaneously on two devices, an advertiser and a scanner. This device is the advertiser.</string>
<string name="ble_advertiser_service_name">Bluetooth LE Advertiser Handler Service</string>
- <string name="ble_privacy_mac_start">Start</string>
- <string name="ble_privacy_mac_stop">Stop</string>
<string name="ble_privacy_mac_name">BLE Privacy Mac</string>
<string name="ble_privacy_mac_info">BLE Advertiser should advertise in non-repeating MAC address.</string>
- <string name="ble_power_level_start">Start</string>
- <string name="ble_power_level_stop">Stop</string>
<string name="ble_power_level_name">BLE Tx Power Level</string>
<string name="ble_power_level_info">BLE Advertiser advertises in 4 different power levels. Scanner should receive them in different strength of Rssi, cannot receive weak signals beyond several feet.</string>
+ <string name="ble_advertiser_scan_filter_name">BLE Hardware Scan Filter</string>
+ <string name="ble_advertiser_scan_filter_info">BLE Advertiser advertises with 2 different data separately. One can wake up the scanner, the other cannot.</string>
+ <string name="ble_advertiser_scannable">Scannable advertising</string>
+ <string name="ble_advertiser_unscannable">Unscannble advertising</string>
+ <string name="ble_advertiser_start">Start</string>
+ <string name="ble_advertiser_stop">Stop</string>
<!-- BLE scanner side strings -->
<string name="ble_scanner_test_name">BLE Scanner Test</string>
@@ -249,6 +251,10 @@
<string name="ble_low">Low</string>
<string name="ble_medium">Medium</string>
<string name="ble_high">High</string>
+ <string name="ble_scanner_scan_filter_name">BLE Hardware Scan Filter</string>
+ <string name="ble_scanner_scan_filter_info">Lock the screen of scanner, and connect to monsoon. It will not wake up when advertiser is advertising unscannable, and scanner is scanning with filter.</string>
+ <string name="ble_scan_with_filter">Scan with filter</string>
+ <string name="ble_scan_without_filter">Scan without filter</string>
<!-- Strings for FeatureSummaryActivity -->
<string name="feature_summary">Hardware/Software Feature Summary</string>
@@ -442,6 +448,12 @@
<!-- Magnetic Field -->
<string name="snsr_mag_m_test">Magnetic Field Measurement Tests</string>
+ <!-- Step Counter and Detector -->
+ <string name="snsr_step_counter_test">Step Counter and Detector Tests</string>
+
+ <!-- Significant Motion -->
+ <string name="snsr_significant_motion_test">Significant Motion Tests</string>
+
<!-- Strings for Sample Test Activities -->
<string name="share_button_text">Share</string>
<string name="sample_framework_test">Sample Framework Test</string>
@@ -955,7 +967,6 @@
2. Verify that a timer is started and the timers UI is shown with a timer named "Start Timer Test".\n
3. Verify that the timer rings after 30 seconds.\n
</string>
-
<!-- Strings for LockConfirmBypassTest -->
<string name="lock_confirm_test_title">Keyguard Password Verification</string>
<string name="lock_set_button_text">Set password</string>
@@ -966,4 +977,31 @@
\nThen click the \"Change password\" button to change it. You should be prompted for the current password first. If you are not, then mark the test as failed.
</string>
+ <!-- String for Projection Tests -->
+ <string name="test_category_projection">Projection Tests</string>
+ <string name="projection_service_name">Projection Service</string>
+ <string name="pca_info">This tests whether or not OpenGL projection works.\n
+ You should see two "tumbling cubes." Tapping the screen should case the cubes to explode.</string>
+ <string name="pca_test">Projection Cube Test</string>
+ <string name="pwa_info">This tests whether or displaying widets and keyfocus navigation works.\n
+ You should see four buttons on the bottom of the screen.\n
+ Pressing the "up" and "down" buttons should highlight different buttons.\n
+ Further, you should also be able to touch them and they should highlight as ususual.</string>
+ <string name="pwa_test">Projection Widget Test</string>
+ <string name="pwa_button_up">Up</string>
+ <string name="pwa_button_down">Down</string>
+ <string name="pwa_button_left">Left</string>
+ <string name="pwa_button_right">Right</string>
+ <string name="pwa_button">Button</string>
+ <string name="pla_test">Projection Scrolling List Test</string>
+ <string name="pla_info">This tests whether a projected list view will scroll properly\n
+ You should see 50 list items and be able to scroll up and down the list</string>
+ <string name="pva_test">Projection Video Playback Test</string>
+ <string name="pva_info">This tests whether video playback works when projected.\n
+ You should see a blinking white box and here a beep that is synchronized with each blink</string>
+ <string name="pta_test">Projection Multitouch Test</string>
+ <string name="pta_info">This tests whether multitouch works.\n
+ Touching the screen should render a dot at the location you touched.\n
+ Touching with additional fingers will render additoinal dots and you should be able to drag them around.</string>
+
</resources>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserHardwareScanFilterActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserHardwareScanFilterActivity.java
new file mode 100644
index 0000000..242bb08
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserHardwareScanFilterActivity.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+public class BleAdvertiserHardwareScanFilterActivity extends PassFailButtons.Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ble_advertiser_hardware_scan_filter);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_advertiser_scan_filter_name,
+ R.string.ble_advertiser_scan_filter_info, -1);
+
+ ((Button) findViewById(R.id.ble_advertiser_scannable_start))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(BleAdvertiserHardwareScanFilterActivity.this,
+ BleAdvertiserService.class);
+ intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+ BleAdvertiserService.COMMAND_START_SCANNABLE);
+ startService(intent);
+ }
+ });
+ ((Button)findViewById(R.id.ble_advertiser_scannable_stop))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ stopAdvertising();
+ }
+ });
+ ((Button)findViewById(R.id.ble_advertiser_unscannable_start))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(BleAdvertiserHardwareScanFilterActivity.this,
+ BleAdvertiserService.class);
+ intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+ BleAdvertiserService.COMMAND_START_UNSCANNABLE);
+ startService(intent);
+ }
+ });
+ ((Button)findViewById(R.id.ble_advertiser_unscannable_stop))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ stopAdvertising();
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BleAdvertiserService.BLE_START_SCANNABLE);
+ filter.addAction(BleAdvertiserService.BLE_START_UNSCANNABLE);
+ filter.addAction(BleAdvertiserService.BLE_STOP_SCANNABLE);
+ filter.addAction(BleAdvertiserService.BLE_STOP_UNSCANNABLE);
+ registerReceiver(onBroadcast, filter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(onBroadcast);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopAdvertising();
+ }
+
+ private void showMessage(String msg) {
+ Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
+ }
+
+ private void stopAdvertising() {
+ Intent intent = new Intent(BleAdvertiserHardwareScanFilterActivity.this,
+ BleAdvertiserService.class);
+ intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+ BleAdvertiserService.COMMAND_STOP_SCANNABLE);
+ intent.putExtra(BleAdvertiserService.EXTRA_COMMAND,
+ BleAdvertiserService.COMMAND_STOP_UNSCANNABLE);
+ startService(intent);
+ }
+
+ private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case BleAdvertiserService.BLE_START_SCANNABLE:
+ showMessage("Start advertising, this should be scanned");
+ break;
+ case BleAdvertiserService.BLE_START_UNSCANNABLE:
+ showMessage("Start advertising, this should not be scanned");
+ break;
+ case BleAdvertiserService.BLE_STOP_SCANNABLE:
+ case BleAdvertiserService.BLE_STOP_UNSCANNABLE:
+ showMessage("Stop advertising");
+ break;
+ }
+ }
+ };
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
index 7191f58..e21c89e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
@@ -48,6 +48,10 @@
public static final int COMMAND_STOP_ADVERTISE = 1;
public static final int COMMAND_START_POWER_LEVEL = 2;
public static final int COMMAND_STOP_POWER_LEVEL = 3;
+ public static final int COMMAND_START_SCANNABLE = 4;
+ public static final int COMMAND_STOP_SCANNABLE = 5;
+ public static final int COMMAND_START_UNSCANNABLE = 6;
+ public static final int COMMAND_STOP_UNSCANNABLE = 7;
public static final String BLE_START_ADVERTISE =
"com.android.cts.verifier.bluetooth.BLE_START_ADVERTISE";
@@ -57,6 +61,14 @@
"com.android.cts.verifier.bluetooth.BLE_START_POWER_LEVEL";
public static final String BLE_STOP_POWER_LEVEL =
"com.android.cts.verifier.bluetooth.BLE_STOP_POWER_LEVEL";
+ public static final String BLE_START_SCANNABLE =
+ "com.android.cts.verifier.bluetooth.BLE_START_SCANNABLE";
+ public static final String BLE_START_UNSCANNABLE =
+ "com.android.cts.verifier.bluetooth.BLE_START_UNSCANNABLE";
+ public static final String BLE_STOP_SCANNABLE =
+ "com.android.cts.verifier.bluetooth.BLE_STOP_SCANNABLE";
+ public static final String BLE_STOP_UNSCANNABLE =
+ "com.android.cts.verifier.bluetooth.BLE_STOP_UNSCANNABLE";
public static final String EXTRA_COMMAND =
"com.android.cts.verifier.bluetooth.EXTRA_COMMAND";
@@ -67,11 +79,18 @@
UUID.fromString("00008888-0000-1000-8000-00805f9b34fb");
protected static final UUID SCAN_RESP_UUID =
UUID.fromString("00007777-0000-1000-8000-00805f9b34fb");
+ protected static final UUID SCANNABLE_UUID =
+ UUID.fromString("00006666-0000-1000-8000-00805f9b34fb");
+ protected static final UUID UNSCANNABLE_UUID =
+ UUID.fromString("00005555-0000-1000-8000-00805f9b34fb");
+
public static final byte MANUFACTURER_TEST_ID = (byte)0x07;
public static final byte[] PRIVACY_MAC_DATA = new byte[]{3, 1, 4};
public static final byte[] PRIVACY_RESPONSE = new byte[]{9, 2, 6};
public static final byte[] POWER_LEVEL_DATA = new byte[]{1, 5, 0};
public static final byte[] POWER_LEVEL_MASK = new byte[]{1, 1, 0};
+ public static final byte[] SCANNABLE_DATA = new byte[]{5, 3, 5};
+ public static final byte[] UNSCANNABLE_DATA = new byte[]{8, 9, 7};
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
@@ -82,6 +101,10 @@
private int[] mPowerLevel;
private Map<Integer, AdvertiseCallback> mPowerCallback;
+ private int mAdvertiserStatus;
+
+ private AdvertiseCallback mScannableCallback;
+ private AdvertiseCallback mUnscannableCallback;
@Override
public void onCreate() {
@@ -92,9 +115,12 @@
mAdvertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
mGattServer = mBluetoothManager.openGattServer(getApplicationContext(),
new BluetoothGattServerCallback() {});
- mCallback = new BLEAdvertiseCallback();
mHandler = new Handler();
+ mAdvertiserStatus = 0;
+ mCallback = new BLEAdvertiseCallback();
+ mScannableCallback = new BLEAdvertiseCallback();
+ mUnscannableCallback = new BLEAdvertiseCallback();
mPowerLevel = new int[]{
AdvertiseSettings.ADVERTISE_TX_POWER_ULTRA_LOW,
AdvertiseSettings.ADVERTISE_TX_POWER_LOW,
@@ -123,55 +149,92 @@
mAdvertiser.stopAdvertising(mCallback);
}
+ private void stopAdvertiser() {
+ if ((mAdvertiserStatus & (1 << COMMAND_START_ADVERTISE)) > 0) {
+ mAdvertiser.stopAdvertising(mCallback);
+ }
+ if ((mAdvertiserStatus & (1 << COMMAND_START_POWER_LEVEL)) > 0) {
+ for (int t : mPowerLevel) {
+ mAdvertiser.stopAdvertising(mPowerCallback.get(t));
+ }
+ }
+ if ((mAdvertiserStatus & (1 << COMMAND_START_SCANNABLE)) > 0) {
+ mAdvertiser.stopAdvertising(mScannableCallback);
+ }
+ if ((mAdvertiserStatus & (1 << COMMAND_START_UNSCANNABLE)) > 0) {
+ mAdvertiser.stopAdvertising(mUnscannableCallback);
+ }
+ mAdvertiserStatus = 0;
+ }
+
+ private AdvertiseData generateAdvertiseData(UUID uuid, byte[] data) {
+ return new AdvertiseData.Builder()
+ .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
+ .addServiceData(new ParcelUuid(uuid), data)
+ .setIncludeTxPowerLevel(true)
+ .build();
+ }
+
+ private AdvertiseSettings generateSetting(int power) {
+ return new AdvertiseSettings.Builder()
+ .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
+ .setTxPowerLevel(power)
+ .setIsConnectable(false)
+ .build();
+ }
+
private void handleIntent(Intent intent) {
int command = intent.getIntExtra(EXTRA_COMMAND, -1);
+ if (command >= 0) {
+ stopAdvertiser();
+ mAdvertiserStatus |= (1 << command);
+ }
switch (command) {
case COMMAND_START_ADVERTISE:
- AdvertiseData data = new AdvertiseData.Builder()
- .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
- .addServiceData(new ParcelUuid(PRIVACY_MAC_UUID), PRIVACY_MAC_DATA)
- .build();
- AdvertiseData response = new AdvertiseData.Builder()
- .addManufacturerData(MANUFACTURER_TEST_ID, new byte[]{MANUFACTURER_TEST_ID, 0})
- .addServiceData(new ParcelUuid(SCAN_RESP_UUID), PRIVACY_RESPONSE)
- .build();
+ AdvertiseData data = generateAdvertiseData(PRIVACY_MAC_UUID, PRIVACY_MAC_DATA);
+ AdvertiseData response = generateAdvertiseData(SCAN_RESP_UUID, PRIVACY_RESPONSE);
+ AdvertiseSettings setting =
+ generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
- AdvertiseSettings setting = new AdvertiseSettings.Builder()
- .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
- .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM)
- .setIsConnectable(false)
- .build();
mAdvertiser.startAdvertising(setting, data, response, mCallback);
sendBroadcast(new Intent(BLE_START_ADVERTISE));
break;
case COMMAND_STOP_ADVERTISE:
- mAdvertiser.stopAdvertising(mCallback);
sendBroadcast(new Intent(BLE_STOP_ADVERTISE));
break;
case COMMAND_START_POWER_LEVEL:
for (int t : mPowerLevel) {
- AdvertiseData d = new AdvertiseData.Builder()
- .addManufacturerData(MANUFACTURER_TEST_ID,
- new byte[]{MANUFACTURER_TEST_ID, 0})
- .addServiceData(new ParcelUuid(POWER_LEVEL_UUID),
- new byte[]{1, 5, (byte)t})
- .setIncludeTxPowerLevel(true)
- .build();
- AdvertiseSettings settings = new AdvertiseSettings.Builder()
- .setTxPowerLevel(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
- .setTxPowerLevel(t)
- .build();
+ AdvertiseData d =
+ generateAdvertiseData(POWER_LEVEL_UUID, new byte[]{1, 5, (byte)t});
+ AdvertiseSettings settings = generateSetting(t);
mAdvertiser.startAdvertising(settings, d, mPowerCallback.get(t));
}
sendBroadcast(new Intent(BLE_START_POWER_LEVEL));
break;
case COMMAND_STOP_POWER_LEVEL:
- for (int t : mPowerLevel) {
- mAdvertiser.stopAdvertising(mPowerCallback.get(t));
- }
sendBroadcast(new Intent(BLE_STOP_POWER_LEVEL));
break;
+ case COMMAND_START_SCANNABLE:
+ data = generateAdvertiseData(SCANNABLE_UUID, SCANNABLE_DATA);
+ setting = generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
+
+ mAdvertiser.startAdvertising(setting, data, mScannableCallback);
+ sendBroadcast(new Intent(BLE_START_SCANNABLE));
+ break;
+ case COMMAND_START_UNSCANNABLE:
+ data = generateAdvertiseData(UNSCANNABLE_UUID, UNSCANNABLE_DATA);
+ setting = generateSetting(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM);
+
+ mAdvertiser.startAdvertising(setting, data, mUnscannableCallback);
+ sendBroadcast(new Intent(BLE_START_UNSCANNABLE));
+ break;
+ case COMMAND_STOP_SCANNABLE:
+ sendBroadcast(new Intent(BLE_STOP_SCANNABLE));
+ break;
+ case COMMAND_STOP_UNSCANNABLE:
+ sendBroadcast(new Intent(BLE_STOP_UNSCANNABLE));
+ break;
default:
showMessage("Unrecognized command: " + command);
break;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerHardwareScanFilterActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerHardwareScanFilterActivity.java
new file mode 100644
index 0000000..bf66acd
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerHardwareScanFilterActivity.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.ListView;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.HashMap;
+
+public class BleScannerHardwareScanFilterActivity extends PassFailButtons.Activity {
+
+ private static final String TAG = "BleScannerHardwareScanFilter";
+
+ private ListView mScanResultListView;
+ private MapAdapter mAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ble_scanner_hardware_scan_filter);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_scanner_scan_filter_name,
+ R.string.ble_scanner_scan_filter_info, -1);
+ getPassButton().setEnabled(false);
+
+ mScanResultListView = (ListView)findViewById(R.id.ble_scan_result_list);
+ mAdapter = new MapAdapter();
+ mScanResultListView.setAdapter(mAdapter);
+
+ ((Button) findViewById(R.id.ble_scan_with_filter))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(BleScannerHardwareScanFilterActivity.this,
+ BleScannerService.class);
+ intent.putExtra(BleScannerService.EXTRA_COMMAND,
+ BleScannerService.COMMAND_SCAN_WITH_FILTER);
+ startService(intent);
+ }
+ });
+
+ ((Button) findViewById(R.id.ble_scan_without_filter))
+ .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(BleScannerHardwareScanFilterActivity.this,
+ BleScannerService.class);
+ intent.putExtra(BleScannerService.EXTRA_COMMAND,
+ BleScannerService.COMMAND_SCAN_WITHOUT_FILTER);
+ startService(intent);
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BleScannerService.BLE_SCAN_RESULT);
+ registerReceiver(onBroadcast, filter);
+ }
+
+
+ private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ switch (intent.getAction()) {
+ case BleScannerService.BLE_SCAN_RESULT:
+ String uuid = intent.getStringExtra(BleScannerService.EXTRA_UUID);
+ String data = intent.getStringExtra(BleScannerService.EXTRA_DATA);
+ if (data != null) {
+ mAdapter.addItem(uuid + " : " + data);
+ }
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(onBroadcast);
+ }
+
+ private void stop() {
+ stopService(new Intent(this, BleScannerService.class));
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stop();
+ }
+
+ public class MapAdapter extends BaseAdapter {
+ private Map<String, Integer> mData;
+ private ArrayList<String> mKeys;
+ public MapAdapter() {
+ mData = new HashMap<>();
+ mKeys = new ArrayList<>();
+ }
+
+ @Override
+ public int getCount() {
+ return mData.size();
+ }
+
+ @Override
+ public Object getItem(int position) {
+ return mData.get(mKeys.get(position));
+ }
+
+ @Override
+ public long getItemId(int arg0) {
+ return arg0;
+ }
+
+ public void addItem(String key) {
+ if (!mData.containsKey(key)) {
+ mKeys.add(key);
+ mData.put(key, new Integer(1));
+ } else {
+ mData.put(key, mData.get(key) + 1);
+ }
+ this.notifyDataSetChanged();
+ }
+
+ @Override
+ public View getView(int pos, View view, ViewGroup parent) {
+ if (view == null) {
+ view = LayoutInflater.from(parent.getContext())
+ .inflate(android.R.layout.simple_list_item_2, parent, false);
+ }
+ String key = mKeys.get(pos);
+ String value = getItem(pos).toString();
+ TextView text1 = (TextView) view.findViewById(android.R.id.text1);
+ TextView text2 = (TextView) view.findViewById(android.R.id.text2);
+ text1.setText(key);
+ text2.setText(value);
+ return view;
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
index 495f89d..ee71154 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
@@ -46,6 +46,8 @@
public static final int COMMAND_PRIVACY_MAC = 0;
public static final int COMMAND_POWER_LEVEL = 1;
+ public static final int COMMAND_SCAN_WITH_FILTER = 2;
+ public static final int COMMAND_SCAN_WITHOUT_FILTER = 3;
public static final String BLE_PRIVACY_NEW_MAC_RECEIVE =
"com.android.cts.verifier.bluetooth.BLE_PRIVACY_NEW_MAC_RECEIVE";
@@ -55,6 +57,8 @@
"com.android.cts.verifier.bluetooth.BLE_POWER_LEVEL";
public static final String BLE_SCAN_RESP =
"com.android.cts.verifier.bluetooth.BLE_SCAN_RESP";
+ public static final String BLE_SCAN_RESULT =
+ "com.android.cts.verifier.bluetooth.BLE_SCAN_RESULT";
public static final String EXTRA_COMMAND =
"com.google.cts.verifier.bluetooth.EXTRA_COMMAND";
@@ -66,6 +70,10 @@
"com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL";
public static final String EXTRA_POWER_LEVEL_BIT =
"com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL_BIT";
+ public static final String EXTRA_UUID =
+ "com.google.cts.verifier.bluetooth.EXTRA_UUID";
+ public static final String EXTRA_DATA =
+ "com.google.cts.verifier.bluetooth.EXTRA_DATA";
private static final byte MANUFACTURER_TEST_ID = (byte)0x07;
@@ -123,6 +131,20 @@
.build());
settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
break;
+ case COMMAND_SCAN_WITH_FILTER:
+ mScanner.stopScan(mCallback);
+ filters.add(new ScanFilter.Builder()
+ .setManufacturerData(MANUFACTURER_TEST_ID,
+ new byte[]{MANUFACTURER_TEST_ID, 0})
+ .setServiceData(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID),
+ BleAdvertiserService.SCANNABLE_DATA)
+ .build());
+ settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
+ break;
+ case COMMAND_SCAN_WITHOUT_FILTER:
+ mScanner.stopScan(mCallback);
+ settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
+ break;
}
mOldMac = null;
mScanner.startScan(filters, settingBuilder.build(), mCallback);
@@ -193,6 +215,28 @@
Intent responseIntent = new Intent(BLE_SCAN_RESP);
sendBroadcast(responseIntent);
}
+
+ byte[] data = null;
+ String uuid = "";
+ if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID))) {
+ uuid = BleAdvertiserService.SCANNABLE_UUID.toString();
+ data = serviceData.get(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID));
+ }
+ if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID))) {
+ uuid = BleAdvertiserService.UNSCANNABLE_UUID.toString();
+ data = serviceData.get(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID));
+ }
+ if (uuid.length() > 0) {
+ Intent scanIntent = new Intent(BLE_SCAN_RESULT);
+ scanIntent.putExtra(EXTRA_UUID, uuid);
+ String dataStr = "{";
+ for (byte x : data) {
+ dataStr = dataStr + " " + x;
+ }
+ dataStr = dataStr + "}";
+ scanIntent.putExtra(EXTRA_DATA, dataStr);
+ sendBroadcast(scanIntent);
+ }
}
public void onScanFailed(int errorCode) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl b/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl
new file mode 100644
index 0000000..0bc1b20
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/IProjectionService.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.projection;
+
+import android.view.Surface;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+interface IProjectionService {
+ void startRendering(in Surface surface, int width, int height, int density, int viewType);
+ void stopRendering();
+ void onTouchEvent(in MotionEvent event);
+ void onKeyEvent(in KeyEvent event);
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java
new file mode 100644
index 0000000..4990ed4
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectedPresentation.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection;
+
+import android.app.Presentation;
+import android.content.Context;
+import android.view.Display;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+
+/**
+ * Base class for Presentations which are to be projected onto a VirtualDisplay
+ */
+public abstract class ProjectedPresentation extends Presentation {
+ public ProjectedPresentation(Context outerContext, Display display) {
+ // This theme is required to prevent an extra view from obscuring the presentation
+ super(outerContext, display, android.R.style.Theme_Holo_Light_NoActionBar_TranslucentDecor);
+
+ getWindow().setType(WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
+
+ // So we can control the input
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_LOCAL_FOCUS_MODE);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
+ }
+
+ public void injectTouchEvent(MotionEvent event) {
+ getWindow().setLocalFocus(true, true);
+ getWindow().injectInputEvent(event);
+ }
+
+ public void injectKeyEvent(KeyEvent event) {
+ getWindow().setLocalFocus(true, false);
+ getWindow().injectInputEvent(event);
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
new file mode 100644
index 0000000..18d9d43
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
@@ -0,0 +1,160 @@
+/*
+ * 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.projection;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.SurfaceTexture;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.Surface;
+import android.view.TextureView;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+/**
+ * Base activity for each projection test case. Handles the service connection and TextureView
+ * listeners
+ */
+public abstract class ProjectionActivity extends PassFailButtons.Activity
+ implements TextureView.SurfaceTextureListener {
+ private static final String TAG = ProjectionActivity.class.getSimpleName();
+ protected Intent mStartIntent;
+ protected TextureView mTextureView;
+ protected volatile Surface mSurface;
+ protected int mWidth;
+ protected int mHeight;
+ protected ProjectionPresentationType mType;
+
+ protected IProjectionService mService;
+ protected final ServiceConnection mConnection = new ServiceConnection() {
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ mService = IProjectionService.Stub.asInterface(binder);
+ new Handler().post(new Runnable() {
+
+ @Override
+ public void run() {
+ Log.i(TAG, "onServiceConnected thread " + Thread.currentThread());
+ DisplayMetrics metrics = ProjectionActivity.this.getResources().getDisplayMetrics();
+ try {
+ mService.startRendering(mSurface, mWidth, mHeight, metrics.densityDpi, mType.ordinal());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to execute startRendering", e);
+ }
+ }
+
+ });
+
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ mService = null;
+ }
+
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ mStartIntent = new Intent(this, ProjectionService.class);
+
+ }
+
+ protected View setContentViewAndInfoResources(int layoutId, int titleId, int infoId) {
+ View view = getLayoutInflater().inflate(layoutId, null);
+ setContentView(view);
+
+ mTextureView = (TextureView) view.findViewById(R.id.texture_view);
+ mTextureView.setSurfaceTextureListener(this);
+ mTextureView.setOnTouchListener(new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ if (mService != null) {
+ try {
+ mService.onTouchEvent(event);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to execute onTouchEvent", e);
+ }
+ }
+ return true;
+ }
+
+ });
+
+ setInfoResources(titleId, infoId, -1);
+ setPassFailButtonClickListeners();
+ return view;
+ }
+
+ @Override
+ public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
+ Log.i(TAG, "onSurfaceTextureAvailable " + "w: " + width + " h: " + height);
+ mSurface = new Surface(surface);
+ mWidth = width;
+ mHeight = height;
+ if (mService == null) {
+ bindService(mStartIntent, mConnection,
+ Context.BIND_AUTO_CREATE);
+ }
+
+ }
+
+ @Override
+ public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
+ Log.i(TAG, "onSurfaceTextureDestroyed");
+ if (mService != null) {
+ try {
+ mService.stopRendering();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to execute stopRendering", e);
+ }
+ }
+ mSurface.release();
+ mSurface = null;
+
+ unbindService(mConnection);
+ return true;
+ }
+
+ @Override
+ public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
+ Log.i(TAG, "onSurfaceTextureSizeChanged " + surface.toString() + "w: " + width + " h: "
+ + height);
+ }
+
+ @Override
+ public void onSurfaceTextureUpdated(SurfaceTexture surface) {
+ Log.i(TAG, "onSurfaceTextureUpdated " + surface.toString());
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java
new file mode 100644
index 0000000..28f5d46
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionPresentationType.java
@@ -0,0 +1,25 @@
+/*
+ * 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.projection;
+
+public enum ProjectionPresentationType {
+ TUMBLING_CUBES,
+ BASIC_WIDGETS,
+ MULTI_TOUCH,
+ SCROLLING_LIST,
+ VIDEO_PLAYBACK
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java
new file mode 100644
index 0000000..bfe5a30
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionService.java
@@ -0,0 +1,147 @@
+/*
+ * 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.projection;
+
+import android.app.Service;
+import android.content.Intent;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.VirtualDisplay;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.Surface;
+
+import com.android.cts.verifier.projection.cube.CubePresentation;
+import com.android.cts.verifier.projection.list.ListPresentation;
+import com.android.cts.verifier.projection.touch.TouchPresentation;
+import com.android.cts.verifier.projection.video.VideoPresentation;
+import com.android.cts.verifier.projection.widgets.WidgetPresentation;
+
+/**
+ * Service to handle rendering of views on a virtual display and to forward input events to the
+ * display
+ */
+public class ProjectionService extends Service {
+ private final String TAG = ProjectionService.class.getSimpleName();
+ private final String DISPLAY_NAME = "CtsVerifier Virtual Display";
+
+ private Handler mUIHandler;
+
+ private ProjectedPresentation createPresentation(int typeOrdinal) {
+ ProjectionPresentationType type = ProjectionPresentationType.values()[typeOrdinal];
+ switch (type) {
+ case TUMBLING_CUBES:
+ return new CubePresentation(ProjectionService.this, mDisplay.getDisplay());
+
+ case BASIC_WIDGETS:
+ return new WidgetPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+ case SCROLLING_LIST:
+ return new ListPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+ case VIDEO_PLAYBACK:
+ return new VideoPresentation(ProjectionService.this, mDisplay.getDisplay());
+
+ case MULTI_TOUCH:
+ return new TouchPresentation(ProjectionService.this, mDisplay.getDisplay());
+ }
+
+ return null;
+ }
+
+ private class ProjectionServiceBinder extends IProjectionService.Stub {
+ @Override
+ public void startRendering(final Surface surface, final int width, final int height,
+ final int density,
+ final int viewType) throws RemoteException {
+ mUIHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ DisplayManager manager = (DisplayManager) getSystemService(DISPLAY_SERVICE);
+ Log.i(TAG, "Surface " + surface.toString() + ": "
+ + Boolean.toString(surface.isValid()));
+ mDisplay = manager.createVirtualDisplay(DISPLAY_NAME, width, height, density,
+ surface, 0);
+ mPresentation = createPresentation(viewType);
+ if (mPresentation == null) {
+ return;
+ }
+
+ mPresentation.show();
+ }
+ });
+ }
+
+ @Override
+ public void stopRendering() throws RemoteException {
+ mUIHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mPresentation != null) {
+ mPresentation.dismiss();
+ mPresentation = null;
+ }
+ }
+
+ });
+ }
+
+ @Override
+ public void onTouchEvent(final MotionEvent event) throws RemoteException {
+ mUIHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mPresentation != null) {
+ mPresentation.injectTouchEvent(event);
+ }
+ }
+
+ });
+ }
+
+ @Override
+ public void onKeyEvent(final KeyEvent event) throws RemoteException {
+ mUIHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ if (mPresentation != null) {
+ mPresentation.injectKeyEvent(event);
+ }
+ }
+
+ });
+ }
+ }
+
+ private final IBinder mBinder = new ProjectionServiceBinder();
+ private VirtualDisplay mDisplay;
+ private ProjectedPresentation mPresentation;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ Log.i(TAG, "onBind");
+ mUIHandler = new Handler(Looper.getMainLooper());
+ return mBinder;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java
new file mode 100644
index 0000000..0b521fb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/Cube.java
@@ -0,0 +1,100 @@
+/*
+ * 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.projection.cube;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * A vertex shaded cube.
+ */
+class Cube
+{
+ public Cube()
+ {
+ int one = 0x10000;
+ int vertices[] = {
+ -one, -one, -one,
+ one, -one, -one,
+ one, one, -one,
+ -one, one, -one,
+ -one, -one, one,
+ one, -one, one,
+ one, one, one,
+ -one, one, one,
+ };
+
+ int colors[] = {
+ 0, 0, 0, one,
+ one, 0, 0, one,
+ one, one, 0, one,
+ 0, one, 0, one,
+ 0, 0, one, one,
+ one, 0, one, one,
+ one, one, one, one,
+ 0, one, one, one,
+ };
+
+ byte indices[] = {
+ 0, 4, 5, 0, 5, 1,
+ 1, 5, 6, 1, 6, 2,
+ 2, 6, 7, 2, 7, 3,
+ 3, 7, 4, 3, 4, 0,
+ 4, 7, 6, 4, 6, 5,
+ 3, 0, 1, 3, 1, 2
+ };
+
+ // Buffers to be passed to gl*Pointer() functions
+ // must be direct, i.e., they must be placed on the
+ // native heap where the garbage collector cannot
+ // move them.
+ //
+ // Buffers with multi-byte datatypes (e.g., short, int, float)
+ // must have their byte order set to native order
+
+ ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);
+ vbb.order(ByteOrder.nativeOrder());
+ mVertexBuffer = vbb.asIntBuffer();
+ mVertexBuffer.put(vertices);
+ mVertexBuffer.position(0);
+
+ ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);
+ cbb.order(ByteOrder.nativeOrder());
+ mColorBuffer = cbb.asIntBuffer();
+ mColorBuffer.put(colors);
+ mColorBuffer.position(0);
+
+ mIndexBuffer = ByteBuffer.allocateDirect(indices.length);
+ mIndexBuffer.put(indices);
+ mIndexBuffer.position(0);
+ }
+
+ public void draw(GL10 gl)
+ {
+ gl.glFrontFace(GL10.GL_CW);
+ gl.glVertexPointer(3, GL10.GL_FIXED, 0, mVertexBuffer);
+ gl.glColorPointer(4, GL10.GL_FIXED, 0, mColorBuffer);
+ gl.glDrawElements(GL10.GL_TRIANGLES, 36, GL10.GL_UNSIGNED_BYTE, mIndexBuffer);
+ }
+
+ private IntBuffer mVertexBuffer;
+ private IntBuffer mColorBuffer;
+ private ByteBuffer mIndexBuffer;
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java
new file mode 100644
index 0000000..bf7825b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubePresentation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.cube;
+
+import android.content.Context;
+import android.opengl.GLSurfaceView;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.View.OnTouchListener;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+
+
+/**
+ * Render tumbling cubes
+ *
+ */
+public class CubePresentation extends ProjectedPresentation {
+ public CubePresentation(Context context, Display display) {
+ super(context, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.pca_cubes, null);
+ setContentView(view);
+
+ GLSurfaceView cubeView = (GLSurfaceView) view.findViewById(R.id.cube_view);
+ final CubeRenderer renderer = new CubeRenderer(true);
+ cubeView.setRenderer(renderer);
+
+ cubeView.setOnTouchListener(new OnTouchListener() {
+
+ @Override
+ public boolean onTouch(View view, MotionEvent event) {
+ renderer.explode();
+ return true;
+ }
+ });
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java
new file mode 100644
index 0000000..9205fec
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/CubeRenderer.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.cube;
+
+import android.opengl.GLSurfaceView;
+
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.opengles.GL10;
+
+/**
+ * Render a pair of tumbling cubes.
+ */
+public class CubeRenderer implements GLSurfaceView.Renderer {
+
+ private final boolean mTranslucentBackground;
+
+ private final Cube mCube;
+ private float mAngle;
+ private float mScale = 1.0f;
+ private boolean mExploding;
+
+ public CubeRenderer(boolean useTranslucentBackground) {
+ mTranslucentBackground = useTranslucentBackground;
+ mCube = new Cube();
+ }
+
+ public void explode() {
+ mExploding = true;
+ }
+
+ @Override
+ public void onDrawFrame(GL10 gl) {
+ /*
+ * Usually, the first thing one might want to do is to clear
+ * the screen. The most efficient way of doing this is to use
+ * glClear().
+ */
+
+ gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
+
+ /*
+ * Now we're ready to draw some 3D objects
+ */
+
+ gl.glMatrixMode(GL10.GL_MODELVIEW);
+ gl.glLoadIdentity();
+ gl.glTranslatef(0, 0, -3.0f);
+ gl.glRotatef(mAngle, 0, 1, 0);
+ gl.glRotatef(mAngle*0.25f, 1, 0, 0);
+
+ gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
+ gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
+
+ gl.glScalef(mScale, mScale, mScale);
+ mCube.draw(gl);
+
+ gl.glRotatef(mAngle*2.0f, 0, 1, 1);
+ gl.glTranslatef(0.5f, 0.5f, 0.5f);
+
+ mCube.draw(gl);
+
+ mAngle += 1.2f;
+
+ if (mExploding) {
+ mScale *= 1.02f;
+ if (mScale > 4.0f) {
+ mScale = 1.0f;
+ mExploding = false;
+ }
+ }
+ }
+
+ @Override
+ public void onSurfaceChanged(GL10 gl, int width, int height) {
+ gl.glViewport(0, 0, width, height);
+
+ /*
+ * Set our projection matrix. This doesn't have to be done
+ * each time we draw, but usually a new projection needs to
+ * be set when the viewport is resized.
+ */
+
+ float ratio = (float) width / height;
+ gl.glMatrixMode(GL10.GL_PROJECTION);
+ gl.glLoadIdentity();
+ gl.glFrustumf(-ratio, ratio, -1, 1, 1, 10);
+ }
+
+ @Override
+ public void onSurfaceCreated(GL10 gl, EGLConfig config) {
+ /*
+ * By default, OpenGL enables features that improve quality
+ * but reduce performance. One might want to tweak that
+ * especially on software renderer.
+ */
+ gl.glDisable(GL10.GL_DITHER);
+
+ /*
+ * Some one-time OpenGL initialization can be made here
+ * probably based on features of this particular context
+ */
+ gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT,
+ GL10.GL_FASTEST);
+
+ if (mTranslucentBackground) {
+ gl.glClearColor(0,0,0,0);
+ } else {
+ gl.glClearColor(1,1,1,1);
+ }
+ gl.glEnable(GL10.GL_CULL_FACE);
+ gl.glShadeModel(GL10.GL_SMOOTH);
+ gl.glEnable(GL10.GL_DEPTH_TEST);
+ }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java
new file mode 100644
index 0000000..0ef9a30
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/cube/ProjectionCubeActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.cube;
+
+import com.android.cts.verifier.R;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionCubeActivity extends ProjectionActivity {
+ private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentViewAndInfoResources(R.layout.pa_main, R.string.pca_test, R.string.pca_info);
+ mType = ProjectionPresentationType.TUMBLING_CUBES;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
new file mode 100644
index 0000000..dad4945
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.list;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.View;
+import android.widget.ArrayAdapter;
+import android.widget.ListView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+import java.util.ArrayList;
+
+/**
+ * Render a list view that scrolls
+ */
+public class ListPresentation extends ProjectedPresentation {
+ private ArrayList<String> mItemList = new ArrayList<String>();
+ private static final int NUM_ITEMS = 50; // Enough to make the list scroll
+
+ /**
+ * @param outerContext
+ * @param display
+ */
+ public ListPresentation(Context outerContext, Display display) {
+ super(outerContext, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.pla_list, null);
+ setContentView(view);
+
+ for (int i = 0; i < NUM_ITEMS; ++i) {
+ mItemList.add("Item #" + i);
+ }
+
+ ListView listView = (ListView) view.findViewById(R.id.pla_list);
+
+ listView.setAdapter(new ArrayAdapter<String>(getContext(),
+ android.R.layout.simple_list_item_1, mItemList));
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java
new file mode 100644
index 0000000..c166320
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ProjectionListActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.list;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+import com.android.cts.verifier.projection.cube.ProjectionCubeActivity;
+
+public class ProjectionListActivity extends ProjectionActivity {
+ private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentViewAndInfoResources(R.layout.pa_main, R.string.pla_test, R.string.pla_info);
+ mType = ProjectionPresentationType.SCROLLING_LIST;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java
new file mode 100644
index 0000000..ed1d881
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/ProjectionTouchActivity.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.touch;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+import com.android.cts.verifier.projection.cube.ProjectionCubeActivity;
+
+public class ProjectionTouchActivity extends ProjectionActivity {
+ private static final String TAG = ProjectionCubeActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentViewAndInfoResources(R.layout.pa_main, R.string.pta_test, R.string.pta_info);
+ mType = ProjectionPresentationType.MULTI_TOUCH;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java
new file mode 100644
index 0000000..c88fd79
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPointView.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.touch;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Point;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+
+import java.util.ArrayList;
+import java.util.List;
+/*
+ * Simple view that draws a circle where a touch is registered
+ */
+public class TouchPointView extends View {
+ @SuppressWarnings("unused")
+ private static final String TAG = TouchPointView.class.getSimpleName();
+
+ private final int[] mColors = {
+ Color.RED,
+ Color.GREEN,
+ Color.BLUE,
+ Color.YELLOW,
+ Color.MAGENTA,
+ Color.BLACK,
+ Color.DKGRAY
+ };
+ List<Finger> mFingers;
+ Paint mPaint;
+
+ public TouchPointView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public TouchPointView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ mFingers = new ArrayList<Finger>();
+
+ mPaint = new Paint();
+ mPaint.setStyle(Paint.Style.FILL);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mFingers.clear();
+ if (event.getActionMasked() == MotionEvent.ACTION_UP) {
+ invalidate();
+ return true;
+ }
+ for (int i = 0; i < event.getPointerCount(); i++) {
+ int pointerId = event.getPointerId(i);
+ int pointerIndex = event.findPointerIndex(pointerId);
+ Finger finger = new Finger();
+ finger.point = new Point((int)event.getX(pointerIndex), (int)event.getY(pointerIndex));
+ finger.pointerId = pointerId;
+
+ mFingers.add(finger);
+ }
+ invalidate();
+ return true;
+ }
+
+ @Override
+ public void onDraw(Canvas canvas) {
+ int radius = canvas.getWidth() / 20;
+ for (int i = 0; i < mFingers.size(); i++) {
+ Finger finger = mFingers.get(i);
+ Point point = finger.point;
+ int color = mColors[finger.pointerId % mColors.length];
+ mPaint.setColor(color);
+ canvas.drawCircle(point.x, point.y, radius, mPaint);
+ }
+ }
+
+ private class Finger {
+ public Point point;
+ public int pointerId;
+ }
+}
+
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPresentation.java
new file mode 100644
index 0000000..84834b5
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/touch/TouchPresentation.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.touch;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Display;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+/**
+ * Draw the TouchPointview to see if multitouch works in projected presentations
+ */
+public class TouchPresentation extends ProjectedPresentation {
+
+ /**
+ * @param outerContext
+ * @param display
+ */
+ public TouchPresentation(Context outerContext, Display display) {
+ super(outerContext, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pta_touch);
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java
new file mode 100644
index 0000000..7a32b27
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/ProjectionVideoActivity.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.video;
+
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionVideoActivity extends ProjectionActivity {
+ private static final String TAG = ProjectionVideoActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
+ setContentViewAndInfoResources(R.layout.pa_main, R.string.pva_test, R.string.pva_info);
+ mType = ProjectionPresentationType.VIDEO_PLAYBACK;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java
new file mode 100644
index 0000000..4275cb8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/video/VideoPresentation.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.video;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.Display;
+import android.view.View;
+import android.widget.VideoView;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+
+/**
+ * Play a test video that determines if the video and audio are in sync in projected presentations
+ */
+public class VideoPresentation extends ProjectedPresentation {
+
+ /**
+ * @param outerContext
+ * @param display
+ */
+ public VideoPresentation(Context outerContext, Display display) {
+ super(outerContext, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ View view = getLayoutInflater().inflate(R.layout.pva_video, null);
+ setContentView(view);
+ VideoView videoView = (VideoView) view.findViewById(R.id.video_view);
+ videoView.setOnPreparedListener(new OnPreparedListener() {
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ mp.setLooping(true);
+ }
+ });
+ String packageName = getContext().getPackageName();
+ Uri uri = Uri.parse("android.resource://" + packageName + "/" + R.raw.test_video);
+ videoView.setVideoURI(uri);
+ videoView.start();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
new file mode 100644
index 0000000..9b862de
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.widgets;
+
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectionActivity;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+public class ProjectionWidgetActivity extends ProjectionActivity {
+ private static final String TAG = ProjectionWidgetActivity.class.getSimpleName();
+
+ private class InjectDPadClickListener implements OnClickListener {
+ private int mKeyCode;
+
+ InjectDPadClickListener(int keyCode) {
+ mKeyCode = keyCode;
+ }
+
+ @Override
+ public void onClick(View view) {
+ if (mService != null) {
+ try {
+ mService.onKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, mKeyCode));
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error executing onKeyEvent", e);
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = setContentViewAndInfoResources(R.layout.pwa_widgets, R.string.pwa_test,
+ R.string.pwa_info);
+
+ mType = ProjectionPresentationType.BASIC_WIDGETS;
+
+ Button button;
+ {
+ button = (Button) view.findViewById(R.id.up_button);
+ button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_UP));
+ }
+ {
+ button = (Button) view.findViewById(R.id.down_button);
+ button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_DOWN));
+ }
+ {
+ button = (Button) view.findViewById(R.id.right_button);
+ button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_RIGHT));
+ }
+ {
+ button = (Button) view.findViewById(R.id.left_button);
+ button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_LEFT));
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java
new file mode 100644
index 0000000..8af2757
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/WidgetPresentation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.projection.widgets;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.view.Display;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.projection.ProjectedPresentation;
+import com.android.cts.verifier.projection.ProjectionPresentationType;
+
+/**
+ * Check if widgets display and that key focus works in projected mode
+ */
+public class WidgetPresentation extends ProjectedPresentation {
+
+ /**
+ * @param outerContext
+ * @param display
+ */
+ public WidgetPresentation(Context outerContext, Display display) {
+ super(outerContext, display);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.pwa_buttons);
+ }
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index 5a0af28..edac3c5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -26,30 +26,43 @@
/**
* Semi-automated test that focuses on characteristics associated with Accelerometer measurements.
*/
-public class AccelerometerMeasurementTestActivity extends BaseSensorSemiAutomatedTestActivity {
- @Override
- protected void onRun() throws Throwable {
- verifyMeasurements(
+public class AccelerometerMeasurementTestActivity extends BaseSensorTestActivity {
+ public AccelerometerMeasurementTestActivity() {
+ super(AccelerometerMeasurementTestActivity.class);
+ }
+
+ public String testFaceUp() throws Throwable {
+ return verifyMeasurements(
"Place the device in a flat surface with the screen facing the ceiling",
0, 0, SensorManager.STANDARD_GRAVITY);
+ }
- delayedVerifyMeasurements(
+ public String testFaceDown() throws Throwable {
+ return delayedVerifyMeasurements(
"Press 'Next' and place the device in a flat surface with the screen facing it",
0, 0, -SensorManager.STANDARD_GRAVITY);
+ }
- verifyMeasurements(
+ public String testRightSide() throws Throwable {
+ return verifyMeasurements(
"Place the device in a flat surface resting vertically on its right side",
-SensorManager.STANDARD_GRAVITY, 0, 0);
+ }
- verifyMeasurements(
+ public String testLeftSide() throws Throwable {
+ return verifyMeasurements(
"Place the device in a flat surface resting vertically on its left side",
SensorManager.STANDARD_GRAVITY, 0, 0);
+ }
- verifyMeasurements(
+ public String testTopSide() throws Throwable {
+ return verifyMeasurements(
"Place the device in a flat surface resting vertically on its top side",
0, -SensorManager.STANDARD_GRAVITY, 0);
+ }
- verifyMeasurements(
+ public String testBottomSide() throws Throwable {
+ return verifyMeasurements(
"Place the device in a flat surface resting vertically on its bottom side",
0, SensorManager.STANDARD_GRAVITY, 0);
}
@@ -73,7 +86,7 @@
* - the values representing the expectation of the test
* - the mean of values sampled from the sensor
*/
- private void verifyMeasurements(float ... expectations) throws Throwable {
+ private String verifyMeasurements(float ... expectations) throws Throwable {
Thread.sleep(500 /*ms*/);
TestSensorOperation verifyMeasurements = new TestSensorOperation(
getApplicationContext(),
@@ -85,10 +98,10 @@
expectations,
new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
verifyMeasurements.execute();
- logSuccess();
+ return null;
}
- private void delayedVerifyMeasurements(
+ private String delayedVerifyMeasurements(
String message,
float ... expectations) throws Throwable {
appendText(String.format("\n%s.", message));
@@ -97,17 +110,17 @@
Thread.sleep(TimeUnit.MILLISECONDS.convert(10, TimeUnit.SECONDS));
try {
- verifyMeasurements(expectations);
+ return verifyMeasurements(expectations);
} finally {
playSound();
}
}
- private void verifyMeasurements(String message, float ... expectations) throws Throwable {
+ private String verifyMeasurements(String message, float ... expectations) throws Throwable {
appendText(String.format("\n%s.", message));
appendText("Press 'Next' when ready and keep the device steady.");
waitForUser();
- verifyMeasurements(expectations);
+ return verifyMeasurements(expectations);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
index c634e16..a550cde 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorSemiAutomatedTestActivity.java
@@ -16,64 +16,20 @@
package com.android.cts.verifier.sensors;
-import android.app.Activity;
import android.graphics.Color;
import android.hardware.cts.helpers.SensorNotSupportedException;
-import android.media.MediaPlayer;
-import android.os.Bundle;
-import android.provider.Settings;
-import android.text.Spannable;
-import android.text.SpannableStringBuilder;
-import android.text.method.ScrollingMovementMethod;
-import android.text.style.ForegroundColorSpan;
-import android.view.View;
-import android.widget.TextView;
-
-import com.android.cts.verifier.R;
-import com.android.cts.verifier.TestResult;
-
-import java.security.InvalidParameterException;
-import java.util.concurrent.Semaphore;
/**
* Base class to author Sensor semi-automated test cases.
* These tests can only wait for operators to notify at some intervals, but the test needs to be
* autonomous to verify the data collected.
+ *
+ * @deprecated use {@link BaseSensorTestActivity} instead.
*/
-public abstract class BaseSensorSemiAutomatedTestActivity
- extends Activity
- implements View.OnClickListener, Runnable {
- protected final String LOG_TAG = "TestRunner";
-
- private final Semaphore mSemaphore = new Semaphore(0);
-
- private TextView mLogView;
- private View mNextView;
- private Thread mWorkerThread;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.snsr_semi_auto_test);
- }
-
- @Override
- public void onStart() {
- super.onStart();
-
- mLogView = (TextView) this.findViewById(R.id.log_text);
- mNextView = this.findViewById(R.id.next_button);
- mNextView.setOnClickListener(this);
- mLogView.setMovementMethod(new ScrollingMovementMethod());
-
- updateButton(false /*enabled*/);
- mWorkerThread = new Thread(this);
- mWorkerThread.start();
- }
-
- @Override
- public void onClick(View target) {
- mSemaphore.release();
+@Deprecated
+public abstract class BaseSensorSemiAutomatedTestActivity extends BaseSensorTestActivity {
+ public BaseSensorSemiAutomatedTestActivity() {
+ super(BaseSensorSemiAutomatedTestActivity.class);
}
@Override
@@ -90,7 +46,7 @@
testResult = SensorTestResult.FAIL;
message = e.getMessage();
}
- setTestResult(testResult, message);
+ setTestResult(getTestId(), testResult, message);
appendText("\nTest completed. Press 'Next' to finish.\n");
waitForUser();
finish();
@@ -106,125 +62,11 @@
*/
protected abstract void onRun() throws Throwable;
- /**
- * Helper methods for subclasses to interact with the UI and the operator.
- */
- protected void appendText(String text, int textColor) {
- this.runOnUiThread(new TextAppender(mLogView, text, textColor));
- }
-
- protected void appendText(String text) {
- this.runOnUiThread(new TextAppender(mLogView, text));
- }
-
- protected void clearText() {
- this.runOnUiThread(new Runnable() {
- @Override
- public void run() {
- mLogView.setText("");
- }
- });
- }
-
- protected void updateButton(boolean enabled) {
- this.runOnUiThread(new ButtonEnabler(this.mNextView, enabled));
- }
-
- protected void waitForUser() {
- updateButton(true);
- try {
- mSemaphore.acquire();
- } catch(InterruptedException e) {}
- updateButton(false);
- }
-
protected void logSuccess() {
- appendText("PASS", Color.GREEN);
+ appendText("SUCCESS", Color.GREEN);
}
- protected void playSound() {
- MediaPlayer player = MediaPlayer.create(this, Settings.System.DEFAULT_NOTIFICATION_URI);
- player.start();
- try {
- Thread.sleep(500);
- } catch(InterruptedException e) {
- } finally {
- player.stop();
- }
- }
-
- /**
- * Private methods.
- */
private String getTestId() {
return this.getClass().getName();
}
-
- private void setTestResult(SensorTestResult testResult, String message) {
- int textColor;
- switch(testResult) {
- case SKIPPED:
- textColor = Color.YELLOW;
- TestResult.setPassedResult(this, this.getTestId(), message);
- break;
- case PASS:
- textColor = Color.GREEN;
- TestResult.setPassedResult(this, this.getTestId(), message);
- break;
- case FAIL:
- textColor = Color.RED;
- TestResult.setFailedResult(this, this.getTestId(), message);
- break;
- default:
- throw new InvalidParameterException("Unrecognized testResult.");
- }
- appendText(message, textColor);
- }
-
- private enum SensorTestResult {
- SKIPPED,
- PASS,
- FAIL
- }
-
- private class TextAppender implements Runnable {
- private final TextView mTextView;
- private final SpannableStringBuilder mMessageBuilder;
-
- public TextAppender(TextView textView, String message, int textColor) {
- mTextView = textView;
- mMessageBuilder = new SpannableStringBuilder(message + "\n");
-
- ForegroundColorSpan colorSpan = new ForegroundColorSpan(textColor);
- mMessageBuilder.setSpan(
- colorSpan,
- 0 /*start*/,
- message.length(),
- Spannable.SPAN_INCLUSIVE_INCLUSIVE);
- }
-
- public TextAppender(TextView textView, String message) {
- this(textView, message, textView.getCurrentTextColor());
- }
-
- @Override
- public void run() {
- mTextView.append(mMessageBuilder);
- }
- }
-
- private class ButtonEnabler implements Runnable {
- private final View mButtonView;
- private final boolean mButtonEnabled;
-
- public ButtonEnabler(View buttonView, boolean buttonEnabled) {
- mButtonView = buttonView;
- mButtonEnabled = buttonEnabled;
- }
-
- @Override
- public void run() {
- mButtonView.setEnabled(mButtonEnabled);
- }
- }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
new file mode 100644
index 0000000..01616e1
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BaseSensorTestActivity.java
@@ -0,0 +1,415 @@
+/*
+ * 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.sensors;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+import android.annotation.NonNull;
+import android.app.Activity;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.graphics.Color;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.media.MediaPlayer;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.TextUtils;
+import android.text.method.ScrollingMovementMethod;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.view.View;
+import android.widget.TextView;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.security.InvalidParameterException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Base class to author Sensor test cases. It provides access to the following flow:
+ * Activity set up
+ * for test unit : all test units
+ * execute test unit
+ * Activity clean up
+ *
+ * Each test unit can wait for operators to notify at some intervals, but the test needs to be
+ * autonomous to verify the data collected.
+ */
+public abstract class BaseSensorTestActivity
+ extends Activity
+ implements View.OnClickListener, Runnable {
+ protected final String LOG_TAG = "TestRunner";
+
+ protected final Class mTestClass;
+
+ private final Semaphore mSemaphore = new Semaphore(0);
+
+ private TextView mLogView;
+ private View mNextView;
+ private Thread mWorkerThread;
+ private CountDownLatch mCountDownLatch;
+
+ protected BaseSensorTestActivity(Class testClass) {
+ mTestClass = testClass;
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.snsr_semi_auto_test);
+
+ mLogView = (TextView) this.findViewById(R.id.log_text);
+ mNextView = this.findViewById(R.id.next_button);
+ mNextView.setOnClickListener(this);
+ mLogView.setMovementMethod(new ScrollingMovementMethod());
+
+ updateButton(false /*enabled*/);
+ mWorkerThread = new Thread(this);
+ mWorkerThread.start();
+ }
+
+ @Override
+ public void onClick(View target) {
+ mSemaphore.release();
+ }
+
+ @Override
+ public void run() {
+ String testClassName = mTestClass.getName();
+
+ try {
+ activitySetUp();
+ } catch (Throwable e) {
+ setTestResult(testClassName, SensorTestResult.SKIPPED, e.getMessage());
+ return;
+ }
+
+ // TODO: it might be necessary to implement fall through so passed tests do not need to
+ // be re-executed
+ int testPassedCounter = 0;
+ int testSkippedCounter = 0;
+ int testFailedCounter = 0;
+ for (Method testMethod : findTestMethods()) {
+ String testName = String.format("%s.%s", testClassName, testMethod.getName());
+ try {
+ appendText("\nExecuting test case '" + testName + "'...");
+ String testDetails = (String) testMethod.invoke(this);
+ setTestResult(testName, SensorTestResult.PASS, testDetails);
+ ++testPassedCounter;
+ } catch (InvocationTargetException e) {
+ // get the inner exception, because we use reflection APIs to execute the test
+ Throwable cause = e.getCause();
+ SensorTestResult testResult;
+ if (cause instanceof SensorNotSupportedException) {
+ testResult = SensorTestResult.SKIPPED;
+ ++testSkippedCounter;
+ } else {
+ testResult = SensorTestResult.FAIL;
+ ++testFailedCounter;
+ }
+ setTestResult(testName, testResult, cause.getMessage());
+ } catch (Throwable e) {
+ setTestResult(testName, SensorTestResult.FAIL, e.getMessage());
+ ++testFailedCounter;
+ }
+ }
+ setOverallTestResult(
+ testClassName,
+ testPassedCounter,
+ testSkippedCounter,
+ testFailedCounter);
+
+ try {
+ activityCleanUp();
+ } catch (Throwable e) {
+ appendText("An error occurred on Activity CleanUp.");
+ appendText(e.getLocalizedMessage(), Color.RED);
+ }
+
+ appendText("\nTest completed. Press 'Next' to finish.\n");
+ waitForUser();
+ finish();
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ mCountDownLatch.countDown();
+ }
+
+ protected enum SensorTestResult {
+ SKIPPED,
+ PASS,
+ FAIL
+ }
+
+ /**
+ * For use only by {@link BaseSensorSemiAutomatedTestActivity} and other base classes.
+ */
+ protected void setTestResult(String testId, SensorTestResult testResult, String testDetails) {
+ int textColor;
+ int logPriority;
+ String testResultString;
+ switch(testResult) {
+ case SKIPPED:
+ textColor = Color.YELLOW;
+ logPriority = Log.INFO;
+ testResultString = "SKIPPED";
+ TestResult.setPassedResult(this, testId, testDetails);
+ break;
+ case PASS:
+ textColor = Color.GREEN;
+ logPriority = Log.DEBUG;
+ testResultString = "PASS";
+ TestResult.setPassedResult(this, testId, testDetails);
+ break;
+ case FAIL:
+ textColor = Color.RED;
+ logPriority = Log.ERROR;
+ testResultString = "FAIL";
+ TestResult.setFailedResult(this, testId, testDetails);
+ break;
+ default:
+ throw new InvalidParameterException("Unrecognized testResult.");
+ }
+ if (TextUtils.isEmpty(testDetails)) {
+ testDetails = testResultString;
+ }
+ appendText(testDetails, textColor);
+ Log.println(logPriority, LOG_TAG, testDetails);
+ }
+
+ /**
+ * A general set up routine. It executes only once before the first test case.
+ *
+ * @throws Throwable An exception that denotes the failure of set up. No tests will be executed.
+ */
+ protected void activitySetUp() throws Throwable {}
+
+ /**
+ * A general clean up routine. It executes upon successful execution of {@link #activitySetUp()}
+ * and after all the test cases.
+ *
+ * @throws Throwable An exception that will be logged and ignored, for ease of implementation
+ * by subclasses.
+ */
+ protected void activityCleanUp() throws Throwable {}
+
+ protected void appendText(String text, int textColor) {
+ this.runOnUiThread(new TextAppender(mLogView, text, textColor));
+ }
+
+ protected void appendText(String text) {
+ this.runOnUiThread(new TextAppender(mLogView, text));
+ }
+
+ protected void clearText() {
+ this.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mLogView.setText("");
+ }
+ });
+ }
+
+ protected void updateButton(boolean enabled) {
+ this.runOnUiThread(new ButtonEnabler(this.mNextView, enabled));
+ }
+
+ protected void waitForUser() {
+ updateButton(true);
+ try {
+ mSemaphore.acquire();
+ } catch(InterruptedException e) {}
+ updateButton(false);
+ }
+
+ protected void playSound() {
+ MediaPlayer player = MediaPlayer.create(this, Settings.System.DEFAULT_NOTIFICATION_URI);
+ player.start();
+ try {
+ Thread.sleep(500);
+ } catch(InterruptedException e) {
+ } finally {
+ player.stop();
+ }
+ }
+
+ @NonNull
+ private List<Method> findTestMethods() {
+ ArrayList<Method> testMethods = new ArrayList<Method>();
+ for (Method method : mTestClass.getDeclaredMethods()) {
+ if (Modifier.isPublic(method.getModifiers())
+ && method.getParameterTypes().length == 0
+ && method.getName().startsWith("test")
+ && method.getReturnType().equals(String.class)) {
+ testMethods.add(method);
+ }
+ }
+ return testMethods;
+ }
+
+ private void setOverallTestResult(
+ String testClassName,
+ int testPassedCount,
+ int testSkippedCount,
+ int testFailedCount) {
+ SensorTestResult overallTestResult = SensorTestResult.PASS;
+ if (testFailedCount > 0) {
+ overallTestResult = SensorTestResult.FAIL;
+ } else if (testSkippedCount > 0 || testPassedCount == 0) {
+ overallTestResult = SensorTestResult.SKIPPED;
+ }
+
+ String testSummary = String.format(
+ "\n\nTestsPassed=%d, TestsSkipped=%d, TestFailed=%d",
+ testPassedCount,
+ testSkippedCount,
+ testFailedCount);
+ setTestResult(testClassName, overallTestResult, testSummary);
+ }
+
+ private class TextAppender implements Runnable {
+ private final TextView mTextView;
+ private final SpannableStringBuilder mMessageBuilder;
+
+ public TextAppender(TextView textView, String message, int textColor) {
+ mTextView = textView;
+ mMessageBuilder = new SpannableStringBuilder(message + "\n");
+
+ ForegroundColorSpan colorSpan = new ForegroundColorSpan(textColor);
+ mMessageBuilder.setSpan(
+ colorSpan,
+ 0 /*start*/,
+ message.length(),
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ }
+
+ public TextAppender(TextView textView, String message) {
+ this(textView, message, textView.getCurrentTextColor());
+ }
+
+ @Override
+ public void run() {
+ mTextView.append(mMessageBuilder);
+ }
+ }
+
+ private class ButtonEnabler implements Runnable {
+ private final View mButtonView;
+ private final boolean mButtonEnabled;
+
+ public ButtonEnabler(View buttonView, boolean buttonEnabled) {
+ mButtonView = buttonView;
+ mButtonEnabled = buttonEnabled;
+ }
+
+ @Override
+ public void run() {
+ mButtonView.setEnabled(mButtonEnabled);
+ }
+ }
+
+ // TODO: ideally we want to store the original state of each feature, and make sure that we can
+ // restore their values at the end of the test
+
+ protected void askToSetAirplaneMode() throws InterruptedException {
+ if (isAirplaneModeOn()) {
+ appendText("Airplane mode set.");
+ return;
+ }
+
+ appendText("You will be redirected to set 'Airplane Mode' ON, after doing so, go back to " +
+ "this App. Press Next to continue.\n");
+ waitForUser();
+ launchAndWaitForSubactivity(Settings.ACTION_WIRELESS_SETTINGS);
+
+ if (!isAirplaneModeOn()) {
+ throw new IllegalStateException("Airplane Mode is not set.");
+ }
+ }
+
+ protected void askToSetScreenOffTimeout(int timeoutInSec) throws InterruptedException {
+ long timeoutInMs = TimeUnit.SECONDS.toMillis(timeoutInSec);
+ if (isScreenOffTimeout(timeoutInMs)) {
+ appendText("Screen Off Timeout set to: " + timeoutInSec + " seconds.");
+ return;
+ }
+
+ appendText("You will be redirected to set 'Display Sleep' to " + timeoutInSec + " seconds" +
+ ", after doing so, go back to this App. Press Next to continue.\n");
+ waitForUser();
+ launchAndWaitForSubactivity(Settings.ACTION_DISPLAY_SETTINGS);
+
+ if (!isScreenOffTimeout(timeoutInMs)) {
+ throw new IllegalStateException("'Display Sleep' not set to " + timeoutInSec +
+ " seconds.");
+ }
+ }
+
+ private void launchAndWaitForSubactivity(String action) throws InterruptedException {
+ launchAndWaitForSubactivity(new Intent(action));
+ }
+
+ private void launchAndWaitForSubactivity(Intent intent) throws InterruptedException {
+ mCountDownLatch = new CountDownLatch(1);
+ startActivityForResult(intent, 0);
+ mCountDownLatch.await();
+ }
+
+ private boolean isAirplaneModeOn() {
+ ContentResolver contentResolver = getContentResolver();
+ int airplaneModeOn;
+ // Settings.System.AIRPLANE_MODE_ON is deprecated in API 17
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
+ try {
+ airplaneModeOn =
+ Settings.System.getInt(contentResolver, Settings.System.AIRPLANE_MODE_ON);
+ } catch (Settings.SettingNotFoundException e) {
+ airplaneModeOn = 0;
+ }
+ } else {
+ try {
+ airplaneModeOn =
+ Settings.Global.getInt(contentResolver, Settings.Global.AIRPLANE_MODE_ON);
+ } catch (Settings.SettingNotFoundException e) {
+ airplaneModeOn = 0;
+ }
+ }
+ return airplaneModeOn != 0;
+ }
+
+ private boolean isScreenOffTimeout(long expectedTimeoutInMs) {
+ ContentResolver contentResolver = getContentResolver();
+ long screenOffTimeoutInMs;
+ try {
+ screenOffTimeoutInMs =
+ Settings.System.getLong(contentResolver, Settings.System.SCREEN_OFF_TIMEOUT);
+ } catch(Settings.SettingNotFoundException e) {
+ screenOffTimeoutInMs = Integer.MAX_VALUE;
+ }
+ return screenOffTimeoutInMs <= expectedTimeoutInMs;
+ }
+}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
new file mode 100644
index 0000000..9258ba6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -0,0 +1,224 @@
+/*
+ * 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.sensors;
+
+import junit.framework.Assert;
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Vibrator;
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+class TriggerListener extends TriggerEventListener {
+ // how much difference between system time and event time considered to be
+ // acceptable [msec]
+ private final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS = 500;
+
+ // state used for internal recording of the event detection
+ private boolean mEventDetected = false;
+
+ public void onTrigger(TriggerEvent event) {
+ final long NANOS_PER_MS = 1000000L;
+
+ Assert.assertEquals("values should be of length 1 for significant motion event", 1,
+ event.values.length);
+ Assert.assertEquals("values[0] should be 1.0 for significant motion event", 1.0f,
+ event.values[0]);
+
+ // Check that timestamp is within MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS
+ // It might take time to determine Significant Motion, but then that
+ // event should be reported to the host in a timely fashion.
+ long timeReportedMillis = event.timestamp / NANOS_PER_MS;
+ long timeActualMillis = System.currentTimeMillis();
+ Assert.assertEquals("Incorrect time reported in the event",
+ timeReportedMillis, timeActualMillis, MAX_ACCEPTABLE_EVENT_TIME_DELAY_MILLIS);
+
+ // Verify event type is truly Significant Motion
+ Assert.assertEquals("Triggered event type is not Significant Motion",
+ event.sensor.getType(), Sensor.TYPE_SIGNIFICANT_MOTION);
+
+ // Event detected flag should be false if indeed only one event per
+ // request
+ Assert.assertFalse("Significant Motion sensor did not automatically "
+ + "disable itself from subsequent detection", mEventDetected);
+
+ // audible cue to indicate Significant Motion occurred
+ beep();
+ mEventDetected = true;
+ }
+
+ public boolean wasEventTriggered() {
+ return mEventDetected;
+ }
+
+ public void reset() {
+ mEventDetected = false;
+ }
+
+ private void beep() {
+ final ToneGenerator tg = new ToneGenerator(
+ AudioManager.STREAM_NOTIFICATION, 100);
+ tg.startTone(ToneGenerator.TONE_PROP_BEEP);
+ }
+}
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+public class SignificantMotionTestActivity extends BaseSensorSemiAutomatedTestActivity {
+ // minimum time for test to consider valid [msec]
+ private final int MIN_TEST_TIME_MILLIS = 20000;
+ private final int VIBRATE_DURATION_MILLIS = 10000;
+
+ private SensorManager mSensorManager;
+ private Sensor mSensorSignificantMotion;
+ private final TriggerListener mTriggeredListener = new TriggerListener();
+ private long mTestStartTimestamp;
+ private static int sNumPassedTests = 0;
+
+ @Override
+ protected void onRun() throws Throwable {
+ switch (sNumPassedTests) {
+ // avoid re-running passed tests, so purposely want fallthroughs here
+ case 0:
+ // use walking to change location and trigger significant motion
+ runTest("walk 15 steps for significant motion to be detected", true, false, false);
+ case 1:
+ runTest("walk another 15 steps to ensure significant motion "
+ + "is not reported after trigger cancelled", false, true, false);
+ case 2:
+ // use vibrator to ensure significant motion is not triggered
+ runTest("leave the device on a level surface", false, false, true);
+ case 3:
+ // use natural motion that does not change location to ensure
+ // significant motion is not triggered
+ runTest("hold the device in hand while performing natural "
+ + "hand movements", false, false, false);
+ case 4:
+ runTest("keep the device in pocket and move naturally while "
+ + "sitting in a chair", false, false, false);
+ default:
+ break;
+ }
+ }
+
+ private void vibrateDevice(int timeInMs) {
+ Vibrator vibrator = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);
+ vibrator.vibrate(timeInMs);
+ }
+
+ /**
+ * @param instructions Instruction to be shown to testers
+ * @param isMotionExpected Should the device detect significant motion event
+ * for this test?
+ * @param cancelEventNotification If TRUE, motion notifications will be
+ * requested first and request will be cancelled
+ * @param vibrate If TRUE, vibration will be concurrent with the test
+ * @throws Throwable
+ */
+ private void runTest(String instructions, final boolean isMotionExpected,
+ final boolean cancelEventNotification, final boolean vibrate) throws Throwable {
+
+ appendText("Click 'Next' and " + instructions);
+ waitForUser();
+
+ if (vibrate) {
+ vibrateDevice(VIBRATE_DURATION_MILLIS);
+ }
+
+ mTestStartTimestamp = System.currentTimeMillis();
+ startMeasurements(cancelEventNotification);
+
+ long testTime = System.currentTimeMillis() - mTestStartTimestamp;
+
+ while (!mTriggeredListener.wasEventTriggered()
+ && testTime < MIN_TEST_TIME_MILLIS) {
+ int timeWaitSec = Math
+ .round((MIN_TEST_TIME_MILLIS - testTime) / 1000);
+ clearText();
+ appendText("Current test: " + instructions);
+ appendText(
+ String.format("%d seconds for the test to complete", timeWaitSec),
+ Color.GRAY);
+
+ Thread.sleep(1000);
+ testTime = System.currentTimeMillis() - mTestStartTimestamp;
+ }
+ clearText();
+ appendText("Current test: " + instructions);
+ playSound();
+ verifyMeasurements(isMotionExpected);
+ sNumPassedTests++;
+ }
+
+ private void startMeasurements(boolean isCancelTriggerRequested) throws Throwable {
+ mTriggeredListener.reset();
+
+ mSensorManager.requestTriggerSensor(mTriggeredListener, mSensorSignificantMotion);
+
+ if (isCancelTriggerRequested) {
+ mSensorManager.cancelTriggerSensor(mTriggeredListener, mSensorSignificantMotion);
+ }
+ }
+
+ private void verifyMeasurements(boolean isMotionExpected) throws Throwable {
+ Assert.assertEquals("Significant motion event expected/detected mismatch: "
+ + isMotionExpected + " / " + mTriggeredListener.wasEventTriggered(),
+ isMotionExpected, mTriggeredListener.wasEventTriggered());
+ appendText("Significant motion event " + isMotionExpected + " as expected", Color.GRAY);
+ logSuccess();
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ mSensorManager = (SensorManager) getApplicationContext()
+ .getSystemService(Context.SENSOR_SERVICE);
+
+ mSensorSignificantMotion = mSensorManager
+ .getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
+
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (mSensorManager != null && mSensorSignificantMotion != null) {
+ mSensorManager.requestTriggerSensor(mTriggeredListener,
+ mSensorSignificantMotion);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (mSensorManager != null && mSensorSignificantMotion != null) {
+ mSensorManager.cancelTriggerSensor(mTriggeredListener,
+ mSensorSignificantMotion);
+ }
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
new file mode 100644
index 0000000..0dfe341
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/StepCounterTestActivity.java
@@ -0,0 +1,361 @@
+/*
+ * 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.sensors;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import junit.framework.Assert;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Color;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
+import android.os.Build;
+import android.os.SystemClock;
+import android.os.Vibrator;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.R;
+
+@TargetApi(Build.VERSION_CODES.KITKAT)
+public class StepCounterTestActivity extends BaseSensorSemiAutomatedTestActivity
+ implements SensorEventListener {
+
+ private SensorManager mSensorManager;
+ private Sensor mSensorStepCounter;
+ private Sensor mSensorStepDetector;
+
+ private int mStepsReported = 0; // number of steps as reported by user
+ private int mInitialStepCount = 0; // step counter at the start of test
+ private int mStepsDetected = 0; // number of steps during the test
+
+ private List<Long> mTimestampsUserReported = new ArrayList<Long>();
+ private List<Long> mTimestampsStepCounter = new ArrayList<Long>();
+ private List<Long> mTimestampsStepDetector = new ArrayList<Long>();
+
+ private final int MIN_TEST_TIME_MILLIS = 20000; // 20 sec
+ private final double NANOSECONDS_IN_SEC = 1e9;
+ private final int MIN_NUM_STEPS_PER_TEST = 10;
+ private final int MAX_STEP_DISCREPANCY = 4;
+ private final int MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS = 8;
+
+ private boolean mCheckForMotion = false;
+
+ private Sensor mSensorAcceleration;
+ private boolean mMoveDetected = false;
+ private static int sNumPassedTests = 0;
+
+ @Override
+ protected void onRun() throws Throwable {
+ View screen = (View) findViewById(R.id.log_text).getParent();
+ Assert.assertNotNull(screen);
+ screen.setOnClickListener(mClickListener);
+
+ switch (sNumPassedTests) {
+ // avoid re-running passed tests, so purposely want fallthroughs here
+ case 0:
+ runTest("walk at least " + MIN_NUM_STEPS_PER_TEST
+ + " steps and tap on the screen with each step",
+ MIN_NUM_STEPS_PER_TEST, MAX_STEP_DISCREPANCY, false, false);
+ case 1:
+ runTest("hold device still in hand", 0, MAX_STEP_DISCREPANCY, true, true);
+ case 2:
+ runTest("wave device in hand throughout test", 0, MAX_STEP_DISCREPANCY, false,
+ true);
+ default:
+ break;
+ }
+ }
+
+ private OnClickListener mClickListener = new OnClickListener() {
+ public void onClick(View v) {
+ if (!mCheckForMotion) {
+ SensorCtsHelper.beep(ToneGenerator.TONE_PROP_BEEP);
+ mTimestampsUserReported.add(SystemClock.elapsedRealtimeNanos());
+ mStepsReported = mTimestampsUserReported.size();
+ }
+ }
+ };
+
+ /**
+ * @param instructions Instruction to be shown to testers
+ * @param expectedSteps Number of steps expected in this test
+ * @param tolerance Number of steps the count can be off by and still pass
+ * @param vibrate If TRUE, vibration will be concurrent with the test
+ * @param onlyWarn If TRUE, only warn the user if the test fails. This
+ * option will be removed on a future release of CTS. TODO:
+ * remove this option
+ * @throws Throwable
+ */
+ static long[] sVibratePattern = {
+ 1000L, 500L, 1000L, 750L, 1000L, 500L, 1000L, 750L, 1000L, 1000L, 500L, 1000L,
+ 750L, 1000L, 500L, 1000L
+ };
+ private void runTest(String instructions, int expectedSteps, int tolerance, boolean vibrate,
+ boolean onlyWarn)
+ throws Throwable {
+
+ mTimestampsUserReported.clear();
+ mTimestampsStepCounter.clear();
+ mTimestampsStepDetector.clear();
+
+ mMoveDetected = false;
+ mCheckForMotion = true;
+
+ appendText("Click 'Next' and " + instructions);
+ waitForUser();
+
+ mInitialStepCount = 0;
+ mStepsDetected = 0;
+ mStepsReported = 0;
+ if (vibrate) {
+ vibrate(sVibratePattern);
+ }
+
+ mCheckForMotion = (expectedSteps == 0);
+ startMeasurements();
+
+ long testStartTime = System.currentTimeMillis();
+ long testTime = 0;
+
+ while (testTime < MIN_TEST_TIME_MILLIS) {
+ int timeWaitSec = Math.round((MIN_TEST_TIME_MILLIS - testTime) / 1000);
+ clearText();
+ appendText("Current test: " + instructions);
+ appendText(String.format("%d seconds left, %d steps detected, %d reported",
+ timeWaitSec, mStepsDetected, mStepsReported), Color.GRAY);
+ Thread.sleep(1000);
+ testTime = System.currentTimeMillis() - testStartTime;
+ }
+ clearText();
+ appendText("Current test: " + instructions);
+ verifyMeasurements(expectedSteps, tolerance, onlyWarn);
+ appendText(mERNWarning + "\n" + mSCWarning, Color.YELLOW);
+ mCheckForMotion = false;
+ sNumPassedTests++;
+ mERNWarning = "";
+ mSCWarning = "";
+ }
+
+ private void startMeasurements() throws Throwable {
+ mSensorStepCounter = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
+ if (mSensorStepCounter != null) {
+ mSensorManager.registerListener(this, mSensorStepCounter,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ appendText("Failed test, step counter sensor was not found", Color.RED);
+ Assert.fail("Step counter sensor was not found");
+ }
+
+ mSensorStepDetector = mSensorManager.getDefaultSensor(Sensor.TYPE_STEP_DETECTOR);
+ if (mSensorStepDetector != null) {
+ mSensorManager.registerListener(this, mSensorStepDetector,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ } else {
+ appendText("Failed test, step detector sensor was not found", Color.RED);
+ Assert.fail("Step detector sensor was not found");
+ }
+
+ mSensorAcceleration = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (mSensorAcceleration != null && mCheckForMotion) {
+ mSensorManager.registerListener(this, mSensorAcceleration,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+ private void verifyMeasurements(int stepsExpected, int tolerance, boolean onlyWarn)
+ throws Throwable {
+ if (mSensorManager != null) {
+ mSensorManager.unregisterListener(this);
+ }
+
+ Assert.assertFalse(String.format("You need to report at least %d steps", stepsExpected),
+ mStepsReported < stepsExpected);
+ double maxStepReportTime = compareTimestamps();
+ Assert.assertTrue(String.format("Step report time %f longer than %d seconds",
+ maxStepReportTime, MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS),
+ maxStepReportTime < MAX_TOLERANCE_STEP_TIME_LATENCY_SECONDS);
+
+ if (mCheckForMotion && !mMoveDetected) {
+ String message = "Movement is needed during this test";
+
+ warnOrAssert(onlyWarn, message);
+ }
+
+ if (Math.abs(mStepsDetected - mStepsReported) > tolerance) {
+ String message = String.format("Step count test: "
+ + "detected %d steps but %d were expected (to within %d steps)",
+ mStepsDetected, mStepsReported, tolerance);
+ warnOrAssert(onlyWarn, message);
+ }
+
+ appendText("PASS step count test", Color.GREEN);
+
+ if (Math.abs(mTimestampsStepDetector.size() - mStepsReported) > tolerance) {
+ String message = String.format("Step detector test: "
+ + "detected %d steps but %d were expected (to within %d steps)",
+ mTimestampsStepDetector.size(), mStepsReported, tolerance);
+ warnOrAssert(onlyWarn, message);
+ }
+
+ appendText("PASS step detection test", Color.GREEN);
+
+ logSuccess();
+ }
+
+ public final void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ private void warnOrAssert(boolean onlyWarn, String message) throws Throwable {
+ if (onlyWarn) {
+ appendText("WARNING: " + message, Color.YELLOW);
+ } else {
+ Assert.fail("FAILED " + message);
+ }
+ }
+
+ String mERNWarning = "";
+ String mSCWarning = "";
+
+ public long checkTimestamp(long eventTimestamp) {
+ long timestamp = SystemClock.elapsedRealtimeNanos();
+ if (Math.abs(timestamp - eventTimestamp) > MIN_TEST_TIME_MILLIS * 1e6) {
+ // elapsedRealtimeNanos will lead to test failure, warn for now
+ mERNWarning = "WARNING: elapsedRealtimeNanos is significantly different than "
+ + " sensor event timestamps. This should be rectified.";
+ } else {
+ timestamp = eventTimestamp;
+ }
+ return timestamp;
+ }
+
+ public void onStepCounterChanged(SensorEvent event) throws Throwable {
+ int steps = (int) event.values[0] - mInitialStepCount;
+
+ if (mInitialStepCount == 0) { // set the initial number of steps
+ mInitialStepCount = steps;
+ } else if (steps > 0) {
+ mTimestampsStepCounter.add(checkTimestamp(event.timestamp));
+ Assert.assertTrue(String.format("Step counter did not increase monotonically: "
+ + "%d changed to %d", mStepsDetected, steps), steps >= mStepsDetected);
+ mStepsDetected = steps;
+ } else {
+ Assert.fail("Step Counter change called when no steps reported");
+ }
+ }
+
+ public void onStepDetectorChanged(SensorEvent event) throws Throwable {
+ Assert.assertEquals("Incorrect value[0] in step detector event", event.values[0], 1.0f);
+ mTimestampsStepDetector.add(checkTimestamp(event.timestamp));
+ }
+
+ public final void onSensorChanged(SensorEvent event) {
+ int type = event.sensor.getType();
+ try {
+ if (type == Sensor.TYPE_STEP_COUNTER) {
+ onStepCounterChanged(event);
+ } else if (type == Sensor.TYPE_STEP_DETECTOR) {
+ onStepDetectorChanged(event);
+ } else if (type == Sensor.TYPE_ACCELEROMETER) {
+ mMoveDetected = SensorCtsHelper.checkMovementDetection(event);
+ } else {
+ Assert.fail("Sensor type " + type + " called when not registered for by this test");
+ }
+ } catch (Throwable ae) {
+ mSCWarning = ae.getMessage();
+ }
+ }
+
+ protected double compareTimestamps() {
+ double timeDeltaInSec;
+ double maxTimeDeltaInSec = 0;
+ StringBuilder reportLine = new StringBuilder();
+ reportLine.append("Reported Step: Step Detector / Counter Latency (sec)\n");
+ for (int eventCounter = 0; eventCounter < mStepsReported; eventCounter++) {
+ reportLine.append((eventCounter + 1) + ": ");
+
+ if (eventCounter < mTimestampsStepDetector.size()) {
+ timeDeltaInSec = (mTimestampsStepDetector.get(eventCounter)
+ - mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
+ maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
+ reportLine.append(String.format("%.2f", timeDeltaInSec));
+ } else {
+ reportLine.append("--");
+ }
+
+ reportLine.append(" / ");
+ if (eventCounter < mTimestampsStepCounter.size()) {
+ timeDeltaInSec = (mTimestampsStepCounter.get(eventCounter)
+ - mTimestampsUserReported.get(eventCounter)) / NANOSECONDS_IN_SEC;
+ maxTimeDeltaInSec = Math.max(maxTimeDeltaInSec, Math.abs(timeDeltaInSec));
+ reportLine.append(String.format("%.2f", timeDeltaInSec));
+ } else {
+ reportLine.append("--");
+ }
+ reportLine.append("\n");
+ }
+ appendText(reportLine.toString(), Color.GRAY);
+
+ return maxTimeDeltaInSec;
+ }
+
+ protected void vibrate(long[] pattern) {
+ Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
+ if(v==null) {
+ appendText("Cannot access vibrator for this test...continuing anyway", Color.YELLOW);
+ } else {
+ v.vibrate(pattern, -1);
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if (mSensorManager == null) {
+ mSensorManager = (SensorManager) getApplicationContext()
+ .getSystemService(Context.SENSOR_SERVICE);
+ }
+
+ if (mSensorStepCounter != null) {
+ mSensorManager.registerListener(this, mSensorStepCounter,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (mSensorStepDetector != null) {
+ mSensorManager.registerListener(this, mSensorStepDetector,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ if (mSensorAcceleration != null && mCheckForMotion) {
+ mSensorManager.registerListener(this, mSensorAcceleration,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (mSensorManager != null) {
+ mSensorManager.unregisterListener(this);
+ }
+ }
+}
diff --git a/suite/cts/deviceTests/simplecpu/src/com/android/cts/simplecpu/SimpleCpuTest.java b/suite/cts/deviceTests/simplecpu/src/com/android/cts/simplecpu/SimpleCpuTest.java
index 7485abe..ce0feac 100644
--- a/suite/cts/deviceTests/simplecpu/src/com/android/cts/simplecpu/SimpleCpuTest.java
+++ b/suite/cts/deviceTests/simplecpu/src/com/android/cts/simplecpu/SimpleCpuTest.java
@@ -76,7 +76,7 @@
}
// will exceed L2
- @TimeoutReq(minutes = 20)
+ @TimeoutReq(minutes = 30)
public void testMatrixMultiplication600() {
doMatrixMultiplication(NUMBER_REPEAT, 600);
}
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index 2cd2284..c167278 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -168,6 +168,12 @@
<string name="long_text">This is a really long string which exceeds the width of the view.
New devices have a much larger screen which actually enables long strings to be displayed
with no fading. I have made this string longer to fix this case. If you are correcting this
-text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string! </string>
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string!
+I think so, so how about double this string, like copy and paste!
+This is a really long string which exceeds the width of the view.
+New devices have a much larger screen which actually enables long strings to be displayed
+with no fading. I have made this string longer to fix this case. If you are correcting this
+text, I would love to see the kind of devices you guys now use! Guys, maybe some devices need longer string!
+I think so, so how about double this string, like copy and paste! </string>
<string name="rectangle200">"M 0,0 l 200,0 l 0, 200 l -200, 0 z"</string>
</resources>
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index bbfd7b5..1f4fc49 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -70,8 +70,10 @@
private static final String TAG = "CameraTestUtils";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- // Only test the preview and video size that is no larger than 1080p.
- public static final Size PREVIEW_SIZE_BOUND = new Size(1920, 1080);
+ public static final Size SIZE_BOUND_1080P = new Size(1920, 1088);
+ public static final Size SIZE_BOUND_2160P = new Size(3840, 2160);
+ // Only test the preview size that is no larger than 1080p.
+ public static final Size PREVIEW_SIZE_BOUND = SIZE_BOUND_1080P;
// Default timeouts for reaching various states
public static final int CAMERA_OPEN_TIMEOUT_MS = 2000;
public static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
index fdf4774..72e18bb 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/MultiViewTest.java
@@ -17,41 +17,40 @@
package android.hardware.camera2.cts;
import static android.hardware.camera2.cts.CameraTestUtils.*;
+
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
+import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.cts.CameraTestUtils.ImageVerifierListener;
import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase;
import android.hardware.camera2.cts.testcases.Camera2MultiViewTestCase.CameraPreviewListener;
import android.media.ImageReader;
import android.os.SystemClock;
+import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
+import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
* CameraDevice test by using combination of SurfaceView, TextureView and ImageReader
*/
public class MultiViewTest extends Camera2MultiViewTestCase {
-
+ private static final String TAG = "MultiViewTest";
private final static long WAIT_FOR_COMMAND_TO_COMPLETE = 2000;
private final static long PREVIEW_TIME_MS = 2000;
- private enum NumberOfPreview {
- ONE(1), TWO(2);
-
- private final int mValue;
- private NumberOfPreview(int value) { mValue = value; }
- public int getValue() { return mValue; }
- }
-
public void testTextureViewPreview() throws Exception {
for (String cameraId : mCameraIds) {
openCamera(cameraId);
- textureViewPreview(NumberOfPreview.ONE, /*testImagerReader*/false);
- closeCamera();
+ List<TextureView> views = Arrays.asList(mTextureView[0]);
+ textureViewPreview(cameraId, views, /*withImagerReader*/false);
+ closeCamera(cameraId);
}
}
@@ -59,13 +58,15 @@
for (String cameraId : mCameraIds) {
try {
openCamera(cameraId);
- int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+ int maxNumStreamsProc =
+ getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 2) {
continue;
}
- textureViewPreview(NumberOfPreview.ONE, /*testImagerReader*/true);
+ List<TextureView> views = Arrays.asList(mTextureView[0]);
+ textureViewPreview(cameraId, views, /*withImagerReader*/true);
} finally {
- closeCamera();
+ closeCamera(cameraId);
}
}
}
@@ -74,13 +75,15 @@
for (String cameraId : mCameraIds) {
try {
openCamera(cameraId);
- int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+ int maxNumStreamsProc =
+ getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 2) {
continue;
}
- textureViewPreview(NumberOfPreview.TWO, /*testImagerReader*/false);
+ List<TextureView> views = Arrays.asList(mTextureView[0], mTextureView[1]);
+ textureViewPreview(cameraId, views, /*withImagerReader*/false);
} finally {
- closeCamera();
+ closeCamera(cameraId);
}
}
}
@@ -89,32 +92,65 @@
for (String cameraId : mCameraIds) {
try {
openCamera(cameraId);
- int maxNumStreamsProc = mStaticInfo.getMaxNumOutputStreamsProcessedChecked();
+ int maxNumStreamsProc =
+ getStaticInfo(cameraId).getMaxNumOutputStreamsProcessedChecked();
if (maxNumStreamsProc < 3) {
continue;
}
- textureViewPreview(NumberOfPreview.TWO, /*testImagerReader*/true);
+ List<TextureView> views = Arrays.asList(mTextureView[0], mTextureView[1]);
+ textureViewPreview(cameraId, views, /*withImagerReader*/true);
} finally {
- closeCamera();
+ closeCamera(cameraId);
+ }
+ }
+ }
+
+ public void testDualCameraPreview() throws Exception {
+ final int NUM_CAMERAS_TESTED = 2;
+ if (mCameraIds.length < NUM_CAMERAS_TESTED) {
+ return;
+ }
+
+ try {
+ for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
+ openCamera(mCameraIds[i]);
+ List<TextureView> views = Arrays.asList(mTextureView[i]);
+ startTextureViewPreview(mCameraIds[i], views, /*withImagerReader*/false);
+ }
+ // TODO: check the framerate is correct
+ SystemClock.sleep(PREVIEW_TIME_MS);
+ for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
+ stopPreview(mCameraIds[i]);
+ }
+ } catch (BlockingOpenException e) {
+ // The only error accepted is ERROR_MAX_CAMERAS_IN_USE, which means HAL doesn't support
+ // concurrent camera streaming
+ assertEquals("Camera device open failed",
+ CameraDevice.StateListener.ERROR_MAX_CAMERAS_IN_USE, e.getCode());
+ Log.i(TAG, "Camera HAL does not support dual camera preview. Skip the test");
+ } finally {
+ for (int i = 0; i < NUM_CAMERAS_TESTED; i++) {
+ closeCamera(mCameraIds[i]);
}
}
}
/**
- * Test camera Preview using one texture view
+ * Start camera preview using input texture views and/or one image reader
*/
- private void textureViewPreview(NumberOfPreview n, boolean testImagerReader)
+ private void startTextureViewPreview(
+ String cameraId, List<TextureView> views, boolean withImagerReader)
throws Exception {
- int numPreview = n.getValue();
- Size previewSize = mOrderedPreviewSizes.get(0);
+ int numPreview = views.size();
+ Size previewSize = getOrderedPreviewSizes(cameraId).get(0);
CameraPreviewListener[] previewListener =
new CameraPreviewListener[numPreview];
SurfaceTexture[] previewTexture = new SurfaceTexture[numPreview];
List<Surface> surfaces = new ArrayList<Surface>();
// Prepare preview surface.
- for (int i = 0; i < numPreview; i++) {
- TextureView view = mTextureView[i];
+ int i = 0;
+ for (TextureView view : views) {
previewListener[i] = new CameraPreviewListener();
view.setSurfaceTextureListener(previewListener[i]);
previewTexture[i] = getAvailableSurfaceTexture(WAIT_FOR_COMMAND_TO_COMPLETE, view);
@@ -123,9 +159,10 @@
// Correct the preview display rotation.
updatePreviewDisplayRotation(previewSize, view);
surfaces.add(new Surface(previewTexture[i]));
+ i++;
}
- if (testImagerReader) {
+ if (withImagerReader) {
ImageVerifierListener yuvListener =
new ImageVerifierListener(previewSize, ImageFormat.YUV_420_888);
ImageReader yuvReader = makeImageReader(previewSize,
@@ -133,19 +170,29 @@
surfaces.add(yuvReader.getSurface());
}
- startPreview(surfaces, null);
+ startPreview(cameraId, surfaces, null);
- for (int i = 0; i < numPreview; i++) {
- TextureView view = mTextureView[i];
+ i = 0;
+ for (TextureView view : views) {
boolean previewDone =
previewListener[i].waitForPreviewDone(WAIT_FOR_COMMAND_TO_COMPLETE);
assertTrue("Unable to start preview " + i, previewDone);
view.setSurfaceTextureListener(null);
+ i++;
}
+ }
+
+ /**
+ * Test camera preview using input texture views and/or one image reader
+ */
+ private void textureViewPreview(
+ String cameraId, List<TextureView> views, boolean testImagerReader)
+ throws Exception {
+ startTextureViewPreview(cameraId, views, testImagerReader);
// TODO: check the framerate is correct
SystemClock.sleep(PREVIEW_TIME_MS);
- stopPreview();
+ stopPreview(cameraId);
}
}
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 652462c..05b6243 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -56,7 +56,6 @@
private static final String TAG = "RecordingTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG_DUMP = false;
- private static final Size VIDEO_SIZE_BOUND = new Size(1920, 1080);
private static final int RECORDING_DURATION_MS = 2000;
private static final int DURATION_MARGIN_MS = 400;
private static final int FRAME_DURATION_ERROR_TOLERANCE_MS = 3;
@@ -116,8 +115,8 @@
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
openDevice(mCameraIds[i]);
- mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
- VIDEO_SIZE_BOUND);
+
+ initSupportedVideoSize(mCameraIds[i]);
basicRecordingTestByCamera();
} finally {
@@ -145,8 +144,7 @@
mMediaRecorder = new MediaRecorder();
openDevice(mCameraIds[i]);
- mSupportedVideoSizes = getSupportedVideoSizes(mCamera.getId(), mCameraManager,
- VIDEO_SIZE_BOUND);
+ initSupportedVideoSize(mCameraIds[i]);
recordingSizeTestByCamera();
} finally {
@@ -470,6 +468,18 @@
}
/**
+ * Initialize the supported video sizes.
+ */
+ private void initSupportedVideoSize(String cameraId) throws Exception {
+ Size maxVideoSize = SIZE_BOUND_1080P;
+ if (CamcorderProfile.hasProfile(CamcorderProfile.QUALITY_2160P)) {
+ maxVideoSize = SIZE_BOUND_2160P;
+ }
+ mSupportedVideoSizes =
+ getSupportedVideoSizes(cameraId, mCameraManager, maxVideoSize);
+ }
+
+ /**
* Simple wrapper to wrap normal/burst video snapshot tests
*/
private void videoSnapshotHelper(boolean burstTest) throws Exception {
@@ -478,9 +488,10 @@
Log.i(TAG, "Testing video snapshot for camera " + id);
// Re-use the MediaRecorder object for the same camera device.
mMediaRecorder = new MediaRecorder();
+
openDevice(id);
- mSupportedVideoSizes =
- getSupportedVideoSizes(id, mCameraManager, VIDEO_SIZE_BOUND);
+
+ initSupportedVideoSize(id);
videoSnapshotTestByCamera(burstTest);
} finally {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index d91efbb..f7d743e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -48,7 +48,10 @@
import com.android.ex.camera2.blocking.BlockingSessionListener;
import com.android.ex.camera2.blocking.BlockingStateListener;
+import junit.framework.Assert;
+
import java.util.List;
+import java.util.HashMap;
/**
* Camera2 test case base class by using mixed SurfaceView and TextureView as rendering target.
@@ -62,23 +65,18 @@
private static final long SHORT_SLEEP_WAIT_TIME_MS = 100;
// Default timeouts for reaching various camera states
private static final int CAMERA_CLOSE_TIMEOUT_MS = 2000;
- private static final int CAMERA_IDLE_TIMEOUT_MS = 2000;
- private static final int CAMERA_BUSY_TIMEOUT_MS = 500;
protected TextureView[] mTextureView = new TextureView[2];
- protected Context mContext;
- protected CameraManager mCameraManager;
protected String[] mCameraIds;
- protected HandlerThread mHandlerThread;
protected Handler mHandler;
- protected BlockingStateListener mCameraListener;
- // Per device fields:
- protected BlockingSessionListener mSessionListener;
- protected CameraCaptureSession mSession;
- protected CameraDevice mCamera;
- protected StaticMetadata mStaticInfo;
- protected List<Size> mOrderedPreviewSizes;
+ private CameraManager mCameraManager;
+ private BlockingStateListener mCameraListener;
+ private HandlerThread mHandlerThread;
+ private Context mContext;
+
+ private CameraHolder[] mCameraHolders;
+ private HashMap<String, Integer> mCameraIdMap;
public Camera2MultiViewTestCase() {
super(Camera2MultiViewStubActivity.class);
@@ -97,10 +95,17 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mCameraListener = new BlockingStateListener();
- Camera2MultiViewStubActivity activity = (Camera2MultiViewStubActivity)mContext;
+ Camera2MultiViewStubActivity activity = (Camera2MultiViewStubActivity) mContext;
mTextureView[0] = activity.getTextureView(0);
mTextureView[1] = activity.getTextureView(1);
assertNotNull("Unable to get texture view", mTextureView);
+ mCameraIdMap = new HashMap<String, Integer>();
+ int numCameras = mCameraIds.length;
+ mCameraHolders = new CameraHolder[numCameras];
+ for (int i = 0; i < numCameras; i++) {
+ mCameraHolders[i] = new CameraHolder(mCameraIds[i]);
+ mCameraIdMap.put(mCameraIds[i], i);
+ }
}
@Override
@@ -108,9 +113,11 @@
mHandlerThread.quitSafely();
mHandler = null;
mCameraListener = null;
- if (mCamera != null) {
- mCamera.close();
- mCamera = null;
+ for (CameraHolder camera : mCameraHolders) {
+ if (camera.isOpenned()) {
+ camera.close();
+ camera = null;
+ }
}
super.tearDown();
}
@@ -210,46 +217,41 @@
}
protected void openCamera(String cameraId) throws Exception {
- assertNull("Camera is already opened", mCamera);
- mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
- cameraId, mCameraListener, mHandler);
- mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
- CheckLevel.ASSERT, /*collector*/null);
- mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
- assertNotNull(String.format("Failed to open camera device ID: %s", cameraId), mCamera);
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertFalse("Camera has already opened", camera.isOpenned());
+ camera.open();
+ return;
}
- protected void closeCamera() throws Exception {
- assertNotNull("Camera is already closed!", mCamera);
- mCamera.close();
- mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
- mCamera = null;
- mSession = null;
- mStaticInfo = null;
- mOrderedPreviewSizes = null;
+ protected void closeCamera(String cameraId) throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ camera.close();
}
- protected void startPreview(List<Surface> outputSurfaces, CaptureListener listener)
+ protected void startPreview(
+ String cameraId, List<Surface> outputSurfaces, CaptureListener listener)
throws Exception {
- mSessionListener = new BlockingSessionListener();
- mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
-
- // TODO: vary the different settings like crop region to cover more cases.
- CaptureRequest.Builder captureBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
- for (Surface surface : outputSurfaces) {
- captureBuilder.addTarget(surface);
- }
- mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " is not openned", camera.isOpenned());
+ camera.startPreview(outputSurfaces, listener);
}
- protected void stopPreview() throws Exception {
- if (VERBOSE) Log.v(TAG, "Stopping preview and waiting for idle");
- // Stop repeat, wait for captures to complete, and disconnect from surfaces
- mSession.close();
- mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
- mSessionListener = null;
+ protected void stopPreview(String cameraId) throws Exception {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera " + cameraId + " preview is not running", camera.isPreviewStarted());
+ camera.stopPreview();
+ }
+
+ protected StaticMetadata getStaticInfo(String cameraId) {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera is not openned", camera.isOpenned());
+ return camera.getStaticInfo();
+ }
+
+ protected List<Size> getOrderedPreviewSizes(String cameraId) {
+ CameraHolder camera = getCameraHolder(cameraId);
+ assertTrue("Camera is not openned", camera.isOpenned());
+ return camera.getOrderedPreviewSizes();
}
/**
@@ -321,5 +323,91 @@
return true;
}
}
+
+ private CameraHolder getCameraHolder(String cameraId) {
+ Integer cameraIdx = mCameraIdMap.get(cameraId);
+ if (cameraIdx == null) {
+ Assert.fail("Unknown camera Id");
+ }
+ return mCameraHolders[cameraIdx];
+ }
+
+ // Per device fields
+ private class CameraHolder {
+ private String mCameraId;
+ private CameraCaptureSession mSession;
+ private CameraDevice mCamera;
+ private StaticMetadata mStaticInfo;
+ private List<Size> mOrderedPreviewSizes;
+ private BlockingSessionListener mSessionListener;
+
+ public CameraHolder(String id){
+ mCameraId = id;
+ }
+
+ public StaticMetadata getStaticInfo() {
+ return mStaticInfo;
+ }
+
+ public List<Size> getOrderedPreviewSizes() {
+ return mOrderedPreviewSizes;
+ }
+
+ public void open() throws Exception {
+ assertNull("Camera is already opened", mCamera);
+ mCamera = (new BlockingCameraManager(mCameraManager)).openCamera(
+ mCameraId, mCameraListener, mHandler);
+ mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraId),
+ CheckLevel.ASSERT, /*collector*/null);
+ mOrderedPreviewSizes = getSupportedPreviewSizes(
+ mCameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ assertNotNull(String.format("Failed to open camera device ID: %s", mCameraId), mCamera);
+ }
+
+ public boolean isOpenned() {
+ return (mCamera != null);
+ }
+
+ public void close() throws Exception {
+ if (!isOpenned()) {
+ return;
+ }
+ mCamera.close();
+ mCameraListener.waitForState(STATE_CLOSED, CAMERA_CLOSE_TIMEOUT_MS);
+ mCamera = null;
+ mSession = null;
+ mStaticInfo = null;
+ mOrderedPreviewSizes = null;
+ }
+
+ public void startPreview(List<Surface> outputSurfaces, CaptureListener listener)
+ throws Exception {
+ mSessionListener = new BlockingSessionListener();
+ mSession = configureCameraSession(mCamera, outputSurfaces, mSessionListener, mHandler);
+
+ // TODO: vary the different settings like crop region to cover more cases.
+ CaptureRequest.Builder captureBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ for (Surface surface : outputSurfaces) {
+ captureBuilder.addTarget(surface);
+ }
+ mSession.setRepeatingRequest(captureBuilder.build(), listener, mHandler);
+ }
+
+ public boolean isPreviewStarted() {
+ return (mSession != null);
+ }
+
+ public void stopPreview() throws Exception {
+ if (VERBOSE) Log.v(TAG,
+ "Stopping camera " + mCameraId +" preview and waiting for idle");
+ // Stop repeat, wait for captures to complete, and disconnect from surfaces
+ mSession.close();
+ mSessionListener.getStateWaiter().waitForState(
+ SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
+ mSessionListener = null;
+ }
+ }
}
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 ed55b01..ed6560f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -17,7 +17,10 @@
import android.content.Context;
import android.hardware.Sensor;
+import android.hardware.SensorEvent;
import android.hardware.SensorManager;
+import android.media.AudioManager;
+import android.media.ToneGenerator;
import java.util.ArrayList;
import java.util.Collection;
@@ -248,4 +251,45 @@
}
return sensorManager;
}
+
+ public static void beep(int tone) {
+ ToneGenerator mToneGenerator = new ToneGenerator(AudioManager.STREAM_NOTIFICATION, 100);
+ mToneGenerator.startTone(tone);
+ }
+
+ private final static float mAccelerationThresholdForMoveDetection = 4.0f;
+ private static float[] mGravity = {0.0f, 0.0f, 0.0f};
+ public static boolean checkMovementDetection(SensorEvent event) {
+ // Alpha is calculated as t / (t + dT),
+ // where t is the low-pass filter's time-constant and
+ // dT is the event delivery rate.
+ boolean mMoveDetected = false;
+ final float alpha = 0.8f;
+ float[] linear_acceleration = {0.0f, 0.0f, 0.0f};
+
+ if (mGravity[0] == 0f && mGravity[2] == 0f) {
+ mGravity[0] = event.values[0];
+ mGravity[1] = event.values[1];
+ mGravity[2] = event.values[2];
+ } else {
+ // Isolate the force of gravity with the low-pass filter.
+ mGravity[0] = alpha * mGravity[0] + (1 - alpha) * event.values[0];
+ mGravity[1] = alpha * mGravity[1] + (1 - alpha) * event.values[1];
+ mGravity[2] = alpha * mGravity[2] + (1 - alpha) * event.values[2];
+ }
+
+ // Remove the gravity contribution with the high-pass filter.
+ linear_acceleration[0] = event.values[0] - mGravity[0];
+ linear_acceleration[1] = event.values[1] - mGravity[1];
+ linear_acceleration[2] = event.values[2] - mGravity[2];
+
+ float totalAcceleration = Math.abs(linear_acceleration[0])
+ + Math.abs(linear_acceleration[1])
+ + Math.abs(linear_acceleration[2]);
+
+ if (totalAcceleration > mAccelerationThresholdForMoveDetection) {
+ mMoveDetected = true;
+ }
+ return mMoveDetected;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 13e5fd9..3eaafd4 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -110,6 +110,7 @@
public void testMusicActive() throws Exception {
MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);
+ assertNotNull(mp);
mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
mp.start();
Thread.sleep(TIME_TO_PLAY);
@@ -393,6 +394,7 @@
mAudioManager.adjustVolume(ADJUST_RAISE, 0);
MediaPlayer mp = MediaPlayer.create(mContext, MP3_TO_PLAY);
+ assertNotNull(mp);
mp.setAudioStreamType(STREAM_MUSIC);
mp.setLooping(true);
mp.start();
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index e47bf1f..3cb0181 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,7 +29,7 @@
private static final String LOG_TAG = "BuildVersionTest";
private static final Set<String> EXPECTED_RELEASES =
- new HashSet<String>(Arrays.asList("4.4W", "4.4", "4.4.1", "4.4.2", "4.4.3"));
+ new HashSet<String>(Arrays.asList("4.4W.1", "4.4W", "4.4", "4.4.1", "4.4.2", "4.4.3"));
private static final int EXPECTED_SDK = 20;
private static final Set<String> EXPECTED_BUILD_VARIANTS =
new HashSet<String>(Arrays.asList("user", "userdebug", "eng"));
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_PhoneLookup.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_PhoneLookup.java
new file mode 100644
index 0000000..434ac20
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_PhoneLookup.java
@@ -0,0 +1,195 @@
+/*
+ * 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.provider.cts;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.AndroidTestCase;
+
+/**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup}.
+ * <p>
+ * This covers {@link PhoneLookup#CONTENT_FILTER_URI} and
+ * {@link PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+ *
+ * TODO We don't yet have tests to cover cross-user provider access for the later, since multi-user
+ * cases aren't well supported in CTS yet. Tracking in internal bug/16462089 .
+ */
+public class ContactsContract_PhoneLookup extends AndroidTestCase {
+ private ContentResolver mResolver;
+ private ContactsContract_TestDataBuilder mBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mResolver = getContext().getContentResolver();
+ ContentProviderClient provider =
+ mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+ mBuilder = new ContactsContract_TestDataBuilder(provider);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mBuilder.cleanup();
+ }
+
+ private long[] setupTestData() throws Exception {
+ TestRawContact rawContact = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.DISPLAY_NAME, "Hot Tamale")
+ .insert();
+ rawContact.newDataRow(Phone.CONTENT_ITEM_TYPE)
+ .with(Phone.DATA, "1111222333444")
+ .with(Email.TYPE, Phone.TYPE_HOME)
+ .insert().load().getId();
+ rawContact.load();
+ TestContact contact = rawContact.getContact().load();
+
+ TestRawContact rawContact2 = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.DISPLAY_NAME, "Cold Tamago")
+ .insert();
+ rawContact2.newDataRow(Phone.CONTENT_ITEM_TYPE)
+ .with(Phone.DATA, "2111222333444")
+ .with(Phone.TYPE, Phone.TYPE_OTHER)
+ .insert().load();
+
+ rawContact2.load();
+ TestContact contact2 = rawContact2.getContact().load();
+
+ return new long[] {
+ contact.getId(), contact2.getId()
+ };
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookup_nomatch() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.CONTENT_FILTER_URI.buildUpon()
+ .appendPath("no-such-phone-number").build();
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids /*, empty */);
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookup_found1() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.CONTENT_FILTER_URI.buildUpon()
+ .appendPath("1111222333444").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(PhoneLookup._ID, ids[0]);
+ expected.put(PhoneLookup.NUMBER, "1111222333444");
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookup_found2() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.CONTENT_FILTER_URI.buildUpon()
+ .appendPath("2111222333444").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(PhoneLookup._ID, ids[1]);
+ expected.put(PhoneLookup.NUMBER, "2111222333444");
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookupEnterprise_nomatch() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("no-such-phone-number").build();
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids /*, empty */);
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookupEnterprise_found1() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("1111222333444").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(PhoneLookup._ID, ids[0]);
+ expected.put(PhoneLookup.NUMBER, "1111222333444");
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ /**
+ * Test for {@link android.provider.ContactsContract.PhoneLookup#ENTERPRISE_CONTENT_FILTER_URI}.
+ */
+ public void testPhoneLookupEnterprise_found2() throws Exception {
+ long[] ids = setupTestData();
+ final Uri uri = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon()
+ .appendPath("2111222333444").build();
+
+ final ContentValues expected = new ContentValues();
+ expected.put(PhoneLookup._ID, ids[1]);
+ expected.put(PhoneLookup.NUMBER, "2111222333444");
+
+ assertCursorStoredValuesWithContactsFilter(uri, ids, expected);
+ }
+
+ private void assertCursorStoredValuesWithContactsFilter(Uri uri, long[] contactsId,
+ ContentValues... expected) {
+ // We need this helper function to add a filter for specific contacts because
+ // otherwise tests will fail if performed on a device with existing contacts data
+ StringBuilder sb = new StringBuilder();
+ sb.append(Contacts._ID + " in ");
+ sb.append("(");
+ for (int i = 0; i < contactsId.length; i++) {
+ if (i != 0) sb.append(",");
+ sb.append(contactsId[i]);
+ }
+ sb.append(")");
+ DatabaseAsserts.assertStoredValuesInUriMatchExactly(mResolver, uri, null,
+ sb.toString(), null, null, false, expected);
+ }
+}
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index 8a4d603..2912b19 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -27,6 +27,7 @@
static final String[] CERTIFICATE_DATA = {
"91:C6:D6:EE:3E:8A:C8:63:84:E5:48:C2:99:29:5C:75:6C:81:7B:81",
"4A:65:D5:F4:1D:EF:39:B8:B8:90:4A:4A:D3:64:81:33:CF:C7:A1:D1",
+ "16:32:47:8D:89:F9:21:3A:92:00:85:63:F5:A4:A7:D3:12:40:8A:D6",
"4D:23:78:EC:91:95:39:B5:00:7F:75:8F:03:3B:21:1E:C5:4D:8B:CF",
"E7:B4:F6:9D:61:EC:90:69:DB:7E:90:A7:40:1A:3C:F4:7D:4F:E8:EE",
"DD:E1:D2:A9:01:80:2E:1D:87:5E:84:B3:80:7E:4B:B1:FD:99:41:34",
@@ -38,19 +39,18 @@
"4F:99:AA:93:FB:2B:D1:37:26:A1:99:4A:CE:7F:F0:05:F2:93:5D:1E",
"74:20:74:41:72:9C:DD:92:EC:79:31:D8:23:10:8D:C2:81:92:E2:BB",
"40:54:DA:6F:1C:3F:40:74:AC:ED:0F:EC:CD:DB:79:D1:53:FB:90:1D",
- "43:F9:B1:10:D5:BA:FD:48:22:52:31:B0:D0:08:2B:37:2F:EF:9A:54",
"F4:8B:11:BF:DE:AB:BE:94:54:20:71:E6:41:DE:6B:BE:88:2B:40:B9",
"58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0",
"55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1",
"D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD",
"78:6A:74:AC:76:AB:14:7F:9C:6A:30:50:BA:9E:A8:7E:FE:9A:CE:3C",
+ "09:3C:61:F3:8B:8B:DC:7D:55:DF:75:38:02:05:00:E1:25:F5:C8:36",
"8E:1C:74:F8:A6:20:B9:E5:8A:F4:61:FA:EC:2B:47:56:51:1A:52:C6",
"27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
"AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
"8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
"AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
"DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
- "5F:4E:1F:CF:31:B7:91:3B:85:0B:54:F6:E5:FF:50:1A:2B:6F:C6:CF",
"74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
"85:B5:FF:67:9B:0C:79:96:1F:C8:6E:44:22:00:46:13:DB:17:92:84",
"3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F",
@@ -68,42 +68,36 @@
"1B:4B:39:61:26:27:6B:64:91:A2:68:6D:D7:02:43:21:2D:1F:1D:96",
"77:47:4F:C6:30:E4:0F:4C:47:64:3F:84:BA:B8:C6:95:4A:8A:41:EC",
"8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4",
- "02:72:68:29:3E:5F:5D:17:AA:A4:B3:C3:E6:36:1E:1F:92:57:5E:AA",
"2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19",
- "97:81:79:50:D8:1C:96:70:CC:34:D8:09:CF:79:44:31:36:7E:F4:74",
"85:A4:08:C0:9C:19:3E:5D:51:58:7D:CD:D6:13:30:FD:8C:DE:37:BF",
"58:11:9F:0E:12:82:87:EA:50:FD:D9:87:45:6F:4F:78:DC:FA:D6:D4",
"6B:2F:34:AD:89:58:BE:62:FD:B0:6B:5C:CE:BB:9D:D9:4F:4E:39:F3",
"9B:AA:E5:9F:56:EE:21:CB:43:5A:BE:25:93:DF:A7:F0:40:D1:1D:CB",
"36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54",
+ "1B:8E:EA:57:96:29:1A:C9:39:EA:B8:0A:81:1A:73:73:C0:93:79:67",
"B4:35:D4:E1:11:9D:1C:66:90:A7:49:EB:B3:94:BD:63:7B:A7:82:B7",
"A9:E9:78:08:14:37:58:88:F2:05:19:B0:6D:2B:0D:2B:60:16:90:7D",
"60:D6:89:74:B5:C2:65:9E:8A:0F:C1:88:7C:88:D2:46:69:1B:18:2C",
"D2:32:09:AD:23:D3:14:23:21:74:E4:0D:7F:9D:62:13:97:86:63:3A",
"66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B",
- "87:9F:4B:EE:05:DF:98:58:3B:E3:60:D6:33:E7:0D:3F:FE:98:71:AF",
"DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9",
"22:D5:D8:DF:8F:02:31:D1:8D:F7:9D:B7:CF:8A:2D:64:C9:3F:6C:3A",
"F3:73:B3:87:06:5A:28:84:8A:F2:F3:4A:CE:19:2B:DD:C7:8E:9C:AC",
"06:08:3F:59:3F:15:A1:04:A0:69:A4:6B:A9:03:D0:06:B7:97:09:91",
- "E3:92:51:2F:0A:CF:F5:05:DF:F6:DE:06:7F:75:37:E1:65:EA:57:4B",
"43:13:BB:96:F1:D5:86:9B:C1:4E:6A:92:F6:CF:F6:34:69:87:82:37",
"F1:8B:53:8D:1B:E9:03:B6:A6:F0:56:43:5B:17:15:89:CA:F3:6B:F2",
"05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43",
- "93:E6:AB:22:03:03:B5:23:28:DC:DA:56:9E:BA:E4:D1:D1:CC:FB:65",
"62:52:DC:40:F7:11:43:A2:2F:DE:9E:F7:34:8E:06:42:51:B1:81:18",
"70:17:9B:86:8C:00:A4:FA:60:91:52:22:3F:9F:3E:32:BD:E0:05:62",
"A0:A1:AB:90:C9:FC:84:7B:3B:12:61:E8:97:7D:5F:D3:22:61:D3:CC",
"85:37:1C:A6:E5:50:14:3D:CE:28:03:47:1B:DE:3A:09:E8:F8:77:0F",
"7E:78:4A:10:1C:82:65:CC:2D:E1:F1:6D:47:B4:40:CA:D9:0A:19:45",
"D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49",
- "A1:DB:63:93:91:6F:17:E4:18:55:09:40:04:15:C7:02:40:B0:AE:6B",
"B8:01:86:D1:EB:9C:86:A5:41:04:CF:30:54:F3:4C:52:B7:E5:58:C6",
"2E:14:DA:EC:28:F0:FA:1E:8E:38:9A:4E:AB:EB:26:C0:0A:D3:83:C3",
"DE:28:F4:A4:FF:E5:B9:2F:A3:C5:03:D1:A3:49:A7:F9:96:2A:82:12",
"80:25:EF:F4:6E:70:C8:D4:72:24:65:84:FE:40:3B:8A:8D:6A:DB:F5",
"CA:3A:FB:CF:12:40:36:4B:44:B2:16:20:88:80:48:39:19:93:7C:F7",
- "69:BD:8C:F4:9C:D3:00:FB:59:2E:17:93:CA:55:6A:F3:EC:AA:35:FB",
"13:2D:0D:45:53:4B:69:97:CD:B2:D5:C3:39:E2:55:76:60:9B:5C:C6",
"5F:B7:EE:06:33:E2:59:DB:AD:0C:4C:9A:E6:D3:8F:1A:61:C7:DC:25",
"49:0A:75:74:DE:87:0A:47:FE:58:EE:F6:C7:6B:EB:C6:0B:12:40:99",
@@ -115,10 +109,12 @@
"FA:B7:EE:36:97:26:62:FB:2D:B0:2A:F6:BF:03:FD:E8:7C:4B:2F:9B",
"8B:AF:4C:9B:1D:F0:2A:92:F7:DA:12:8E:B9:1B:AC:F4:98:60:4B:6F",
"9F:74:4E:9F:2B:4D:BA:EC:0F:31:2C:50:B6:56:3B:8E:2D:93:C3:11",
+ "A1:4B:48:D9:43:EE:0A:0E:40:90:4F:3C:E0:A4:C0:91:93:51:5D:3F",
"C9:A8:B9:E7:55:80:5E:58:E3:53:77:A7:25:EB:AF:C3:7B:27:CC:D7",
"1F:49:14:F7:D8:74:95:1D:DD:AE:02:C0:BE:FD:3A:2D:82:75:51:85",
"B5:61:EB:EA:A4:DE:E4:25:4B:69:1A:98:A5:57:47:C2:34:C7:D9:71",
"07:E0:32:E0:20:B7:2C:3F:19:2F:06:28:A2:59:3A:19:A7:0F:06:9E",
+ "B9:42:94:BF:91:EA:8F:B6:4B:E6:10:97:C7:FB:00:13:59:B6:76:CB",
"D6:DA:A8:20:8D:09:D2:15:4D:24:B5:2F:CB:34:6E:B2:58:B2:8A:58",
"32:3C:11:8E:1B:F7:B8:B6:52:54:E2:E2:10:0D:D6:02:90:37:F0:96",
"E7:A1:90:29:D3:D5:52:DC:0D:0F:C6:92:D3:EA:88:0D:15:2E:1A:6B",
@@ -126,6 +122,7 @@
"FE:B8:C4:32:DC:F9:76:9A:CE:AE:3D:D8:90:8F:FD:28:86:65:64:7D",
"4A:BD:EE:EC:95:0D:35:9C:89:AE:C7:52:A1:2C:5B:29:F6:D6:AA:0C",
"33:9B:6B:14:50:24:9B:55:7A:01:87:72:84:D9:E0:2F:C3:D2:D8:E9",
+ "DD:FB:16:CD:49:31:C9:73:A2:03:7D:3F:C8:3A:4D:7D:77:5D:05:E4",
"2A:B6:28:48:5E:78:FB:F3:AD:9E:79:10:DD:6B:DF:99:72:2C:96:E5",
"36:B1:2B:49:F9:81:9E:D7:4C:9E:BC:38:0F:C6:56:8F:5D:AC:B2:F7",
"37:F7:6D:E6:07:7C:90:C5:B1:3E:93:1A:B7:41:10:B4:F2:E4:9A:27",
@@ -139,22 +136,22 @@
"CF:9E:87:6D:D3:EB:FC:42:26:97:A3:B5:A3:7A:A0:76:A9:06:23:48",
"2B:B1:F5:3E:55:0C:1D:C5:F1:D4:E6:B7:6A:46:4B:55:06:02:AC:21",
"47:BE:AB:C9:22:EA:E8:0E:78:78:34:62:A7:9F:45:C2:54:FD:E6:8B",
- "31:7A:2A:D0:7F:2B:33:5E:F5:A1:C3:4E:4B:57:E8:B7:D8:F1:FC:A6",
"39:21:C1:15:C1:5D:0E:CA:5C:CB:5B:C4:F0:7D:21:D8:05:0B:56:6A",
"3A:44:73:5A:E5:81:90:1F:24:86:61:46:1E:3B:9C:C4:5F:F5:3A:1B",
"B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9",
"E0:AB:05:94:20:72:54:93:05:60:62:02:36:70:F7:CD:2E:FC:66:66",
"D3:C0:63:F2:19:ED:07:3E:34:AD:5D:75:0B:32:76:29:FF:D5:9A:F2",
+ "F5:17:A2:4F:9A:48:C6:C9:F8:A2:00:26:9F:DC:0F:48:2C:AB:30:89",
"3B:C0:38:0B:33:C3:F6:A6:0C:86:15:22:93:D9:DF:F5:4B:81:C0:04",
"C8:EC:8C:87:92:69:CB:4B:AB:39:E9:8D:7E:57:67:F3:14:95:73:9D",
"03:9E:ED:B8:0B:E7:A0:3C:69:53:89:3B:20:D2:D9:32:3A:4C:2A:FD",
- "CB:A1:C5:F8:B0:E3:5E:B8:B9:45:12:D3:F9:34:A2:E9:06:10:D3:36",
+ "DF:3C:24:F9:BF:D6:66:76:1B:26:80:73:FE:06:D1:CC:8D:4F:82:A4",
"51:C6:E7:08:49:06:6E:F3:92:D4:5C:A0:0D:6D:A3:62:8F:C3:52:39",
"B8:23:6B:00:2F:1D:16:86:53:01:55:6C:11:A4:37:CA:EB:FF:C3:BB",
"10:1D:FA:3F:D5:0B:CB:BB:9B:B5:60:0C:19:55:A4:1A:F4:73:3A:04",
"87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
+ "59:0D:2D:7D:88:4F:40:2E:61:7E:A5:62:32:17:65:CF:17:D8:94:E9",
"AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
- "21:FC:BD:8E:7F:6C:AF:05:1B:D1:B3:43:EC:A8:E7:61:47:F2:0F:8A",
"5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
"2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
"F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33",
@@ -162,6 +159,7 @@
"D8:A6:33:2C:E0:03:6F:B1:85:F6:63:4F:7D:6A:06:65:26:32:28:27",
"9F:AD:91:A6:CE:6A:C6:C5:00:47:C4:4E:C9:D4:A5:0D:92:D8:49:79",
"CC:AB:0E:A0:4C:23:01:D6:69:7B:DD:37:9F:CD:12:EB:24:E3:94:9D",
+ "48:12:BD:92:3C:A8:C4:39:06:E7:30:6D:27:96:E6:A4:CF:22:2E:7D",
"F9:B5:B6:32:45:5F:9C:BE:EC:57:5F:80:DC:E9:6E:2C:C7:B2:78:B7",
"5F:3A:FC:0A:8B:64:F6:86:67:34:74:DF:7E:A9:A2:FE:F9:FA:7A:51",
"E6:21:F3:35:43:79:05:9A:4B:68:30:9D:8A:2F:74:22:15:87:EC:79",
@@ -169,10 +167,9 @@
"89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E",
"E0:B4:32:2E:B2:F6:A5:68:B6:54:53:84:48:18:4A:50:36:87:43:84",
"61:57:3A:11:DF:0E:D8:7E:D5:92:65:22:EA:D0:56:D7:44:B3:23:71",
- "99:A6:9B:E6:1A:FE:88:6B:4D:2B:82:00:7C:B8:54:FC:31:7E:15:39",
+ "7E:04:DE:89:6A:3E:66:6D:00:E6:87:D3:3F:FA:D9:3B:E8:3D:34:9E",
"6E:3A:55:A4:19:0C:19:5C:93:84:3C:C0:DB:72:2E:31:30:61:F0:B1",
"31:F1:FD:68:22:63:20:EE:C6:3B:3F:9D:EA:4A:3E:53:7C:7C:39:17",
- "E5:DF:74:3C:B6:01:C4:9B:98:43:DC:AB:8C:E8:6A:81:10:9F:E4:8E",
"F9:CD:0E:2C:DA:76:24:C1:8F:BD:F0:F0:AB:B6:45:B8:F7:FE:D5:7A",
"23:88:C9:D3:71:CC:9E:96:3D:FF:7D:3C:A7:CE:FC:D6:25:EC:19:0D",
"8C:96:BA:EB:DD:2B:07:07:48:EE:30:32:66:A0:F3:98:6E:7C:AE:58",
diff --git a/tests/tests/tv/Android.mk b/tests/tests/tv/Android.mk
index 5df50ca..2ffe166 100644
--- a/tests/tests/tv/Android.mk
+++ b/tests/tests/tv/Android.mk
@@ -24,8 +24,12 @@
LOCAL_PACKAGE_NAME := CtsTvTestCases
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+LOCAL_INSTRUMENTATION_FOR := CtsTestStubs
+
LOCAL_SDK_VERSION := current
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index 66f4ac0..fb84509 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -21,8 +21,8 @@
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <uses-permission android:name="android.permission.READ_EPG_DATA" />
- <uses-permission android:name="android.permission.WRITE_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java b/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java
index 661a8d7..c2c9a1b 100644
--- a/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java
+++ b/tests/tests/tv/src/android/media/tv/cts/StubTunerTvInputService.java
@@ -31,9 +31,20 @@
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
public class StubTunerTvInputService extends TvInputService {
+ private static final List<TvTrackInfo> mTrackList = new ArrayList<>();
+
+ public static void clearTracks() {
+ mTrackList.clear();
+ }
+
+ public static void injectTrack(TvTrackInfo... tracks) {
+ mTrackList.addAll(Arrays.asList(tracks));
+ }
+
public static void insertChannels(ContentResolver resolver, TvInputInfo info) {
if (!info.getServiceInfo().name.equals(StubTunerTvInputService.class.getName())) {
throw new IllegalArgumentException("info mismatch");
@@ -69,41 +80,16 @@
return new StubSessionImpl(this);
}
- private static class StubSessionImpl extends Session {
+ static class StubSessionImpl extends Session {
private static final int[] COLORS = { Color.RED, Color.GREEN, Color.BLUE };
private Surface mSurface;
private Object mLock = new Object();
private int mCurrentIndex = -1;
private Context mContext;
- private final List<TvTrackInfo> mTrackList = new ArrayList<>();
- private final TvTrackInfo mVideoTrack1;
- private final TvTrackInfo mVideoTrack2;
- private final TvTrackInfo mAudioTrack1;
- private final TvTrackInfo mAudioTrack2;
- private final TvTrackInfo mSubtitleTrack1;
- private final TvTrackInfo mSubtitleTrack2;
StubSessionImpl(Context context) {
super(context);
mContext = context;
- mVideoTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-HD")
- .setVideoHeight(1920).setVideoWidth(1080).build();
- mVideoTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-SD")
- .setVideoHeight(640).setVideoWidth(360).build();
- mAudioTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-stereo-eng")
- .setLanguage("eng").setAudioChannelCount(2).setAudioSampleRate(48000).build();
- mAudioTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-mono-esp")
- .setLanguage("esp").setAudioChannelCount(1).setAudioSampleRate(48000).build();
- mSubtitleTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-eng")
- .setLanguage("eng").build();
- mSubtitleTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-esp")
- .setLanguage("esp").build();
- mTrackList.add(mVideoTrack1);
- mTrackList.add(mVideoTrack2);
- mTrackList.add(mAudioTrack1);
- mTrackList.add(mAudioTrack2);
- mTrackList.add(mSubtitleTrack1);
- mTrackList.add(mSubtitleTrack2);
}
@Override
@@ -112,9 +98,13 @@
private void updateSurfaceLocked() {
if (mCurrentIndex >= 0 && mSurface != null) {
- Canvas c = mSurface.lockCanvas(null);
- c.drawColor(COLORS[mCurrentIndex]);
- mSurface.unlockCanvasAndPost(c);
+ try {
+ Canvas c = mSurface.lockCanvas(null);
+ c.drawColor(COLORS[mCurrentIndex]);
+ mSurface.unlockCanvasAndPost(c);
+ } catch (IllegalArgumentException e) {
+ mSurface = null;
+ }
}
}
@@ -133,8 +123,8 @@
@Override
public boolean onTune(Uri channelUri) {
+ notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
synchronized (mLock) {
- notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
String[] projection = { TvContract.Channels.COLUMN_INTERNAL_PROVIDER_DATA };
Cursor cursor = mContext.getContentResolver().query(
channelUri, projection, null, null, null);
@@ -153,13 +143,22 @@
// Notify tracks
if (mCurrentIndex == 0) {
notifyTracksChanged(mTrackList);
- notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, mVideoTrack1.getId());
- notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, mAudioTrack1.getId());
- notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, null);
+ for (TvTrackInfo track : mTrackList) {
+ if (track.getType() == TvTrackInfo.TYPE_VIDEO) {
+ notifyTrackSelected(TvTrackInfo.TYPE_VIDEO, track.getId());
+ break;
+ }
+ }
+ for (TvTrackInfo track : mTrackList) {
+ if (track.getType() == TvTrackInfo.TYPE_AUDIO) {
+ notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, track.getId());
+ break;
+ }
+ }
}
- notifyVideoAvailable();
- return true;
}
+ notifyVideoAvailable();
+ return true;
}
@Override
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
new file mode 100644
index 0000000..5e6478b
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContentRatingTest.java
@@ -0,0 +1,71 @@
+/*
+ * 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.tv.cts;
+
+import android.media.tv.TvContentRating;
+
+import java.util.List;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for {@link android.media.tv.TvContentRating}.
+ */
+public class TvContentRatingTest extends TestCase {
+
+ public void testCreateRating() throws Exception {
+ final String DOMAIN = "android.media.tv";
+ final String RATING_SYSTEM = "US_TVPG";
+ final String MAIN_RATING = "US_TVPG_TV_MA";
+ final String SUB_RATING_1 = "US_TVPG_TV_S";
+ final String SUB_RATING_2 = "US_TVPG_TV_V";
+
+ TvContentRating rating = TvContentRating.createRating(DOMAIN, RATING_SYSTEM, MAIN_RATING,
+ SUB_RATING_1, SUB_RATING_2);
+ assertEquals(DOMAIN, rating.getDomain());
+ assertEquals(RATING_SYSTEM, rating.getRatingSystem());
+ assertEquals(MAIN_RATING, rating.getMainRating());
+ List<String> subRatings = rating.getSubRatings();
+ assertEquals(2, subRatings.size());
+ assertTrue("Sub-ratings does not contain " + SUB_RATING_1,
+ subRatings.contains(SUB_RATING_1));
+ assertTrue("Sub-ratings does not contain " + SUB_RATING_2,
+ subRatings.contains(SUB_RATING_2));
+ }
+
+ public void testFlattenAndUnflatten() throws Exception {
+ final String DOMAIN = "android.media.tv";
+ final String RATING_SYSTEM = "US_TVPG";
+ final String MAIN_RATING = "US_TVPG_TV_MA";
+ final String SUB_RATING_1 = "US_TVPG_TV_S";
+ final String SUB_RATING_2 = "US_TVPG_TV_V";
+
+ String flattened = TvContentRating.createRating(DOMAIN, RATING_SYSTEM, MAIN_RATING,
+ SUB_RATING_1, SUB_RATING_2).flattenToString();
+ TvContentRating rating = TvContentRating.unflattenFromString(flattened);
+
+ assertEquals(DOMAIN, rating.getDomain());
+ assertEquals(RATING_SYSTEM, rating.getRatingSystem());
+ assertEquals(MAIN_RATING, rating.getMainRating());
+ List<String> subRatings = rating.getSubRatings();
+ assertEquals(2, subRatings.size());
+ assertTrue("Sub-ratings does not contain " + SUB_RATING_1,
+ subRatings.contains(SUB_RATING_1));
+ assertTrue("Sub-ratings does not contain " + SUB_RATING_2,
+ subRatings.contains(SUB_RATING_2));
+ }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index e9f7423..6d31a80 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -259,10 +259,20 @@
}
}
+ private void verifyLogoIsReadable(Uri logoUri) throws Exception {
+ try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) {
+ try (InputStream is = fd.createInputStream()) {
+ // Assure that the stream is decodable as a Bitmap.
+ BitmapFactory.decodeStream(is);
+ }
+ }
+ }
+
public void testChannelLogo() throws Exception {
// Set-up: add a channel.
ContentValues values = createDummyChannelValues(mInputId);
- Uri logoUri = TvContract.buildChannelLogoUri(mContentResolver.insert(mChannelsUri, values));
+ Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+ Uri logoUri = TvContract.buildChannelLogoUri(channelUri);
Bitmap logo = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.robot);
// Write
@@ -276,22 +286,15 @@
Thread.sleep(OPERATION_TIME);
// Read and verify
- try (AssetFileDescriptor fd = mContentResolver.openAssetFileDescriptor(logoUri, "r")) {
- try (InputStream is = fd.createInputStream()) {
- // Assure that the stream is decodable as a Bitmap.
- BitmapFactory.decodeStream(is);
- }
- }
+ verifyLogoIsReadable(logoUri);
+
+ // Read and verify using alternative logo URI.
+ verifyLogoIsReadable(TvContract.buildChannelLogoUri(ContentUris.parseId(channelUri)));
}
- public void testProgramsTable() throws Exception {
- // Set-up: add a channel.
- ContentValues values = createDummyChannelValues(mInputId);
- long channelId = ContentUris.parseId(mContentResolver.insert(mChannelsUri, values));
- Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
-
+ public void verifyProgramsTable(Uri programsUri, long channelId) {
// Test: insert
- values = createDummyProgramValues(channelId);
+ ContentValues values = createDummyProgramValues(channelId);
Uri rowUri = mContentResolver.insert(programsUri, values);
long programId = ContentUris.parseId(rowUri);
@@ -314,56 +317,62 @@
}
}
+ public void testProgramsTable() throws Exception {
+ // Set-up: add a channel.
+ ContentValues values = createDummyChannelValues(mInputId);
+ Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+ long channelId = ContentUris.parseId(channelUri);
+
+ verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelId), channelId);
+ verifyProgramsTable(TvContract.buildProgramsUriForChannel(channelUri), channelId);
+ }
+
+ private void verifyOverlap(long startMillis, long endMillis, int expectedCount,
+ long channelId, Uri channelUri) {
+ try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
+ channelId, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
+ assertEquals(expectedCount, cursor.getCount());
+ }
+ try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(
+ channelUri, startMillis, endMillis), PROGRAMS_PROJECTION, null, null, null)) {
+ assertEquals(expectedCount, cursor.getCount());
+ }
+ }
+
public void testProgramsScheduleOverlap() throws Exception {
- final long startMillis = 1403712000000l; // Jun 25 2014 16:00 UTC
- final long endMillis = 1403719200000l; // Jun 25 2014 18:00 UTC
+ final long programStartMillis = 1403712000000l; // Jun 25 2014 16:00 UTC
+ final long programEndMillis = 1403719200000l; // Jun 25 2014 18:00 UTC
final long hour = 3600000l;
// Set-up: add a channel and program.
ContentValues values = createDummyChannelValues(mInputId);
- long channelId = ContentUris.parseId(mContentResolver.insert(mChannelsUri, values));
+ Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+ long channelId = ContentUris.parseId(channelUri);
Uri programsUri = TvContract.buildProgramsUriForChannel(channelId);
values = createDummyProgramValues(channelId);
- values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, startMillis);
- values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, endMillis);
+ values.put(TvContract.Programs.COLUMN_START_TIME_UTC_MILLIS, programStartMillis);
+ values.put(TvContract.Programs.COLUMN_END_TIME_UTC_MILLIS, programEndMillis);
mContentResolver.insert(programsUri, values);
// Overlap 1: starts early, ends early.
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- startMillis - hour, endMillis - hour), PROGRAMS_PROJECTION, null, null, null)) {
- assertEquals(1, cursor.getCount());
- }
+ verifyOverlap(programStartMillis - hour, programEndMillis - hour, 1, channelId, channelUri);
// Overlap 2: starts early, ends late.
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- startMillis - hour, endMillis + hour), PROGRAMS_PROJECTION, null, null, null)) {
- assertEquals(1, cursor.getCount());
- }
+ verifyOverlap(programStartMillis - hour, programEndMillis + hour, 1, channelId, channelUri);
// Overlap 3: starts early, ends late.
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- startMillis + hour / 2, endMillis - hour / 2), PROGRAMS_PROJECTION,
- null, null, null)) {
- assertEquals(1, cursor.getCount());
- }
+ verifyOverlap(programStartMillis + hour / 2, programEndMillis - hour / 2, 1,
+ channelId, channelUri);
// Overlap 4: starts late, ends late.
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- startMillis + hour, endMillis + hour), PROGRAMS_PROJECTION, null, null, null)) {
- assertEquals(1, cursor.getCount());
- }
+ verifyOverlap(programStartMillis + hour, programEndMillis + hour, 1, channelId, channelUri);
// Non-overlap 1: ends too early.
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- startMillis - hour, startMillis - hour / 2), PROGRAMS_PROJECTION,
- null, null, null)) {
- assertEquals(0, cursor.getCount());
- }
+ verifyOverlap(programStartMillis - hour, programStartMillis - hour / 2, 0,
+ channelId, channelUri);
// Non-overlap 2: starts too late
- try (Cursor cursor = mContentResolver.query(TvContract.buildProgramsUriForChannel(channelId,
- endMillis + hour, endMillis + hour * 2), PROGRAMS_PROJECTION, null, null, null)) {
- assertEquals(0, cursor.getCount());
- }
+ verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
+ channelId, channelUri);
}
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
index 6de70f4..960aef7 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
@@ -32,9 +32,13 @@
import android.net.Uri;
import android.util.ArrayMap;
import android.util.SparseIntArray;
+import android.view.InputEvent;
+import android.view.KeyEvent;
import com.android.cts.tv.R;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -61,7 +65,7 @@
public boolean isVideoAvailable(String inputId) {
synchronized (mLock) {
Boolean available = mVideoAvailableMap.get(inputId);
- return available == null ? true : available.booleanValue();
+ return available == null ? false : available.booleanValue();
}
}
@@ -158,6 +162,24 @@
mTvView.setTvInputListener(mListener);
}
+ @Override
+ protected void tearDown() throws Exception {
+ StubTunerTvInputService.deleteChannels(mActivity.getContentResolver(), mStubInfo);
+ StubTunerTvInputService.clearTracks();
+ try {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTvView.reset();
+ }
+ });
+ } catch (Throwable t) {
+ throw new RuntimeException(t);
+ }
+ mInstrumentation.waitForIdleSync();
+ super.tearDown();
+ }
+
public void testConstructor() throws Exception {
new TvView(mActivity);
@@ -171,9 +193,8 @@
Uri uri = TvContract.buildChannelsUriForInput(mStubInfo.getId());
String[] projection = { TvContract.Channels._ID };
- Cursor cursor = mActivity.getContentResolver().query(
- uri, projection, null, null, null);
- try {
+ try (Cursor cursor = mActivity.getContentResolver().query(
+ uri, projection, null, null, null)) {
while (cursor != null && cursor.moveToNext()) {
long channelId = cursor.getLong(0);
Uri channelUri = TvContract.buildChannelUri(channelId);
@@ -190,20 +211,7 @@
runOnEachChannel.run();
}
}
- } finally {
- if (cursor != null) {
- cursor.close();
- }
}
- runTestOnUiThread(new Runnable() {
- @Override
- public void run() {
- mTvView.reset();
- }
- });
- mInstrumentation.waitForIdleSync();
-
- StubTunerTvInputService.deleteChannels(mActivity.getContentResolver(), mStubInfo);
}
public void testSimpleTune() throws Throwable {
@@ -213,7 +221,8 @@
tryTuneAllChannels(null);
}
- private void selectTrackAndVerify(final int type, final TvTrackInfo track) {
+ private void selectTrackAndVerify(final int type, final TvTrackInfo track,
+ List<TvTrackInfo> tracks) {
final int previousGeneration = mListener.getSelectedTrackGeneration(
mStubInfo.getId(), type);
mTvView.selectTrack(type, track == null ? null : track.getId());
@@ -224,13 +233,65 @@
mStubInfo.getId(), type) > previousGeneration;
}
}.run();
- assertEquals(mTvView.getSelectedTrack(type), track == null ? null : track.getId());
+ String selectedTrackId = mTvView.getSelectedTrack(type);
+ assertEquals(selectedTrackId, track == null ? null : track.getId());
+ if (selectedTrackId != null) {
+ TvTrackInfo selectedTrack = null;
+ for (TvTrackInfo item : tracks) {
+ if (item.getId().equals(selectedTrackId)) {
+ selectedTrack = item;
+ break;
+ }
+ }
+ assertNotNull(selectedTrack);
+ assertEquals(track.getType(), selectedTrack.getType());
+ assertEquals(track.getExtra(), selectedTrack.getExtra());
+ switch (track.getType()) {
+ case TvTrackInfo.TYPE_VIDEO:
+ assertEquals(track.getVideoHeight(), selectedTrack.getVideoHeight());
+ assertEquals(track.getVideoWidth(), selectedTrack.getVideoWidth());
+ break;
+ case TvTrackInfo.TYPE_AUDIO:
+ assertEquals(track.getAudioChannelCount(),
+ selectedTrack.getAudioChannelCount());
+ assertEquals(track.getAudioSampleRate(), selectedTrack.getAudioSampleRate());
+ assertEquals(track.getLanguage(), selectedTrack.getLanguage());
+ break;
+ case TvTrackInfo.TYPE_SUBTITLE:
+ assertEquals(track.getLanguage(), selectedTrack.getLanguage());
+ break;
+ default:
+ fail("Unrecognized type: " + track.getType());
+ }
+ }
}
public void testTrackChange() throws Throwable {
if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
+ TvTrackInfo videoTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-HD")
+ .setVideoHeight(1920).setVideoWidth(1080).build();
+ TvTrackInfo videoTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-SD")
+ .setVideoHeight(640).setVideoWidth(360).build();
+ TvTrackInfo audioTrack1 =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-stereo-eng")
+ .setLanguage("eng").setAudioChannelCount(2).setAudioSampleRate(48000).build();
+ TvTrackInfo audioTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-mono-esp")
+ .setLanguage("esp").setAudioChannelCount(1).setAudioSampleRate(48000).build();
+ TvTrackInfo subtitleTrack1 =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-eng")
+ .setLanguage("eng").build();
+ TvTrackInfo subtitleTrack2 =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-esp")
+ .setLanguage("esp").build();
+
+ StubTunerTvInputService.injectTrack(videoTrack1, videoTrack2, audioTrack1, audioTrack2,
+ subtitleTrack1, subtitleTrack2);
+
+ final List<TvTrackInfo> tracks = new ArrayList<TvTrackInfo>();
+ Collections.addAll(tracks, videoTrack1, videoTrack2, audioTrack1, audioTrack2,
+ subtitleTrack1, subtitleTrack2);
tryTuneAllChannels(new Runnable() {
@Override
public void run() {
@@ -245,11 +306,70 @@
for (int type : types) {
final int typeF = type;
for (TvTrackInfo track : mTvView.getTracks(type)) {
- selectTrackAndVerify(type, track);
+ selectTrackAndVerify(type, track, tracks);
}
- selectTrackAndVerify(TvTrackInfo.TYPE_SUBTITLE, null);
+ selectTrackAndVerify(TvTrackInfo.TYPE_SUBTITLE, null, tracks);
}
}
});
}
+
+ private void verifyKeyEvent(final KeyEvent keyEvent, final InputEvent[] unhandledEvent) {
+ unhandledEvent[0] = null;
+ mInstrumentation.sendKeySync(keyEvent);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return unhandledEvent[0] != null;
+ }
+ }.run();
+ assertTrue(unhandledEvent[0] instanceof KeyEvent);
+ KeyEvent unhandled = (KeyEvent) unhandledEvent[0];
+ assertEquals(unhandled.getAction(), keyEvent.getAction());
+ assertEquals(unhandled.getKeyCode(), keyEvent.getKeyCode());
+ }
+
+ public void testOnUnhandledInputEventListener() throws Throwable {
+ final InputEvent[] unhandledEvent = { null };
+ mTvView.setOnUnhandledInputEventListener(new TvView.OnUnhandledInputEventListener() {
+ @Override
+ public boolean onUnhandledInputEvent(InputEvent event) {
+ unhandledEvent[0] = event;
+ return true;
+ }
+ });
+
+ StubTunerTvInputService.insertChannels(mActivity.getContentResolver(), mStubInfo);
+
+ Uri uri = TvContract.buildChannelsUriForInput(mStubInfo.getId());
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = mActivity.getContentResolver().query(
+ uri, projection, null, null, null)) {
+ assertNotNull(cursor);
+ assertTrue(cursor.moveToNext());
+ long channelId = cursor.getLong(0);
+ Uri channelUri = TvContract.buildChannelUri(channelId);
+ mTvView.tune(mStubInfo.getId(), channelUri);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mListener.isVideoAvailable(mStubInfo.getId());
+ }
+ }.run();
+ }
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTvView.setFocusable(true);
+ mTvView.requestFocus();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ assertTrue(mTvView.isFocused());
+
+ verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
+ verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
+ }
}
diff --git a/tests/tests/util/src/android/util/cts/ArrayMapTest.java b/tests/tests/util/src/android/util/cts/ArrayMapTest.java
new file mode 100644
index 0000000..7fdd0da
--- /dev/null
+++ b/tests/tests/util/src/android/util/cts/ArrayMapTest.java
@@ -0,0 +1,500 @@
+/*
+ * 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.util.cts;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.test.AndroidTestCase;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+public class ArrayMapTest extends AndroidTestCase {
+ static final boolean DEBUG = false;
+
+ static final int OP_ADD = 1;
+ static final int OP_REM = 2;
+
+ static int[] OPS = new int[] {
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD, OP_ADD,
+ OP_ADD, OP_ADD, OP_ADD,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM,
+ OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM, OP_REM,
+ };
+
+ static int[] KEYS = new int[] {
+ // General adding and removing.
+ -1, 1900, 600, 200, 1200, 1500, 1800, 100, 1900,
+ 2100, 300, 800, 600, 1100, 1300, 2000, 1000, 1400,
+ 600, -1, 1900, 600, 300, 2100, 200, 800, 800,
+ 1800, 1500, 1300, 1100, 2000, 1400, 1000, 1200, 1900,
+
+ // Shrink when removing item from end.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 300, 200, 100,
+
+ // Shrink when removing item from middle.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 200, 300, 100,
+
+ // Shrink when removing item from front.
+ 100, 200, 300, 400, 500, 600, 700, 800, 900,
+ 900, 800, 700, 600, 500, 400, 100, 200, 300,
+
+ // Test hash collisions.
+ 105, 106, 108, 104, 102, 102, 107, 5, 205,
+ 4, 202, 203, 3, 5, 101, 109, 200, 201,
+ 0, -1, 100,
+ 106, 108, 104, 102, 103, 105, 107, 101, 109,
+ -1, 100, 0,
+ 4, 5, 3, 5, 200, 203, 202, 201, 205,
+ };
+
+ public static class ControlledHash implements Parcelable {
+ final int mValue;
+
+ ControlledHash(int value) {
+ mValue = value;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ return mValue == ((ControlledHash)o).mValue;
+ }
+
+ @Override
+ public final int hashCode() {
+ return mValue/100;
+ }
+
+ @Override
+ public final String toString() {
+ return Integer.toString(mValue);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mValue);
+ }
+
+ public static final Parcelable.Creator<ControlledHash> CREATOR
+ = new Parcelable.Creator<ControlledHash>() {
+ public ControlledHash createFromParcel(Parcel in) {
+ return new ControlledHash(in.readInt());
+ }
+
+ public ControlledHash[] newArray(int size) {
+ return new ControlledHash[size];
+ }
+ };
+ }
+
+ private static boolean compare(Object v1, Object v2) {
+ if (v1 == null) {
+ return v2 == null;
+ }
+ if (v2 == null) {
+ return false;
+ }
+ return v1.equals(v2);
+ }
+
+ private static void compareMaps(HashMap map, ArrayMap array) {
+ if (map.size() != array.size()) {
+ fail("Bad size: expected " + map.size() + ", got " + array.size());
+ }
+
+ Set<Map.Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Object expValue = entry.getValue();
+ Object gotValue = array.get(entry.getKey());
+ if (!compare(expValue, gotValue)) {
+ fail("Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + entry.getKey());
+ }
+ }
+
+ for (int i=0; i<array.size(); i++) {
+ Object gotValue = array.valueAt(i);
+ Object key = array.keyAt(i);
+ Object expValue = map.get(key);
+ if (!compare(expValue, gotValue)) {
+ fail("Bad value: expected " + expValue + ", got " + gotValue
+ + " at key " + key);
+ }
+ }
+
+ if (map.entrySet().hashCode() != array.entrySet().hashCode()) {
+ fail("Entry set hash codes differ: map=0x"
+ + Integer.toHexString(map.entrySet().hashCode()) + " array=0x"
+ + Integer.toHexString(array.entrySet().hashCode()));
+ }
+
+ if (!map.entrySet().equals(array.entrySet())) {
+ fail("Failed calling equals on map entry set against array set");
+ }
+
+ if (!array.entrySet().equals(map.entrySet())) {
+ fail("Failed calling equals on array entry set against map set");
+ }
+
+ if (map.keySet().hashCode() != array.keySet().hashCode()) {
+ fail("Key set hash codes differ: map=0x"
+ + Integer.toHexString(map.keySet().hashCode()) + " array=0x"
+ + Integer.toHexString(array.keySet().hashCode()));
+ }
+
+ if (!map.keySet().equals(array.keySet())) {
+ fail("Failed calling equals on map key set against array set");
+ }
+
+ if (!array.keySet().equals(map.keySet())) {
+ fail("Failed calling equals on array key set against map set");
+ }
+
+ if (!map.keySet().containsAll(array.keySet())) {
+ fail("Failed map key set contains all of array key set");
+ }
+
+ if (!array.keySet().containsAll(map.keySet())) {
+ fail("Failed array key set contains all of map key set");
+ }
+
+ if (!array.containsAll(map.keySet())) {
+ fail("Failed array contains all of map key set");
+ }
+
+ if (!map.entrySet().containsAll(array.entrySet())) {
+ fail("Failed map entry set contains all of array entry set");
+ }
+
+ if (!array.entrySet().containsAll(map.entrySet())) {
+ fail("Failed array entry set contains all of map entry set");
+ }
+ }
+
+ private static void validateArrayMap(ArrayMap array) {
+ Set<Map.Entry> entrySet = array.entrySet();
+ int index=0;
+ Iterator<Map.Entry> entryIt = entrySet.iterator();
+ while (entryIt.hasNext()) {
+ Map.Entry entry = entryIt.next();
+ Object value = entry.getKey();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map entry set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ value = entry.getValue();
+ realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map entry set: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+
+ index = 0;
+ Set keySet = array.keySet();
+ Iterator keyIt = keySet.iterator();
+ while (keyIt.hasNext()) {
+ Object value = keyIt.next();
+ Object realValue = array.keyAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map key set: expected key " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+
+ index = 0;
+ Collection valueCol = array.values();
+ Iterator valueIt = valueCol.iterator();
+ while (valueIt.hasNext()) {
+ Object value = valueIt.next();
+ Object realValue = array.valueAt(index);
+ if (!compare(realValue, value)) {
+ fail("Bad array map value col: expected value " + realValue
+ + ", got " + value + " at index " + index);
+ }
+ index++;
+ }
+ }
+
+ private static void compareBundles(Bundle bundle1, Bundle bundle2) {
+ Set<String> keySet1 = bundle1.keySet();
+ Iterator<String> iterator1 = keySet1.iterator();
+ while (iterator1.hasNext()) {
+ String key = iterator1.next();
+ int value1 = bundle1.getInt(key);
+ if (bundle2.get(key) == null) {
+ fail("Bad Bundle: bundle2 didn't have expected key " + key);
+ }
+ int value2 = bundle2.getInt(key);
+ if (value1 != value2) {
+ fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
+ }
+ }
+ Set<String> keySet2 = bundle2.keySet();
+ Iterator<String> iterator2 = keySet2.iterator();
+ while (iterator2.hasNext()) {
+ String key = iterator2.next();
+ if (bundle1.get(key) == null) {
+ fail("Bad Bundle: bundle1 didn't have expected key " + key);
+ }
+ int value1 = bundle1.getInt(key);
+ int value2 = bundle2.getInt(key);
+ if (value1 != value2) {
+ fail("Bad Bundle: at key key " + key + " expected " + value1 + ", got " + value2);
+ }
+ }
+ }
+
+ private static void dump(Map map, ArrayMap array) {
+ Log.e("test", "HashMap of " + map.size() + " entries:");
+ Set<Map.Entry> mapSet = map.entrySet();
+ for (Map.Entry entry : mapSet) {
+ Log.e("test", " " + entry.getKey() + " -> " + entry.getValue());
+ }
+ Log.e("test", "ArrayMap of " + array.size() + " entries:");
+ for (int i=0; i<array.size(); i++) {
+ Log.e("test", " " + array.keyAt(i) + " -> " + array.valueAt(i));
+ }
+ }
+
+ private static void dump(ArrayMap map1, ArrayMap map2) {
+ Log.e("test", "ArrayMap of " + map1.size() + " entries:");
+ for (int i=0; i<map2.size(); i++) {
+ Log.e("test", " " + map1.keyAt(i) + " -> " + map1.valueAt(i));
+ }
+ Log.e("test", "ArrayMap of " + map2.size() + " entries:");
+ for (int i=0; i<map2.size(); i++) {
+ Log.e("test", " " + map2.keyAt(i) + " -> " + map2.valueAt(i));
+ }
+ }
+
+ private static void dump(Bundle bundle1, Bundle bundle2) {
+ Log.e("test", "First Bundle of " + bundle1.size() + " entries:");
+ Set<String> keys1 = bundle1.keySet();
+ for (String key : keys1) {
+ Log.e("test", " " + key + " -> " + bundle1.get(key));
+ }
+ Log.e("test", "Second Bundle of " + bundle2.size() + " entries:");
+ Set<String> keys2 = bundle2.keySet();
+ for (String key : keys2) {
+ Log.e("test", " " + key + " -> " + bundle2.get(key));
+ }
+ }
+
+ public void testBasicArrayMap() {
+ HashMap<ControlledHash, Integer> hashMap = new HashMap<ControlledHash, Integer>();
+ ArrayMap<ControlledHash, Integer> arrayMap = new ArrayMap<ControlledHash, Integer>();
+ Bundle bundle = new Bundle();
+
+ for (int i=0; i<OPS.length; i++) {
+ Integer oldHash;
+ Integer oldArray;
+ ControlledHash key = KEYS[i] < 0 ? null : new ControlledHash(KEYS[i]);
+ String strKey = KEYS[i] < 0 ? null : Integer.toString(KEYS[i]);
+ switch (OPS[i]) {
+ case OP_ADD:
+ if (DEBUG) Log.i("test", "Adding key: " + key);
+ oldHash = hashMap.put(key, i);
+ oldArray = arrayMap.put(key, i);
+ bundle.putInt(strKey, i);
+ break;
+ case OP_REM:
+ if (DEBUG) Log.i("test", "Removing key: " + key);
+ oldHash = hashMap.remove(key);
+ oldArray = arrayMap.remove(key);
+ bundle.remove(strKey);
+ break;
+ default:
+ fail("Bad operation " + OPS[i] + " @ " + i);
+ return;
+ }
+ if (!compare(oldHash, oldArray)) {
+ String msg = "Bad result: expected " + oldHash + ", got " + oldArray;
+ Log.e("test", msg);
+ dump(hashMap, arrayMap);
+ fail(msg);
+ }
+ try {
+ validateArrayMap(arrayMap);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(hashMap, arrayMap);
+ throw e;
+ }
+ try {
+ compareMaps(hashMap, arrayMap);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(hashMap, arrayMap);
+ throw e;
+ }
+ Parcel parcel = Parcel.obtain();
+ bundle.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Bundle bundle2 = parcel.readBundle();
+ try {
+ compareBundles(bundle, bundle2);
+ } catch (Throwable e) {
+ Log.e("test", e.getMessage());
+ dump(bundle, bundle2);
+ throw e;
+ }
+ }
+
+ arrayMap.put(new ControlledHash(50000), 100);
+ ControlledHash lookup = new ControlledHash(50000);
+ Iterator<ControlledHash> it = arrayMap.keySet().iterator();
+ while (it.hasNext()) {
+ if (it.next().equals(lookup)) {
+ it.remove();
+ }
+ }
+ if (arrayMap.containsKey(lookup)) {
+ String msg = "Bad map iterator: didn't remove test key";
+ Log.e("test", msg);
+ dump(hashMap, arrayMap);
+ fail(msg);
+ }
+
+ //Log.e("test", "Test successful; printing final map.");
+ //dump(hashMap, arrayMap);
+ }
+
+ public void testCopyArrayMap() {
+ // map copy constructor test
+ ArrayMap newMap = new ArrayMap<Integer, String>();
+ for (int i = 0; i < 10; ++i) {
+ newMap.put(i, String.valueOf(i));
+ }
+ ArrayMap mapCopy = new ArrayMap(newMap);
+ if (!compare(mapCopy, newMap)) {
+ String msg = "ArrayMap copy constructor failure: expected " +
+ newMap + ", got " + mapCopy;
+ Log.e("test", msg);
+ dump(newMap, mapCopy);
+ fail(msg);
+ return;
+ }
+ }
+
+ public void testEqualsArrayMap() {
+ ArrayMap<Integer, String> map1 = new ArrayMap<Integer, String>();
+ ArrayMap<Integer, String> map2 = new ArrayMap<Integer, String>();
+ HashMap<Integer, String> map3 = new HashMap<Integer, String>();
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ fail("ArrayMap equals failure for empty maps " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ for (int i = 0; i < 10; ++i) {
+ String value = String.valueOf(i);
+ map1.put(i, value);
+ map2.put(i, value);
+ map3.put(i, value);
+ }
+ if (!compare(map1, map2) || !compare(map1, map3) || !compare(map3, map2)) {
+ fail("ArrayMap equals failure for populated maps " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ map1.remove(0);
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ fail("ArrayMap equals failure for map size " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+
+ map1.put(0, "-1");
+ if (compare(map1, map2) || compare(map1, map3) || compare(map3, map1)) {
+ fail("ArrayMap equals failure for map contents " + map1 + ", " +
+ map2 + ", " + map3);
+ }
+ }
+
+ /**
+ * Test creating a malformed array map with duplicated keys and that we will catch this
+ * when unparcelling.
+ */
+ public void testDuplicateKeys() throws NoSuchMethodException,
+ InvocationTargetException, IllegalAccessException, NoSuchFieldException {
+ ArrayMap<String, Object> map1 = new ArrayMap(2);
+
+ Method appendMethod = ArrayMap.class.getMethod("append", Object.class, Object.class);
+ appendMethod.invoke(map1, Integer.toString(100000), "foo");
+ appendMethod.invoke(map1, Integer.toString(100000), "bar");
+
+ // Now parcel/unparcel, and verify we get the expected error.
+ Parcel parcel = Parcel.obtain();
+ Method writeArrayMapMethod = Parcel.class.getMethod("writeArrayMap", ArrayMap.class);
+ writeArrayMapMethod.invoke(parcel, map1);
+ parcel.setDataPosition(0);
+ ArrayMap<String, Object> map2 = new ArrayMap(2);
+
+ try {
+ Parcel.class.getMethod("readArrayMap", ArrayMap.class, ClassLoader.class).invoke(
+ parcel, map2, null);
+ } catch (InvocationTargetException e) {
+ Throwable cause = e.getCause();
+ if (cause instanceof IllegalArgumentException) {
+ // Good!
+ return;
+ }
+ throw e;
+ }
+
+ String msg = "Didn't throw expected IllegalArgumentException";
+ Log.e("test", msg);
+ dump(map1, map2);
+ fail(msg);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/GridViewTest.java b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
index 1dd4764..bd42e35 100644
--- a/tests/tests/widget/src/android/widget/cts/GridViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GridViewTest.java
@@ -234,7 +234,21 @@
}
public void testSetHorizontalSpacing() {
+ testSetHorizontalSpacing(View.LAYOUT_DIRECTION_LTR);
+ }
+
+ public void testSetHorizontalSpacingRTL() {
+ testSetHorizontalSpacing(View.LAYOUT_DIRECTION_RTL);
+ }
+
+ public void testSetHorizontalSpacing(final int layoutDir) {
mGridView = findGridViewById(R.id.gridview);
+ mActivity.runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mGridView.setLayoutDirection(layoutDir);
+ }
+ });
mGridView.setStretchMode(GridView.NO_STRETCH);
// Number of columns should be big enough, otherwise the
// horizontal spacing cannot be correctly verified.
@@ -252,7 +266,11 @@
View child0 = mGridView.getChildAt(0);
View child1 = mGridView.getChildAt(1);
- assertEquals(0, child1.getLeft() - child0.getRight());
+ if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
+ assertEquals(0, child1.getLeft() - child0.getRight());
+ } else {
+ assertEquals(0, child0.getLeft() - child1.getRight());
+ }
mActivity.runOnUiThread(new Runnable() {
public void run() {
@@ -263,7 +281,11 @@
child0 = mGridView.getChildAt(0);
child1 = mGridView.getChildAt(1);
- assertEquals(5, child1.getLeft() - child0.getRight());
+ if (layoutDir == View.LAYOUT_DIRECTION_LTR) {
+ assertEquals(5, child1.getLeft() - child0.getRight());
+ } else {
+ assertEquals(5, child0.getLeft() - child1.getRight());
+ }
}
public void testSetVerticalSpacing() {
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/Test_invoke_direct.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/Test_invoke_direct.java
index 1772f22..c5402b3 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/Test_invoke_direct.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/Test_invoke_direct.java
@@ -111,8 +111,9 @@
//@uses dot.junit.opcodes.invoke_direct.TSuper
try {
new T_invoke_direct_6();
- fail("expected NoSuchMethodError");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
+ } catch (Throwable t) {
+ DxUtil.checkVerifyException(t);
}
}
@@ -241,14 +242,22 @@
/**
* @constraint n/a
* @title Attempt to invoke static method. Java throws IncompatibleClassChangeError
- * on first access but Dalvik throws VerifyError on class loading.
+ * on first access. Dalvik threw VerifyError on class loading.
*/
public void testVFE15() {
try {
- Class.forName("dot.junit.opcodes.invoke_direct.d.T_invoke_direct_11");
- fail("expected a verification exception");
+ Class<?> c = Class.forName("dot.junit.opcodes.invoke_direct.d.T_invoke_direct_11");
+ // Attempt to instantiate and run.
+ Object o = c.newInstance();
+ java.lang.reflect.Method m = c.getDeclaredMethod("run");
+ m.invoke(o);
+ fail("expected an invocation target exception with an incompatible class change error");
+ } catch (java.lang.reflect.InvocationTargetException ite) {
+ if (!(ite.getCause() instanceof IncompatibleClassChangeError)) {
+ fail("expected an incompatible class change error");
+ }
} catch (Throwable t) {
- DxUtil.checkVerifyException(t);
+ throw new RuntimeException(t);
}
}
@@ -277,8 +286,7 @@
//@uses dot.junit.opcodes.invoke_direct.TAbstract
try {
new T_invoke_direct_13().run();
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7.d
index 2c1b99b..13432bd 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7.d
@@ -27,7 +27,7 @@
.method public run()I
.limit regs 3
- invoke-direct {v1}, dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7/toInt()I
+ invoke-direct {v2}, dot/junit/opcodes/invoke_direct/d/T_invoke_direct_7/toInt()I
const v0, 0
return v0
.end method
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/Test_invoke_direct_range.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/Test_invoke_direct_range.java
index d4f194b..1fe95c2 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/Test_invoke_direct_range.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/Test_invoke_direct_range.java
@@ -111,8 +111,9 @@
//@uses dot.junit.opcodes.invoke_direct_range.TSuper
try {
new T_invoke_direct_range_6();
- fail("expected NoSuchMethodError");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
+ } catch (Throwable t) {
+ DxUtil.checkVerifyException(t);
}
}
@@ -241,14 +242,22 @@
/**
* @constraint n/a
* @title Attempt to invoke static method. Java throws IncompatibleClassChangeError
- * on first access but Dalvik throws VerifyError on class loading.
+ * on first access. Dalvik threw VerifyError on class loading.
*/
public void testVFE15() {
try {
- Class.forName("dot.junit.opcodes.invoke_direct_range.d.T_invoke_direct_range_11");
- fail("expected a verification exception");
+ Class<?> c = Class.forName("dot.junit.opcodes.invoke_direct_range.d.T_invoke_direct_range_11");
+ // Attempt to instantiate and run.
+ Object o = c.newInstance();
+ java.lang.reflect.Method m = c.getDeclaredMethod("run");
+ m.invoke(o);
+ fail("expected an invocation target exception with an incompatible class change error");
+ } catch (java.lang.reflect.InvocationTargetException ite) {
+ if (!(ite.getCause() instanceof IncompatibleClassChangeError)) {
+ fail("expected an incompatible class change error");
+ }
} catch (Throwable t) {
- DxUtil.checkVerifyException(t);
+ throw new RuntimeException(t);
}
}
@@ -278,8 +287,7 @@
//@uses dot.junit.opcodes.invoke_direct_range.TAbstract
try {
new T_invoke_direct_range_13().run();
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7.d b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7.d
index f0254f4..0e7aab0 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7.d
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7.d
@@ -27,7 +27,7 @@
.method public run()I
.limit regs 3
- invoke-direct/range {v1}, dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7/toInt()I
+ invoke-direct/range {v2}, dot/junit/opcodes/invoke_direct_range/d/T_invoke_direct_range_7/toInt()I
const v0, 0
return v0
.end method
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface/Test_invoke_interface.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface/Test_invoke_interface.java
index f846374..69c54e1 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface/Test_invoke_interface.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface/Test_invoke_interface.java
@@ -166,8 +166,7 @@
public void testVFE2() {
try {
new T_invoke_interface_4().run();
- fail("expected NoSuchMethodError or IncompatibleClassChangeError");
- } catch (NoSuchMethodError t) {
+ fail("expected an IncompatibleClassChangeError");
} catch (IncompatibleClassChangeError e) {
}
}
@@ -220,8 +219,7 @@
//@uses dot.junit.opcodes.invoke_interface.d.T_invoke_interface_18
try {
new T_invoke_interface_18().run(new ITestImpl());
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}
@@ -236,8 +234,7 @@
//@uses dot.junit.opcodes.invoke_interface.ITestImpl
try {
new T_invoke_interface_20().run(new ITestImpl());
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}
diff --git a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface_range/Test_invoke_interface_range.java b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface_range/Test_invoke_interface_range.java
index 9f98d4c..5af318a 100644
--- a/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface_range/Test_invoke_interface_range.java
+++ b/tools/vm-tests-tf/src/dot/junit/opcodes/invoke_interface_range/Test_invoke_interface_range.java
@@ -143,8 +143,7 @@
public void testVFE2() {
try {
new T_invoke_interface_range_4().run();
- fail("expected NoSuchMethodError or IncompatibleClassChangeError");
- } catch (NoSuchMethodError t) {
+ fail("expected an IncompatibleClassChangeError");
} catch (IncompatibleClassChangeError e) {
}
}
@@ -197,8 +196,7 @@
//@uses dot.junit.opcodes.invoke_interface_range.d.T_invoke_interface_range_18
try {
new T_invoke_interface_range_18().run(new ITestImpl());
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}
@@ -213,8 +211,7 @@
//@uses dot.junit.opcodes.invoke_interface_range.ITestImpl
try {
new T_invoke_interface_range_20().run(new ITestImpl());
- fail("expected NoSuchMethodError or verification exception");
- } catch (NoSuchMethodError t) {
+ fail("expected a verification exception");
} catch (Throwable t) {
DxUtil.checkVerifyException(t);
}