Merge "Add Virtualizer audio effect tests for capabilities API" into lmp-mr1-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 12b89b9..16f9ec1 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -68,6 +68,7 @@
CtsDeviceTaskswitchingAppB \
CtsDeviceTaskswitchingControl \
CtsDeviceUi \
+ CtsHostsideNetworkTestsApp \
CtsIntentReceiverApp \
CtsIntentSenderApp \
CtsManagedProfileApp \
@@ -174,6 +175,7 @@
CtsDevicePolicyManagerTestCases \
CtsDumpsysHostTestCases \
CtsHostJank \
+ CtsHostsideNetworkTests \
CtsHostUi \
CtsMonkeyTestCases \
CtsThemeHostTestCases \
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index a531f3b..22540b8 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -123,6 +123,21 @@
"android.tonemap.mode": 1,
}
+def fastest_auto_capture_request(props):
+ """Return an auto capture request for the fastest capture.
+
+ Args:
+ props: the object returned from its.device.get_camera_properties().
+
+ Returns:
+ A capture request with everything set to auto and all filters that
+ may slow down capture set to OFF or FAST if possible
+ """
+ req = auto_capture_request()
+ turn_slow_filters_off(props, req)
+
+ return req
+
def get_available_output_sizes(fmt, props):
"""Return a sorted list of available output sizes for a given format.
@@ -143,16 +158,16 @@
return out_sizes
def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
- """ Check and set controlKey to off or fast in req
+ """Check and set controlKey to off or fast in req.
Args:
props: the object returned from its.device.get_camera_properties().
- req: the input request.
+ req: the input request. filter will be set to OFF or FAST if possible.
available_modes: the key to check available modes.
filter: the filter key
Returns:
- None. control_key will be set to OFF or FAST if possible.
+ Nothing.
"""
if props.has_key(available_modes):
if 0 in props[available_modes]:
@@ -160,6 +175,33 @@
elif 1 in props[available_modes]:
req[filter] = 1
+def turn_slow_filters_off(props, req):
+ """Turn filters that may slow FPS down to OFF or FAST in input request.
+
+ This function modifies the request argument, such that filters that may
+ reduce the frames-per-second throughput of the camera device will be set to
+ OFF or FAST if possible.
+
+ Args:
+ props: the object returned from its.device.get_camera_properties().
+ req: the input request.
+
+ Returns:
+ Nothing.
+ """
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.noiseReduction.availableNoiseReductionModes",
+ "android.noiseReduction.mode")
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.colorCorrection.availableAberrationModes",
+ "android.colorCorrection.aberrationMode")
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.hotPixel.availableHotPixelModes",
+ "android.hotPixel.mode")
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.edge.availableEdgeModes",
+ "android.edge.mode")
+
def get_fastest_manual_capture_settings(props):
"""Return a capture request and format spec for the fastest capture.
@@ -178,18 +220,7 @@
e = min(props['android.sensor.info.exposureTimeRange'])
req = manual_capture_request(s,e)
- set_filter_off_or_fast_if_possible(props, req,
- "android.noiseReduction.availableNoiseReductionModes",
- "android.noiseReduction.mode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.colorCorrection.availableAberrationModes",
- "android.colorCorrection.aberrationMode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.hotPixel.availableHotPixelModes",
- "android.hotPixel.mode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.edge.availableEdgeModes",
- "android.edge.mode")
+ turn_slow_filters_off(props, req)
return req, out_spec
diff --git a/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py b/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
index fdf72be..87500c7 100644
--- a/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
+++ b/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
@@ -48,7 +48,7 @@
cam.do_3a(lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.blackLevel.lock"] = True
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
diff --git a/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py b/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
index a8d1d45..932c051 100644
--- a/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
+++ b/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
@@ -47,7 +47,7 @@
cam.do_3a(lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.blackLevel.lock"] = True
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1/test_locked_burst.py
index 5cea30c..90662db 100644
--- a/apps/CameraITS/tests/scene1/test_locked_burst.py
+++ b/apps/CameraITS/tests/scene1/test_locked_burst.py
@@ -41,7 +41,7 @@
cam.do_3a(do_af=True, lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 3aa620e..13d6dc3 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
android:versionCode="5"
- android:versionName="5.0_r1.92">
+ android:versionName="5.0_r1.94">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="21"/>
@@ -242,8 +242,8 @@
<service android:name=".bluetooth.BleScannerService"
android:label="@string/ble_scanner_service_name" />
- <!-- TODO: Enable when test quality issues listed in b/18283088 is resolved -->
- <!-- activity android:name=".bluetooth.BleClientTestActivity"
+ <!-- Uncomment until b/15657182, b/18283088 fixed
+ <activity android:name=".bluetooth.BleClientStartActivity"
android:label="@string/ble_client_test_name"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
@@ -254,98 +254,9 @@
<meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
<meta-data android:name="test_required_features"
android:value="android.hardware.bluetooth_le"/>
- </activity -->
-
- <activity android:name=".bluetooth.BleClientConnectActivity"
- android:label="@string/ble_client_connect_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.BleClientTestActivity" />
</activity>
- <activity android:name=".bluetooth.BleDiscoverServiceActivity"
- android:label="@string/ble_discover_service_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleClientCharacteristicActivity"
- android:label="@string/ble_client_characteristic_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleNotifyCharacteristicActivity"
- android:label="@string/ble_notify_characteristic_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleClientDescriptorActivity"
- android:label="@string/ble_client_descriptor_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleReliableWriteActivity"
- android:label="@string/ble_reliable_write_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleReadRssiActivity"
- android:label="@string/ble_read_rssi_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.BleClientTestActivity" />
- </activity>
-
- <activity android:name=".bluetooth.BleClientDisconnectActivity"
- android:label="@string/ble_client_disconnect_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.BleClientTestActivity" />
- </activity>
-
- <!-- TODO: Enable when test quality issues listed in b/18283088 is resolved -->
- <!-- activity android:name=".bluetooth.BleServerStartActivity"
+ <activity android:name=".bluetooth.BleServerStartActivity"
android:label="@string/ble_server_start_name"
android:configChanges="keyboardHidden|orientation|screenSize">
<intent-filter>
@@ -356,7 +267,7 @@
<meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
<meta-data android:name="test_required_features"
android:value="android.hardware.bluetooth_le"/>
- </activity -->
+ </activity> -->
<!-- TODO: Enable when test quality issues listed in b/18282549 is resolved -->
<!-- activity android:name=".bluetooth.BleScannerTestActivity"
@@ -1120,6 +1031,19 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_notifications" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.watch" />
+ </activity>
+
+ <activity android:name=".notifications.PackagePriorityVerifierActivity"
+ android:label="@string/package_priority_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_notifications" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.watch" />
</activity>
<service android:name=".notifications.MockListener"
@@ -1226,6 +1150,8 @@
<meta-data android:name="test_category" android:value="@string/test_category_other" />
<meta-data android:name="test_required_features"
android:value="android.software.app_widgets" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.software.leanback" />
</activity>
<activity android:name=".deskclock.DeskClockTestsActivity"
@@ -1452,7 +1378,30 @@
<!-- Used by the SensorTestScreenManipulator to reset the screen timeout after turn off. -->
<activity android:name=".os.TimeoutResetActivity"/>
- <activity android:name=".tv.TvInputDiscoveryTestActivity" android:label="@string/tv">
+ <activity android:name=".tv.TvInputDiscoveryTestActivity"
+ android:label="@string/tv_input_discover_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_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
+ <activity android:name=".tv.ParentalControlTestActivity"
+ android:label="@string/tv_parental_control_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_tv" />
+ <meta-data android:name="test_required_features"
+ android:value="android.software.live_tv" />
+ </activity>
+
+ <activity android:name=".tv.MultipleTracksTestActivity"
+ android:label="@string/tv_multiple_tracks_test">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
@@ -1483,6 +1432,14 @@
android:resource="@xml/mock_tv_input_service" />
</service>
+ <receiver android:name=".tv.TvInputReceiver">
+ <intent-filter>
+ <action android:name="android.media.tv.action.QUERY_CONTENT_RATING_SYSTEMS" />
+ </intent-filter>
+ <meta-data android:name="android.media.tv.metadata.CONTENT_RATING_SYSTEMS"
+ android:resource="@xml/mock_content_rating_systems" />
+ </receiver>
+
</application>
</manifest>
diff --git a/apps/CtsVerifier/res/layout/ble_client_connect.xml b/apps/CtsVerifier/res/layout/ble_client_connect.xml
deleted file mode 100644
index 30b4edb..0000000
--- a/apps/CtsVerifier/res/layout/ble_client_connect.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?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"
- >
- <Button android:id="@+id/ble_scan_start"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ble_scan_start"/>
- <Button android:id="@+id/ble_scan_stop"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ble_scan_stop"/>
- </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_client_read_write.xml b/apps/CtsVerifier/res/layout/ble_client_read_write.xml
deleted file mode 100644
index a263916..0000000
--- a/apps/CtsVerifier/res/layout/ble_client_read_write.xml
+++ /dev/null
@@ -1,71 +0,0 @@
-<?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"
- >
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- >
- <EditText android:id="@+id/write_text"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:text="@string/ble_test_text"
- android:hint="@string/ble_write_hint"
- android:padding="10dip"
- />
- <Button android:id="@+id/ble_write"
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:text="@string/ble_write"
- />
- </LinearLayout>
-
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- >
- <TextView android:id="@+id/read_text"
- android:layout_width="0dp"
- android:layout_weight="1"
- android:layout_height="wrap_content"
- android:hint="@string/ble_read_hint"
- android:padding="10dip"
- android:textSize="18sp"
- />
- <Button android:id="@+id/ble_read"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/ble_read"
- />
- </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_client_test.xml b/apps/CtsVerifier/res/layout/ble_client_start.xml
similarity index 79%
rename from apps/CtsVerifier/res/layout/ble_client_test.xml
rename to apps/CtsVerifier/res/layout/ble_client_start.xml
index 660abd5..c377ca1 100644
--- a/apps/CtsVerifier/res/layout/ble_client_test.xml
+++ b/apps/CtsVerifier/res/layout/ble_client_start.xml
@@ -19,15 +19,17 @@
android:orientation="vertical"
android:padding="10dip"
>
- <ListView android:id="@+id/ble_client_tests"
- android:layout_height="fill_parent"
+ <include android:id="@+id/pass_fail_buttons"
android:layout_width="match_parent"
- android:padding="10dip"
- />
-
- <include android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
layout="@layout/pass_fail_buttons"
/>
-</RelativeLayout>
\ No newline at end of file
+ <ListView android:id="@+id/ble_server_tests"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"
+ android:layout_above="@id/pass_fail_buttons"
+ android:layout_alignParentTop="true"
+ android:padding="10dip"
+ />
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml b/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml
deleted file mode 100644
index 786918a..0000000
--- a/apps/CtsVerifier/res/layout/ble_notify_characteristic.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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"
- >
- <RelativeLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- >
- <Button android:id="@+id/ble_notify"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="@string/ble_begin_notification"
- android:padding="10dip"
- />
- <TextView android:id="@+id/ble_notify_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_below="@id/ble_notify"
- android:textSize="20sp"
- android:padding="10dip"
- />
- </RelativeLayout>
-
- <include android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- layout="@layout/pass_fail_buttons"
- />
-</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_read_rssi.xml b/apps/CtsVerifier/res/layout/ble_read_rssi.xml
deleted file mode 100644
index 8aa3193..0000000
--- a/apps/CtsVerifier/res/layout/ble_read_rssi.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-<?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"
- >
- <RelativeLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true"
- >
- <Button android:id="@+id/ble_read_rssi"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:text="@string/ble_read_rssi"
- android:padding="10dip"
- />
- <TextView android:id="@+id/ble_rssi_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerHorizontal="true"
- android:layout_below="@id/ble_read_rssi"
- android:textSize="20sp"
- android:padding="10dip"
- />
- </RelativeLayout>
-
- <include android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- layout="@layout/pass_fail_buttons"
- />
-</RelativeLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/ble_reliable_write.xml b/apps/CtsVerifier/res/layout/ble_reliable_write.xml
deleted file mode 100644
index 05b1812..0000000
--- a/apps/CtsVerifier/res/layout/ble_reliable_write.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?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"
- >
- <EditText android:id="@+id/write_text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/ble_test_text"
- android:hint="@string/ble_write_hint"
- android:padding="5dip"
- />
- <LinearLayout android:orientation="horizontal"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- >
- <Button android:id="@+id/ble_begin"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/ble_begin_write"
- />
- <Button android:id="@+id/ble_write"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/ble_write"
- />
- <Button android:id="@+id/ble_execute"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/ble_execute_write"
- />
- </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_server_start.xml b/apps/CtsVerifier/res/layout/ble_server_start.xml
index 9ce714d..c377ca1 100644
--- a/apps/CtsVerifier/res/layout/ble_server_start.xml
+++ b/apps/CtsVerifier/res/layout/ble_server_start.xml
@@ -19,7 +19,7 @@
android:orientation="vertical"
android:padding="10dip"
>
- <include android:id="@+id/pass_fail_buttons"
+ <include android:id="@+id/pass_fail_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
@@ -32,4 +32,4 @@
android:layout_alignParentTop="true"
android:padding="10dip"
/>
-</RelativeLayout>
\ No newline at end of file
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_server_start_item.xml b/apps/CtsVerifier/res/layout/ble_test_item.xml
similarity index 100%
rename from apps/CtsVerifier/res/layout/ble_server_start_item.xml
rename to apps/CtsVerifier/res/layout/ble_test_item.xml
diff --git a/apps/CtsVerifier/res/layout/tv_overlay.xml b/apps/CtsVerifier/res/layout/tv_overlay.xml
new file mode 100644
index 0000000..8cd5dd8
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/tv_overlay.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:id="@+id/overlay_view_text"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="20sp"
+ android:text="@string/overlay_view_text">
+ </TextView>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 383dcae..b1ee418 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -188,14 +188,16 @@
<!-- BLE client side strings -->
<string name="ble_client_service_name">Bluetooth LE GATT Client Handler Service</string>
<string name="ble_client_test_name">BLE Client Test</string>
- <string name="ble_client_connect_name">1. BLE Client Connect</string>
- <string name="ble_discover_service_name">2. BLE Discover Service</string>
- <string name="ble_client_characteristic_name">3. BLE Read/Write Characteristic</string>
- <string name="ble_reliable_write_name">4. BLE Reliable Write</string>
- <string name="ble_notify_characteristic_name">5. BLE Notify Characteristic</string>
- <string name="ble_client_descriptor_name">6. BLE Read/Write Descriptor</string>
- <string name="ble_read_rssi_name">7. BLE Read RSSI</string>
- <string name="ble_client_disconnect_name">8. BLE Client Disconnect</string>
+ <string name="ble_client_connect_name">BLE Client Connect</string>
+ <string name="ble_discover_service_name">BLE Discover Service</string>
+ <string name="ble_read_characteristic_name">BLE Read Characteristic</string>
+ <string name="ble_write_characteristic_name">BLE Write Characteristic</string>
+ <string name="ble_reliable_write_name">BLE Reliable Write</string>
+ <string name="ble_notify_characteristic_name">BLE Notify Characteristic</string>
+ <string name="ble_read_descriptor_name">BLE Read Descriptor</string>
+ <string name="ble_write_descriptor_name">BLE Write Descriptor</string>
+ <string name="ble_read_rssi_name">BLE Read RSSI</string>
+ <string name="ble_client_disconnect_name">BLE Client Disconnect</string>
<string name="ble_client_test_info">The BLE test must be done simultaneously on two devices. This device is the client. All tests listed here must be done in order.</string>
<string name="ble_client_send_connect_info">Type in the Bluetooth address of the remote device to connect to, and verify that the devices are connected.</string>
<string name="ble_discover_service_info">Verify that the service is discovered when you press the "Discover Service" button.</string>
@@ -969,6 +971,14 @@
itself according to the current rotation of the device.</string>
<string name="test_category_notifications">Notifications</string>
+ <string name="package_priority_test">Notification Package Priority Test</string>
+ <string name="package_priority_info">This test checks that the NotificationManagerService respects
+ user preferences about relative package priorities.
+ </string>
+ <string name="package_priority_high">Find \"%s\" under \"App notifications\" in the \"Sound & notifications\" settings panel, and mark it as having notification priority.</string>
+ <string name="package_priority_default">Find \"%s\" under \"App notifications\" in the \"Sound & notifications\" settings panel, and make sure it has default priority.</string>
+ <string name="package_priority_user_order">Check that ranker respects user priorities.</string>
+
<string name="attention_test">Notification Attention Management Test</string>
<string name="attention_info">This test checks that the NotificationManagerService is
respecting user preferences about notification ranking and filtering.
@@ -1322,29 +1332,88 @@
<string name="js_any_connectivity_test">Device with no connectivity will not execute a job with an unmetered connectivity constraint.</string>
<string name="js_no_connectivity_test">Device with no connectivity will still execute a job with no connectivity constraints.</string>
- <!-- String for TV app Tests -->
- <string name="tv">TV App Behavior Verifier</string>
- <string name="tv_info">
- This test verifies that the default TV app is invoked via intents and issues appropriate
- calls to framework APIs, so that TV input apps work properly with the default TV app.
- </string>
+ <!-- String for Live Channels app Tests -->
- <string name="tv_input_discover_test">TV app input discoverability test</string>
+ <string name="tv_input_discover_test">3rd-party TV input app discoverability test</string>
+ <string name="tv_input_discover_test_info">
+ This test verifies that the pre-loaded Live Channels app is invoked via intents and issues
+ appropriate calls to framework APIs, so that TV input apps work properly with the pre-loaded
+ Live Channels app.
+ </string>
<string name="tv_input_discover_test_go_to_setup">
- Press the \"Launch TV app\" button, and set up the newly installed TV input: \"CTS Verifier\".
+ Press the \"Launch Live Channels\" button, and set up the newly installed TV input:
+ \"CTS Verifier\".
</string>
<string name="tv_input_discover_test_verify_setup">
Setup activity must have been started.
</string>
<string name="tv_input_discover_test_tune_to_channel">
- Press the \"Launch TV app\" button, and tune to the channel named \"Dummy\" from
+ Press the \"Launch Live Channels\" button, and tune to the channel named \"Dummy\" from
\"CTS Verifier\" input. If necessary, configure the channel to be visible.
</string>
<string name="tv_input_discover_test_verify_tune">
Tune command must be called.
</string>
+ <string name="tv_input_discover_test_verify_overlay_view">
+ Overlay view must be shown. Verify that there is a text view displaying \"Overlay View Dummy Text\"
+ when you tune to the \"Dummy\" channel.
+ </string>
- <string name="tv_launch_tv_app">Launch TV app</string>
+ <string name="tv_parental_control_test">Live Channels app parental control test</string>
+ <string name="tv_parental_control_test_info">
+ This test verifies that the default Live Channels app invokes proper parental control APIs in
+ the framework.
+ </string>
+ <string name="tv_parental_control_test_turn_on_parental_control">
+ Press the \"Launch Live Channels\" button, and turn on the parental control. If it\'s on
+ already, turn it off and on again.
+ </string>
+ <string name="tv_parental_control_test_verify_receive_broadcast1">
+ TV input service must have received ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED broadcast.
+ </string>
+ <string name="tv_parental_control_test_block_tv_ma">
+ Press the \"Launch Live Channels\" button, and block \"Fake\" rating for \"CtsVerifier\" rating
+ system in the parental control settings. You may have to enable the rating system if it is
+ disabled by default. If it\'s already blocked, unblock, save, and then block again.
+ </string>
+ <string name="tv_parental_control_test_verify_receive_broadcast2">
+ TV input service must have received ACTION_BLOCKED_RATINGS_CHANGED broadcast.
+ </string>
+ <string name="tv_parental_control_test_block_unblock">
+ Press the \"Launch Live Channels\" button; verify that the channel is blocked visually.
+ Try unblock the screen by entering PIN; verify that it\'s unblocked visually.
+ </string>
+
+ <string name="tv_launch_tv_app">Launch Live Channels</string>
+ <string name="tv_channel_not_found">
+ CtsVerifier channel is not set up. Please set up before proceeding.
+ </string>
+
+ <string name="tv_multiple_tracks_test">Live Channels app multiple tracks / subtitle test</string>
+ <string name="tv_multiple_tracks_test_info">
+ This test verifies that the default Live Channels app invokes proper mulitple tracks / subtitle
+ APIs in the framework.
+ </string>
+ <string name="tv_multiple_tracks_test_select_subtitle">
+ Press the \"Launch Live Channels\" button. Verify that the closed caption is off by default.
+ Set closed caption to English.
+ </string>
+ <string name="tv_multiple_tracks_test_verify_set_caption_enabled">
+ Caption should be enabled.
+ </string>
+ <string name="tv_multiple_tracks_test_verify_select_subtitle">
+ The English subtitle track should be selected.
+ </string>
+ <string name="tv_multiple_tracks_test_select_audio">
+ Press the \"Launch Live Channels\" button. Verify that the audio track is English by default.
+ Select Spanish audio track.
+ </string>
+ <string name="tv_multiple_tracks_test_verify_select_audio">
+ The Spanish audio track should be selected.
+ </string>
+
+ <string name="overlay_view_text">Overlay View Dummy Text</string>
+ <string name="fake_rating">Fake</string>
<!-- A list of fully-qualified test classes that should not be run. -->
<string-array name="disabled_tests" />
diff --git a/apps/CtsVerifier/res/xml/mock_content_rating_systems.xml b/apps/CtsVerifier/res/xml/mock_content_rating_systems.xml
new file mode 100644
index 0000000..245d7f5
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/mock_content_rating_systems.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<rating-system-definitions xmlns:android="http://schemas.android.com/apk/res/android"
+ android:versionCode="1">
+ <rating-system-definition android:name="CTS_VERIFIER"
+ android:title="CtsVerifier"
+ android:description="@string/app_name">
+ <rating-definition android:name="MOCK_FAKE"
+ android:title="Fake"
+ android:contentAgeHint="0"
+ android:description="@string/fake_rating" />
+ </rating-system-definition>
+</rating-system-definitions>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java
deleted file mode 100644
index 1e1941f..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientCharacteristicActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.bluetooth;
-
-public class BleClientCharacteristicActivity extends BleReadWriteActivity {
- public BleClientCharacteristicActivity() {
- super(BleReadWriteActivity.CHARACTERISTIC);
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java
deleted file mode 100755
index 4e1c268..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientConnectActivity.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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.EditText;
-import android.widget.Toast;
-
-public class BleClientConnectActivity extends PassFailButtons.Activity {
-
- private EditText mEditText;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_client_connect);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_client_connect_name,
- R.string.ble_client_send_connect_info, -1);
- getPassButton().setEnabled(false);
-
- ((Button) findViewById(R.id.ble_scan_start)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleClientConnectActivity.this,
- BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_SCAN_START);
- startService(intent);
- }
- });
-
- ((Button) findViewById(R.id.ble_scan_stop)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleClientConnectActivity.this,
- BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_SCAN_STOP);
- startService(intent);
- }
- });
-
- IntentFilter filter = new IntentFilter();
- filter.addAction(BleClientService.BLE_BLUETOOTH_CONNECTED);
- registerReceiver(onBroadcast, filter);
- }
-
- @Override
- protected void onDestroy(){
- super.onDestroy();
- unregisterReceiver(onBroadcast);
- }
-
- private void showMessage(String msg) {
- Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
- }
-
- private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- showMessage("Bluetooth LE connected");
- getPassButton().setEnabled(true);
- }
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java
deleted file mode 100644
index ab2229a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDescriptorActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.bluetooth;
-
-public class BleClientDescriptorActivity extends BleReadWriteActivity {
- public BleClientDescriptorActivity() {
- super(BleReadWriteActivity.DESCRIPTOR);
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java
deleted file mode 100644
index 083d327..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientDisconnectActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.bluetooth;
-
-public class BleClientDisconnectActivity extends BleButtonActivity {
- public BleClientDisconnectActivity() {
- super(BleButtonActivity.DISCONNECT);
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
index 6765362..10f862d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java
@@ -96,6 +96,8 @@
"com.android.cts.verifier.bluetooth.EXTRA_DESCRIPTOR_VALUE";
public static final String EXTRA_RSSI_VALUE =
"com.android.cts.verifier.bluetooth.EXTRA_RSSI_VALUE";
+ public static final String EXTRA_ERROR_MESSAGE =
+ "com.android.cts.verifier.bluetooth.EXTRA_ERROR_MESSAGE";
private static final UUID SERVICE_UUID =
UUID.fromString("00009999-0000-1000-8000-00805f9b34fb");
@@ -106,13 +108,15 @@
private static final UUID DESCRIPTOR_UUID =
UUID.fromString("00009996-0000-1000-8000-00805f9b34fb");
+ private static final String WRITE_VALUE = "TEST";
+
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
private BluetoothDevice mDevice;
private BluetoothGatt mBluetoothGatt;
+ private BluetoothLeScanner mScanner;
private Handler mHandler;
private Context mContext;
- private BluetoothLeScanner mScanner;
@Override
public void onCreate() {
@@ -120,14 +124,14 @@
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
+ mScanner = mBluetoothAdapter.getBluetoothLeScanner();
mHandler = new Handler();
mContext = this;
- mScanner = mBluetoothAdapter.getBluetoothLeScanner();
+ startScan();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
- if (intent != null) handleIntent(intent);
return START_NOT_STICKY;
}
@@ -141,68 +145,10 @@
super.onDestroy();
mBluetoothGatt.disconnect();
mBluetoothGatt.close();
+ mBluetoothGatt = null;
stopScan();
}
- private void handleIntent(Intent intent) {
- int command = intent.getIntExtra(EXTRA_COMMAND, -1);
- String address = intent.getStringExtra(BluetoothDevice.EXTRA_DEVICE); // sometimes null
- String writeValue = intent.getStringExtra(EXTRA_WRITE_VALUE); // sometimes null
- boolean enable = intent.getBooleanExtra(EXTRA_BOOL, false);
- BluetoothGattService service;
- BluetoothGattCharacteristic characteristic;
- BluetoothGattDescriptor descriptor;
-
- switch (command) {
- case COMMAND_CONNECT:
- mDevice = mBluetoothAdapter.getRemoteDevice(address);
- mBluetoothGatt = mDevice.connectGatt(this, false, mGattCallbacks);
- break;
- case COMMAND_DISCONNECT:
- if (mBluetoothGatt != null) mBluetoothGatt.disconnect();
- break;
- case COMMAND_DISCOVER_SERVICE:
- if (mBluetoothGatt != null) mBluetoothGatt.discoverServices();
- break;
- case COMMAND_READ_RSSI:
- if (mBluetoothGatt != null) mBluetoothGatt.readRemoteRssi();
- break;
- case COMMAND_WRITE_CHARACTERISTIC:
- writeCharacteristic(writeValue);
- break;
- case COMMAND_READ_CHARACTERISTIC:
- readCharacteristic();
- break;
- case COMMAND_WRITE_DESCRIPTOR:
- writeDescriptor(writeValue);
- break;
- case COMMAND_READ_DESCRIPTOR:
- readDescriptor();
- break;
- case COMMAND_SET_NOTIFICATION:
- setNotification(enable);
- break;
- case COMMAND_BEGIN_WRITE:
- if (mBluetoothGatt != null) mBluetoothGatt.beginReliableWrite();
- break;
- case COMMAND_EXECUTE_WRITE:
- if (mBluetoothGatt != null) mBluetoothGatt.executeReliableWrite();
- break;
- case COMMAND_ABORT_RELIABLE:
- if (mBluetoothGatt != null) mBluetoothGatt.abortReliableWrite(mDevice);
- break;
- case COMMAND_SCAN_START:
- startScan();
- break;
- case COMMAND_SCAN_STOP:
- stopScan();
- break;
- default:
- showMessage("Unrecognized command: " + command);
- break;
- }
- }
-
private void writeCharacteristic(String writeValue) {
BluetoothGattCharacteristic characteristic = getCharacteristic(CHARACTERISTIC_UUID);
if (characteristic == null) return;
@@ -233,55 +179,69 @@
mBluetoothGatt.setCharacteristicNotification(characteristic, enable);
}
+ private void notifyError(String message) {
+ showMessage(message);
+ }
+
private void notifyConnected() {
+ showMessage("BLE connected");
Intent intent = new Intent(BLE_BLUETOOTH_CONNECTED);
sendBroadcast(intent);
}
private void notifyDisconnected() {
+ showMessage("BLE disconnected");
Intent intent = new Intent(BLE_BLUETOOTH_DISCONNECTED);
sendBroadcast(intent);
}
private void notifyServicesDiscovered() {
+ showMessage("Service discovered");
Intent intent = new Intent(BLE_SERVICES_DISCOVERED);
sendBroadcast(intent);
}
private void notifyCharacteristicRead(String value) {
+ showMessage("Characteristic read: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_READ);
intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
sendBroadcast(intent);
}
- private void notifyCharacteristicWrite() {
+ private void notifyCharacteristicWrite(String value) {
+ showMessage("Characteristic write: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_WRITE);
sendBroadcast(intent);
}
private void notifyCharacteristicChanged(String value) {
+ showMessage("Characteristic changed: " + value);
Intent intent = new Intent(BLE_CHARACTERISTIC_CHANGED);
intent.putExtra(EXTRA_CHARACTERISTIC_VALUE, value);
sendBroadcast(intent);
}
private void notifyDescriptorRead(String value) {
+ showMessage("Descriptor read: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_READ);
intent.putExtra(EXTRA_DESCRIPTOR_VALUE, value);
sendBroadcast(intent);
}
- private void notifyDescriptorWrite() {
+ private void notifyDescriptorWrite(String value) {
+ showMessage("Descriptor write: " + value);
Intent intent = new Intent(BLE_DESCRIPTOR_WRITE);
sendBroadcast(intent);
}
private void notifyReliableWriteCompleted() {
+ showMessage("Reliable write compelte");
Intent intent = new Intent(BLE_RELIABLE_WRITE_COMPLETED);
sendBroadcast(intent);
}
private void notifyReadRemoteRssi(int rssi) {
+ showMessage("Remote rssi read: " + rssi);
Intent intent = new Intent(BLE_READ_REMOTE_RSSI);
intent.putExtra(EXTRA_RSSI_VALUE, rssi);
sendBroadcast(intent);
@@ -330,88 +290,159 @@
});
}
+ private void sleep(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Error in thread sleep", e);
+ }
+ }
+
+ private void reliableWrite() {
+ mBluetoothGatt.beginReliableWrite();
+ sleep(1000);
+ writeCharacteristic(WRITE_VALUE);
+ sleep(1000);
+ if (!mBluetoothGatt.executeReliableWrite()) {
+ Log.w(TAG, "reliable write failed");
+ }
+ sleep(1000);
+ mBluetoothGatt.abortReliableWrite();
+ }
+
private final BluetoothGattCallback mGattCallbacks = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (DEBUG) Log.d(TAG, "onConnectionStateChange");
if (status == BluetoothGatt.GATT_SUCCESS) {
- if (newState == BluetoothProfile.STATE_CONNECTED) notifyConnected();
- else if (status == BluetoothProfile.STATE_DISCONNECTED) {
+ if (newState == BluetoothProfile.STATE_CONNECTED) {
+ notifyConnected();
+ stopScan();
+ sleep(1000);
+ mBluetoothGatt.discoverServices();
+ } else if (status == BluetoothProfile.STATE_DISCONNECTED) {
notifyDisconnected();
- showMessage("Bluetooth LE disconnected");
}
+ } else {
+ showMessage("Failed to connect");
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
+ if (DEBUG) Log.d(TAG, "onServiceDiscovered");
if ((status == BluetoothGatt.GATT_SUCCESS) &&
(mBluetoothGatt.getService(SERVICE_UUID) != null)) {
notifyServicesDiscovered();
- }
- }
-
- @Override
- public void onCharacteristicRead(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic, int status) {
- if ((status == BluetoothGatt.GATT_SUCCESS) &&
- (characteristic.getUuid().equals(CHARACTERISTIC_UUID))) {
- notifyCharacteristicRead(characteristic.getStringValue(0));
+ sleep(1000);
+ writeCharacteristic(WRITE_VALUE);
}
}
@Override
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
+ String value = characteristic.getStringValue(0);
if (DEBUG) Log.d(TAG, "onCharacteristicWrite: characteristic.val="
- + characteristic.getStringValue(0) + " status=" + status);
+ + value + " status=" + status);
BluetoothGattCharacteristic mCharacteristic = getCharacteristic(CHARACTERISTIC_UUID);
if ((status == BluetoothGatt.GATT_SUCCESS) &&
- (characteristic.getStringValue(0).equals(mCharacteristic.getStringValue(0)))) {
- notifyCharacteristicWrite();
+ (value.equals(mCharacteristic.getStringValue(0)))) {
+ notifyCharacteristicWrite(value);
+ sleep(1000);
+ readCharacteristic();
+ } else {
+ notifyError("Failed to write characteristic: " + value);
}
}
@Override
- public void onCharacteristicChanged(BluetoothGatt gatt,
- BluetoothGattCharacteristic characteristic) {
- if (characteristic.getUuid().equals(UPDATE_CHARACTERISTIC_UUID))
- notifyCharacteristicChanged(characteristic.getStringValue(0));
- }
-
- @Override
- public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
- int status) {
+ public void onCharacteristicRead(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic, int status) {
+ if (DEBUG) Log.d(TAG, "onCharacteristicRead");
if ((status == BluetoothGatt.GATT_SUCCESS) &&
- (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
- notifyDescriptorRead(new String(descriptor.getValue()));
+ (characteristic.getUuid().equals(CHARACTERISTIC_UUID))) {
+ notifyCharacteristicRead(characteristic.getStringValue(0));
+ sleep(1000);
+ writeDescriptor(WRITE_VALUE);
+ } else {
+ notifyError("Failed to read characteristic");
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
int status) {
+ if (DEBUG) Log.d(TAG, "onDescriptorWrite");
if ((status == BluetoothGatt.GATT_SUCCESS) &&
(descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
- notifyDescriptorWrite();
+ notifyDescriptorWrite(new String(descriptor.getValue()));
+ sleep(1000);
+ readDescriptor();
+ } else {
+ notifyError("Failed to write descriptor");
+ }
+ }
+
+ @Override
+ public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
+ int status) {
+ if (DEBUG) Log.d(TAG, "onDescriptorRead");
+ if ((status == BluetoothGatt.GATT_SUCCESS) &&
+ (descriptor.getUuid() != null) &&
+ (descriptor.getUuid().equals(DESCRIPTOR_UUID))) {
+ notifyDescriptorRead(new String(descriptor.getValue()));
+ sleep(1000);
+ setNotification(true);
+ } else {
+ notifyError("Failed to read descriptor");
+ }
+ }
+
+ @Override
+ public void onCharacteristicChanged(BluetoothGatt gatt,
+ BluetoothGattCharacteristic characteristic) {
+ if (DEBUG) Log.d(TAG, "onCharacteristicChanged");
+ if ((characteristic.getUuid() != null) &&
+ (characteristic.getUuid().equals(UPDATE_CHARACTERISTIC_UUID))) {
+ notifyCharacteristicChanged(characteristic.getStringValue(0));
+ setNotification(false);
+ sleep(1000);
+ mBluetoothGatt.readRemoteRssi();
}
}
@Override
public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) notifyReliableWriteCompleted();
+ if (DEBUG) Log.d(TAG, "onReliableWriteComplete: " + status);
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ notifyReliableWriteCompleted();
+ } else {
+ notifyError("Failed to complete reliable write: " + status);
+ }
+ sleep(1000);
+ mBluetoothGatt.disconnect();
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
- if (status == BluetoothGatt.GATT_SUCCESS) notifyReadRemoteRssi(rssi);
+ if (DEBUG) Log.d(TAG, "onReadRemoteRssi");
+ if (status == BluetoothGatt.GATT_SUCCESS) {
+ notifyReadRemoteRssi(rssi);
+ } else {
+ notifyError("Failed to read remote rssi");
+ }
+ sleep(1000);
+ reliableWrite();
}
};
private final ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
- mBluetoothGatt = result.getDevice().connectGatt(mContext, false, mGattCallbacks);
+ if (mBluetoothGatt == null) {
+ mBluetoothGatt = result.getDevice().connectGatt(mContext, false, mGattCallbacks);
+ }
}
};
@@ -420,7 +451,7 @@
List<ScanFilter> filter = Arrays.asList(new ScanFilter.Builder().setServiceUuid(
new ParcelUuid(BleServerService.ADV_SERVICE_UUID)).build());
ScanSettings setting = new ScanSettings.Builder()
- .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER).build();
+ .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY).build();
mScanner.startScan(filter, setting, mScanCallback);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientStartActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientStartActivity.java
new file mode 100644
index 0000000..5a4e327
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientStartActivity.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.bluetooth;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.os.Handler;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.ListView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class BleClientStartActivity extends PassFailButtons.Activity {
+
+ private TestAdapter mTestAdapter;
+ private int mAllPassed;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.ble_server_start);
+ setPassFailButtonClickListeners();
+ setInfoResources(R.string.ble_server_start_name,
+ R.string.ble_server_start_info, -1);
+ getPassButton().setEnabled(false);
+
+ mTestAdapter = new TestAdapter(this, setupTestList());
+ ListView listView = (ListView) findViewById(R.id.ble_server_tests);
+ listView.setAdapter(mTestAdapter);
+
+ mAllPassed = 0;
+ startService(new Intent(this, BleClientService.class));
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(BleClientService.BLE_BLUETOOTH_CONNECTED);
+ filter.addAction(BleClientService.BLE_BLUETOOTH_DISCONNECTED);
+ filter.addAction(BleClientService.BLE_SERVICES_DISCOVERED);
+ filter.addAction(BleClientService.BLE_CHARACTERISTIC_READ);
+ filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE);
+ filter.addAction(BleClientService.BLE_CHARACTERISTIC_CHANGED);
+ filter.addAction(BleClientService.BLE_DESCRIPTOR_READ);
+ filter.addAction(BleClientService.BLE_DESCRIPTOR_WRITE);
+ filter.addAction(BleClientService.BLE_RELIABLE_WRITE_COMPLETED);
+ filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI);
+ registerReceiver(onBroadcast, filter);
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ unregisterReceiver(onBroadcast);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ stopService(new Intent(this, BleClientService.class));
+ }
+
+ private List<Integer> setupTestList() {
+ ArrayList<Integer> testList = new ArrayList<Integer>();
+ testList.add(R.string.ble_client_connect_name);
+ testList.add(R.string.ble_discover_service_name);
+ testList.add(R.string.ble_read_characteristic_name);
+ testList.add(R.string.ble_write_characteristic_name);
+ testList.add(R.string.ble_reliable_write_name);
+ testList.add(R.string.ble_notify_characteristic_name);
+ testList.add(R.string.ble_read_descriptor_name);
+ testList.add(R.string.ble_write_descriptor_name);
+ testList.add(R.string.ble_read_rssi_name);
+ testList.add(R.string.ble_client_disconnect_name);
+ return testList;
+ }
+
+ private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ if (action == BleClientService.BLE_BLUETOOTH_CONNECTED) {
+ mTestAdapter.setTestPass(0);
+ mAllPassed |= 0x01;
+ } else if (action == BleClientService.BLE_SERVICES_DISCOVERED) {
+ mTestAdapter.setTestPass(1);
+ mAllPassed |= 0x02;
+ } else if (action == BleClientService.BLE_CHARACTERISTIC_READ) {
+ mTestAdapter.setTestPass(2);
+ mAllPassed |= 0x04;
+ } else if (action == BleClientService.BLE_CHARACTERISTIC_WRITE) {
+ mTestAdapter.setTestPass(3);
+ mAllPassed |= 0x08;
+ } else if (action == BleClientService.BLE_RELIABLE_WRITE_COMPLETED) {
+ mTestAdapter.setTestPass(4);
+ mAllPassed |= 0x10;
+ } else if (action == BleClientService.BLE_CHARACTERISTIC_CHANGED) {
+ mTestAdapter.setTestPass(5);
+ mAllPassed |= 0x20;
+ } else if (action == BleClientService.BLE_DESCRIPTOR_READ) {
+ mTestAdapter.setTestPass(6);
+ mAllPassed |= 0x40;
+ } else if (action == BleClientService.BLE_DESCRIPTOR_WRITE) {
+ mTestAdapter.setTestPass(7);
+ mAllPassed |= 0x80;
+ } else if (action == BleClientService.BLE_READ_REMOTE_RSSI) {
+ mTestAdapter.setTestPass(8);
+ mAllPassed |= 0x100;
+ } else if (action == BleClientService.BLE_BLUETOOTH_DISCONNECTED) {
+ mTestAdapter.setTestPass(9);
+ mAllPassed |= 0x200;
+ }
+ mTestAdapter.notifyDataSetChanged();
+ if (mAllPassed == 0x3FF) getPassButton().setEnabled(true);
+ }
+ };
+
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java
deleted file mode 100644
index a13d934..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestActivity.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT 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.ManifestTestListAdapter;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import android.os.Bundle;
-
-public class BleClientTestActivity extends PassFailButtons.TestListActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_list);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_client_test_name, R.string.ble_client_test_info, -1);
-
- setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java
deleted file mode 100644
index 6896b04..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleDiscoverServiceActivity.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.bluetooth;
-
-public class BleDiscoverServiceActivity extends BleButtonActivity {
- public BleDiscoverServiceActivity() {
- super(BleButtonActivity.DISCOVER_SERVICE);
- }
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java
deleted file mode 100644
index e0c79bf..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleNotifyCharacteristicActivity.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class BleNotifyCharacteristicActivity extends PassFailButtons.Activity {
-
- private boolean mEnable;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_notify_characteristic);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_notify_characteristic_name,
- R.string.ble_notify_characteristic_info, -1);
-
- mEnable = false;
-
- ((Button) findViewById(R.id.ble_notify)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- mEnable = !mEnable;
- if (mEnable) ((Button) v).setText(getString(R.string.ble_stop_notification));
- else ((Button) v).setText(getString(R.string.ble_begin_notification));
-
- Intent intent = new Intent(BleNotifyCharacteristicActivity.this,
- BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_SET_NOTIFICATION);
- intent.putExtra(BleClientService.EXTRA_BOOL, mEnable);
- startService(intent);
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter filter = new IntentFilter();
- filter.addAction(BleClientService.BLE_CHARACTERISTIC_CHANGED);
- registerReceiver(onBroadcast, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- unregisterReceiver(onBroadcast);
- mEnable = false;
- Intent intent = new Intent(BleNotifyCharacteristicActivity.this,
- BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_SET_NOTIFICATION);
- intent.putExtra(BleClientService.EXTRA_BOOL, mEnable);
- startService(intent);
- }
-
- private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String value = intent.getStringExtra(BleClientService.EXTRA_CHARACTERISTIC_VALUE);
- ((TextView) findViewById(R.id.ble_notify_text)).setText(value);
- }
- };
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java
deleted file mode 100644
index 800499c..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadRssiActivity.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class BleReadRssiActivity extends PassFailButtons.Activity {
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_read_rssi);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_read_rssi_name,
- R.string.ble_read_rssi_info, -1);
-
- ((Button) findViewById(R.id.ble_read_rssi)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleReadRssiActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_READ_RSSI);
- startService(intent);
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter filter = new IntentFilter();
- filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI);
- registerReceiver(onBroadcast, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- unregisterReceiver(onBroadcast);
- }
-
- private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- int rssi = intent.getIntExtra(BleClientService.EXTRA_RSSI_VALUE, 128);
- ((TextView) findViewById(R.id.ble_rssi_text)).setText(Integer.toString(rssi));
- }
- };
-}
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java
deleted file mode 100644
index 8041ce0..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReadWriteActivity.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.TextView;
-import android.widget.Toast;
-
-class BleReadWriteActivity extends PassFailButtons.Activity {
-
- static final int CHARACTERISTIC = 0;
- static final int DESCRIPTOR = 1;
-
- private int mWriteCommand;
- private int mReadCommand;
- private String mWriteFilter;
- private String mReadFilter;
- private String mExtraValue;
- private int mName;
- private EditText mEditText;
-
- BleReadWriteActivity(int target) {
- if (target == CHARACTERISTIC) {
- mWriteCommand = BleClientService.COMMAND_WRITE_CHARACTERISTIC;
- mReadCommand = BleClientService.COMMAND_READ_CHARACTERISTIC;
- mWriteFilter = BleClientService.BLE_CHARACTERISTIC_WRITE;
- mReadFilter = BleClientService.BLE_CHARACTERISTIC_READ;
- mExtraValue = BleClientService.EXTRA_CHARACTERISTIC_VALUE;
- mName = R.string.ble_client_characteristic_name;
- } else if (target == DESCRIPTOR) {
- mWriteCommand = BleClientService.COMMAND_WRITE_DESCRIPTOR;
- mReadCommand = BleClientService.COMMAND_READ_DESCRIPTOR;
- mWriteFilter = BleClientService.BLE_DESCRIPTOR_WRITE;
- mReadFilter = BleClientService.BLE_DESCRIPTOR_READ;
- mExtraValue = BleClientService.EXTRA_DESCRIPTOR_VALUE;
- mName = R.string.ble_client_descriptor_name;
- }
- }
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_client_read_write);
- setPassFailButtonClickListeners();
- setInfoResources(mName, R.string.ble_read_write_info, -1);
-
- mEditText = (EditText) findViewById(R.id.write_text);
-
- ((Button) findViewById(R.id.ble_write)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- String writeValue = mEditText.getText().toString();
- Intent intent = new Intent(BleReadWriteActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND, mWriteCommand);
- intent.putExtra(BleClientService.EXTRA_WRITE_VALUE, writeValue);
- startService(intent);
- mEditText.setText("");
- }
- });
-
- ((Button) findViewById(R.id.ble_read)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleReadWriteActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND, mReadCommand);
- startService(intent);
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter filter = new IntentFilter();
- filter.addAction(mReadFilter);
- filter.addAction(mWriteFilter);
- registerReceiver(onBroadcast, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- unregisterReceiver(onBroadcast);
- }
-
- private void showMessage(String msg) {
- Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
- }
-
- private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action == mWriteFilter)
- showMessage("Write successful callback");
- else if (action == mReadFilter) {
- String value = intent.getStringExtra(mExtraValue);
- ((TextView) findViewById(R.id.read_text)).setText(value);
- }
- }
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java
deleted file mode 100644
index 9b65bb4..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleReliableWriteActivity.java
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package 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.View;
-import android.view.View.OnClickListener;
-import android.widget.Button;
-import android.widget.EditText;
-import android.widget.Toast;
-
-public class BleReliableWriteActivity extends PassFailButtons.Activity {
-
- EditText mEditText;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_reliable_write);
- setPassFailButtonClickListeners();
- setInfoResources(R.string.ble_reliable_write_name, R.string.ble_reliable_write_info, -1);
- getPassButton().setEnabled(false);
- ((Button) findViewById(R.id.ble_execute)).setEnabled(false);
-
- mEditText = (EditText) findViewById(R.id.write_text);
-
- ((Button) findViewById(R.id.ble_begin)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_BEGIN_WRITE);
- startService(intent);
- }
- });
-
- ((Button) findViewById(R.id.ble_write)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- String writeValue = mEditText.getText().toString();
- Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_WRITE_CHARACTERISTIC);
- intent.putExtra(BleClientService.EXTRA_WRITE_VALUE, writeValue);
- startService(intent);
- mEditText.setText("");
- }
- });
-
- ((Button) findViewById(R.id.ble_execute)).setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent(BleReliableWriteActivity.this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND,
- BleClientService.COMMAND_EXECUTE_WRITE);
- startService(intent);
- }
- });
- }
-
- @Override
- public void onResume() {
- super.onResume();
- IntentFilter filter = new IntentFilter();
- filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE);
- filter.addAction(BleClientService.BLE_RELIABLE_WRITE_COMPLETED);
- registerReceiver(onBroadcast, filter);
- }
-
- @Override
- public void onPause() {
- super.onPause();
- unregisterReceiver(onBroadcast);
- Intent intent = new Intent(this, BleClientService.class);
- intent.putExtra(BleClientService.EXTRA_COMMAND, BleClientService.COMMAND_ABORT_RELIABLE);
- startService(intent);
- }
-
- private void showMessage(String msg) {
- Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
- }
-
- private BroadcastReceiver onBroadcast = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action == BleClientService.BLE_CHARACTERISTIC_WRITE) {
- showMessage("Write value verified.");
- ((Button) findViewById(R.id.ble_execute)).setEnabled(true);
- } else if (action == BleClientService.BLE_RELIABLE_WRITE_COMPLETED) {
- showMessage("Reliable write completed.");
- getPassButton().setEnabled(true);
- }
- }
- };
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java
index 631fe36..8f4ed56 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/TestAdapter.java
@@ -80,7 +80,7 @@
if (convertView != null) {
vg = (ViewGroup) convertView;
} else {
- vg = (ViewGroup) inflater.inflate(R.layout.ble_server_start_item, null);
+ vg = (ViewGroup) inflater.inflate(R.layout.ble_test_item, null);
}
Test test = tests.get(position);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
index c0895d7..105e92b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureSummaryActivity.java
@@ -220,6 +220,9 @@
new Feature(PackageManager.FEATURE_SENSOR_RELATIVE_HUMIDITY, false),
new Feature(PackageManager.FEATURE_VERIFIED_BOOT, false),
+ // Features explicitly made optional in L
+ new Feature("PackageManager.FEATURE_LOCATION_NETWORK", false),
+
// New hidden features in L
new Feature("android.hardware.ethernet", false),
new Feature("android.hardware.hdmi.cec", false),
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index d65af80..b658e4f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -16,9 +16,7 @@
package com.android.cts.verifier.notifications;
-import android.annotation.SuppressLint;
import android.app.Activity;
-import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
@@ -38,20 +36,12 @@
import android.widget.TextView;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import com.android.cts.verifier.nfc.TagVerifierActivity;
-import org.json.JSONException;
-import org.json.JSONObject;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Set;
-import java.util.UUID;
import java.util.concurrent.LinkedBlockingQueue;
-import static com.android.cts.verifier.notifications.MockListener.*;
-
public abstract class InteractiveVerifierActivity extends PassFailButtons.Activity
implements Runnable {
private static final String TAG = "InteractiveVerifier";
@@ -223,17 +213,18 @@
}
protected View createNlsSettingsItem(ViewGroup parent, int messageId) {
- return createUserItem(parent, messageId, R.string.nls_start_settings);
+ return createUserItem(parent, R.string.nls_start_settings, messageId);
}
- protected View createRetryItem(ViewGroup parent, int messageId) {
- return createUserItem(parent, messageId, R.string.attention_ready);
+ protected View createRetryItem(ViewGroup parent, int messageId, Object... messageFormatArgs) {
+ return createUserItem(parent, R.string.attention_ready, messageId, messageFormatArgs);
}
- protected View createUserItem(ViewGroup parent, int messageId, int actionId) {
+ protected View createUserItem(ViewGroup parent, int actionId, int messageId,
+ Object... messageFormatArgs) {
View item = mInflater.inflate(R.layout.nls_item, parent, false);
TextView instructions = (TextView) item.findViewById(R.id.nls_instructions);
- instructions.setText(messageId);
+ instructions.setText(getString(messageId, messageFormatArgs));
Button button = (Button) item.findViewById(R.id.nls_action_button);
button.setText(actionId);
button.setTag(actionId);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 3d0e0d7..c80f371 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -31,7 +31,9 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
public class MockListener extends NotificationListenerService {
static final String TAG = "MockListener";
@@ -67,8 +69,10 @@
private ArrayList<String> mPosted = new ArrayList<String>();
private ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
+ private ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
private ArrayList<String> mRemoved = new ArrayList<String>();
private ArrayList<String> mOrder = new ArrayList<>();
+ private Set<String> mTestPackages = new HashSet<>();
private BroadcastReceiver mReceiver;
private int mDND = -1;
@@ -77,6 +81,9 @@
super.onCreate();
Log.d(TAG, "created");
+ mTestPackages.add("com.android.cts.verifier");
+ mTestPackages.add("com.android.cts.robot");
+
mPosted = new ArrayList<String>();
mRemoved = new ArrayList<String>();
@@ -123,10 +130,13 @@
setResultCode(Activity.RESULT_OK);
} else if (SERVICE_CLEAR_ONE.equals(action)) {
Log.d(TAG, "SERVICE_CLEAR_ONE");
- MockListener.this.cancelNotification(
- context.getApplicationInfo().packageName,
- intent.getStringExtra(EXTRA_TAG),
- intent.getIntExtra(EXTRA_CODE, 0));
+ String tag = intent.getStringExtra(EXTRA_TAG);
+ String key = mNotificationKeys.get(tag);
+ if (key != null) {
+ MockListener.this.cancelNotification(key);
+ } else {
+ Log.w(TAG, "Notification does not exist: " + tag);
+ }
} else if (SERVICE_CLEAR_ALL.equals(action)) {
Log.d(TAG, "SERVICE_CLEAR_ALL");
MockListener.this.cancelAllNotifications();
@@ -205,6 +215,7 @@
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
+ if (!mTestPackages.contains(sbn.getPackageName())) { return; }
Log.d(TAG, "posted: " + sbn.getTag());
mPosted.add(sbn.getTag());
JSONObject notification = new JSONObject();
@@ -216,6 +227,7 @@
notification.put(JSON_ICON, sbn.getNotification().icon);
notification.put(JSON_FLAGS, sbn.getNotification().flags);
mNotifications.put(sbn.getKey(), notification);
+ mNotificationKeys.put(sbn.getTag(), sbn.getKey());
} catch (JSONException e) {
Log.e(TAG, "failed to pack up notification payload", e);
}
@@ -227,6 +239,7 @@
Log.d(TAG, "removed: " + sbn.getTag());
mRemoved.add(sbn.getTag());
mNotifications.remove(sbn.getKey());
+ mNotificationKeys.remove(sbn.getTag());
onNotificationRankingUpdate(rankingMap);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
new file mode 100644
index 0000000..a6affb3
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -0,0 +1,258 @@
+/*
+ * 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.notifications;
+
+import android.app.Notification;
+import android.content.Intent;
+import android.util.Log;
+import android.view.View;
+import android.view.ViewGroup;
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Tests that the notification ranker honors user preferences about package priority.
+ * Users can, in Settings, specify a package as being high priority. This should
+ * result in the notificaitons from that package being ranked higher than those from
+ * other packages.
+ */
+public class PackagePriorityVerifierActivity
+ extends InteractiveVerifierActivity {
+ private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST";
+ private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL";
+ private static final String EXTRA_ID = "ID";
+ private static final String EXTRA_NOTIFICATION = "NOTIFICATION";
+ private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot";
+ private CharSequence mAppLabel;
+
+ @Override
+ int getTitleResource() {
+ return R.string.package_priority_test;
+ }
+
+ @Override
+ int getInstructionsResource() {
+ return R.string.package_priority_info;
+ }
+
+ // Test Setup
+
+ @Override
+ protected List<InteractiveTestCase> createTestItems() {
+ mAppLabel = getString(R.string.app_name);
+ List<InteractiveTestCase> tests = new ArrayList<>(17);
+ tests.add(new IsEnabledTest());
+ tests.add(new ServiceStartedTest());
+ tests.add(new WaitForSetPriorityDefault());
+ tests.add(new DefaultOrderTest());
+ tests.add(new WaitForSetPriorityHigh());
+ tests.add(new PackagePriorityOrderTest());
+ return tests;
+ }
+
+ // Tests
+
+ /** Wait for the user to set the target package priority to default. */
+ protected class WaitForSetPriorityDefault extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createRetryItem(parent, R.string.package_priority_default, mAppLabel);
+ }
+
+ @Override
+ void setUp() {
+ Log.i("WaitForSetPriorityDefault", "waiting for user");
+ status = WAIT_FOR_USER;
+ }
+
+ @Override
+ void test() {
+ status = PASS;
+ next();
+ }
+
+ @Override
+ void tearDown() {
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ /** Wait for the user to set the target package priority to high. */
+ protected class WaitForSetPriorityHigh extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createRetryItem(parent, R.string.package_priority_high, mAppLabel);
+ }
+
+ @Override
+ void setUp() {
+ Log.i("WaitForSetPriorityHigh", "waiting for user");
+ status = WAIT_FOR_USER;
+ }
+
+ @Override
+ void test() {
+ status = PASS;
+ next();
+ }
+
+ @Override
+ void tearDown() {
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ /**
+ * With default priority, the notifcations should be reverse-ordered by time.
+ * A is before B, and therefor should B should rank before A.
+ */
+ protected class DefaultOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.attention_default_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications();
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = indexOfPackageInKeys(orderedKeys, getPackageName());
+ int rankB = indexOfPackageInKeys(orderedKeys, NOTIFICATION_BOT_PACKAGE);
+ if (rankB != -1 && rankB < rankA) {
+ status = PASS;
+ } else {
+ logFail("expected rankA (" + rankA + ") > rankB (" + rankB + ")");
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ cancelNotifications();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+
+ /**
+ * With higher package priority, A should rank above B.
+ */
+ protected class PackagePriorityOrderTest extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.package_priority_user_order);
+ }
+
+ @Override
+ void setUp() {
+ sendNotifications();
+ status = READY;
+ // wait for notifications to move through the system
+ delay();
+ }
+
+ @Override
+ void test() {
+ MockListener.probeListenerOrder(mContext,
+ new MockListener.StringListResultCatcher() {
+ @Override
+ public void accept(List<String> orderedKeys) {
+ int rankA = indexOfPackageInKeys(orderedKeys, getPackageName());
+ int rankB = indexOfPackageInKeys(orderedKeys, NOTIFICATION_BOT_PACKAGE);
+ if (rankA != -1 && rankA < rankB) {
+ status = PASS;
+ } else {
+ logFail("expected rankA (" + rankA + ") < rankB (" + rankB + ")");
+ status = FAIL;
+ }
+ next();
+ }
+ });
+ delay(); // in case the catcher never returns
+ }
+
+ @Override
+ void tearDown() {
+ cancelNotifications();
+ MockListener.resetListenerData(mContext);
+ delay();
+ }
+ }
+ // Utilities
+
+ private void sendNotifications() {
+ // post ours first, with an explicit time in the past to avoid any races.
+ Notification.Builder alice = new Notification.Builder(mContext)
+ .setContentTitle("alice title")
+ .setContentText("alice content")
+ .setSmallIcon(R.drawable.ic_stat_alice)
+ .setWhen(System.currentTimeMillis() - 10000L)
+ .setPriority(Notification.PRIORITY_DEFAULT);
+ mNm.notify(0, alice.build());
+
+ // then post theirs, so it should be higher by default due to recency
+ Notification.Builder bob = new Notification.Builder(mContext)
+ .setContentTitle("bob title")
+ .setContentText("bob content")
+ .setSmallIcon(android.R.drawable.stat_notify_error) // must be global resource
+ .setWhen(System.currentTimeMillis())
+ .setPriority(Notification.PRIORITY_DEFAULT);
+ Intent postIntent = new Intent(ACTION_POST);
+ postIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
+ postIntent.putExtra(EXTRA_ID, 0);
+ postIntent.putExtra(EXTRA_NOTIFICATION, bob.build());
+ sendBroadcast(postIntent);
+ }
+
+ private void cancelNotifications() {
+ //cancel ours
+ mNm.cancelAll();
+ //cancel theirs
+ Intent cancelIntent = new Intent(ACTION_CANCEL);
+ cancelIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
+ cancelIntent.putExtra(EXTRA_ID, 0);
+ sendBroadcast(cancelIntent);
+ }
+
+ /** Search a list of notification keys for a given packageName. */
+ private int indexOfPackageInKeys(List<String> orderedKeys, String packageName) {
+ for (int i = 0; i < orderedKeys.size(); i++) {
+ if (orderedKeys.get(i).contains(packageName)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
index 18d9d43..77de71d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/ProjectionActivity.java
@@ -150,6 +150,8 @@
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.i(TAG, "onSurfaceTextureSizeChanged " + surface.toString() + "w: " + width + " h: "
+ height);
+ mWidth = width;
+ mHeight = height;
}
@Override
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
index 5dddf5c..46abaaa 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/list/ListPresentation.java
@@ -50,7 +50,7 @@
setContentView(view);
for (int i = 0; i < NUM_ITEMS; ++i) {
- mItemList.add("Item #" + 1 + i);
+ mItemList.add("Item #" + (1 + i));
}
ListView listView = (ListView) view.findViewById(R.id.pla_list);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
index 8c5b18c..f4460de 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
@@ -16,41 +16,225 @@
package com.android.cts.verifier.tv;
+import com.android.cts.verifier.R;
+
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvInputManager;
import android.media.tv.TvInputService;
+import android.media.tv.TvTrackInfo;
import android.net.Uri;
-import android.util.Pair;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
import android.view.Surface;
+import android.view.LayoutInflater;
import android.view.View;
+import android.widget.TextView;
+
+import com.android.cts.verifier.R;
+
+import java.util.ArrayList;
+import java.util.List;
public class MockTvInputService extends TvInputService {
private static final String TAG = "MockTvInputService";
+ private static final String BROADCAST_ACTION = "action";
+ private static final String SELECT_TRACK_TYPE = "type";
+ private static final String SELECT_TRACK_ID = "id";
+ private static final String CAPTION_ENABLED = "enabled";
+
private static Object sLock = new Object();
- private static Pair<View, Runnable> sTuneCallback = null;
+ private static Callback sTuneCallback = null;
+ private static Callback sOverlayViewCallback = null;
+ private static Callback sBroadcastCallback = null;
+ private static Callback sUnblockContentCallback = null;
+ private static Callback sSelectTrackCallback = null;
+ private static Callback sSetCaptionEnabledCallback = null;
+ private static TvContentRating sRating = null;
+
+ static final TvTrackInfo sEngAudioTrack =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio_eng")
+ .setAudioChannelCount(2)
+ .setAudioSampleRate(48000)
+ .setLanguage("eng")
+ .build();
+ static final TvTrackInfo sSpaAudioTrack =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio_spa")
+ .setAudioChannelCount(2)
+ .setAudioSampleRate(48000)
+ .setLanguage("spa")
+ .build();
+ static final TvTrackInfo sEngSubtitleTrack =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_eng")
+ .setLanguage("eng")
+ .build();
+ static final TvTrackInfo sSpaSubtitleTrack =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_spa")
+ .setLanguage("spa")
+ .build();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (sLock) {
+ if (sBroadcastCallback != null) {
+ String expectedAction =
+ sBroadcastCallback.getBundle().getString(BROADCAST_ACTION);
+ if (intent.getAction().equals(expectedAction)) {
+ sBroadcastCallback.post();
+ sBroadcastCallback = null;
+ }
+ }
+ }
+ }
+ };
static void expectTune(View postTarget, Runnable successCallback) {
synchronized (sLock) {
- sTuneCallback = Pair.create(postTarget, successCallback);
+ sTuneCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectBroadcast(View postTarget, String action, Runnable successCallback) {
+ synchronized (sLock) {
+ sBroadcastCallback = new Callback(postTarget, successCallback);
+ sBroadcastCallback.getBundle().putString(BROADCAST_ACTION, action);
+ }
+ }
+
+ static void expectUnblockContent(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sUnblockContentCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void setBlockRating(TvContentRating rating) {
+ synchronized (sLock) {
+ sRating = rating;
+ }
+ }
+
+ static void expectOverlayView(View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sOverlayViewCallback = new Callback(postTarget, successCallback);
+ }
+ }
+
+ static void expectSelectTrack(int type, String id, View postTarget, Runnable successCallback) {
+ synchronized (sLock) {
+ sSelectTrackCallback = new Callback(postTarget, successCallback);
+ sSelectTrackCallback.getBundle().putInt(SELECT_TRACK_TYPE, type);
+ sSelectTrackCallback.getBundle().putString(SELECT_TRACK_ID, id);
+ }
+ }
+
+ static void expectSetCaptionEnabled(boolean enabled, View postTarget,
+ Runnable successCallback) {
+ synchronized (sLock) {
+ sSetCaptionEnabledCallback = new Callback(postTarget, successCallback);
+ sSetCaptionEnabledCallback.getBundle().putBoolean(CAPTION_ENABLED, enabled);
}
}
@Override
+ public void onCreate() {
+ super.onCreate();
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED);
+ intentFilter.addAction(TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED);
+ registerReceiver(mBroadcastReceiver, intentFilter);
+ }
+
+ @Override
+ public void onDestroy() {
+ unregisterReceiver(mBroadcastReceiver);
+ super.onDestroy();
+ }
+
+ @Override
public Session onCreateSession(String inputId) {
- return new MockSessionImpl(this);
+ Session session = new MockSessionImpl(this);
+ session.setOverlayViewEnabled(true);
+ return session;
}
private static class MockSessionImpl extends Session {
+ private final Context mContext;
+ private Surface mSurface = null;
+ private List<TvTrackInfo> mTracks = new ArrayList<>();
+
private MockSessionImpl(Context context) {
super(context);
+ mContext = context;
+ mTracks.add(sEngAudioTrack);
+ mTracks.add(sSpaAudioTrack);
+ mTracks.add(sEngSubtitleTrack);
+ mTracks.add(sSpaSubtitleTrack);
}
@Override
public void onRelease() {
}
+ private void draw() {
+ Surface surface = mSurface;
+ if (surface == null) return;
+ if (!surface.isValid()) return;
+
+ Canvas c = surface.lockCanvas(null);
+ if (c == null) return;
+ try {
+ Bitmap b = BitmapFactory.decodeResource(
+ mContext.getResources(), R.drawable.icon);
+ int srcWidth = b.getWidth();
+ int srcHeight = b.getHeight();
+ int dstWidth = c.getWidth();
+ int dstHeight = c.getHeight();
+ c.drawColor(Color.BLACK);
+ c.drawBitmap(b, new Rect(0, 0, srcWidth, srcHeight),
+ new Rect(10, 10, dstWidth - 10, dstHeight - 10), null);
+ } finally {
+ surface.unlockCanvasAndPost(c);
+ }
+ }
+
+ @Override
+ public View onCreateOverlayView() {
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
+ LAYOUT_INFLATER_SERVICE);
+ View view = inflater.inflate(R.layout.tv_overlay, null);
+ TextView textView = (TextView) view.findViewById(R.id.overlay_view_text);
+ textView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ Callback overlayViewCallback = null;
+ synchronized (sLock) {
+ overlayViewCallback = sOverlayViewCallback;
+ sOverlayViewCallback = null;
+ }
+ if (overlayViewCallback != null) {
+ overlayViewCallback.post();
+ }
+ }
+ });
+ return view;
+ }
+
@Override
public boolean onSetSurface(Surface surface) {
+ mSurface = surface;
+ draw();
return true;
}
@@ -60,24 +244,82 @@
@Override
public boolean onTune(Uri channelUri) {
- Pair<View, Runnable> tuneCallback = null;
synchronized (sLock) {
- tuneCallback = sTuneCallback;
- sTuneCallback = null;
+ if (sRating != null) {
+ notifyContentBlocked(sRating);
+ }
+ if (sTuneCallback != null) {
+ sTuneCallback.post();
+ sTuneCallback = null;
+ }
+ if (sRating == null) {
+ notifyContentAllowed();
+ }
}
- if (tuneCallback != null) {
- tuneCallback.first.post(tuneCallback.second);
- }
+ notifyVideoAvailable();
+ notifyTracksChanged(mTracks);
+ notifyTrackSelected(TvTrackInfo.TYPE_AUDIO, sEngAudioTrack.getId());
+ notifyTrackSelected(TvTrackInfo.TYPE_SUBTITLE, null);
return true;
}
@Override
public boolean onSelectTrack(int type, String trackId) {
+ synchronized (sLock) {
+ if (sSelectTrackCallback != null) {
+ Bundle bundle = sSelectTrackCallback.getBundle();
+ if (bundle.getInt(SELECT_TRACK_TYPE) == type
+ && bundle.getString(SELECT_TRACK_ID).equals(trackId)) {
+ sSelectTrackCallback.post();
+ sSelectTrackCallback = null;
+ }
+ }
+ }
+ notifyTrackSelected(type, trackId);
return true;
}
@Override
public void onSetCaptionEnabled(boolean enabled) {
+ synchronized (sLock) {
+ if (sSetCaptionEnabledCallback != null) {
+ Bundle bundle = sSetCaptionEnabledCallback.getBundle();
+ if (bundle.getBoolean(CAPTION_ENABLED) == enabled) {
+ sSetCaptionEnabledCallback.post();
+ sSetCaptionEnabledCallback = null;
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onUnblockContent(TvContentRating unblockedRating) {
+ synchronized (sLock) {
+ if (sRating != null && sRating.equals(unblockedRating)) {
+ sUnblockContentCallback.post();
+ sRating = null;
+ notifyContentAllowed();
+ }
+ }
+ }
+ }
+
+ private static class Callback {
+ private final View mPostTarget;
+ private final Runnable mAction;
+ private final Bundle mBundle = new Bundle();
+
+ Callback(View postTarget, Runnable action) {
+ mPostTarget = postTarget;
+ mAction = action;
+ }
+
+ public void post() {
+ mPostTarget.post(mAction);
+ }
+
+ public Bundle getBundle() {
+ return mBundle;
}
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
index 00f0091..81a8edc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -30,7 +30,7 @@
public class MockTvInputSetupActivity extends Activity {
private static final String TAG = "MockTvInputSetupActivity";
- private static final String CHANNEL_NUMBER = "999-999";
+ private static final String CHANNEL_NUMBER = "999-0";
private static final String CHANNEL_NAME = "Dummy";
private static Object sLock = new Object();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
new file mode 100644
index 0000000..66af4c6
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MultipleTracksTestActivity.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import com.android.cts.verifier.R;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvTrackInfo;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Tests for verifying TV app behavior on multiple tracks and subtitle.
+ */
+public class MultipleTracksTestActivity extends TvAppVerifierActivity
+ implements View.OnClickListener {
+ private static final String TAG = "MultipleTracksTestActivity";
+
+ private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
+
+ private View mSelectSubtitleItem;
+ private View mVerifySetCaptionEnabledItem;
+ private View mVerifySelectSubtitleItem;
+ private View mSelectAudioItem;
+ private View mVerifySelectAudioItem;
+
+ private Intent mTvAppIntent = null;
+
+ @Override
+ public void onClick(View v) {
+ final View postTarget = getPostTarget();
+
+ if (containsButton(mSelectSubtitleItem, v)) {
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifySetCaptionEnabledItem, false);
+ setPassState(mVerifySelectSubtitleItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectSetCaptionEnabled(true, postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSelectSubtitleItem, true);
+ setPassState(mVerifySetCaptionEnabledItem, true);
+ Integer tag = (Integer) mSelectAudioItem.getTag();
+ if (tag == 0) {
+ mSelectAudioItem.setTag(Integer.valueOf(1));
+ } else if (tag == 1) {
+ setButtonEnabled(mSelectAudioItem, true);
+ }
+ }
+ });
+ MockTvInputService.expectSelectTrack(TvTrackInfo.TYPE_SUBTITLE,
+ MockTvInputService.sEngSubtitleTrack.getId(), postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSelectSubtitleItem, true);
+ setPassState(mVerifySelectSubtitleItem, true);
+ Integer tag = (Integer) mSelectAudioItem.getTag();
+ if (tag == 0) {
+ mSelectAudioItem.setTag(Integer.valueOf(1));
+ } else if (tag == 1) {
+ setButtonEnabled(mSelectAudioItem, true);
+ }
+ }
+ });
+ } else if (containsButton(mSelectAudioItem, v)) {
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifySelectAudioItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectSelectTrack(TvTrackInfo.TYPE_AUDIO,
+ MockTvInputService.sSpaAudioTrack.getId(), postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mSelectAudioItem, true);
+ setPassState(mVerifySelectAudioItem, true);
+ getPassButton().setEnabled(true);
+ }
+ });
+ }
+ if (mTvAppIntent == null) {
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI,
+ projection, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ mTvAppIntent = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(cursor.getLong(0)));
+ }
+ }
+ if (mTvAppIntent == null) {
+ Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+ startActivity(mTvAppIntent);
+ }
+
+ @Override
+ protected void createTestItems() {
+ mSelectSubtitleItem = createUserItem(
+ R.string.tv_multiple_tracks_test_select_subtitle,
+ R.string.tv_launch_tv_app, this);
+ setButtonEnabled(mSelectSubtitleItem, true);
+ mVerifySetCaptionEnabledItem = createAutoItem(
+ R.string.tv_multiple_tracks_test_verify_set_caption_enabled);
+ mVerifySelectSubtitleItem = createAutoItem(
+ R.string.tv_multiple_tracks_test_verify_select_subtitle);
+ mSelectAudioItem = createUserItem(
+ R.string.tv_multiple_tracks_test_select_audio,
+ R.string.tv_launch_tv_app, this);
+ mSelectAudioItem.setTag(Integer.valueOf(0));
+ mVerifySelectAudioItem = createAutoItem(
+ R.string.tv_multiple_tracks_test_verify_select_audio);
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_multiple_tracks_test,
+ R.string.tv_multiple_tracks_test_info, -1);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java
new file mode 100644
index 0000000..284b485
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/ParentalControlTestActivity.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.verifier.tv;
+
+import com.android.cts.verifier.R;
+
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.tv.TvContentRating;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputManager;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Tests for verifying TV app behavior on parental control.
+ */
+public class ParentalControlTestActivity extends TvAppVerifierActivity
+ implements View.OnClickListener {
+ private static final String TAG = "ParentalControlTestActivity";
+
+ private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
+
+ private View mTurnOnParentalControlItem;
+ private View mVerifyReceiveBroadcast1Item;
+ private View mBlockTvMaItem;
+ private View mVerifyReceiveBroadcast2Item;
+ private View mBlockUnblockItem;
+
+ private Intent mTvAppIntent = null;
+
+ @Override
+ public void onClick(View v) {
+ final View postTarget = getPostTarget();
+
+ if (containsButton(mTurnOnParentalControlItem, v)) {
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifyReceiveBroadcast1Item, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectBroadcast(postTarget,
+ TvInputManager.ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mTurnOnParentalControlItem, true);
+ setPassState(mVerifyReceiveBroadcast1Item, true);
+ setButtonEnabled(mBlockTvMaItem, true);
+ }
+ });
+ } else if (containsButton(mBlockTvMaItem, v)) {
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mVerifyReceiveBroadcast2Item, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.expectBroadcast(postTarget,
+ TvInputManager.ACTION_BLOCKED_RATINGS_CHANGED, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mBlockTvMaItem, true);
+ setPassState(mVerifyReceiveBroadcast2Item, true);
+ setButtonEnabled(mBlockUnblockItem, true);
+ }
+ });
+ } else if (containsButton(mBlockUnblockItem, v)) {
+ final Runnable failCallback = new Runnable() {
+ @Override
+ public void run() {
+ setPassState(mBlockUnblockItem, false);
+ }
+ };
+ postTarget.postDelayed(failCallback, TIMEOUT_MS);
+ MockTvInputService.setBlockRating(TvContentRating.createRating(
+ "com.android.cts.verifier", "CTS_VERIFIER", "FAKE"));
+ MockTvInputService.expectUnblockContent(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mBlockUnblockItem, true);
+ getPassButton().setEnabled(true);
+ }
+ });
+ }
+ if (mTvAppIntent == null) {
+ String[] projection = { TvContract.Channels._ID };
+ try (Cursor cursor = getContentResolver().query(TvContract.Channels.CONTENT_URI,
+ projection, null, null, null)) {
+ if (cursor != null && cursor.moveToNext()) {
+ mTvAppIntent = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(cursor.getLong(0)));
+ }
+ }
+ if (mTvAppIntent == null) {
+ Toast.makeText(this, R.string.tv_channel_not_found, Toast.LENGTH_SHORT).show();
+ return;
+ }
+ }
+ startActivity(mTvAppIntent);
+ }
+
+ @Override
+ protected void createTestItems() {
+ mTurnOnParentalControlItem = createUserItem(
+ R.string.tv_parental_control_test_turn_on_parental_control,
+ R.string.tv_launch_tv_app, this);
+ setButtonEnabled(mTurnOnParentalControlItem, true);
+ mVerifyReceiveBroadcast1Item = createAutoItem(
+ R.string.tv_parental_control_test_verify_receive_broadcast1);
+ mBlockTvMaItem = createUserItem(R.string.tv_parental_control_test_block_tv_ma,
+ R.string.tv_launch_tv_app, this);
+ mVerifyReceiveBroadcast2Item = createAutoItem(
+ R.string.tv_parental_control_test_verify_receive_broadcast2);
+ mBlockUnblockItem = createUserItem(R.string.tv_parental_control_test_block_unblock,
+ R.string.tv_launch_tv_app, this);
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_parental_control_test,
+ R.string.tv_parental_control_test_info, -1);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index cb6df8a..3529237 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -30,14 +30,11 @@
import android.widget.TextView;
/**
- * Tests for verifying TV app behavior.
+ * Base class for TV app tests.
*/
public abstract class TvAppVerifierActivity extends PassFailButtons.Activity {
private static final String TAG = "TvAppVerifierActivity";
- protected static final Intent TV_APP_INTENT = new Intent(Intent.ACTION_VIEW,
- TvContract.buildChannelUri(0));
-
private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
private LayoutInflater mInflater;
@@ -59,7 +56,7 @@
createTestItems();
setContentView(view);
setPassFailButtonClickListeners();
- setInfoResources(R.string.tv, R.string.tv_info, -1);
+ setInfoResources();
getPassButton().setEnabled(false);
}
@@ -81,6 +78,8 @@
protected abstract void createTestItems();
+ protected abstract void setInfoResources();
+
/**
* Call this to create a test step where the user must perform some action.
*/
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
index cee05e2..3d17a1a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -16,23 +16,31 @@
package com.android.cts.verifier.tv;
-import com.android.cts.verifier.R;
-
+import android.content.Intent;
+import android.media.tv.TvContract;
import android.view.View;
+import com.android.cts.verifier.R;
+
/**
- * Tests for verifying TV app behavior.
+ * Tests for verifying TV app behavior for third-party TV input apps.
*/
public class TvInputDiscoveryTestActivity extends TvAppVerifierActivity
implements View.OnClickListener {
private static final String TAG = "TvInputDiscoveryTestActivity";
+ private static final Intent TV_APP_INTENT = new Intent(Intent.ACTION_VIEW,
+ TvContract.buildChannelUri(0));
+
private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
private View mGoToSetupItem;
private View mVerifySetupItem;
private View mTuneToChannelItem;
private View mVerifyTuneItem;
+ private View mVerifyOverlayViewItem;
+ private boolean mTuneVerified;
+ private boolean mOverlayViewVerified;
@Override
public void onClick(View v) {
@@ -66,10 +74,21 @@
MockTvInputService.expectTune(postTarget, new Runnable() {
@Override
public void run() {
- postTarget.removeCallbacks(failCallback);
setPassState(mTuneToChannelItem, true);
setPassState(mVerifyTuneItem, true);
- getPassButton().setEnabled(true);
+
+ mTuneVerified = true;
+ updatePassState(postTarget, failCallback);
+ }
+ });
+ MockTvInputService.expectOverlayView(postTarget, new Runnable() {
+ @Override
+ public void run() {
+ postTarget.removeCallbacks(failCallback);
+ setPassState(mVerifyOverlayViewItem, true);
+
+ mOverlayViewVerified = true;
+ updatePassState(postTarget, failCallback);
}
});
}
@@ -85,5 +104,20 @@
mTuneToChannelItem = createUserItem(R.string.tv_input_discover_test_tune_to_channel,
R.string.tv_launch_tv_app, this);
mVerifyTuneItem = createAutoItem(R.string.tv_input_discover_test_verify_tune);
+ mVerifyOverlayViewItem = createAutoItem(
+ R.string.tv_input_discover_test_verify_overlay_view);
+ }
+
+ private void updatePassState(View postTarget, Runnable failCallback) {
+ if (mTuneVerified && mOverlayViewVerified) {
+ postTarget.removeCallbacks(failCallback);
+ getPassButton().setEnabled(true);
+ }
+ }
+
+ @Override
+ protected void setInfoResources() {
+ setInfoResources(R.string.tv_input_discover_test,
+ R.string.tv_input_discover_test_info, -1);
}
}
diff --git a/build/test_package.mk b/build/test_package.mk
index acb8121..72972b2 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -37,7 +37,7 @@
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
$(hide) $(ACP) -fp $(call intermediates-dir-for,APPS,$(PRIVATE_PACKAGE))/package.apk $@
-$(cts_library_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
+$(cts_package_xml): PRIVATE_SRC_DIRS := $(cts_src_dirs)
$(cts_package_xml): PRIVATE_INSTRUMENTATION := $(LOCAL_INSTRUMENTATION_FOR)
$(cts_package_xml): PRIVATE_PACKAGE := $(LOCAL_PACKAGE_NAME)
ifneq ($(filter cts/suite/cts/%, $(LOCAL_PATH)),)
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 9dc51c9..4d9ef00 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -181,7 +181,7 @@
true /* reinstall */, options);
assertNotNull("app upgrade with different cert than existing app installed " +
"successfully", installResult);
- assertEquals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES", installResult);
+ assertEquals("INSTALL_FAILED_UPDATE_INCOMPATIBLE", installResult);
}
finally {
getDevice().uninstallPackage(SIMPLE_APP_PKG);
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index 264c0b1..caa3e46 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -57,7 +57,10 @@
private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
private static final String APK_mips = "CtsSplitApp_mips.apk";
+ private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk";
private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
+
+ private static final String APK_DIFF_CERT = "CtsSplitAppDiffCert.apk";
private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk";
private static final String APK_FEATURE = "CtsSplitAppFeature.apk";
@@ -218,8 +221,6 @@
public void testDiffCertInherit() throws Exception {
new InstallMultiple().addApk(APK).run();
- // TODO: remove this once we fix 17900178
- runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
}
@@ -229,8 +230,6 @@
public void testDiffVersionInherit() throws Exception {
new InstallMultiple().addApk(APK).run();
- // TODO: remove this once we fix 17900178
- runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
}
@@ -252,6 +251,16 @@
// TODO: flesh out this test
}
+ /**
+ * Verify that installing a new version of app wipes code cache.
+ */
+ public void testClearCodeCache() throws Exception {
+ new InstallMultiple().addApk(APK).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheWrite");
+ new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheRead");
+ }
+
class InstallMultiple {
private List<String> mArgs = new ArrayList<>();
private List<File> mApks = new ArrayList<>();
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 18ee963..98610a0 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -82,6 +82,25 @@
return new UiObject(rootsList.childSelector(new UiSelector().text(label)));
}
+ private UiObject findDocument(String label) throws UiObjectNotFoundException {
+ final UiSelector docList = new UiSelector().resourceId(
+ "com.android.documentsui:id/container_directory").childSelector(
+ new UiSelector().resourceId("com.android.documentsui:id/list"));
+
+ // Wait for the first list item to appear
+ assertTrue("First list item",
+ new UiObject(docList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
+
+ // Now scroll around to find our item
+ new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
+ return new UiObject(docList.childSelector(new UiSelector().text(label)));
+ }
+
+ private UiObject findSaveButton() throws UiObjectNotFoundException {
+ return new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
+ .childSelector(new UiSelector().resourceId("android:id/button1")));
+ }
+
public void testOpenSimple() throws Exception {
if (!supportedHardware()) return;
@@ -108,7 +127,7 @@
findRoot("CtsLocal").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("FILE1")).click();
+ findDocument("FILE1").click();
final Result result = mActivity.getResult();
final Uri uri = result.data.getData();
@@ -137,8 +156,7 @@
findRoot("CtsCreate").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
- .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+ findSaveButton().click();
final Result result = mActivity.getResult();
final Uri uri = result.data.getData();
@@ -164,13 +182,12 @@
// Pick file2, which should be selected since MIME matches, then try
// picking a non-matching MIME, which should leave file2 selected.
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("FILE2")).click();
+ findDocument("FILE2").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("FILE1")).click();
+ findDocument("FILE1").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
- .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+ findSaveButton().click();
final Result result = mActivity.getResult();
final Uri uri = result.data.getData();
@@ -188,10 +205,9 @@
findRoot("CtsCreate").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("DIR2")).click();
+ findDocument("DIR2").click();
mDevice.waitForIdle();
- new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
- .childSelector(new UiSelector().resourceId("android:id/button1"))).click();
+ findSaveButton().click();
final Result result = mActivity.getResult();
final Uri uri = result.data.getData();
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 277a1a2..5046458 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -37,6 +37,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
@@ -211,9 +212,8 @@
assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta)));
// And that we can access resources from feature
- // TODO: enable these once 17924027 is fixed
-// assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
-// assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+ assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
+ assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -292,8 +292,7 @@
assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled));
// And that we can access resources from feature
- // TODO: enable these once 17924027 is fixed
-// assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+ assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -310,6 +309,16 @@
assertEquals(0, result.size());
}
+ public void testCodeCacheWrite() throws Exception {
+ assertTrue(new File(getContext().getFilesDir(), "normal.raw").createNewFile());
+ assertTrue(new File(getContext().getCodeCacheDir(), "cache.raw").createNewFile());
+ }
+
+ public void testCodeCacheRead() throws Exception {
+ assertTrue(new File(getContext().getFilesDir(), "normal.raw").exists());
+ assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists());
+ }
+
private static void updateDpi(Resources r, int densityDpi) {
final Configuration c = new Configuration(r.getConfiguration());
c.densityDpi = densityDpi;
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
index 5378266..cf16307 100644
--- a/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionDiffCert/src/com/android/cts/usespermissiondiffcertapp/AccessPermissionWithDiffSigTest.java
@@ -429,6 +429,7 @@
boolean mHaveResult = false;
boolean mGoodResult = false;
boolean mSucceeded = false;
+ static final int TIMEOUT_MS = 30000;
@Override
public void onReceive(Context context, Intent intent) {
@@ -456,10 +457,10 @@
final long startTime = SystemClock.uptimeMillis();
while (!mHaveResult) {
try {
- wait(5000);
+ wait(TIMEOUT_MS);
} catch (InterruptedException e) {
}
- if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+ if (SystemClock.uptimeMillis() >= (startTime + TIMEOUT_MS)) {
throw new RuntimeException("Timeout");
}
}
@@ -477,10 +478,10 @@
final long startTime = SystemClock.uptimeMillis();
while (!mHaveResult) {
try {
- wait(5000);
+ wait(TIMEOUT_MS);
} catch (InterruptedException e) {
}
- if (SystemClock.uptimeMillis() >= (startTime+5000)) {
+ if (SystemClock.uptimeMillis() >= (startTime + TIMEOUT_MS)) {
throw new RuntimeException("Timeout");
}
}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index 4668faf..94862e7 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -523,7 +523,7 @@
}
private void checkProcess(String[] parts) {
- assertEquals(9, parts.length);
+ assertTrue(parts.length >= 9);
assertNotNull(parts[4]); // process
assertInteger(parts[5]); // userMillis
assertInteger(parts[6]); // systemMillis
@@ -658,7 +658,7 @@
}
private void checkMisc(String[] parts) {
- assertEquals(20, parts.length);
+ assertTrue(parts.length >= 20);
assertInteger(parts[4]); // screenOnTime
assertInteger(parts[5]); // phoneOnTime
assertInteger(parts[6]); // wifiOnTime
diff --git a/hostsidetests/net/Android.mk b/hostsidetests/net/Android.mk
new file mode 100644
index 0000000..6637d61
--- /dev/null
+++ b/hostsidetests/net/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsHostsideNetworkTests
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/net/app/Android.mk b/hostsidetests/net/app/Android.mk
new file mode 100644
index 0000000..29b620d
--- /dev/null
+++ b/hostsidetests/net/app/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/net/app/AndroidManifest.xml b/hostsidetests/net/app/AndroidManifest.xml
new file mode 100644
index 0000000..cdde7dc
--- /dev/null
+++ b/hostsidetests/net/app/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.net.hostside">
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".MyActivity" />
+ <service android:name=".MyVpnService"
+ android:permission="android.permission.BIND_VPN_SERVICE">
+ <intent-filter>
+ <action android:name="android.net.VpnService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.net.hostside" />
+
+</manifest>
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
new file mode 100644
index 0000000..375c852
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.net.hostside;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.view.WindowManager;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MyActivity extends Activity {
+ private final LinkedBlockingQueue<Integer> mResult = new LinkedBlockingQueue<>(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mResult.offer(resultCode) == false) {
+ throw new RuntimeException("Queue is full! This should never happen");
+ }
+ }
+
+ public Integer getResult(int timeoutMs) throws InterruptedException {
+ return mResult.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
new file mode 100644
index 0000000..1a12aaa
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -0,0 +1,155 @@
+/*
+ * 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.net.hostside;
+
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+
+public class MyVpnService extends VpnService {
+
+ private static String TAG = "MyVpnService";
+ private static int MTU = 1799;
+
+ private ParcelFileDescriptor mFd = null;
+ private UdpReflector mUdpReflector = null;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String packageName = getPackageName();
+ String cmd = intent.getStringExtra(packageName + ".cmd");
+ if ("disconnect".equals(cmd)) {
+ stop();
+ } else if ("connect".equals(cmd)) {
+ start(packageName, intent);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ private void start(String packageName, Intent intent) {
+ Builder builder = new Builder();
+
+ String addresses = intent.getStringExtra(packageName + ".addresses");
+ if (addresses != null) {
+ String[] addressArray = addresses.split(",");
+ for (int i = 0; i < addressArray.length; i++) {
+ String[] prefixAndMask = addressArray[i].split("/");
+ try {
+ InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+ int prefixLength = Integer.parseInt(prefixAndMask[1]);
+ builder.addAddress(address, prefixLength);
+ } catch (UnknownHostException|NumberFormatException|
+ ArrayIndexOutOfBoundsException e) {
+ continue;
+ }
+ }
+ }
+
+ String routes = intent.getStringExtra(packageName + ".routes");
+ if (routes != null) {
+ String[] routeArray = routes.split(",");
+ for (int i = 0; i < routeArray.length; i++) {
+ String[] prefixAndMask = routeArray[i].split("/");
+ try {
+ InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+ int prefixLength = Integer.parseInt(prefixAndMask[1]);
+ builder.addRoute(address, prefixLength);
+ } catch (UnknownHostException|NumberFormatException|
+ ArrayIndexOutOfBoundsException e) {
+ continue;
+ }
+ }
+ }
+
+ String allowed = intent.getStringExtra(packageName + ".allowedapplications");
+ if (allowed != null) {
+ String[] packageArray = allowed.split(",");
+ for (int i = 0; i < packageArray.length; i++) {
+ String allowedPackage = packageArray[i];
+ if (!TextUtils.isEmpty(allowedPackage)) {
+ try {
+ builder.addAllowedApplication(allowedPackage);
+ } catch(NameNotFoundException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ String disallowed = intent.getStringExtra(packageName + ".disallowedapplications");
+ if (disallowed != null) {
+ String[] packageArray = disallowed.split(",");
+ for (int i = 0; i < packageArray.length; i++) {
+ String disallowedPackage = packageArray[i];
+ if (!TextUtils.isEmpty(disallowedPackage)) {
+ try {
+ builder.addDisallowedApplication(disallowedPackage);
+ } catch(NameNotFoundException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ builder.setMtu(MTU);
+ builder.setBlocking(true);
+ builder.setSession("MyVpnService");
+
+ Log.i(TAG, "Establishing VPN,"
+ + " addresses=" + addresses
+ + " routes=" + routes
+ + " allowedApplications=" + allowed
+ + " disallowedApplications=" + disallowed);
+
+ mFd = builder.establish();
+ Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
+
+ mUdpReflector = new UdpReflector(mFd.getFileDescriptor(), MTU);
+ mUdpReflector.start();
+ }
+
+ private void stop() {
+ if (mUdpReflector != null) {
+ mUdpReflector.interrupt();
+ mUdpReflector = null;
+ }
+ try {
+ if (mFd != null) {
+ Log.i(TAG, "Closing filedescriptor");
+ mFd.close();
+ }
+ } catch(IOException e) {
+ } finally {
+ mFd = null;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ stop();
+ super.onDestroy();
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/UdpReflector.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/UdpReflector.java
new file mode 100644
index 0000000..a730fed
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/UdpReflector.java
@@ -0,0 +1,113 @@
+/*
+ * 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.net.hostside;
+
+import android.system.Os;
+import android.system.ErrnoException;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class UdpReflector extends Thread {
+
+ private static int IPV4_HEADER_LENGTH = 20;
+ private static int IPV6_HEADER_LENGTH = 40;
+ private static int UDP_HEADER_LENGTH = 8;
+
+ private static int IPV4_PROTO_OFFSET = 9;
+ private static int IPV6_PROTO_OFFSET = 6;
+ private static int IPPROTO_UDP = 17;
+
+ private static int IPV4_ADDR_OFFSET = 12;
+ private static int IPV6_ADDR_OFFSET = 8;
+ private static int IPV4_ADDR_LENGTH = 4;
+ private static int IPV6_ADDR_LENGTH = 16;
+
+ private static String TAG = "UdpReflector";
+
+ private FileDescriptor mFd;
+ private byte[] mBuf;
+
+ public UdpReflector(FileDescriptor fd, int mtu) {
+ super("UdpReflector");
+ mFd = fd;
+ mBuf = new byte[mtu];
+ }
+
+ private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
+ for (int i = 0; i < len; i++) {
+ byte b = buf[pos1 + i];
+ buf[pos1 + i] = buf[pos2 + i];
+ buf[pos2 + i] = b;
+ }
+ }
+
+ /** Reads one packet from our mFd, and possibly writes the packet back. */
+ private void processPacket() {
+ int len;
+ try {
+ len = Os.read(mFd, mBuf, 0, mBuf.length);
+ } catch (ErrnoException|IOException e) {
+ Log.e(TAG, "Error reading packet: " + e.getMessage());
+ return;
+ }
+
+ int version = mBuf[0] >> 4;
+ int addressOffset, protoOffset, headerLength, addressLength;
+ if (version == 4) {
+ headerLength = IPV4_HEADER_LENGTH;
+ protoOffset = IPV4_PROTO_OFFSET;
+ addressOffset = IPV4_ADDR_OFFSET;
+ addressLength = IPV4_ADDR_LENGTH;
+ } else if (version == 6) {
+ headerLength = IPV6_HEADER_LENGTH;
+ protoOffset = IPV6_PROTO_OFFSET;
+ addressOffset = IPV6_ADDR_OFFSET;
+ addressLength = IPV6_ADDR_LENGTH;
+ } else {
+ return;
+ }
+
+ if (len < headerLength + UDP_HEADER_LENGTH || mBuf[protoOffset] != IPPROTO_UDP) {
+ return;
+ }
+
+ // Swap src and dst IP addresses.
+ swapBytes(mBuf, addressOffset, addressOffset + addressLength, addressLength);
+
+ // Swap dst and src ports.
+ int portOffset = headerLength;
+ swapBytes(mBuf, portOffset, portOffset + 2, 2);
+
+ // Send the packet back. We don't need to recalculate the checksum because we didn't change
+ // the packet bytes, we only moved them around.
+ try {
+ len = Os.write(mFd, mBuf, 0, len);
+ } catch (ErrnoException|IOException e) {
+ Log.e(TAG, "Error writing packet: " + e.getMessage());
+ }
+ }
+
+ public void run() {
+ Log.i(TAG, "UdpReflector starting fd=" + mFd + " valid=" + mFd.valid());
+ while (!interrupted() && mFd.valid()) {
+ processPacket();
+ }
+ Log.i(TAG, "UdpReflector exiting fd=" + mFd + " valid=" + mFd.valid());
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
new file mode 100644
index 0000000..cdd370e
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -0,0 +1,281 @@
+/*
+ * 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.net.hostside;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.os.SystemClock;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.test.MoreAsserts;
+import android.test.InstrumentationTestCase;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Inet6Address;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Tests for {@link DocumentsProvider} and interaction with platform intents
+ * like {@link Intent#ACTION_OPEN_DOCUMENT}.
+ */
+public class VpnTest extends InstrumentationTestCase {
+
+ public static String TAG = "VpnTest";
+ public static int TIMEOUT_MS = 3 * 1000;
+
+ private UiDevice mDevice;
+ private MyActivity mActivity;
+ private String mPackageName;
+ private ConnectivityManager mCM;
+ Network mNetwork;
+ NetworkCallback mCallback;
+ final Object mLock = new Object();
+
+ private boolean supportedHardware() {
+ final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ return !pm.hasSystemFeature("android.hardware.type.television") &&
+ !pm.hasSystemFeature("android.hardware.type.watch");
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mNetwork = null;
+ mCallback = null;
+
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+ MyActivity.class, null);
+ mPackageName = mActivity.getPackageName();
+ mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+ mDevice.waitForIdle();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mCallback != null) {
+ mCM.unregisterNetworkCallback(mCallback);
+ }
+ Log.i(TAG, "Stopping VPN");
+ stopVpn();
+ mActivity.finish();
+ super.tearDown();
+ }
+
+ private void prepareVpn() throws Exception {
+ final int REQUEST_ID = 42;
+
+ // Attempt to prepare.
+ Log.i(TAG, "Preparing VPN");
+ Intent intent = VpnService.prepare(mActivity);
+
+ if (intent != null) {
+ // Start the confirmation dialog and click OK.
+ mActivity.startActivityForResult(intent, REQUEST_ID);
+ mDevice.waitForIdle();
+
+ String packageName = intent.getComponent().getPackageName();
+ String resourceIdRegex = "android:id/button1$|button_start_vpn";
+ final UiObject okButton = new UiObject(new UiSelector()
+ .className("android.widget.Button")
+ .packageName(packageName)
+ .resourceIdMatches(resourceIdRegex));
+ if (okButton.waitForExists(TIMEOUT_MS) == false) {
+ mActivity.finishActivity(REQUEST_ID);
+ fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " +
+ "to display the VPN confirmation dialog, but this test could not find the " +
+ "button to allow the VPN application to connect. Please ensure that the " +
+ "component displays a button with a resource ID matching the regexp: '" +
+ resourceIdRegex + "'.");
+ }
+
+ // Click the button and wait for RESULT_OK.
+ okButton.click();
+ try {
+ int result = mActivity.getResult(TIMEOUT_MS);
+ if (result != MyActivity.RESULT_OK) {
+ fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " +
+ "the button matching the regular expression '" + resourceIdRegex +
+ "' of " + intent.getComponent() + "'. Please ensure that clicking on " +
+ "that button allows the VPN application to connect. " +
+ "Return value: " + result);
+ }
+ } catch (InterruptedException e) {
+ fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms");
+ }
+
+ // Now we should be prepared.
+ intent = VpnService.prepare(mActivity);
+ if (intent != null) {
+ fail("VpnService.prepare returned non-null even after the VPN dialog " +
+ intent.getComponent() + "returned RESULT_OK.");
+ }
+ }
+ }
+
+ private void startVpn(
+ String[] addresses, String[] routes,
+ String allowedApplications, String disallowedApplications) throws Exception {
+
+ prepareVpn();
+
+ // Register a callback so we will be notified when our VPN comes up.
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ mCallback = new NetworkCallback() {
+ public void onAvailable(Network network) {
+ synchronized (mLock) {
+ Log.i(TAG, "Got available callback for network=" + network);
+ mNetwork = network;
+ mLock.notify();
+ }
+ }
+ };
+ mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown.
+
+ // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
+ Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", "connect")
+ .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
+ .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
+ .putExtra(mPackageName + ".allowedapplications", allowedApplications)
+ .putExtra(mPackageName + ".disallowedapplications", disallowedApplications);
+ mActivity.startService(intent);
+ synchronized (mLock) {
+ if (mNetwork == null) {
+ mLock.wait(TIMEOUT_MS);
+ }
+ }
+
+ if (mNetwork == null) {
+ fail("VPN did not become available after " + TIMEOUT_MS + "ms");
+ }
+
+ // Unfortunately, when the available callback fires, the VPN UID ranges are not yet
+ // configured. Give the system some time to do so. http://b/18436087 .
+ try { Thread.sleep(300); } catch(InterruptedException e) {}
+ }
+
+ private void stopVpn() {
+ // Simply calling mActivity.stopService() won't stop the service, because the system binds
+ // to the service for the purpose of sending it a revoke command if another VPN comes up,
+ // and stopping a bound service has no effect. Instead, "start" the service again with an
+ // Intent that tells it to disconnect.
+ Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", "disconnect");
+ mActivity.startService(intent);
+ }
+
+ private static void checkUdpEcho(
+ String to, String expectedFrom, boolean expectReply) throws IOException {
+ DatagramSocket s;
+ InetAddress address = InetAddress.getByName(to);
+ if (address instanceof Inet6Address) { // http://b/18094870
+ s = new DatagramSocket(0, InetAddress.getByName("::"));
+ } else {
+ s = new DatagramSocket();
+ }
+ s.setSoTimeout(100); // ms.
+
+ String msg = "Hello, world!";
+ DatagramPacket p = new DatagramPacket(msg.getBytes(), msg.length());
+ s.connect(address, 7);
+
+ if (expectedFrom != null) {
+ assertEquals("Unexpected source address: ",
+ expectedFrom, s.getLocalAddress().getHostAddress());
+ }
+
+ try {
+ if (expectReply) {
+ s.send(p);
+ s.receive(p);
+ MoreAsserts.assertEquals(msg.getBytes(), p.getData());
+ } else {
+ try {
+ s.send(p);
+ s.receive(p);
+ fail("Received unexpected reply");
+ } catch(IOException expected) {}
+ }
+ } finally {
+ s.close();
+ }
+ }
+
+ private static void expectUdpEcho(String to, String expectedFrom) throws IOException {
+ checkUdpEcho(to, expectedFrom, true);
+ }
+
+ private static void expectNoUdpEcho(String to) throws IOException {
+ checkUdpEcho(to, null, false);
+ }
+
+ public void testDefault() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ "", "");
+
+ expectUdpEcho("192.0.2.251", "192.0.2.2");
+ expectUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+ }
+
+ public void testAppAllowed() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"},
+ mPackageName, "");
+
+ expectUdpEcho("192.0.2.251", "192.0.2.2");
+ expectUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+ }
+
+ public void testAppDisallowed() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ "", mPackageName);
+
+ expectNoUdpEcho("192.0.2.251");
+ expectNoUdpEcho("2001:db8:dead:beef::f00");
+ }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
new file mode 100644
index 0000000..a7698f3
--- /dev/null
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
@@ -0,0 +1,105 @@
+/*
+ * 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.net;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+
+import java.util.Map;
+
+public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+ private static final String TEST_PKG = "com.android.cts.net.hostside";
+ private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+
+ private IAbi mAbi;
+ private CtsBuildHelper mCtsBuild;
+
+ @Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ assertNotNull(mAbi);
+ assertNotNull(mCtsBuild);
+
+ getDevice().uninstallPackage(TEST_PKG);
+
+ assertNull(getDevice().installPackage(mCtsBuild.getTestApp(TEST_APK), false));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ public void testVpn() throws Exception {
+ runDeviceTests(TEST_PKG, ".VpnTest");
+ }
+
+ public void runDeviceTests(String packageName, String testClassName)
+ throws DeviceNotAvailableException {
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+ "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+
+ final CollectingTestListener listener = new CollectingTestListener();
+ getDevice().runInstrumentationTests(testRunner, listener);
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new AssertionError("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+
+ if (result.hasFailedTests()) {
+ // build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+ for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ }
+}
diff --git a/hostsidetests/security/Android.mk b/hostsidetests/security/Android.mk
index 6ff0ebf..0c976a3 100644
--- a/hostsidetests/security/Android.mk
+++ b/hostsidetests/security/Android.mk
@@ -25,7 +25,7 @@
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
-LOCAL_JAVA_LIBRARIES := cts-tradefed ddmlib-prebuilt tradefed-prebuilt
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
LOCAL_CTS_TEST_PACKAGE := android.host.security
diff --git a/hostsidetests/theme/assets/21/hdpi.zip b/hostsidetests/theme/assets/21/hdpi.zip
index 9cc179c..0fa67b7 100644
--- a/hostsidetests/theme/assets/21/hdpi.zip
+++ b/hostsidetests/theme/assets/21/hdpi.zip
Binary files differ
diff --git a/libs/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
index 8e88b47..eab4808 100644
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -17,9 +17,11 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.net.Uri;
import java.lang.reflect.Method;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
@@ -29,7 +31,12 @@
import java.io.IOException;
public class MediaUtils {
- final public static String TAG = "MediaUtils";
+ private static final String TAG = "MediaUtils";
+
+ private static final int ALL_AV_TRACKS = -1;
+
+ private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+
/**
* Finds test name (heuristically) and prints out standard skip message.
@@ -117,11 +124,33 @@
skipTest(TAG, reason);
}
+ public static boolean check(boolean result, String message) {
+ if (!result) {
+ skipTest(message);
+ }
+ return result;
+ }
+
+ public static boolean canDecode(MediaFormat format) {
+ if (sMCL.findDecoderForFormat(format) == null) {
+ Log.i(TAG, "no decoder for " + format);
+ return false;
+ }
+ return true;
+ }
+
+ public static boolean hasCodecForTrack(MediaExtractor ex, int track) {
+ int count = ex.getTrackCount();
+ if (track < 0 || track >= count) {
+ throw new IndexOutOfBoundsException(track + " not in [0.." + (count - 1) + "]");
+ }
+ return canDecode(ex.getTrackFormat(track));
+ }
+
/**
* return true iff all audio and video tracks are supported
*/
public static boolean hasCodecsForMedia(MediaExtractor ex) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (int i = 0; i < ex.getTrackCount(); ++i) {
MediaFormat format = ex.getTrackFormat(i);
// only check for audio and video codecs
@@ -129,8 +158,7 @@
if (!mime.startsWith("audio/") && !mime.startsWith("video/")) {
continue;
}
- if (mcl.findDecoderForFormat(format) == null) {
- Log.i(TAG, "no decoder for " + format);
+ if (!canDecode(format)) {
return false;
}
}
@@ -142,46 +170,69 @@
*/
public static boolean hasCodecForMediaAndDomain(MediaExtractor ex, String mimePrefix) {
mimePrefix = mimePrefix.toLowerCase();
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
for (int i = 0; i < ex.getTrackCount(); ++i) {
MediaFormat format = ex.getTrackFormat(i);
String mime = format.getString(MediaFormat.KEY_MIME);
if (mime.toLowerCase().startsWith(mimePrefix)) {
- if (mcl.findDecoderForFormat(format) == null) {
- Log.i(TAG, "no decoder for " + format);
- } else {
+ if (canDecode(format)) {
return true;
}
+ Log.i(TAG, "no decoder for " + format);
}
}
return false;
}
+ private static boolean hasCodecsForResourceCombo(
+ Context context, int resourceId, int track, String mimePrefix) {
+ try {
+ AssetFileDescriptor afd = null;
+ MediaExtractor ex = null;
+ try {
+ afd = context.getResources().openRawResourceFd(resourceId);
+ ex = new MediaExtractor();
+ ex.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ if (mimePrefix != null) {
+ return hasCodecForMediaAndDomain(ex, mimePrefix);
+ } else if (track == ALL_AV_TRACKS) {
+ return hasCodecsForMedia(ex);
+ } else {
+ return hasCodecForTrack(ex, track);
+ }
+ } finally {
+ if (ex != null) {
+ ex.release();
+ }
+ if (afd != null) {
+ afd.close();
+ }
+ }
+ } catch (IOException e) {
+ Log.i(TAG, "could not open resource");
+ }
+ return false;
+ }
+
/**
* return true iff all audio and video tracks are supported
*/
public static boolean hasCodecsForResource(Context context, int resourceId) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- try {
- AssetFileDescriptor afd = null;
- MediaExtractor ex = null;
- try {
- afd = context.getResources().openRawResourceFd(resourceId);
- ex = new MediaExtractor();
- ex.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- return hasCodecsForMedia(ex);
- } finally {
- if (ex != null) {
- ex.release();
- }
- if (afd != null) {
- afd.close();
- }
- }
- } catch (IOException e) {
- Log.i(TAG, "could not open resource");
- }
- return false;
+ return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, null /* mimePrefix */);
+ }
+
+ public static boolean checkCodecsForResource(Context context, int resourceId) {
+ return check(hasCodecsForResource(context, resourceId), "no decoder found");
+ }
+
+ /**
+ * return true iff track is supported.
+ */
+ public static boolean hasCodecForResource(Context context, int resourceId, int track) {
+ return hasCodecsForResourceCombo(context, resourceId, track, null /* mimePrefix */);
+ }
+
+ public static boolean checkCodecForResource(Context context, int resourceId, int track) {
+ return check(hasCodecForResource(context, resourceId, track), "no decoder found");
}
/**
@@ -189,26 +240,90 @@
*/
public static boolean hasCodecForResourceAndDomain(
Context context, int resourceId, String mimePrefix) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ return hasCodecsForResourceCombo(context, resourceId, ALL_AV_TRACKS, mimePrefix);
+ }
+
+ /**
+ * return true iff all audio and video tracks are supported
+ */
+ public static boolean hasCodecsForPath(Context context, String path) {
+ MediaExtractor ex = null;
try {
- AssetFileDescriptor afd = null;
- MediaExtractor ex = null;
- try {
- afd = context.getResources().openRawResourceFd(resourceId);
- ex = new MediaExtractor();
- ex.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
- return hasCodecForMediaAndDomain(ex, mimePrefix);
- } finally {
- if (ex != null) {
- ex.release();
- }
- if (afd != null) {
- afd.close();
- }
+ ex = new MediaExtractor();
+ Uri uri = Uri.parse(path);
+ String scheme = uri.getScheme();
+ if (scheme == null) { // file
+ ex.setDataSource(path);
+ } else if (scheme.equalsIgnoreCase("file")) {
+ ex.setDataSource(uri.getPath());
+ } else {
+ ex.setDataSource(context, uri, null);
}
+ return hasCodecsForMedia(ex);
} catch (IOException e) {
- Log.i(TAG, "could not open resource");
+ Log.i(TAG, "could not open path " + path);
+ } finally {
+ if (ex != null) {
+ ex.release();
+ }
}
return false;
}
+
+ public static boolean checkCodecsForPath(Context context, String path) {
+ return check(hasCodecsForPath(context, path), "no decoder found");
+ }
+
+ private static boolean hasCodecForMime(boolean encoder, String mime) {
+ for (MediaCodecInfo info : sMCL.getCodecInfos()) {
+ if (encoder != info.isEncoder()) {
+ continue;
+ }
+
+ for (String type : info.getSupportedTypes()) {
+ if (type.equalsIgnoreCase(mime)) {
+ Log.i(TAG, "found codec " + info.getName() + " for mime " + mime);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private static boolean hasCodecForMimes(boolean encoder, String[] mimes) {
+ for (String mime : mimes) {
+ if (!hasCodecForMime(encoder, mime)) {
+ Log.i(TAG, "no " + (encoder ? "encoder" : "decoder") + " for mime " + mime);
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ public static boolean hasEncoder(String... mimes) {
+ return hasCodecForMimes(true /* encoder */, mimes);
+ }
+
+ public static boolean hasDecoder(String... mimes) {
+ return hasCodecForMimes(false /* encoder */, mimes);
+ }
+
+ public static boolean checkDecoder(String... mimes) {
+ return check(hasCodecForMimes(false /* encoder */, mimes), "no decoder found");
+ }
+
+ public static boolean checkEncoder(String... mimes) {
+ return check(hasCodecForMimes(true /* encoder */, mimes), "no encoder found");
+ }
+
+ public static boolean canDecodeVideo(String mime, int width, int height, float rate) {
+ MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+ format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
+ return canDecode(format);
+ }
+
+ public static boolean checkDecoderForFormat(MediaFormat format) {
+ return check(canDecode(format), "no decoder for " + format);
+ }
}
diff --git a/tests/app/AndroidManifest.xml b/tests/app/AndroidManifest.xml
index b628a0c..0d61e20 100644
--- a/tests/app/AndroidManifest.xml
+++ b/tests/app/AndroidManifest.xml
@@ -40,6 +40,7 @@
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.BODY_SENSORS" />
<application android:label="Android TestCase"
android:icon="@drawable/size_48x48"
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 924a0ce..c0875c1 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -22,6 +22,30 @@
bug: 17595050
},
{
+ description: "the SSLCertificateSocketFactoryTest often fails because of lack of live internet or short timeout, it should be refactored to do a local server testing",
+ names: [
+ "android.net.cts.SSLCertificateSocketFactoryTest#testCreateSocket",
+ "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_bind",
+ "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_simple",
+ "android.net.cts.SSLCertificateSocketFactoryTest#test_createSocket_wrapping"
+ ],
+ bug: 18682315
+},
+{
+ description: "the test result are too much dependent on live-internet connection, which for some devices might not exist",
+ names: [
+ "android.net.wifi.cts.NsdManagerTest#testAndroidTestCaseSetupProperly"
+ ],
+ bug: 18680089
+},
+{
+ description: "AudioPolicyBinder tests are not yet robust enough",
+ names: [
+ "android.security.cts.AudioPolicyBinderTest"
+ ],
+ bug: 18461670
+},
+{
description: "Not all jdwp features are currently supported. These tests will fail",
names: [
"org.apache.harmony.jpda.tests.jdwp.DebuggerOnDemand.OnthrowDebuggerLaunchTest#testDebuggerLaunch001",
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 87f0238..f5e1d48 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -35,7 +35,7 @@
public class AccessibilityNodeInfoTest extends AndroidTestCase {
/** The number of properties of the {@link AccessibilityNodeInfo} class. */
- private static final int NON_STATIC_FIELD_COUNT = 28;
+ private static final int NON_STATIC_FIELD_COUNT = 30;
@SmallTest
public void testMarshaling() throws Exception {
@@ -54,7 +54,7 @@
AccessibilityNodeInfo receivedInfo = AccessibilityNodeInfo.CREATOR.createFromParcel(parcel);
// make sure all fields properly marshaled
- assertEqualsAccessiblityNodeInfo(sentInfo, receivedInfo);
+ assertEqualsAccessibilityNodeInfo(sentInfo, receivedInfo);
}
/**
@@ -204,7 +204,7 @@
* <code>receviedInfo</code> to verify that the received node info is
* the one that is expected.
*/
- public static void assertEqualsAccessiblityNodeInfo(AccessibilityNodeInfo expectedInfo,
+ public static void assertEqualsAccessibilityNodeInfo(AccessibilityNodeInfo expectedInfo,
AccessibilityNodeInfo receivedInfo) {
Rect expectedBounds = new Rect();
Rect receivedBounds = new Rect();
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 306428f..0163a58 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -174,8 +174,8 @@
}
- public void testClone() {
- AnimatorSet set1 = new AnimatorSet();
+ public void testClone() throws Throwable {
+ final AnimatorSet set1 = new AnimatorSet();
final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
set1.addListener(setListener);
ObjectAnimator animator1 = new ObjectAnimator();
@@ -198,7 +198,12 @@
AnimateObject target = new AnimateObject();
set1.setTarget(target);
- set1.start();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ set1.start();
+ }
+ });
assertTrue(set1.isStarted());
animator1.getListeners();
diff --git a/tests/tests/app/src/android/app/cts/ServiceTest.java b/tests/tests/app/src/android/app/cts/ServiceTest.java
index b66f4c8..8ba1816 100644
--- a/tests/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/tests/app/src/android/app/cts/ServiceTest.java
@@ -448,4 +448,17 @@
// expected
}
}
+
+ @MediumTest
+ public void testImplicitIntentFailsOnApiLevel21() throws Exception {
+ Intent intent = new Intent(LocalService.SERVICE_LOCAL);
+ EmptyConnection conn = new EmptyConnection();
+ try {
+ mContext.bindService(intent, conn, 0);
+ mContext.unbindService(conn);
+ fail("Implicit intents should be disallowed for apps targeting API 21+");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
}
diff --git a/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
index 495501f..4e57d31 100644
--- a/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
+++ b/tests/tests/app/src/android/app/cts/SystemFeaturesTest.java
@@ -141,8 +141,6 @@
boolean manualSensor = false;
boolean manualPostProcessing = false;
boolean raw = false;
- boolean readSensorSettings = false;
- boolean burstCapture = false;
CameraCharacteristics[] cameraChars = new CameraCharacteristics[cameraIds.length];
for (String cameraId : cameraIds) {
CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId);
@@ -162,14 +160,8 @@
case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW:
raw = true;
break;
- case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
- readSensorSettings = true;
- break;
- case CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
- burstCapture = true;
- break;
default:
- // Capabilities that don't have a matching system feature
+ // Capabilities don't have a matching system feature
break;
}
}
@@ -179,8 +171,6 @@
assertFeature(manualPostProcessing,
PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_POST_PROCESSING);
assertFeature(raw, PackageManager.FEATURE_CAMERA_CAPABILITY_RAW);
- assertFeature(readSensorSettings, PackageManager.FEATURE_CAMERA_CAPABILITY_READ_SENSOR_SETTINGS);
- assertFeature(burstCapture, PackageManager.FEATURE_CAMERA_CAPABILITY_BURST_CAPTURE);
}
private void checkFrontCamera() {
@@ -302,15 +292,58 @@
Sensor.TYPE_STEP_COUNTER);
assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_STEP_DETECTOR,
Sensor.TYPE_STEP_DETECTOR);
- assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE,
- Sensor.TYPE_HEART_RATE);
- assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE_ECG,
- Sensor.TYPE_HEART_RATE);
assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_AMBIENT_TEMPERATURE,
Sensor.TYPE_AMBIENT_TEMPERATURE);
assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_RELATIVE_HUMIDITY,
Sensor.TYPE_RELATIVE_HUMIDITY);
+
+ /*
+ * We have three cases to test for :
+ * Case 1: Device does not have an HRM
+ * FEATURE_SENSOR_HEART_RATE false
+ * FEATURE_SENSOR_HEART_RATE_ECG false
+ * assertFeatureForSensor(TYPE_HEART_RATE) false
+ *
+ * Case 2: Device has a PPG HRM
+ * FEATURE_SENSOR_HEART_RATE true
+ * FEATURE_SENSOR_HEART_RATE_ECG false
+ * assertFeatureForSensor(TYPE_HEART_RATE) true
+ *
+ * Case 3: Device has an ECG HRM
+ * FEATURE_SENSOR_HEART_RATE false
+ * FEATURE_SENSOR_HEART_RATE_ECG true
+ * assertFeatureForSensor(TYPE_HEART_RATE) true
+ */
+
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HEART_RATE_ECG)) {
+ /* Case 3 for FEATURE_SENSOR_HEART_RATE_ECG true case */
+ assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE_ECG,
+ Sensor.TYPE_HEART_RATE);
+
+ /* Remove HEART_RATE from featuresLeft, no way to test that one */
+ assertTrue("Features left " + featuresLeft + " to check did not include "
+ + PackageManager.FEATURE_SENSOR_HEART_RATE,
+ featuresLeft.remove(PackageManager.FEATURE_SENSOR_HEART_RATE));
+ } else if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_HEART_RATE)) {
+ /* Case 1 & 2 for FEATURE_SENSOR_HEART_RATE_ECG false case */
+ assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE_ECG,
+ Sensor.TYPE_HEART_RATE);
+
+ /* Case 1 & 3 for FEATURE_SENSOR_HEART_RATE false case */
+ assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE,
+ Sensor.TYPE_HEART_RATE);
+ } else {
+ /* Case 2 for FEATURE_SENSOR_HEART_RATE true case */
+ assertFeatureForSensor(featuresLeft, PackageManager.FEATURE_SENSOR_HEART_RATE,
+ Sensor.TYPE_HEART_RATE);
+
+ /* Remove HEART_RATE_ECG from featuresLeft, no way to test that one */
+ assertTrue("Features left " + featuresLeft + " to check did not include "
+ + PackageManager.FEATURE_SENSOR_HEART_RATE_ECG,
+ featuresLeft.remove(PackageManager.FEATURE_SENSOR_HEART_RATE_ECG));
+ }
+
assertTrue("Assertions need to be added to this test for " + featuresLeft,
featuresLeft.isEmpty());
}
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index d4fac55..0f9a5cc 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -45,6 +45,7 @@
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.Set;
public class IntentTest extends AndroidTestCase {
@@ -676,7 +677,7 @@
final String compnent =
"component(" + mContext.getPackageName() + "!" + MockActivity.class.getName() + ")";
- uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(1)" + compnent
+ uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(5)" + compnent
+ "extras(Stest=testString!btestbyte=1!"
+ "Btestboolean=true!ctestchar=a!dtestdouble=1d!"
+ "itestint=1!ltestlong=1!stestshort=1!ftestfloat=1f)";
@@ -686,7 +687,7 @@
assertEquals(mComponentName, mIntent.getComponent());
assertEquals("test", (String) (mIntent.getCategories().toArray()[0]));
assertEquals("mtype", mIntent.getType());
- assertEquals(1, mIntent.getFlags());
+ assertEquals(4, mIntent.getFlags());
assertEquals("testString", mIntent.getStringExtra("test"));
assertTrue(mIntent.getBooleanExtra("testboolean", false));
final byte b = 1;
@@ -812,10 +813,13 @@
target = Intent.getIntent(uri);
assertEquals(TEST_TYPE, target.getType());
- mIntent.setFlags(1);
+ mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
uri = mIntent.toURI();
target = Intent.getIntent(uri);
- assertEquals(1, target.getFlags());
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_DOCUMENT, target.getFlags());
String stringValue = "testString";
mIntent.putExtra(TEST_EXTRA_NAME, stringValue);
@@ -869,8 +873,8 @@
}
public void testToURI() {
- mIntent.setFlags(0);
- assertEquals("#Intent;end", mIntent.toURI());
+ mIntent = new Intent();
+ assertEquals("", mIntent.toURI());
mIntent.setData(TEST_URI);
assertTrue(mIntent.toURI().indexOf(TEST_URI.toString()) != -1);
@@ -937,6 +941,321 @@
return uri.toString();
}
+ static Intent makeSelector(Intent baseIntent, Intent selectorIntent) {
+ baseIntent.setSelector(selectorIntent);
+ return baseIntent;
+ }
+
+ public void testUris() {
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;end",
+ null,
+ new Intent().setAction("android.test.FOO"));
+ checkIntentUri(
+ "intent:#Intent;category=android.test.FOO;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;launchFlags=0x20;end",
+ null,
+ new Intent().setAction("android.test.FOO").setFlags(0x20));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah")));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;component=com.exfoo/com.argh.Bar;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar")));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah#fragment")));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;action=android.test.foo;end",
+ null,
+ new Intent().setAction("android.test.foo")
+ .setData(Uri.parse("http://www.example.com/blah")));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;type=image/foo;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.parse("mailto:foo"), "image/foo"));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;S.string=text;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("string", "text"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;S.string=text;end",
+ null,
+ new Intent().setAction("android.test.FOO").putExtra("string", "text"));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;i.int=1000;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("int", 1000));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;l.long=1000;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("long", (long) 1000));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;B.boolean=true;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("boolean", true));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;f.float=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("float", 10.4f));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;d.double=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("double", (double) 10.4));
+ checkIntentUri(
+ "intent:#Intent;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .putExtra("int", 1000).putExtra("long", (long) 1000)
+ .putExtra("boolean", true).putExtra("float", 10.4f));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;scheme=foobar;action=android.test.FOO;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO").setData(Uri.parse("foobar:"))));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;package=com.myapp;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO").setPackage("com.myapp")));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;component=com.exfoo/com.argh.Bar;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO")
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))));
+
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;end",
+ new Intent().setAction("android.test.FOO").setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.intent.action.MAIN;package=com.myapp;end",
+ "android-app://com.myapp",
+ new Intent().setAction(Intent.ACTION_MAIN).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;end",
+ new Intent().setAction(Intent.ACTION_VIEW).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;category=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;category=android.test.FOO;end",
+ new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;launchFlags=0x20;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;launchFlags=0x20;end",
+ new Intent().setAction("android.test.FOO").setFlags(0x20)
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;component=com.exfoo/com.argh.Bar;end",
+ "android-app://com.myapp/http/www.example.com/blah#Intent;component=com.exfoo/com.argh.Bar;end",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah#fragment",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah#fragment"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;action=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah#fragment#Intent;action=android.test.FOO;end",
+ new Intent().setAction("android.test.FOO")
+ .setData(Uri.parse("http://www.example.com/blah#fragment"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;type=image/foo;package=com.myapp;end",
+ "android-app://com.myapp/mailto#Intent;type=image/foo;end",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.parse("mailto:"), "image/foo")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;S.string=text;end",
+ "android-app://com.myapp/mailto#Intent;S.string=text;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;package=com.myapp;S.string=text;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;S.string=text;end",
+ new Intent().setAction("android.test.FOO").putExtra("string", "text")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;i.int=1000;end",
+ "android-app://com.myapp/mailto#Intent;i.int=1000;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("int", 1000)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;l.long=1000;end",
+ "android-app://com.myapp/mailto#Intent;l.long=1000;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("long", (long) 1000)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;B.boolean=true;end",
+ "android-app://com.myapp/mailto#Intent;B.boolean=true;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("boolean", true)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;f.float=10.4;end",
+ "android-app://com.myapp/mailto#Intent;f.float=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("float", 10.4f)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;d.double=10.4;end",
+ "android-app://com.myapp/mailto#Intent;d.double=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("double", (double) 10.4)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;package=com.myapp;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .putExtra("int", 1000).putExtra("long", (long) 1000)
+ .putExtra("boolean", true).putExtra("float", 10.4f)
+ .setPackage("com.myapp"));
+ }
+
+ private boolean compareIntents(Intent expected, Intent actual) {
+ if (!Objects.equals(expected.getAction(), actual.getAction())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getData(), actual.getData())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getType(), actual.getType())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getPackage(), actual.getPackage())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getComponent(), actual.getComponent())) {
+ return false;
+ }
+ if (expected.getFlags() != actual.getFlags()) {
+ return false;
+ }
+ Set<String> expectedCat = expected.getCategories();
+ Set<String> actualCat = actual.getCategories();
+ if (expectedCat != actualCat) {
+ if (expectedCat == null || actualCat == null) {
+ return false;
+ }
+ for (String cat : expectedCat) {
+ if (!actual.hasCategory(cat)) {
+ return false;
+ }
+ }
+ for (String cat : actualCat) {
+ if (!expected.hasCategory(cat)) {
+ return false;
+ }
+ }
+ }
+ Bundle extras1 = expected.getExtras();
+ Bundle extras2 = actual.getExtras();
+ if (extras1 != extras2) {
+ if (extras1 == null || extras2 == null) {
+ return false;
+ }
+ for (String key : extras1.keySet()) {
+ if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+ return false;
+ }
+ }
+ for (String key : extras2.keySet()) {
+ if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void assertEqualsIntent(String msg, Intent expected, Intent actual) {
+ if (!compareIntents(expected, actual)) {
+ failNotEquals(msg, expected, actual);
+ }
+ Intent expectedSel = expected.getSelector();
+ Intent actualSel = actual.getSelector();
+ if (expectedSel != actualSel) {
+ if (expectedSel == null || actualSel == null) {
+ failNotEquals(msg, expected, actual);
+ }
+ if (!compareIntents(expectedSel, actualSel)) {
+ failNotEquals(msg, expected, actual);
+ }
+ }
+ }
+
+ private void checkIntentUri(String intentSchemeUri, String androidAppSchemeUri, Intent intent) {
+ if (intentSchemeUri != null) {
+ try {
+ Intent genIntent = Intent.parseUri(intentSchemeUri, 0);
+ assertEqualsIntent("Implicitly converting " + intentSchemeUri + " to Intent",
+ intent, genIntent);
+ genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_INTENT_SCHEME);
+ assertEqualsIntent("Explicitly converting " + intentSchemeUri + " to Intent",
+ intent, genIntent);
+ } catch (URISyntaxException e) {
+ fail("Failure parsing " + intentSchemeUri + ": " + e);
+ }
+ String genUri = intent.toUri(Intent.URI_INTENT_SCHEME);
+ assertEquals("Converting " + intent + " to intent: uri",
+ intentSchemeUri, genUri);
+ }
+ if (androidAppSchemeUri != null) {
+ try {
+ Intent genIntent = Intent.parseUri(androidAppSchemeUri, 0);
+ assertEqualsIntent("Implicitly converting " + androidAppSchemeUri + " to Intent",
+ intent, genIntent);
+ genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_ANDROID_APP_SCHEME);
+ assertEqualsIntent("Explicitly converting " + androidAppSchemeUri + " to Intent",
+ intent, genIntent);
+ } catch (URISyntaxException e) {
+ fail("Failure parsing " + androidAppSchemeUri + ": " + e);
+ }
+ String genUri = intent.toUri(Intent.URI_ANDROID_APP_SCHEME);
+ assertEquals("Converting " + intent + " to android-app: uri",
+ androidAppSchemeUri, genUri);
+ }
+ }
+
public void testAccessFlags() {
int expected = 1;
mIntent.setFlags(expected);
diff --git a/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
new file mode 100644
index 0000000..35466be
--- /dev/null
+++ b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
@@ -0,0 +1,56 @@
+/*
+ * 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.content.res.cts;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.util.TypedValue;
+
+/**
+ * Tests that private attributes are correctly placed in a separate type to
+ * prevent future releases from stomping over private attributes with new public ones.
+ */
+public class PrivateAttributeTest extends AndroidTestCase {
+
+ private static final int sLastPublicAttr = 0x010104d5;
+
+ public void testNoAttributesAfterLastPublicAttribute() throws Exception {
+ final Resources res = getContext().getResources();
+
+ final String lastPublicName;
+ try {
+ lastPublicName = res.getResourceEntryName(sLastPublicAttr);
+ } catch (Resources.NotFoundException e) {
+ throw new AssertionError("Last public resource was not found", e);
+ }
+
+ int currentAttr = sLastPublicAttr;
+ while (currentAttr < 0x0101ffff) {
+ currentAttr++;
+ try {
+ final String name = res.getResourceEntryName(currentAttr);
+ throw new AssertionError("Found attribute '" + name + "'"
+ + " (0x" + Integer.toHexString(currentAttr) + ")"
+ + " after last public framework attribute "
+ + "'" + lastPublicName + "'"
+ + " (0x" + Integer.toHexString(sLastPublicAttr) + ")");
+ } catch (Resources.NotFoundException e) {
+ // continue
+ }
+ }
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 9f50b43..29c7362 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@@ -1014,6 +1015,24 @@
}
}
+ private void checkAntiBandingMode(CaptureRequest.Builder request, int template) {
+ if (template == CameraDevice.TEMPLATE_MANUAL) {
+ return;
+ }
+
+ List<Integer> availableAntiBandingModes =
+ Arrays.asList(toObject(mStaticInfo.getAeAvailableAntiBandingModesChecked()));
+
+ if (availableAntiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO)) {
+ mCollector.expectKeyValueEquals(request, CONTROL_AE_ANTIBANDING_MODE,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
+ } else {
+ mCollector.expectKeyValueIsIn(request, CONTROL_AE_ANTIBANDING_MODE,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ);
+ }
+ }
+
/**
* <p>Check if 3A metering settings are "up to HAL" in request template</p>
*
@@ -1058,6 +1077,7 @@
checkAfMode(request, template, props);
checkFpsRange(request, template, props);
+ checkAntiBandingMode(request, template);
if (template == CameraDevice.TEMPLATE_MANUAL) {
mCollector.expectKeyValueEquals(request, CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
@@ -1068,8 +1088,6 @@
} else {
mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
- mCollector.expectKeyValueNotEquals(request, CONTROL_AE_ANTIBANDING_MODE,
- CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 32a6880..27ff6d1 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -513,6 +513,11 @@
mCameraManager.registerAvailabilityCallback(ac, mHandler);
String[] cameras = mCameraManager.getCameraIdList();
+ if (cameras.length == 0) {
+ Log.i(TAG, "No cameras present, skipping test");
+ return;
+ }
+
// Verify we received available for all cameras' initial state in a reasonable amount of time
HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
while (expectedAvailableCameras.size() > 0) {
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 303099a..dd17779 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -18,6 +18,7 @@
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PointF;
@@ -492,6 +493,23 @@
}
}
+ public static void dumpFile(String fileName, Bitmap data) {
+ FileOutputStream outStream;
+ try {
+ Log.v(TAG, "output will be saved as " + fileName);
+ outStream = new FileOutputStream(fileName);
+ } catch (IOException ioe) {
+ throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
+ }
+
+ try {
+ data.compress(Bitmap.CompressFormat.JPEG, /*quality*/90, outStream);
+ outStream.close();
+ } catch (IOException ioe) {
+ throw new RuntimeException("failed writing data to file " + fileName, ioe);
+ }
+ }
+
public static void dumpFile(String fileName, byte[] data) {
FileOutputStream outStream;
try {
@@ -682,6 +700,21 @@
}
/**
+ * Returns true if the given {@code array} contains the given element.
+ *
+ * @param array {@code array} to check for {@code elem}
+ * @param elem {@code elem} to test for
+ * @return {@code true} if the given element is contained
+ */
+ public static boolean contains(int[] array, int elem) {
+ if (array == null) return false;
+ for (int i = 0; i < array.length; i++) {
+ if (elem == array[i]) return true;
+ }
+ return false;
+ }
+
+ /**
* Get object array from byte array.
*
* @param array Input byte array to be converted
@@ -779,6 +812,7 @@
validateJpegData(data, width, height, filePath);
break;
case ImageFormat.YUV_420_888:
+ case ImageFormat.YV12:
validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
break;
case ImageFormat.RAW_SENSOR:
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 03a6f76..d70be69 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -2098,15 +2098,18 @@
resultListener = new SimpleCaptureCallback();
startPreview(requestBuilder, previewSz, resultListener);
- long[] frameDurationRange =
- new long[]{(long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
+ waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
+ long[] frameDurationRange = new long[]{
+ (long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
for (int j = 0; j < numFramesVerified; j++) {
CaptureResult result =
resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
validatePipelineDepth(result);
long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
mCollector.expectInRange(
- "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+ "Frame duration must be in the range of " +
+ Arrays.toString(frameDurationRange),
frameDuration,
(long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
(long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 40db2d22c..9089a8c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -16,27 +16,39 @@
package android.hardware.camera2.cts;
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Color;
import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraDevice;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.util.Size;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.ConditionVariable;
import android.util.Log;
+import android.util.Size;
import android.view.Surface;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
+import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import static android.hardware.camera2.cts.CameraTestUtils.dumpFile;
+import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+
/**
* <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
* sends the data to the surface provided by imageReader. Below image formats
@@ -50,10 +62,16 @@
public class ImageReaderTest extends Camera2AndroidTestCase {
private static final String TAG = "ImageReaderTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
// number of frame (for streaming requests) to be verified.
private static final int NUM_FRAME_VERIFIED = 2;
// Max number of images can be accessed simultaneously from ImageReader.
private static final int MAX_NUM_IMAGES = 5;
+ // Max difference allowed between YUV and JPEG patches. This tolerance is intentionally very
+ // generous to avoid false positives due to punch/saturation operations vendors apply to the
+ // JPEG outputs.
+ private static final double IMAGE_DIFFERENCE_TOLERANCE = 30;
private SimpleImageListener mListener;
@@ -194,6 +212,335 @@
}
}
+ /**
+ * Check that the center patches for YUV and JPEG outputs for the same frame match for each YUV
+ * resolution and format supported.
+ */
+ public void testAllOutputYUVResolutions() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
+ openDevice(id);
+
+ // Skip warmup on FULL mode devices.
+ int warmupCaptureNumber = (mStaticInfo.isHardwareLevelLegacy()) ?
+ MAX_NUM_IMAGES - 1 : 0;
+
+ // NV21 isn't supported by ImageReader.
+ final int[] YUVFormats = new int[] {ImageFormat.YUV_420_888, ImageFormat.YV12};
+
+ CameraCharacteristics.Key<StreamConfigurationMap> key =
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+ StreamConfigurationMap config = mStaticInfo.getValueFromKeyNonNull(key);
+ int[] supportedFormats = config.getOutputFormats();
+ List<Integer> supportedYUVFormats = new ArrayList<>();
+ for (int format : YUVFormats) {
+ if (CameraTestUtils.contains(supportedFormats, format)) {
+ supportedYUVFormats.add(format);
+ }
+ }
+
+ Size[] jpegSizes = mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+ StaticMetadata.StreamDirection.Output);
+ assertFalse("JPEG output not supported for camera " + id +
+ ", at least one JPEG output is required.", jpegSizes.length == 0);
+
+ Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
+
+ for (int format : supportedYUVFormats) {
+ Size[] targetCaptureSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(format,
+ StaticMetadata.StreamDirection.Output);
+
+ for (Size captureSz : targetCaptureSizes) {
+ if (VERBOSE) {
+ Log.v(TAG, "Testing yuv size " + captureSz + " and jpeg size "
+ + maxJpegSize + " for camera " + mCamera.getId());
+ }
+
+ ImageReader jpegReader = null;
+ ImageReader yuvReader = null;
+ try {
+ // Create YUV image reader
+ SimpleImageReaderListener yuvListener = new SimpleImageReaderListener();
+ yuvReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
+ yuvListener);
+ Surface yuvSurface = yuvReader.getSurface();
+
+ // Create JPEG image reader
+ SimpleImageReaderListener jpegListener =
+ new SimpleImageReaderListener();
+ jpegReader = createImageReader(maxJpegSize,
+ ImageFormat.JPEG, MAX_NUM_IMAGES, jpegListener);
+ Surface jpegSurface = jpegReader.getSurface();
+
+ // Setup session
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ outputSurfaces.add(yuvSurface);
+ outputSurfaces.add(jpegSurface);
+ createSession(outputSurfaces);
+
+ // Warm up camera preview (mainly to give legacy devices time to do 3A).
+ CaptureRequest.Builder warmupRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ warmupRequest.addTarget(yuvSurface);
+ assertNotNull("Fail to get CaptureRequest.Builder", warmupRequest);
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+ for (int i = 0; i < warmupCaptureNumber; i++) {
+ startCapture(warmupRequest.build(), /*repeating*/false,
+ resultListener, mHandler);
+ }
+ for (int i = 0; i < warmupCaptureNumber; i++) {
+ resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+ Image image = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ image.close();
+ }
+
+ // Capture image.
+ CaptureRequest.Builder mainRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ for (Surface s : outputSurfaces) {
+ mainRequest.addTarget(s);
+ }
+
+ startCapture(mainRequest.build(), /*repeating*/false, resultListener,
+ mHandler);
+
+ // Verify capture result and images
+ resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+
+ Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ Image jpegImage = jpegListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+
+ //Validate captured images.
+ CameraTestUtils.validateImage(yuvImage, captureSz.getWidth(),
+ captureSz.getHeight(), format, /*filePath*/null);
+ CameraTestUtils.validateImage(jpegImage, maxJpegSize.getWidth(),
+ maxJpegSize.getHeight(), ImageFormat.JPEG, /*filePath*/null);
+
+ // Compare the image centers.
+ RectF jpegDimens = new RectF(0, 0, jpegImage.getWidth(),
+ jpegImage.getHeight());
+ RectF yuvDimens = new RectF(0, 0, yuvImage.getWidth(),
+ yuvImage.getHeight());
+
+ // Find scale difference between YUV and JPEG output
+ Matrix m = new Matrix();
+ m.setRectToRect(yuvDimens, jpegDimens, Matrix.ScaleToFit.START);
+ RectF scaledYuv = new RectF();
+ m.mapRect(scaledYuv, yuvDimens);
+ float scale = scaledYuv.width() / yuvDimens.width();
+
+ final int PATCH_DIMEN = 40; // pixels in YUV
+
+ // Find matching square patch of pixels in YUV and JPEG output
+ RectF tempPatch = new RectF(0, 0, PATCH_DIMEN, PATCH_DIMEN);
+ tempPatch.offset(yuvDimens.centerX() - tempPatch.centerX(),
+ yuvDimens.centerY() - tempPatch.centerY());
+ Rect yuvPatch = new Rect();
+ tempPatch.roundOut(yuvPatch);
+
+ tempPatch.set(0, 0, PATCH_DIMEN * scale, PATCH_DIMEN * scale);
+ tempPatch.offset(jpegDimens.centerX() - tempPatch.centerX(),
+ jpegDimens.centerY() - tempPatch.centerY());
+ Rect jpegPatch = new Rect();
+ tempPatch.roundOut(jpegPatch);
+
+ // Decode center patches
+ int[] yuvColors = convertPixelYuvToRgba(yuvPatch.width(),
+ yuvPatch.height(), yuvPatch.left, yuvPatch.top, yuvImage);
+ Bitmap yuvBmap = Bitmap.createBitmap(yuvColors, yuvPatch.width(),
+ yuvPatch.height(), Bitmap.Config.ARGB_8888);
+
+ byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpegImage);
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+ compressedJpegData, /*offset*/0, compressedJpegData.length,
+ /*isShareable*/true);
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap fullSizeJpegBmap = decoder.decodeRegion(jpegPatch, opt);
+ Bitmap jpegBmap = Bitmap.createScaledBitmap(fullSizeJpegBmap,
+ yuvPatch.width(), yuvPatch.height(), /*filter*/true);
+
+ // Compare two patches using average of per-pixel differences
+ double difference = findDifferenceMetric(yuvBmap, jpegBmap);
+
+ Log.i(TAG, "Difference for resolution " + captureSz + " is: " +
+ difference);
+ if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+ // Dump files if running in verbose mode
+ if (DEBUG) {
+ String jpegFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+ "_jpeg.jpg";
+ dumpFile(jpegFileName, jpegBmap);
+ String fullSizeJpegFileName = DEBUG_FILE_NAME_BASE + "/" +
+ captureSz + "_full_jpeg.jpg";
+ dumpFile(fullSizeJpegFileName, compressedJpegData);
+ String yuvFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+ "_yuv.jpg";
+ dumpFile(yuvFileName, yuvBmap);
+ String fullSizeYuvFileName = DEBUG_FILE_NAME_BASE + "/" +
+ captureSz + "_full_yuv.jpg";
+ int[] fullYUVColors = convertPixelYuvToRgba(yuvImage.getWidth(),
+ yuvImage.getHeight(), 0, 0, yuvImage);
+ Bitmap fullYUVBmap = Bitmap.createBitmap(fullYUVColors,
+ yuvImage.getWidth(), yuvImage.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ dumpFile(fullSizeYuvFileName, fullYUVBmap);
+ }
+ fail("Camera " + mCamera.getId() + ": YUV and JPEG image at " +
+ "capture size " + captureSz + " for the same frame are " +
+ "not similar, center patches have difference metric of " +
+ difference);
+ }
+
+ // Stop capture, delete the streams.
+ stopCapture(/*fast*/false);
+ } finally {
+ closeImageReader(jpegReader);
+ jpegReader = null;
+ closeImageReader(yuvReader);
+ yuvReader = null;
+ }
+ }
+ }
+
+ } finally {
+ closeDevice(id);
+ }
+ }
+ }
+
+ /**
+ * Find the difference between two bitmaps using average of per-pixel differences.
+ *
+ * @param a first {@link Bitmap}.
+ * @param b second {@link Bitmap}.
+ * @return the difference.
+ */
+ private static double findDifferenceMetric(Bitmap a, Bitmap b) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+ throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
+ a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
+ b.getHeight());
+ }
+ // TODO: Optimize this in renderscript to avoid copy.
+ int[] aPixels = new int[a.getHeight() * a.getWidth()];
+ int[] bPixels = new int[aPixels.length];
+ a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
+ a.getHeight());
+ b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
+ b.getHeight());
+ double diff = 0;
+ for (int i = 0; i < aPixels.length; i++) {
+ int aPix = aPixels[i];
+ int bPix = bPixels[i];
+
+ diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
+ diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
+ diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
+ }
+ diff /= (aPixels.length * 3);
+ return diff;
+ }
+
+ /**
+ * Convert a rectangular patch in a YUV image to an ARGB color array.
+ *
+ * @param w width of the patch.
+ * @param h height of the patch.
+ * @param wOffset offset of the left side of the patch.
+ * @param hOffset offset of the top of the patch.
+ * @param yuvImage a YUV image to select a patch from.
+ * @return the image patch converted to RGB as an ARGB color array.
+ */
+ private static int[] convertPixelYuvToRgba(int w, int h, int wOffset, int hOffset,
+ Image yuvImage) {
+ final int CHANNELS = 3; // yuv
+ final float COLOR_RANGE = 255f;
+
+ assertTrue("Invalid argument to convertPixelYuvToRgba",
+ w > 0 && h > 0 && wOffset >= 0 && hOffset >= 0);
+ assertNotNull(yuvImage);
+
+ int imageFormat = yuvImage.getFormat();
+ assertTrue("YUV image must have YUV-type format",
+ imageFormat == ImageFormat.YUV_420_888 || imageFormat == ImageFormat.YV12 ||
+ imageFormat == ImageFormat.NV21);
+
+ int height = yuvImage.getHeight();
+ int width = yuvImage.getWidth();
+
+ Rect imageBounds = new Rect(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
+ Rect crop = new Rect(/*left*/wOffset, /*top*/hOffset, /*right*/wOffset + w,
+ /*bottom*/hOffset + h);
+ assertTrue("Output rectangle" + crop + " must lie within image bounds " + imageBounds,
+ imageBounds.contains(crop));
+ Image.Plane[] planes = yuvImage.getPlanes();
+
+ Image.Plane yPlane = planes[0];
+ Image.Plane cbPlane = planes[1];
+ Image.Plane crPlane = planes[2];
+
+ ByteBuffer yBuf = yPlane.getBuffer();
+ int yPixStride = yPlane.getPixelStride();
+ int yRowStride = yPlane.getRowStride();
+ ByteBuffer cbBuf = cbPlane.getBuffer();
+ int cbPixStride = cbPlane.getPixelStride();
+ int cbRowStride = cbPlane.getRowStride();
+ ByteBuffer crBuf = crPlane.getBuffer();
+ int crPixStride = crPlane.getPixelStride();
+ int crRowStride = crPlane.getRowStride();
+
+ int[] output = new int[w * h];
+
+ // TODO: Optimize this with renderscript intrinsics
+ byte[] yRow = new byte[yPixStride * w];
+ byte[] cbRow = new byte[cbPixStride * w / 2];
+ byte[] crRow = new byte[crPixStride * w / 2];
+ yBuf.mark();
+ cbBuf.mark();
+ crBuf.mark();
+ int initialYPos = yBuf.position();
+ int initialCbPos = cbBuf.position();
+ int initialCrPos = crBuf.position();
+ int outputPos = 0;
+ for (int i = hOffset; i < hOffset + h; i++) {
+ yBuf.position(initialYPos + i * yRowStride + wOffset * yPixStride);
+ yBuf.get(yRow);
+ if ((i & 1) == (hOffset & 1)) {
+ cbBuf.position(initialCbPos + (i / 2) * cbRowStride + wOffset * cbPixStride / 2);
+ cbBuf.get(cbRow);
+ crBuf.position(initialCrPos + (i / 2) * crRowStride + wOffset * crPixStride / 2);
+ crBuf.get(crRow);
+ }
+ for (int j = 0, yPix = 0, crPix = 0, cbPix = 0; j < w; j++, yPix += yPixStride) {
+ float y = yRow[yPix] & 0xFF;
+ float cb = cbRow[cbPix] & 0xFF;
+ float cr = crRow[crPix] & 0xFF;
+
+ // convert YUV -> RGB (from JFIF's "Conversion to and from RGB" section)
+ int r = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.402f * (cr - 128)));
+ int g = (int) Math.max(0.0f,
+ Math.min(COLOR_RANGE, y - 0.34414f * (cb - 128) - 0.71414f * (cr - 128)));
+ int b = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.772f * (cb - 128)));
+
+ // Convert to ARGB pixel color (use opaque alpha)
+ output[outputPos++] = Color.rgb(r, g, b);
+
+ if ((j & 1) == 1) {
+ crPix += crPixStride;
+ cbPix += cbPixStride;
+ }
+ }
+ }
+ yBuf.rewind();
+ cbBuf.rewind();
+ crBuf.rewind();
+
+ return output;
+ }
/**
* Test capture a given format stream with yuv stream simultaneously.
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 192bf56..30cc58e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -21,7 +21,6 @@
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
-import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
@@ -29,21 +28,20 @@
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.params.StreamConfigurationMap;
-import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.media.CamcorderProfile;
+import android.media.Image;
import android.media.ImageReader;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
-
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
@@ -52,11 +50,14 @@
public class RobustnessTest extends Camera2AndroidTestCase {
private static final String TAG = "RobustnessTest";
- private static final int FAILED_CONFIGURE_TIMEOUT = 5000; //ms
+ private static final int CONFIGURE_TIMEOUT = 5000; //ms
+ private static final int CAPTURE_TIMEOUT = 1000; //ms
/**
- * Test that a {@link CameraCaptureSession} configured with a {@link Surface} with invalid
- * dimensions fails gracefully.
+ * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing
+ * a dimension other than one of the supported output dimensions. The buffers produced into
+ * this surface are expected have the dimensions of the closest possible buffer size in the
+ * available stream configurations for a surface with this format.
*/
public void testBadSurfaceDimensions() throws Exception {
for (String id : mCameraIds) {
@@ -64,9 +65,21 @@
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
- // Setup Surface with unconfigured dimensions.
- SurfaceTexture surfaceTexture = new SurfaceTexture(0);
- Surface surface = new Surface(surfaceTexture);
+ // Find some size not supported by the camera
+ Size weirdSize = new Size(643, 577);
+ int count = 0;
+ while(mOrderedPreviewSizes.contains(weirdSize)) {
+ // Really, they can't all be supported...
+ weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1);
+ count++;
+ assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100);
+ }
+
+ // Setup imageReader with invalid dimension
+ ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(),
+ weirdSize.getHeight(), ImageFormat.YUV_420_888, 3);
+
+ Surface surface = imageReader.getSurface();
List<Surface> surfaces = new ArrayList<>();
surfaces.add(surface);
@@ -80,13 +93,34 @@
// Check that correct session callback is hit.
CameraCaptureSession.StateCallback sessionListener =
mock(CameraCaptureSession.StateCallback.class);
- mCamera.createCaptureSession(surfaces, sessionListener, mHandler);
- verify(sessionListener, timeout(FAILED_CONFIGURE_TIMEOUT).atLeastOnce()).
- onConfigureFailed(any(CameraCaptureSession.class));
- verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
+ CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera,
+ surfaces, sessionListener, mHandler);
+
+ verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+ onConfigured(any(CameraCaptureSession.class));
+ verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+ onReady(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
- verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
+
+ CameraCaptureSession.CaptureCallback captureListener =
+ mock(CameraCaptureSession.CaptureCallback.class);
+ session.capture(request.build(), captureListener, mHandler);
+
+ verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()).
+ onCaptureCompleted(any(CameraCaptureSession.class),
+ any(CaptureRequest.class), any(TotalCaptureResult.class));
+ verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
+ any(CaptureRequest.class), any(CaptureFailure.class));
+
+ Image image = imageReader.acquireLatestImage();
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ Size actualSize = new Size(imageWidth, imageHeight);
+
+ assertTrue("Camera does not contain outputted image resolution " + actualSize,
+ mOrderedPreviewSizes.contains(actualSize));
} finally {
closeDevice(id);
}
@@ -329,6 +363,16 @@
}
}
+ private final class ImageCloser implements ImageReader.OnImageAvailableListener {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ Image image = reader.acquireLatestImage();
+ if (image != null) {
+ image.close();
+ }
+ }
+ }
+
private void testOutputCombination(String cameraId, int[] config, MaxOutputSizes maxSizes)
throws Exception {
@@ -338,6 +382,7 @@
final int TIMEOUT_FOR_RESULT_MS = 1000;
final int MIN_RESULT_COUNT = 3;
+ ImageCloser imageCloser = new ImageCloser();
// Set up outputs
List<Object> outputTargets = new ArrayList<>();
List<Surface> outputSurfaces = new ArrayList<>();
@@ -363,6 +408,7 @@
Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), JPEG, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageCloser, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
jpegTargets.add(target);
@@ -372,6 +418,7 @@
Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), YUV, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageCloser, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
yuvTargets.add(target);
@@ -381,6 +428,7 @@
Size targetSize = maxSizes.maxRawSize;
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), RAW, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageCloser, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
rawTargets.add(target);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
index 7cf4089..f0e7e57 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -828,8 +828,25 @@
*/
public <T> void expectKeyValueIsIn(CameraCharacteristics characteristics,
CameraCharacteristics.Key<T> key, T... expected) {
- T value;
- if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
+ T value = expectKeyValueNotNull(characteristics, key);
+ if (value == null) {
+ return;
+ }
+ String reason = "Key " + key.getName() + " value " + value
+ + " isn't one of the expected values " + Arrays.deepToString(expected);
+ expectContains(reason, expected, value);
+ }
+
+ /**
+ * Check if the key is non-null, and the key value is one of the expected values.
+ *
+ * @param request The The {@link CaptureRequest#Builder} to get the key from.
+ * @param key The {@link CaptureRequest} key to be checked.
+ * @param expected The expected values of the CaptureRequest key.
+ */
+ public <T> void expectKeyValueIsIn(Builder request, CaptureRequest.Key<T> key, T... expected) {
+ T value = expectKeyValueNotNull(request, key);
+ if (value == null) {
return;
}
String reason = "Key " + key.getName() + " value " + value
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index 9837fb7..549309c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -436,17 +436,23 @@
int[] modes = getValueFromKeyNonNull(key);
boolean foundAuto = false;
+ boolean found50Hz = false;
+ boolean found60Hz = false;
for (int mode : modes) {
checkTrueForKey(key, "mode value " + mode + " is out if range",
mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
foundAuto = true;
- return modes;
+ } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ) {
+ found50Hz = true;
+ } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ) {
+ found60Hz = true;
}
}
- // Must contain AUTO mode.
- checkTrueForKey(key, "AUTO mode is missing", foundAuto);
+ // Must contain AUTO mode or one of 50/60Hz mode.
+ checkTrueForKey(key, "Either AUTO mode or both 50HZ/60HZ mode should present",
+ foundAuto || (found50Hz && found60Hz));
return modes;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 6454678..8dba5d6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -26,7 +26,7 @@
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -35,13 +35,13 @@
*/
public abstract class SensorTestCase extends AndroidTestCase {
// TODO: consolidate all log tags
- protected final String LOG_TAG = "TestRunner";
+ protected static final String LOG_TAG = "TestRunner";
/**
* By default tests need to run in a {@link TestSensorEnvironment} that assumes each sensor is
* running with a load of several listeners, requesting data at different rates.
*
- * In a better world the component acting as builder of {@link ISensorOperation} would compute
+ * In a better world the component acting as builder of {@link SensorOperation} would compute
* this value based on the tests composed.
*
* Ideally, each {@link Sensor} object would expose this information to clients.
@@ -51,9 +51,9 @@
protected SensorTestCase() {}
@Override
- public void runTest() throws Throwable {
+ public void runBare() throws Throwable {
try {
- super.runTest();
+ super.runBare();
} catch (SensorTestStateNotSupportedException e) {
// the sensor state is not supported in the device, log a warning and skip the test
Log.w(LOG_TAG, e.getMessage());
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 6f98e86..a7ad94a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -17,7 +17,7 @@
package android.hardware.cts.helpers;
import android.hardware.Sensor;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import android.util.Log;
import java.io.BufferedWriter;
@@ -34,7 +34,7 @@
import java.util.Set;
/**
- * Class used to store stats related to {@link ISensorOperation}s. Sensor stats may be linked
+ * Class used to store stats related to {@link SensorOperation}s. Sensor stats may be linked
* together so that they form a tree.
*/
public class SensorStats {
@@ -55,8 +55,8 @@
public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
public static final String MAGNITUDE_KEY = "magnitude";
- private final Map<String, Object> mValues = new HashMap<String, Object>();
- private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
+ private final Map<String, Object> mValues = new HashMap<>();
+ private final Map<String, SensorStats> mSensorStats = new HashMap<>();
/**
* Add a value.
@@ -73,7 +73,7 @@
/**
* Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
- * {@link ISensorOperation} tree.
+ * {@link SensorOperation} tree.
*
* @param key the key
* @param stats the sub {@link SensorStats} object.
@@ -104,13 +104,13 @@
/**
* Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
* {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
- * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
- * will contain the entry {@code ("key1__key2", "value")}.
+ * {@code "key1"} containing the key value pair {@code \("key2", "value"\)}, the flattened map
+ * will contain the entry {@code \("key1__key2", "value"\)}.
*
* @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
*/
public synchronized Map<String, Object> flatten() {
- final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+ final Map<String, Object> flattenedMap = new HashMap<>(mValues);
for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
@@ -166,7 +166,7 @@
}
private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
- List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+ List<String> keys = new ArrayList<>(flattenedStats.keySet());
Collections.sort(keys);
return keys;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 5be30a4..d59ea7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -19,12 +19,12 @@
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import java.util.concurrent.TimeUnit;
/**
- * A class that encapsulates base environment information for the {@link ISensorOperation}.
+ * A class that encapsulates base environment information for the {@link SensorOperation}.
* The environment is self contained and carries its state around all the sensor test framework.
*/
public class TestSensorEnvironment {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
deleted file mode 100644
index 5b969f2..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * A {@link ISensorOperation} which contains a common implementation for gathering
- * {@link SensorStats}.
- */
-public abstract class AbstractSensorOperation implements ISensorOperation {
-
- private final SensorStats mStats = new SensorStats();
-
- /**
- * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
- */
- protected void addSensorStats(String key, SensorStats stats) {
- mStats.addSensorStats(key, stats);
- }
-
- /**
- * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
- * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
- * operations. The key added is in the form {@code key + "_" + index} where index may be zero
- * padded.
- */
- protected void addSensorStats(String key, int index, SensorStats stats) {
- addSensorStats(String.format("%s_%03d", key, index), stats);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public SensorStats getStats() {
- return mStats;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public abstract ISensorOperation clone();
-
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 88e4954..8848337 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -29,7 +29,7 @@
import java.util.concurrent.TimeUnit;
/**
- * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * An {@link SensorOperation} which performs another {@link SensorOperation} and then wakes up
* after a specified period of time and waits for the child operation to complete.
* <p>
* This operation can be used to allow the device to go to sleep and wake it up after a specified
@@ -40,11 +40,11 @@
* but wake the device one time at the specified period.
* </p>
*/
-public class AlarmOperation extends AbstractSensorOperation {
+public class AlarmOperation extends SensorOperation {
private static final String ACTION = "AlarmOperationAction";
private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final Context mContext;
private final long mSleepDuration;
private final TimeUnit mTimeUnit;
@@ -55,13 +55,17 @@
/**
* Constructor for {@link DelaySensorOperation}
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the alarm manager
* @param sleepDuration the amount of time to sleep
* @param timeUnit the unit of the duration
*/
- public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+ public AlarmOperation(
+ SensorOperation operation,
+ Context context,
+ long sleepDuration,
TimeUnit timeUnit) {
+ super(operation.getStats());
mOperation = operation;
mContext = context;
mSleepDuration = sleepDuration;
@@ -120,7 +124,7 @@
*/
private synchronized void acquireWakeLock() {
// Don't acquire wake lock if the operation has already completed.
- if (mCompleted == true || mWakeLock != null) {
+ if (mCompleted || mWakeLock != null) {
return;
}
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index b4d1f23..3ee08f6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -22,22 +22,22 @@
import java.util.concurrent.TimeUnit;
/**
- * An {@link ISensorOperation} which delays for a specified period of time before performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which delays for a specified period of time before performing another
+ * {@link SensorOperation}.
*/
-public class DelaySensorOperation implements ISensorOperation {
- private final ISensorOperation mOperation;
+public class DelaySensorOperation extends SensorOperation {
+ private final SensorOperation mOperation;
private final long mDelay;
private final TimeUnit mTimeUnit;
/**
* Constructor for {@link DelaySensorOperation}
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param delay the amount of time to delay
* @param timeUnit the unit of the delay
*/
- public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
+ public DelaySensorOperation(SensorOperation operation, long delay, TimeUnit timeUnit) {
if (operation == null || timeUnit == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index bb64dfa..8cfd351 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -16,17 +16,17 @@
package android.hardware.cts.helpers.sensoroperations;
-import android.hardware.cts.helpers.SensorStats;
-
import junit.framework.Assert;
+import android.hardware.cts.helpers.SensorStats;
+
import java.util.concurrent.TimeUnit;
/**
- * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
- * debugging the framework.
+ * A fake {@link SensorOperation} that will run for a specified time and then pass or fail. Useful
+ * when debugging the framework.
*/
-public class FakeSensorOperation extends AbstractSensorOperation {
+public class FakeSensorOperation extends SensorOperation {
private static final int NANOS_PER_MILLI = 1000000;
private final boolean mFail;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
deleted file mode 100644
index 62a4e9e..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * Interface used by all sensor operations. This allows for complex operations such as chaining
- * operations together or running operations in parallel.
- * <p>
- * Certain restrictions exist for {@link ISensorOperation}s:
- * <p><ul>
- * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
- * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
- * synchronization for operations.</li>
- * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
- * the returned value is undefined.</li>
- * <li>{@link #clone()} may be called any time and should return an operation with the same
- * parameters as the original.</li>
- * </ul>
- */
-public interface ISensorOperation {
-
- /**
- * Executes the sensor operation. This may throw {@link RuntimeException}s such as
- * {@link AssertionError}s.
- *
- * NOTE: the operation is expected to handle interruption by:
- * - cleaning up on {@link InterruptedException}
- * - propagating the exception down the stack
- */
- public void execute() throws InterruptedException;
-
- /**
- * Get the stats for the operation.
- *
- * @return The {@link SensorStats} for the operation.
- */
- public SensorStats getStats();
-
- /**
- * Clones the {@link ISensorOperation}. The implementation should also clone all child
- * operations, so that a cloned operation will run with the exact same parameters as the
- * original. The stats should not be cloned.
- *
- * @return The cloned {@link ISensorOperation}
- */
- public ISensorOperation clone();
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
index 5a4466c..e55c6cb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -22,7 +22,6 @@
import android.os.SystemClock;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -33,14 +32,14 @@
import java.util.concurrent.TimeoutException;
/**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in parallel.
* The children are run in parallel but are given an index label in the order they are added. This
- * class can be combined to compose complex {@link ISensorOperation}s.
+ * class can be combined to compose complex {@link SensorOperation}s.
*/
-public class ParallelSensorOperation extends AbstractSensorOperation {
+public class ParallelSensorOperation extends SensorOperation {
public static final String STATS_TAG = "parallel";
- private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+ private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
private final Long mTimeout;
private final TimeUnit mTimeUnit;
@@ -65,10 +64,10 @@
}
/**
- * Add a set of {@link ISensorOperation}s.
+ * Add a set of {@link SensorOperation}s.
*/
- public void add(ISensorOperation ... operations) {
- for (ISensorOperation operation : operations) {
+ public void add(SensorOperation ... operations) {
+ for (SensorOperation operation : operations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
@@ -77,7 +76,7 @@
}
/**
- * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+ * Executes the {@link SensorOperation}s in parallel. If an exception occurs one or more
* operations, the first exception will be thrown once all operations are completed.
*/
@Override
@@ -92,11 +91,11 @@
executor.allowCoreThreadTimeOut(true);
executor.prestartAllCoreThreads();
- ArrayList<Future<ISensorOperation>> futures = new ArrayList<Future<ISensorOperation>>();
- for (final ISensorOperation operation : mOperations) {
- Future<ISensorOperation> future = executor.submit(new Callable<ISensorOperation>() {
+ ArrayList<Future<SensorOperation>> futures = new ArrayList<>();
+ for (final SensorOperation operation : mOperations) {
+ Future<SensorOperation> future = executor.submit(new Callable<SensorOperation>() {
@Override
- public ISensorOperation call() throws Exception {
+ public SensorOperation call() throws Exception {
operation.execute();
return operation;
}
@@ -111,12 +110,12 @@
}
boolean hasAssertionErrors = false;
- ArrayList<Integer> timeoutIndices = new ArrayList<Integer>();
- ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
+ ArrayList<Integer> timeoutIndices = new ArrayList<>();
+ ArrayList<Throwable> exceptions = new ArrayList<>();
for (int i = 0; i < operationsCount; ++i) {
- Future<ISensorOperation> future = futures.get(i);
+ Future<SensorOperation> future = futures.get(i);
try {
- ISensorOperation operation = getFutureResult(future, executionTimeNs);
+ SensorOperation operation = getFutureResult(future, executionTimeNs);
addSensorStats(STATS_TAG, i, operation.getStats());
} catch (ExecutionException e) {
// extract the exception thrown by the worker thread
@@ -151,7 +150,7 @@
@Override
public ParallelSensorOperation clone() {
ParallelSensorOperation operation = new ParallelSensorOperation();
- for (ISensorOperation subOperation : mOperations) {
+ for (SensorOperation subOperation : mOperations) {
operation.add(subOperation.clone());
}
return operation;
@@ -160,7 +159,7 @@
/**
* Helper method that waits for a {@link Future} to complete, and returns its result.
*/
- private ISensorOperation getFutureResult(Future<ISensorOperation> future, Long timeoutNs)
+ private SensorOperation getFutureResult(Future<SensorOperation> future, Long timeoutNs)
throws ExecutionException, TimeoutException, InterruptedException {
if (timeoutNs == null) {
return future.get();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
index 3d682fe..2e3af36 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -19,22 +19,22 @@
import android.hardware.cts.helpers.SensorStats;
/**
- * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
- * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ * A {@link SensorOperation} that executes a single {@link SensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link SensorOperation}s.
*/
-public class RepeatingSensorOperation extends AbstractSensorOperation {
+public class RepeatingSensorOperation extends SensorOperation {
public static final String STATS_TAG = "repeating";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final int mIterations;
/**
* Constructor for {@link RepeatingSensorOperation}.
*
- * @param operation the {@link ISensorOperation} to run.
+ * @param operation the {@link SensorOperation} to run.
* @param iterations the number of iterations to run the operation for.
*/
- public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+ public RepeatingSensorOperation(SensorOperation operation, int iterations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
@@ -44,13 +44,13 @@
}
/**
- * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
- * in one iterations, it is thrown and all subsequent iterations will not run.
+ * Executes the {@link SensorOperation}s the given number of times. If an exception occurs in
+ * one iterations, it is thrown and all subsequent iterations will not run.
*/
@Override
public void execute() throws InterruptedException {
for(int i = 0; i < mIterations; ++i) {
- ISensorOperation operation = mOperation.clone();
+ SensorOperation operation = mOperation.clone();
try {
operation.execute();
} catch (AssertionError e) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
new file mode 100644
index 0000000..ea16716
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+
+/**
+ * Base class used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link SensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
+ * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
+ * synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
+ * the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public abstract class SensorOperation {
+ private final SensorStats mStats;
+
+ protected SensorOperation() {
+ this(new SensorStats());
+ }
+
+ protected SensorOperation(SensorStats stats) {
+ mStats = stats;
+ }
+
+ /**
+ * @return The {@link SensorStats} for the operation.
+ */
+ public SensorStats getStats() {
+ return mStats;
+ }
+
+ /**
+ * Executes the sensor operation.
+ * This may throw {@link RuntimeException}s such as {@link AssertionError}s.
+ *
+ * NOTE: the operation is expected to handle interruption by:
+ * - cleaning up on {@link InterruptedException}
+ * - propagating the exception down the stack
+ */
+ public abstract void execute() throws InterruptedException;
+
+ /**
+ * @return The cloned {@link SensorOperation}.
+ *
+ * NOTE: The implementation should also clone all child operations, so that a cloned operation
+ * will run with the exact same parameters as the original. The stats should not be cloned.
+ */
+ public abstract SensorOperation clone();
+
+ /**
+ * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+ */
+ protected void addSensorStats(String key, SensorStats stats) {
+ getStats().addSensorStats(key, stats);
+ }
+
+ /**
+ * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+ * to be added. This is useful for {@link SensorOperation}s that have many iterations or child
+ * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+ * padded.
+ */
+ protected void addSensorStats(String key, int index, SensorStats stats) {
+ addSensorStats(String.format("%s_%03d", key, index), stats);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
index bc48725..033f3c5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -24,7 +24,7 @@
import java.util.concurrent.TimeUnit;
/**
- * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * Tests for the primitive {@link SensorOperation}s including {@link DelaySensorOperation},
* {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
* {@link SequentialSensorOperation}.
*/
@@ -38,7 +38,7 @@
public void testFakeSensorOperation() throws InterruptedException {
final int opDurationMs = 100;
- ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
assertFalse(op.getStats().flatten().containsKey("executed"));
long start = System.currentTimeMillis();
@@ -65,12 +65,12 @@
final int subOpDurationMs = 100;
FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
- ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
long startMs = System.currentTimeMillis();
op.execute();
- long dirationMs = System.currentTimeMillis() - startMs;
- long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - dirationMs);
+ long durationMs = System.currentTimeMillis() - startMs;
+ long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - durationMs);
assertTrue(durationDeltaMs < TEST_DURATION_THRESHOLD_MS);
}
@@ -83,7 +83,7 @@
ParallelSensorOperation op = new ParallelSensorOperation();
for (int i = 0; i < subOpCount; i++) {
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -124,7 +124,7 @@
ParallelSensorOperation op = new ParallelSensorOperation();
for (int i = 0; i < subOpCount; i++) {
// Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
- ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+ SensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -164,7 +164,7 @@
ParallelSensorOperation op = new ParallelSensorOperation(1, TimeUnit.SECONDS);
for (int i = 0; i < subOpCount; i++) {
// Trigger timeouts in the 5th, 55th operations (5 seconds vs 1 seconds)
- ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+ SensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
op.add(subOp);
}
@@ -196,8 +196,8 @@
final int iterations = 10;
final int subOpDurationMs = 100;
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
- ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
Set<String> statsKeys = op.getStats().flatten().keySet();
assertEquals(0, statsKeys.size());
@@ -223,7 +223,7 @@
final int iterations = 100;
final int failCount = 75;
- ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+ SensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
private int mExecutedCount = 0;
private SensorStats mFakeStats = new SensorStats();
@@ -249,7 +249,7 @@
return mFakeStats;
}
};
- ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+ SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
Set<String> statsKeys = op.getStats().flatten().keySet();
assertEquals(0, statsKeys.size());
@@ -283,7 +283,7 @@
SequentialSensorOperation op = new SequentialSensorOperation();
for (int i = 0; i < subOpCount; i++) {
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -315,7 +315,7 @@
SequentialSensorOperation op = new SequentialSensorOperation();
for (int i = 0; i < subOpCount; i++) {
// Trigger a failure in the 75th operation only
- ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+ SensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
index 2ed0ca6..85d189a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -18,24 +18,23 @@
import android.hardware.cts.helpers.SensorStats;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.ArrayList;
/**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in a
* sequence. The children are executed in the order they are added. This class can be combined to
- * compose complex {@link ISensorOperation}s.
+ * compose complex {@link SensorOperation}s.
*/
-public class SequentialSensorOperation extends AbstractSensorOperation {
+public class SequentialSensorOperation extends SensorOperation {
public static final String STATS_TAG = "sequential";
- private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+ private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
/**
- * Add a set of {@link ISensorOperation}s.
+ * Add a set of {@link SensorOperation}s.
*/
- public void add(ISensorOperation ... operations) {
- for (ISensorOperation operation : operations) {
+ public void add(SensorOperation ... operations) {
+ for (SensorOperation operation : operations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
@@ -44,13 +43,13 @@
}
/**
- * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+ * Executes the {@link SensorOperation}s in the order they were added. If an exception occurs
* in one operation, it is thrown and all subsequent operations will not run.
*/
@Override
public void execute() throws InterruptedException {
for (int i = 0; i < mOperations.size(); i++) {
- ISensorOperation operation = mOperations.get(i);
+ SensorOperation operation = mOperations.get(i);
try {
operation.execute();
} catch (AssertionError e) {
@@ -69,7 +68,7 @@
@Override
public SequentialSensorOperation clone() {
SequentialSensorOperation operation = new SequentialSensorOperation();
- for (ISensorOperation subOperation : mOperations) {
+ for (SensorOperation subOperation : mOperations) {
operation.add(subOperation.clone());
}
return operation;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 6e53dbb..ded1522 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -35,21 +35,19 @@
import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
import android.os.Handler;
-import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.TimeUnit;
/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
* <p>
* Provides methods to set test expectations as well as providing a set of default expectations
* depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
* events and then run all the tests.
* </p>
*/
-public class TestSensorOperation extends AbstractSensorOperation {
- private final Collection<ISensorVerification> mVerifications =
- new HashSet<ISensorVerification>();
+public class TestSensorOperation extends SensorOperation {
+ private final HashSet<ISensorVerification> mVerifications = new HashSet<>();
private final TestSensorManager mSensorManager;
private final TestSensorEnvironment mEnvironment;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
index b500ea7..5f4f5d8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -22,36 +22,36 @@
import android.os.PowerManager.WakeLock;
/**
- * An {@link ISensorOperation} which holds a wakelock while performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which holds a wake-lock while performing another
+ * {@link SensorOperation}.
*/
-public class WakeLockOperation extends AbstractSensorOperation {
+public class WakeLockOperation extends SensorOperation {
private static final String TAG = "WakeLockOperation";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final Context mContext;
- private final int mWakelockFlags;
+ private final int mWakeLockFlags;
/**
* Constructor for {@link WakeLockOperation}.
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the power manager
- * @param wakelockFlags the flags used when acquiring the wakelock
+ * @param wakeLockFlags the flags used when acquiring the wake-lock
*/
- public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+ public WakeLockOperation(SensorOperation operation, Context context, int wakeLockFlags) {
mOperation = operation;
mContext = context;
- mWakelockFlags = wakelockFlags;
+ mWakeLockFlags = wakeLockFlags;
}
/**
* Constructor for {@link WakeLockOperation}.
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the power manager
*/
- public WakeLockOperation(ISensorOperation operation, Context context) {
+ public WakeLockOperation(SensorOperation operation, Context context) {
this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
}
@@ -61,7 +61,7 @@
@Override
public void execute() throws InterruptedException {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
+ WakeLock wakeLock = pm.newWakeLock(mWakeLockFlags, TAG);
wakeLock.acquire();
try {
@@ -83,7 +83,7 @@
* {@inheritDoc}
*/
@Override
- public ISensorOperation clone() {
- return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+ public SensorOperation clone() {
+ return new WakeLockOperation(mOperation, mContext, mWakeLockFlags);
}
}
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
similarity index 94%
rename from tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
rename to tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
index 9c91510..bf93ab4 100644
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
similarity index 94%
rename from tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
rename to tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
index 8d9ab8e..ae0e0e3 100644
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index 65b436a..0000000
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..b79e3f6
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index 051a6c0..0000000
--- a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..c147461
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index d34d03f..0000000
--- a/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..949b327
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_2840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4 b/tests/tests/media/res/raw/video_2840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4
deleted file mode 100644
index 7b663a4..0000000
--- a/tests/tests/media/res/raw/video_2840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
similarity index 61%
rename from tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
rename to tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
index b9c1134..086f144 100644
--- a/tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ b/tests/tests/media/res/raw/video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4 b/tests/tests/media/res/raw/video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4
new file mode 100644
index 0000000..9277fbd
--- /dev/null
+++ b/tests/tests/media/res/raw/video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index e5a58e6..0000000
--- a/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..d6ed441
--- /dev/null
+++ b/tests/tests/media/res/raw/video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index f64aec3..0000000
--- a/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..c101291
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
deleted file mode 100644
index 59b0c44..0000000
--- a/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..0a33e54
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
similarity index 89%
rename from tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
rename to tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
index d0df713..60860f1 100644
--- a/tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ b/tests/tests/media/res/raw/video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
similarity index 82%
rename from tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
rename to tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
index dbe2f1b..418cc91 100644
--- a/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
+++ b/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 223438f..dbb609d 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -89,7 +89,7 @@
mContext,
MediaFormat.MIMETYPE_VIDEO_VP8,
"OMX.google.vp8.decoder",
- R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+ R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
}
@@ -98,8 +98,8 @@
mContext,
MediaFormat.MIMETYPE_VIDEO_VP9,
"OMX.google.vp9.decoder",
- R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
- R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
+ R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
+ R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz);
}
CodecFactory ALL = new CodecFactory();
diff --git a/tests/tests/media/src/android/media/cts/AudioManagerTest.java b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
index 3f6d200..255baed 100644
--- a/tests/tests/media/src/android/media/cts/AudioManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioManagerTest.java
@@ -41,6 +41,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.media.AudioManager;
+import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.os.Vibrator;
import android.provider.Settings;
@@ -59,6 +60,7 @@
private boolean mUseFixedVolume;
private int[] mMasterVolumeRamp;
private TreeMap<Integer, Integer> mMasterVolumeMap = new TreeMap<Integer, Integer>();
+ private boolean mIsTelevision;
@Override
protected void setUp() throws Exception {
@@ -74,6 +76,10 @@
for (int i = 0; i < mMasterVolumeRamp.length; i+=2) {
mMasterVolumeMap.put(mMasterVolumeRamp[i], mMasterVolumeRamp[i+1]);
}
+ PackageManager packageManager = mContext.getPackageManager();
+ mIsTelevision = packageManager != null
+ && (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || packageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION));
}
public void testMicrophoneMute() throws Exception {
@@ -180,7 +186,7 @@
}
public void testVibrateNotification() throws Exception {
- if (mUseFixedVolume) {
+ if (mUseFixedVolume || !mHasVibrator) {
return;
}
// VIBRATE_SETTING_ON
@@ -241,7 +247,7 @@
}
public void testVibrateRinger() throws Exception {
- if (mUseFixedVolume) {
+ if (mUseFixedVolume || !mHasVibrator) {
return;
}
// VIBRATE_TYPE_RINGER
@@ -307,14 +313,16 @@
assertEquals(RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
mAudioManager.setRingerMode(RINGER_MODE_SILENT);
- if (mUseFixedVolume) {
+ // AudioService#setRingerMode() has:
+ // if (isTelevision) return;
+ if (mUseFixedVolume || mIsTelevision) {
assertEquals(RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
} else {
assertEquals(RINGER_MODE_SILENT, mAudioManager.getRingerMode());
}
mAudioManager.setRingerMode(RINGER_MODE_VIBRATE);
- if (mUseFixedVolume) {
+ if (mUseFixedVolume || mIsTelevision) {
assertEquals(RINGER_MODE_NORMAL, mAudioManager.getRingerMode());
} else {
assertEquals(mHasVibrator ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT,
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 70cb10f..d79baf2 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -22,11 +22,11 @@
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;
@@ -168,20 +168,25 @@
}
public void testBFrames() throws Exception {
- testBFrames(R.raw.video_h264_main_b_frames);
- testBFrames(R.raw.video_h264_main_b_frames_frag);
+ int testsRun =
+ testBFrames(R.raw.video_h264_main_b_frames) +
+ testBFrames(R.raw.video_h264_main_b_frames_frag);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no codec found");
+ }
}
- public void testBFrames(int res) throws Exception {
+ public int testBFrames(int res) throws Exception {
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
MediaExtractor ex = new MediaExtractor();
ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
MediaFormat format = ex.getTrackFormat(0);
String mime = format.getString(MediaFormat.KEY_MIME);
assertTrue("not a video track. Wrong test file?", mime.startsWith("video/"));
- if (!hasCodecForMimeType(mime, false)) {
- Log.i(TAG, "SKIPPING testBFrames(): Could not find a codec for mimeType: " + mime);
- return;
+ if (!MediaUtils.canDecode(format)) {
+ ex.release();
+ fd.close();
+ return 0; // skip
}
MediaCodec dec = MediaCodec.createDecoderByType(mime);
Surface s = getActivity().getSurfaceHolder().getSurface();
@@ -225,6 +230,8 @@
assertTrue("extractor timestamps were ordered, wrong test file?", inputoutoforder);
dec.release();
ex.release();
+ fd.close();
+ return 1;
}
private void testTrackSelection(int resid) throws Exception {
@@ -848,6 +855,10 @@
}
private void testDecode(int testVideo, int frameNum) throws Exception {
+ if (!MediaUtils.checkCodecForResource(mContext, testVideo, 0 /* track */)) {
+ return; // skip
+ }
+
// Decode to Surface.
Surface s = getActivity().getSurfaceHolder().getSurface();
int frames1 = countFrames(testVideo, RESET_MODE_NONE, -1 /* eosframe */, s);
@@ -859,486 +870,296 @@
}
public void testCodecBasicH264() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicH264(): No codec found.");
- return;
- }
testDecode(R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, 240);
}
public void testCodecBasicHEVC() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicHEVC(): No codec found.");
- return;
- }
testDecode(R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, 300);
}
public void testCodecBasicH263() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicH263(): No codec found.");
- return;
- }
testDecode(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, 122);
}
public void testCodecBasicMpeg4() throws Exception {
- if (!hasMpeg4(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicMpeg4(): No codec found.");
- return;
- }
testDecode(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, 249);
}
public void testCodecBasicVP8() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicVP8(): No codec found.");
- return;
- }
- testDecode(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 240);
+ testDecode(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240);
}
public void testCodecBasicVP9() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testCodecBasicVP9(): No codec found.");
- return;
- }
- testDecode(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, 240);
+ testDecode(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, 240);
}
public void testH264Decode320x240() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testH264Decode320x240(): No codec found.");
- return;
- }
testDecode(R.raw.video_320x240_mp4_h264_800kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testH264Decode720x480() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testH264Decode720x480(): No codec found.");
- return;
- }
testDecode(R.raw.video_720x480_mp4_h264_2048kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testH264Decode30fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30));
}
}
public void testH264Decode30fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30)) {
- Log.i(TAG, "SKIPPING testH264Decode30fps1280x720(): Unsupported profile.");
- return;
- }
-
testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testH264Decode60fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60));
}
}
public void testH264Decode60fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60)) {
- Log.i(TAG, "SKIPPING testH264Decode60fps1280x720(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_60fps_aac_stereo_128kbps_44100hz, 596);
}
public void testH264Decode30fps1920x1080Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30));
}
}
public void testH264Decode30fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30)) {
- Log.i(TAG, "SKIPPING testH264Decode30fps1920x1080(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testH264Decode60fps1920x1080Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60));
}
}
public void testH264Decode60fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60)) {
- Log.i(TAG, "SKIPPING testH264Decode60fps1920x1080(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz, 596);
}
public void testVP8Decode320x240() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testVP8Decode320x240(): No codec found.");
- return;
- }
testDecode(R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
}
public void testVP8Decode640x360() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testVP8Decode640x360(): No codec found.");
- return;
- }
- testDecode(R.raw.video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
+ testDecode(R.raw.video_640x360_webm_vp8_2048kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
}
public void testVP8Decode30fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30));
}
}
public void testVP8Decode30fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30)) {
- Log.i(TAG, "SKIPPING testVP8Decode30fps1280x720(): Unsupported profile.");
- return;
- }
- testDecode(R.raw.video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
+ testDecode(R.raw.video_1280x720_webm_vp8_8192kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
}
public void testVP8Decode60fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 60)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 60));
}
}
public void testVP8Decode60fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 60)) {
- Log.i(TAG, "SKIPPING testVP8Decode60fps1280x720(): Unsupported profile.");
- return;
- }
- testDecode(R.raw.video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_44100hz, 249);
+ testDecode(R.raw.video_1280x720_webm_vp8_8192kbps_60fps_vorbis_stereo_128kbps_48000hz, 249);
}
public void testVP8Decode30fps1920x1080Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30));
}
}
public void testVP8Decode30fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30)) {
- Log.i(TAG, "SKIPPING testVP8Decode30fps1920x1080(): Unsupported profile.");
- return;
- }
- testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_44100hz,
+ testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_30fps_vorbis_stereo_128kbps_48000hz,
249);
}
public void testVP8Decode60fps1920x1080Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 60)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 60));
}
}
public void testVP8Decode60fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 60)) {
- Log.i(TAG, "SKIPPING testVP8Decode60fps1920x1080(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz,
249);
}
public void testVP9Decode320x240() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testVP9Decode320x240(): No codec found.");
- return;
- }
- testDecode(R.raw.video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
+ testDecode(R.raw.video_320x240_webm_vp9_600kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
}
public void testVP9Decode640x360() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testVP9Decode640x360(): No codec found.");
- return;
- }
- testDecode(R.raw.video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
+ testDecode(R.raw.video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
}
public void testVP9Decode30fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30));
}
}
public void testVP9Decode30fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30)) {
- Log.i(TAG, "SKIPPING testVP8Decode30fps1920x1080(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
}
public void testVP9Decode30fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP9, 1920, 1080, 30)) {
- Log.d(TAG, "SKIPPING testVP9Decode30fps1920x1080(): Unsupported optional profile.");
- return;
- }
-
- testDecode(R.raw.video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_44100hz,
+ testDecode(R.raw.video_1920x1080_webm_vp9_10240kbps_30fps_vorbis_stereo_128kbps_48000hz,
249);
}
public void testVP9Decode30fps3840x2160() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_VP9, 3840, 2160, 30)) {
- Log.d(TAG, "SKIPPING testVP9Decode30fps3840x2160(): Unsupported optional profile.");
- return;
- }
-
- testDecode(R.raw.video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_44100hz,
+ testDecode(R.raw.video_3840x2160_webm_vp9_20480kbps_30fps_vorbis_stereo_128kbps_48000hz,
249);
}
public void testHEVCDecode352x288() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testHEVCDecode352x288(): No codec found.");
- return;
- }
testDecode(R.raw.video_352x288_mp4_hevc_600kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testHEVCDecode720x480() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testHEVCDecode720x480(): No codec found.");
- return;
- }
testDecode(R.raw.video_720x480_mp4_hevc_1638kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testHEVCDecode30fps1280x720Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30));
}
}
public void testHEVCDecode30fps1280x720() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30)) {
- Log.i(TAG, "SKIPPING testHEVCDecode30fps1280x720(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1280x720_mp4_hevc_4096kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
public void testHEVCDecode30fps1920x1080Tv() throws Exception {
- if (isTv() && !isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30)) {
- fail("Profile is required for TV device.");
+ if (checkTv()) {
+ assertTrue(MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30));
}
}
public void testHEVCDecode30fps1920x1080() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30)) {
- Log.i(TAG, "SKIPPING testHEVCDecode30fps1920x1080(): Unsupported profile.");
- return;
- }
testDecode(R.raw.video_1920x1080_mp4_hevc_10240kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
- public void testHEVCDecode30fps2840x2160() throws Exception {
- if (!isDecodeFormatSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, 2840, 2160, 30)) {
- Log.i(TAG, "SKIPPING testHEVCDecode30fps2840x2160(): Unsupported optional profile.");
- return;
+ public void testHEVCDecode30fps3840x2160() throws Exception {
+ testDecode(R.raw.video_3840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299);
+ }
+
+ private void testCodecEarlyEOS(int resid, int eosFrame) throws Exception {
+ if (!MediaUtils.checkCodecForResource(mContext, resid, 0 /* track */)) {
+ return; // skip
}
- testDecode(R.raw.video_2840x2160_mp4_hevc_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299);
+ Surface s = getActivity().getSurfaceHolder().getSurface();
+ int frames1 = countFrames(resid, RESET_MODE_NONE, eosFrame, s);
+ assertEquals("wrong number of frames decoded", eosFrame, frames1);
}
public void testCodecEarlyEOSH263() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSH263(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
+ testCodecEarlyEOS(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz,
- RESET_MODE_NONE, 64 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 64, frames1);
+ 64 /* eosframe */);
}
public void testCodecEarlyEOSH264() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSH264(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
+ testCodecEarlyEOS(
R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
- RESET_MODE_NONE, 120 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 120, frames1);
+ 120 /* eosframe */);
}
public void testCodecEarlyEOSHEVC() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSHEVC(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
+ testCodecEarlyEOS(
R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
- RESET_MODE_NONE, 120 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 120, frames1);
+ 120 /* eosframe */);
}
public void testCodecEarlyEOSMpeg4() throws Exception {
- if (!hasMpeg4(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSMpeg4(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
+ testCodecEarlyEOS(
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz,
- RESET_MODE_NONE, 120 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 120, frames1);
+ 120 /* eosframe */);
}
public void testCodecEarlyEOSVP8() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSVP8(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
- R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
- RESET_MODE_NONE, 120 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 120, frames1);
+ testCodecEarlyEOS(
+ R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
+ 120 /* eosframe */);
}
public void testCodecEarlyEOSVP9() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testCodecEarlyEOSVP9(): No codec found.");
- return;
- }
- Surface s = getActivity().getSurfaceHolder().getSurface();
- int frames1 = countFrames(
- R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
- RESET_MODE_NONE, 120 /* eosframe */, s);
- assertEquals("wrong number of frames decoded", 120, frames1);
+ testCodecEarlyEOS(
+ R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
+ 120 /* eosframe */);
}
public void testCodecResetsH264WithoutSurface() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsH264WithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, null);
}
public void testCodecResetsH264WithSurface() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsH264WithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz, s);
}
public void testCodecResetsHEVCWithoutSurface() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsHEVCWithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, null);
}
public void testCodecResetsHEVCWithSurface() throws Exception {
- if (!hasHEVC(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsHEVCWithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz, s);
}
public void testCodecResetsH263WithoutSurface() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsH263WithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, null);
}
public void testCodecResetsH263WithSurface() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsH263WithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_stereo_128kbps_22050hz, s);
}
public void testCodecResetsMpeg4WithoutSurface() throws Exception {
- if (!hasMpeg4(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsMpeg4WithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, null);
}
public void testCodecResetsMpeg4WithSurface() throws Exception {
- if (!hasMpeg4(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsMpeg4WithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz, s);
}
public void testCodecResetsVP8WithoutSurface() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsVP8WithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
- R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null);
+ R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null);
}
public void testCodecResetsVP8WithSurface() throws Exception {
- if (!hasVP8(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsVP8WithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
- R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s);
+ R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s);
}
public void testCodecResetsVP9WithoutSurface() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsVP9WithoutSurface(): No codec found.");
- return;
- }
testCodecResets(
- R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, null);
+ R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, null);
}
public void testCodecResetsVP9WithSurface() throws Exception {
- if (!hasVP9(false)) {
- Log.i(TAG, "SKIPPING testCodecResetsVP9WithSurface(): No codec found.");
- return;
- }
Surface s = getActivity().getSurfaceHolder().getSurface();
testCodecResets(
- R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz, s);
+ R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz, s);
}
// public void testCodecResetsOgg() throws Exception {
@@ -1364,6 +1185,10 @@
}
private void testCodecResets(int video, Surface s) throws Exception {
+ if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) {
+ return; // skip
+ }
+
int frames1 = countFrames(video, RESET_MODE_NONE, -1 /* eosframe */, s);
int frames2 = countFrames(video, RESET_MODE_RECONFIGURE, -1 /* eosframe */, s);
int frames3 = countFrames(video, RESET_MODE_FLUSH, -1 /* eosframe */, s);
@@ -1443,11 +1268,8 @@
testFd.getLength());
extractor.selectTrack(0); // consider variable looping on track
MediaFormat format = extractor.getTrackFormat(0);
- String mimeType = format.getString(MediaFormat.KEY_MIME);
- if (!hasCodecForMimeType(mimeType, false)) {
- Log.i(TAG, "SKIPPING testEOSBehavior() for resid=" + movie + " No codec found for "
- + "mimeType = " + mimeType);
- return;
+ if (!MediaUtils.checkDecoderForFormat(format)) {
+ return; // skip
}
List<Long> outputChecksums = new ArrayList<Long>();
List<Long> outputTimestamps = new ArrayList<Long>();
@@ -1753,13 +1575,13 @@
public void testEOSBehaviorVP8() throws Exception {
// this video has an I frame at 46
- testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+ testEOSBehavior(R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
new int[] {46, 47, 57, 45});
}
public void testEOSBehaviorVP9() throws Exception {
// this video has an I frame at 44
- testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_44100hz,
+ testEOSBehavior(R.raw.video_480x360_webm_vp9_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
new int[] {44, 45, 55, 43});
}
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index 9528db9..5f326ee 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.media.Image;
import android.media.Image.Plane;
@@ -28,7 +29,6 @@
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
@@ -100,10 +100,6 @@
* to be supported by hw decoder.
*/
public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
- if (!MediaPlayerTestBase.hasH264(false)) {
- Log.i(TAG, "SKIPPING testSwAVCDecode360pForFlexibleYuv(): no codec found.");
- return;
- }
try {
int format = ImageFormat.YUV_420_888;
videoDecodeToSurface(
@@ -119,10 +115,6 @@
* to be supported by sw decoder.
*/
public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
- if (!MediaPlayerTestBase.hasH264(false)) {
- Log.i(TAG, "SKIPPING testSwAVCDecode360pForFlexibleYuv(): no codec found.");
- return;
- }
try {
int format = ImageFormat.YUV_420_888;
videoDecodeToSurface(
@@ -168,6 +160,10 @@
ByteBuffer[] decoderInputBuffers;
ByteBuffer[] decoderOutputBuffers;
+ if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) {
+ return; // skip
+ }
+
AssetFileDescriptor vidFD = mResources.openRawResourceFd(video);
extractor = new MediaExtractor();
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
index 9515c22..be5099e 100644
--- a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -69,9 +69,9 @@
getLoudnessEnhancer(0);
try {
mLE.setTargetGain(0);
- assertEquals("target gain differs from value set", 0, mLE.getTargetGain());
+ assertEquals("target gain differs from value set", 0.0f, mLE.getTargetGain());
mLE.setTargetGain(800);
- assertEquals("target gain differs from value set", 800, mLE.getTargetGain());
+ assertEquals("target gain differs from value set", 800.0f, mLE.getTargetGain());
} catch (IllegalArgumentException e) {
fail("target gain illegal argument");
} catch (UnsupportedOperationException e) {
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index f966108..9d65a3d 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -16,9 +16,15 @@
package android.media.cts;
import android.content.pm.PackageManager;
+import android.cts.util.MediaUtils;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
+import static android.media.MediaCodecInfo.CodecProfileLevel.*;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaPlayer;
@@ -33,52 +39,76 @@
public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
private static final String TAG = "MediaCodecCapabilitiesTest";
- private static final String AVC_MIME = MediaFormat.MIMETYPE_VIDEO_AVC;
- private static final String HEVC_MIME = MediaFormat.MIMETYPE_VIDEO_HEVC;
private static final int PLAY_TIME_MS = 30000;
// Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
// SHOULD support Main Profile/ Level 4, if supported the device must also support Main
// Profile/Level 4 decoding.
public void testH264EncoderProfileAndLevel() throws Exception {
- if (!hasH264(true /* isEncoder */)) {
- Log.d(TAG, "SKIPPING testH264EncoderProfileAndLevel: No codec found.");
- return;
- }
- boolean testLevel = true;
- if (!supports(AVC_MIME, true /* isEncoder */, CodecProfileLevel.AVCProfileBaseline,
- CodecProfileLevel.AVCLevel3, testLevel)) {
- fail("H.264 Baseline Profile Level 3 support is required by CDD");
+ if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
- if (supports(AVC_MIME, true /* isEncoder */, CodecProfileLevel.AVCProfileMain,
- CodecProfileLevel.AVCLevel4, testLevel)) {
- if (!supports(AVC_MIME, false, CodecProfileLevel.AVCProfileMain,
- CodecProfileLevel.AVCLevel4, testLevel)) {
- fail("If H.264 Main Profile Level 4 encoding is supported, " +
- "the device must also support is the same profile and level for decoding.");
- }
+ assertTrue(
+ "H.264 must support Baseline Profile Level 3",
+ hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
+
+ if (hasEncoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4)) {
+ assertTrue(
+ "H.264 decoder must support Main Profile Level 4 if it can encode it",
+ hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileMain, AVCLevel4));
}
}
// Android device implementations with H.264 decoders, MUST support Baseline Profile Level 3.
// Android Television Devices MUST support High Profile Level 4.2.
public void testH264DecoderProfileAndLevel() throws Exception {
- if (!hasH264(false /* isEncoder */)) {
- Log.d(TAG, "SKIPPING testH264DecoderProfileAndLevel: No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
- CodecProfileLevel.AVCLevel3)) {
- fail("H.264 Baseline Profile Level 3 support is required by CDD");
- }
+
+ assertTrue(
+ "H.264 must support Baseline Profile Level 3",
+ hasDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3));
+
if (isTv()) {
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
- CodecProfileLevel.AVCLevel42)) {
- fail("H.264 High Profile Level 4.2 support is required by CDD for " +
- "television devices");
- }
- }
+ assertTrue(
+ "H.264 must support High Profile Level 4.2 on TV",
+ checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel42));
+ }
+ }
+
+ // Android device implementations with H.263 encoders, MUST support Level 45.
+ public void testH263EncoderProfileAndLevel() throws Exception {
+ if (!MediaUtils.checkEncoder(MIMETYPE_VIDEO_H263)) {
+ return; // skip
+ }
+
+ assertTrue(
+ "H.263 must support Level 45",
+ hasEncoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level45));
+ }
+
+ // Android device implementations with H.263 decoders, MUST support Level 30.
+ public void testH263DecoderProfileAndLevel() throws Exception {
+ if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_H263)) {
+ return; // skip
+ }
+
+ assertTrue(
+ "H.263 must support Level 30",
+ hasDecoder(MIMETYPE_VIDEO_H263, MPEG4ProfileSimple, H263Level30));
+ }
+
+ // Android device implementations with MPEG-4 decoders, MUST support Simple Profile Level 3.
+ public void testMpeg4DecoderProfileAndLevel() throws Exception {
+ if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_MPEG4)) {
+ return; // skip
+ }
+
+ assertTrue(
+ "MPEG-4 must support Simple Profile Level 3",
+ hasDecoder(MIMETYPE_VIDEO_MPEG4, MPEG4ProfileSimple, MPEG4Level3));
}
// Android device implementations, when supporting H.265 codec MUST support the Main Profile
@@ -87,53 +117,39 @@
// When the UHD video decoding profile is supported, it MUST support Main10 Level 5 Main
// Tier profile.
public void testH265DecoderProfileAndLevel() throws Exception {
- MediaCodecInfo info = getMediaCodecInfo(HEVC_MIME, false /* isEncoder */);
- if (info == null) {
- Log.d(TAG, "SKIPPING testH265DecoderProfileAndLevel: No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MIMETYPE_VIDEO_HEVC)) {
+ return; // skip
}
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel3)) {
- fail("H.265 Main Profile Level 3 Main tier support is required by CDD");
- }
+ assertTrue(
+ "H.265 must support Main Profile Main Tier Level 3",
+ hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3));
+
if (isTv()) {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel41)) {
- fail("H.265 Main Profile Level 4.1 Main tier support is required by CDD for " +
- "television devices");
- }
+ assertTrue(
+ "H.265 must support Main Profile Main Tier Level 4.1 on TV",
+ hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
}
- MediaCodecInfo.CodecCapabilities cCaps = info.getCapabilitiesForType(HEVC_MIME);
- MediaCodecInfo.VideoCapabilities vCaps = cCaps.getVideoCapabilities();
- if (vCaps.areSizeAndRateSupported(3840, 2160, 30)) {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain10,
- CodecProfileLevel.HEVCMainTierLevel5)) {
- fail("H.265 Main10 Level 5 Main Tier support is required by CDD when " +
- "the UHD video decoding profile is supported");
- }
+ if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
+ assertTrue(
+ "H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
+ hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
}
}
-
public void testAvcBaseline1() throws Exception {
- if (hasCodec(AVC_MIME) && !supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
- CodecProfileLevel.AVCLevel1)) {
- fail("AVCLevel1 support is required by CDD");
+ if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel1)) {
+ return; // skip
}
- // We don't have a test stream, but at least we're testing
- // that supports() returns true for something.
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testAvcBaseline12() throws Exception {
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline)) {
- return;
- }
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
- CodecProfileLevel.AVCLevel12)) {
- Log.i(TAG, "AvcBaseline12 not supported");
- return; // TODO: Can we make this mandatory?
+ if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel12)) {
+ return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=160&source=youtube&user=android-device-test"
@@ -145,13 +161,8 @@
}
public void testAvcBaseline30() throws Exception {
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline)) {
- return;
- }
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
- CodecProfileLevel.AVCLevel3)) {
- Log.i(TAG, "AvcBaseline30 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileBaseline, AVCLevel3)) {
+ return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=18&source=youtube&user=android-device-test"
@@ -163,13 +174,8 @@
}
public void testAvcHigh31() throws Exception {
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh)) {
- return;
- }
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
- CodecProfileLevel.AVCLevel31)) {
- Log.i(TAG, "AvcHigh31 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel31)) {
+ return; // skip
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ "&itag=22&source=youtube&user=android-device-test"
@@ -182,16 +188,11 @@
}
public void testAvcHigh40() throws Exception {
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh)) {
- return;
- }
- if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
- CodecProfileLevel.AVCLevel4)) {
- Log.i(TAG, "AvcHigh40 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_AVC, AVCProfileHigh, AVCLevel4)) {
+ return; // skip
}
if (Build.VERSION.SDK_INT < 18) {
- Log.i(TAG, "fragmented mp4 not supported");
+ MediaUtils.skipTest(TAG, "fragmented mp4 not supported");
return;
}
playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
@@ -204,127 +205,129 @@
}
public void testHevcMain1() throws Exception {
- if (hasCodec(HEVC_MIME) && !supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel1)) {
- fail("HECLevel1 support is required by CDD");
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel1)) {
+ return; // skip
}
- // We don't have a test stream, but at least we're testing
- // that supports() returns true for something.
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
+
public void testHevcMain2() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel2)) {
- Log.i(TAG, "HevcMain2 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel2)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain21() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel21)) {
- Log.i(TAG, "HevcMain21 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel21)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain3() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel3)) {
- Log.i(TAG, "HevcMain3 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel3)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain31() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel31)) {
- Log.i(TAG, "HevcMain31 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel31)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain4() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel4)) {
- Log.i(TAG, "HevcMain4 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel4)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain41() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel41)) {
- Log.i(TAG, "HevcMain41 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain5() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel5)) {
- Log.i(TAG, "HevcMain5 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel5)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
public void testHevcMain51() throws Exception {
- if (!supports(HEVC_MIME, CodecProfileLevel.HEVCProfileMain,
- CodecProfileLevel.HEVCMainTierLevel51)) {
- Log.i(TAG, "HevcMain51 not supported");
- return;
+ if (!checkDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel51)) {
+ return; // skip
}
+
+ // TODO: add a test stream
+ MediaUtils.skipTest(TAG, "no test stream");
}
- private boolean supports(String mimeType, int profile) {
- return supports(mimeType, false /* isEncoder */, profile, 0, false);
+ private boolean checkDecoder(String mime, int profile, int level) {
+ if (!hasDecoder(mime, profile, level)) {
+ MediaUtils.skipTest(TAG, "no " + mime + " decoder for profile "
+ + profile + " and level " + level);
+ return false;
+ }
+ return true;
}
- private boolean supports(String mimeType, int profile, int level) {
- return supports(mimeType, false /* isEncoder */, profile, level, true);
+ private boolean hasDecoder(String mime, int profile, int level) {
+ return supports(mime, false /* isEncoder */, profile, level);
}
- private boolean supports(String mimeType, boolean isEncoder,
- int profile, int level, boolean testLevel) {
- int numCodecs = MediaCodecList.getCodecCount();
- for (int i = 0; i < numCodecs; i++) {
- MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
- if (isEncoder != codecInfo.isEncoder()) {
+ private boolean hasEncoder(String mime, int profile, int level) {
+ return supports(mime, true /* isEncoder */, profile, level);
+ }
+
+ private boolean supports(
+ String mime, boolean isEncoder, int profile, int level) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (isEncoder != info.isEncoder()) {
continue;
}
+ try {
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ for (CodecProfileLevel pl : caps.profileLevels) {
+ if (pl.profile != profile) {
+ continue;
+ }
- if (!supportsMimeType(codecInfo, mimeType)) {
- continue;
- }
-
- CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
- for (CodecProfileLevel profileLevel : capabilities.profileLevels) {
- if (profileLevel.profile == profile
- && (!testLevel || profileLevel.level >= level)) {
- return true;
+ // H.263 levels are not completely ordered:
+ // Level45 support only implies Level10 support
+ if (mime.equalsIgnoreCase(MIMETYPE_VIDEO_H263)) {
+ if (pl.level != level && pl.level == H263Level45 && level > H263Level10) {
+ continue;
+ }
+ }
+ if (pl.level >= level) {
+ return true;
+ }
}
- }
- }
-
- return false;
- }
-
- private static boolean supportsMimeType(MediaCodecInfo codecInfo, String mimeType) {
- String[] supportedMimeTypes = codecInfo.getSupportedTypes();
- for (String supportedMimeType : supportedMimeTypes) {
- if (mimeType.equalsIgnoreCase(supportedMimeType)) {
- return true;
- }
- }
- return false;
- }
-
- private static boolean hasCodec(String mimeType) {
- MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
- for (MediaCodecInfo info : list.getCodecInfos()) {
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mimeType)) {
- return true;
- }
+ } catch (IllegalArgumentException e) {
}
}
return false;
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index 1019f3f..9442d09 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -50,9 +50,10 @@
mAllCodecs.getCodecInfos();
class CodecType {
- CodecType(String type, boolean isEncoder) {
+ CodecType(String type, boolean isEncoder, MediaFormat sampleFormat) {
mMimeTypeName = type;
mIsEncoder = isEncoder;
+ mSampleFormat = sampleFormat;
}
boolean equals(CodecType codecType) {
@@ -60,10 +61,35 @@
mIsEncoder == codecType.mIsEncoder;
}
- String mMimeTypeName;
- boolean mIsEncoder;
+ boolean canBeFound() {
+ return codecCanBeFound(mIsEncoder, mSampleFormat);
+ }
+
+ @Override
+ public String toString() {
+ return mMimeTypeName + (mIsEncoder ? " encoder" : " decoder") + " for " + mSampleFormat;
+ }
+
+ private String mMimeTypeName;
+ private boolean mIsEncoder;
+ private MediaFormat mSampleFormat;
};
+ class AudioCodec extends CodecType {
+ AudioCodec(String mime, boolean isEncoder, int sampleRate) {
+ super(mime, isEncoder, MediaFormat.createAudioFormat(
+ mime, sampleRate, 1 /* channelCount */));
+ }
+ }
+
+ class VideoCodec extends CodecType {
+ VideoCodec(String mime, boolean isEncoder) {
+ // implicit assumption that QVGA video is always valid
+ super(mime, isEncoder, MediaFormat.createVideoFormat(
+ mime, 176 /* width */, 144 /* height */));
+ }
+ }
+
public static void testMediaCodecXmlFileExist() {
File file = new File(MEDIA_CODEC_XML_FILE);
assertTrue("/etc/media_codecs.xml does not exist", file.exists());
@@ -241,6 +267,9 @@
List<CodecType> requiredList = getRequiredCodecTypes();
List<CodecType> supportedList = getSupportedCodecTypes();
assertTrue(areRequiredCodecTypesSupported(requiredList, supportedList));
+ for (CodecType type : requiredList) {
+ assertTrue("cannot find " + type, type.canBeFound());
+ }
}
private boolean hasCamera() {
@@ -249,80 +278,27 @@
pm.hasSystemFeature(pm.FEATURE_CAMERA);
}
- // H263 baseline profile must be supported
- public void testIsH263BaselineProfileSupported() {
- if (!hasCamera()) {
- Log.d(TAG, "not required without camera");
- return;
- }
-
- int profile = CodecProfileLevel.H263ProfileBaseline;
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_H263, false, profile));
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_H263, true, profile));
+ private boolean hasMicrophone() {
+ PackageManager pm = getContext().getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_MICROPHONE);
}
- // AVC baseline profile must be supported
- public void testIsAVCBaselineProfileSupported() {
- int profile = CodecProfileLevel.AVCProfileBaseline;
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_AVC, false, profile));
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_AVC, true, profile));
+ private boolean isWatch() {
+ PackageManager pm = getContext().getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_WATCH);
}
- // HEVC main profile must be supported
- public void testIsHEVCMainProfileSupported() {
- int profile = CodecProfileLevel.HEVCProfileMain;
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_HEVC, false, profile));
- }
-
- // MPEG4 simple profile must be supported
- public void testIsM4VSimpleProfileSupported() {
- if (!hasCamera()) {
- Log.d(TAG, "not required without camera");
- return;
- }
-
- int profile = CodecProfileLevel.MPEG4ProfileSimple;
- assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_MPEG4, false, profile));
-
- // FIXME: no support for M4v simple profile video encoder
- // assertTrue(checkProfileSupported(MediaFormat.MIMETYPE_VIDEO_MPEG4, true, profile));
- }
-
- /*
- * Find whether the given codec is supported
- */
- private boolean checkProfileSupported(
- String mime, boolean isEncoder, int profile) {
- return profileIsListed(mime, isEncoder, profile) &&
- codecCanBeFound(mime, isEncoder);
- }
-
- private boolean profileIsListed(
- String mime, boolean isEncoder, int profile) {
-
- for (MediaCodecInfo info : mRegularInfos) {
- if (isEncoder != info.isEncoder()) {
- continue;
- }
-
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mime)) {
- CodecCapabilities cap = info.getCapabilitiesForType(type);
- for (CodecProfileLevel pl : cap.profileLevels) {
- if (pl.profile == profile) {
- return true;
- }
- }
- }
- }
- }
- return false;
+ private boolean isHandheld() {
+ // handheld nature is not exposed to package manager, for now
+ // we check for touchscreen and NOT watch and NOT tv
+ PackageManager pm = getContext().getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_TOUCHSCREEN)
+ && !pm.hasSystemFeature(pm.FEATURE_WATCH)
+ && !pm.hasSystemFeature(pm.FEATURE_TELEVISION);
}
// Find whether the given codec can be found using MediaCodecList.find methods.
- private boolean codecCanBeFound(String mime, boolean isEncoder) {
- // implicit assumption that QVGA video is always valid.
- MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
+ private boolean codecCanBeFound(boolean isEncoder, MediaFormat format) {
String codecName = isEncoder
? mRegularCodecs.findEncoderForFormat(format)
: mRegularCodecs.findDecoderForFormat(format);
@@ -361,7 +337,7 @@
assertTrue("Unexpected number of supported types", types.length > 0);
boolean isEncoder = info.isEncoder();
for (int j = 0; j < types.length; ++j) {
- supportedList.add(new CodecType(types[j], isEncoder));
+ supportedList.add(new CodecType(types[j], isEncoder, null /* sampleFormat */));
}
}
return supportedList;
@@ -374,30 +350,53 @@
private List<CodecType> getRequiredCodecTypes() {
List<CodecType> list = new ArrayList<CodecType>(16);
- // Mandatory audio codecs
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AMR_WB, false)); // amrwb decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AMR_WB, true)); // amrwb encoder
+ // Mandatory audio decoders
// flac decoder is not omx-based yet
- // list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_FLAC, false)); // flac decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_FLAC, true)); // flac encoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_MPEG, false)); // mp3 decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AAC, false)); // aac decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AAC, true)); // aac encoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_VORBIS, false)); // vorbis decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AMR_NB, false)); // amrnb decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_AMR_NB, true)); // amrnb encoder
+ // list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_FLAC, false, 8000));
+ // list.add(new CodecType(MediaFormat.MIMETYPE_AUDIO_FLAC, false, 48000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 8000)); // mp3
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_MPEG, false, 48000)); // mp3
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 8000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_VORBIS, false, 48000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 8000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, false, 48000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 8000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_RAW, false, 44100));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_OPUS, false, 48000));
- // Mandatory video codecs
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_AVC, false)); // avc decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_AVC, true)); // avc encoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_HEVC, false)); // hevc decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_H263, false)); // h263 decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_H263, true)); // h263 encoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_MPEG4, false)); // m4v decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_VP8, false)); // vp8 decoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_VP8, true)); // vp8 encoder
- list.add(new CodecType(MediaFormat.MIMETYPE_VIDEO_VP9, false)); // vp9 decoder
+ // Mandatory audio encoders (for non-watch devices with camera)
+
+ if (hasMicrophone() && !isWatch()) {
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 8000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 48000));
+ // flac encoder is not required
+ // list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, true)); // encoder
+ }
+
+ // Mandatory audio encoders for handheld devices
+ if (isHandheld()) {
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, false, 8000)); // decoder
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_NB, true, 8000)); // encoder
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, false, 16000)); // decoder
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AMR_WB, true, 16000)); // encoder
+ }
+
+ // Mandatory video codecs (for non-watch devices)
+
+ if (!isWatch()) {
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, false)); // avc decoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_AVC, true)); // avc encoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, false)); // vp8 decoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP8, true)); // vp8 encoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_VP9, false)); // vp9 decoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_HEVC, false)); // hevc decoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_MPEG4, false)); // m4v decoder
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, false)); // h263 decoder
+ if (hasCamera()) {
+ list.add(new VideoCodec(MediaFormat.MIMETYPE_VIDEO_H263, true)); // h263 encoder
+ }
+ }
return list;
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 33a0ee4..0aff8ed 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -19,6 +19,7 @@
import com.android.cts.media.R;
import android.content.res.AssetFileDescriptor;
+import android.cts.util.MediaUtils;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
@@ -80,27 +81,28 @@
* methods when called in incorrect operational states.
*/
public void testException() throws Exception {
- String mimeType = MediaFormat.MIMETYPE_AUDIO_AMR_WB;
- if (!supportsCodec(mimeType, false)) {
- Log.i(TAG, "No decoder found for mimeType= " + mimeType);
- return;
- }
+ boolean tested = false;
+ // audio decoder (MP3 should be present on all Android devices)
+ MediaFormat format = MediaFormat.createAudioFormat(
+ MediaFormat.MIMETYPE_AUDIO_MPEG, 44100 /* sampleRate */, 2 /* channelCount */);
+ tested = verifyException(format, false /* isEncoder */) || tested;
- MediaFormat[] formatList = new MediaFormat[2];
+ // audio encoder (AMR-WB may not be present on some Android devices)
+ format = MediaFormat.createAudioFormat(
+ MediaFormat.MIMETYPE_AUDIO_AMR_WB, 16000 /* sampleRate */, 1 /* channelCount */);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, 19850);
+ tested = verifyException(format, true /* isEncoder */) || tested;
- // use audio format
- formatList[0] = new MediaFormat();
- formatList[0].setString(MediaFormat.KEY_MIME, mimeType);
- formatList[0].setInteger(MediaFormat.KEY_SAMPLE_RATE, 16000);
- formatList[0].setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
- formatList[0].setInteger(MediaFormat.KEY_BIT_RATE, 19850);
+ // video decoder (H.264/AVC may not be present on some Android devices)
+ format = createMediaFormat();
+ tested = verifyException(format, false /* isEncoder */) || tested;
- // use video format
- formatList[1] = createMediaFormat();
+ // video encoder (H.264/AVC may not be present on some Android devices)
+ tested = verifyException(format, true /* isEncoder */) || tested;
- for (MediaFormat format : formatList) {
- verifyIllegalStateException(format, false);
- verifyIllegalStateException(format, true);
+ // signal test is skipped due to no device media codecs.
+ if (!tested) {
+ MediaUtils.skipTest(TAG, "cannot find any compatible device codecs");
}
}
@@ -123,11 +125,17 @@
}
}
- private static void verifyIllegalStateException(MediaFormat format, boolean isEncoder)
+ private static boolean verifyException(MediaFormat format, boolean isEncoder)
throws IOException {
- MediaCodec codec;
+ String mimeType = format.getString(MediaFormat.KEY_MIME);
+ if (!supportsCodec(mimeType, isEncoder)) {
+ Log.i(TAG, "No " + (isEncoder ? "encoder" : "decoder")
+ + " found for mimeType= " + mimeType);
+ return false;
+ }
// create codec (enter Initialized State)
+ MediaCodec codec;
// create improperly
final String methodName = isEncoder ? "createEncoderByType" : "createDecoderByType";
@@ -270,6 +278,7 @@
fail("stop should not return MediaCodec.CodecException on wrong state");
} catch (IllegalStateException e) { // expected
}
+ return true;
}
/**
@@ -1005,18 +1014,17 @@
* match was found.
*/
private static MediaCodecInfo selectCodec(String mimeType) {
- int numCodecs = MediaCodecList.getCodecCount();
- for (int i = 0; i < numCodecs; i++) {
- MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
-
- if (!codecInfo.isEncoder()) {
+ // FIXME: select codecs based on the complete use-case, not just the mime
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (!info.isEncoder()) {
continue;
}
- String[] types = codecInfo.getSupportedTypes();
+ String[] types = info.getSupportedTypes();
for (int j = 0; j < types.length; j++) {
if (types[j].equalsIgnoreCase(mimeType)) {
- return codecInfo;
+ return info;
}
}
}
@@ -1082,4 +1090,4 @@
}
return false;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
index c5cd04e..32fbfb5 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
@@ -15,6 +15,7 @@
*/
package android.media.cts;
+import android.cts.util.MediaUtils;
import android.media.MediaPlayer;
import android.os.Handler;
import android.os.Looper;
@@ -34,6 +35,7 @@
import java.net.Socket;
import java.util.Random;
+import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
@@ -42,6 +44,7 @@
* from an HTTP server over a simulated "flaky" network.
*/
public class MediaPlayerFlakyNetworkTest extends MediaPlayerTestBase {
+ private static final String PKG = "com.android.cts.media";
private static final String[] TEST_VIDEOS = {
"raw/video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz",
@@ -92,24 +95,37 @@
doPlayStreams(6, 0.00002f);
}
+ private String[] getSupportedVideos() {
+ Vector<String> supported = new Vector<String>();
+ for (String video : TEST_VIDEOS) {
+ if (MediaUtils.hasCodecsForPath(mContext, "android.resource://" + PKG + "/" + video)) {
+ supported.add(video);
+ }
+ }
+ return supported.toArray(new String[supported.size()]);
+ }
+
private void doPlayStreams(int seed, float probability) throws Throwable {
- if (!hasH264(false)) {
+ String[] supported = getSupportedVideos();
+ if (supported.length == 0) {
+ MediaUtils.skipTest("no codec found");
return;
}
+
Random random = new Random(seed);
createHttpServer(seed, probability);
for (int i = 0; i < 10; i++) {
- String video = getRandomTestVideo(random);
+ String video = getRandomTestVideo(random, supported);
doPlayMp4Stream(video, 20000, 5000);
doAsyncPrepareAndRelease(video);
doRandomOperations(video);
}
- doPlayMp4Stream(getRandomTestVideo(random), 30000, 20000);
+ doPlayMp4Stream(getRandomTestVideo(random, supported), 30000, 20000);
releaseHttpServer();
}
- private String getRandomTestVideo(Random random) {
- return TEST_VIDEOS[random.nextInt(TEST_VIDEOS.length)];
+ private String getRandomTestVideo(Random random, String[] videos) {
+ return videos[random.nextInt(videos.length)];
}
private void doPlayMp4Stream(String video, int millisToPrepare, int millisToPlay)
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 108aa8b..65a34f4 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -20,10 +20,9 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
+import android.cts.util.MediaUtils;
import android.media.AudioManager;
import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaPlayer;
@@ -674,11 +673,8 @@
}
});
- try {
- loadResource(R.raw.testvideo);
- } catch (UnsupportedCodecException e) {
- Log.i(LOG_TAG, "SKIPPING testVideoSurfaceResetting(). Could not find codec.");
- return;
+ if (!checkLoadResource(R.raw.testvideo)) {
+ return; // skip;
}
playLoadedVideo(352, 288, -1);
@@ -1030,11 +1026,8 @@
}
public void testDeselectTrack() throws Throwable {
- try {
- loadResource(R.raw.testvideo_with_2_subtitles);
- } catch (UnsupportedCodecException e) {
- Log.i(LOG_TAG, "SKIPPING testDeselectTrack(). Could not find codec.");
- return;
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitles)) {
+ return; // skip;
}
runTestOnUiThread(new Runnable() {
public void run() {
@@ -1106,11 +1099,8 @@
}
public void testChangeSubtitleTrack() throws Throwable {
- try {
- loadResource(R.raw.testvideo_with_2_subtitles);
- } catch (UnsupportedCodecException e) {
- Log.i(LOG_TAG, "SKIPPING testChangeSubtitleTrack(). Could not find codec.");
- return;
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitles)) {
+ return; // skip;
}
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
@@ -1199,11 +1189,8 @@
}
public void testGetTrackInfo() throws Throwable {
- try {
- loadResource(R.raw.testvideo_with_2_subtitles);
- } catch (UnsupportedCodecException e) {
- Log.i(LOG_TAG, "SKIPPING testGetTrackInfo(). Could not find codec.");
- return;
+ if (!checkLoadResource(R.raw.testvideo_with_2_subtitles)) {
+ return; // skip;
}
runTestOnUiThread(new Runnable() {
public void run() {
@@ -1246,17 +1233,23 @@
* The ones being used here are 10 seconds long.
*/
public void testResumeAtEnd() throws Throwable {
- testResumeAtEnd(R.raw.loudsoftmp3);
- testResumeAtEnd(R.raw.loudsoftwav);
- testResumeAtEnd(R.raw.loudsoftogg);
- testResumeAtEnd(R.raw.loudsoftitunes);
- testResumeAtEnd(R.raw.loudsoftfaac);
- testResumeAtEnd(R.raw.loudsoftaac);
+ int testsRun =
+ testResumeAtEnd(R.raw.loudsoftmp3) +
+ testResumeAtEnd(R.raw.loudsoftwav) +
+ testResumeAtEnd(R.raw.loudsoftogg) +
+ testResumeAtEnd(R.raw.loudsoftitunes) +
+ testResumeAtEnd(R.raw.loudsoftfaac) +
+ testResumeAtEnd(R.raw.loudsoftaac);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no decoder found");
+ }
}
- private void testResumeAtEnd(int res) throws Throwable {
-
- loadResource(res);
+ // returns 1 if test was run, 0 otherwise
+ private int testResumeAtEnd(int res) throws Throwable {
+ if (!loadResource(res)) {
+ return 0; // skip
+ }
mMediaPlayer.prepare();
mOnCompletionCalled.reset();
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@@ -1274,16 +1267,14 @@
assertTrue("MediaPlayer should still be playing", mMediaPlayer.isPlaying());
mMediaPlayer.reset();
assertEquals("wrong number of repetitions", 1, mOnCompletionCalled.getNumSignal());
+ return 1;
}
public void testCallback() throws Throwable {
final int mp4Duration = 8484;
- try {
- loadResource(R.raw.testvideo);
- } catch (UnsupportedCodecException e) {
- Log.i(LOG_TAG, "SKIPPING testCallback(). Could not find codec.");
- return;
+ if (!checkLoadResource(R.raw.testvideo)) {
+ return; // skip;
}
mMediaPlayer.setDisplay(getActivity().getSurfaceHolder());
@@ -1357,12 +1348,12 @@
public void testRecordAndPlay() throws Exception {
if (!hasMicrophone()) {
- Log.i(LOG_TAG, "SKIPPING testRecordAndPlay(). No microphone.");
+ MediaUtils.skipTest(LOG_TAG, "no microphone");
return;
}
- if (!hasH263(false)) {
- Log.i(LOG_TAG, "SKIPPING testRecordAndPlay(). Cound not find codec.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)
+ || !MediaUtils.checkEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
}
File outputFile = new File(Environment.getExternalStorageDirectory(),
"record_and_play.3gp");
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
index 986cd08..d089658e 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTestBase.java
@@ -16,15 +16,10 @@
package android.media.cts;
import android.content.Context;
-
import android.content.pm.PackageManager;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
-import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
-import android.media.MediaExtractor;
-import android.media.MediaFormat;
+import android.cts.util.MediaUtils;
import android.media.MediaPlayer;
import android.test.ActivityInstrumentationTestCase2;
@@ -149,9 +144,10 @@
super.tearDown();
}
- protected void loadResource(int resid) throws Exception {
- if (!supportsPlayback(resid)) {
- throw new UnsupportedCodecException();
+ // returns true on success
+ protected boolean loadResource(int resid) throws Exception {
+ if (!MediaUtils.hasCodecsForResource(mContext, resid)) {
+ return false;
}
AssetFileDescriptor afd = mResources.openRawResourceFd(resid);
@@ -170,6 +166,11 @@
afd.close();
}
sUseScaleToFitMode = !sUseScaleToFitMode; // Alternate the scaling mode
+ return true;
+ }
+
+ protected boolean checkLoadResource(int resid) throws Exception {
+ return MediaUtils.check(loadResource(resid), "no decoder found");
}
protected void loadSubtitleSource(int resid) throws Exception {
@@ -208,13 +209,10 @@
}
protected void playVideoTest(int resid, int width, int height) throws Exception {
- if (!supportsPlayback(resid)) {
- LOG.info("SKIPPING playVideoTest() for resid=" + resid
- + " Could not find a codec for playback.");
- return;
+ if (!checkLoadResource(resid)) {
+ return; // skip
}
- loadResource(resid);
playLoadedVideo(width, height, 0);
}
@@ -295,81 +293,6 @@
}
private static class PrepareFailedException extends Exception {}
- public static class UnsupportedCodecException extends Exception {}
-
- public boolean supportsPlayback(int resid) throws IOException {
- // First determine if the device supports playback of the requested resource.
- AssetFileDescriptor fd = mResources.openRawResourceFd(resid);
- MediaExtractor ex = new MediaExtractor();
- ex.setDataSource(fd.getFileDescriptor(), fd.getStartOffset(), fd.getLength());
- MediaFormat format = ex.getTrackFormat(0);
- String mimeType = format.getString(MediaFormat.KEY_MIME);
- return hasCodecForMimeType(mimeType, false);
- }
-
- public boolean supportsPlayback(String path) throws IOException {
- MediaExtractor ex = new MediaExtractor();
- ex.setDataSource(path);
- MediaFormat format = ex.getTrackFormat(0);
- String mimeType = format.getString(MediaFormat.KEY_MIME);
- return hasCodecForMimeType(mimeType, false);
- }
-
- public static boolean hasCodecForMimeType(String mimeType, boolean encoder) {
- MediaCodecList list = new MediaCodecList(MediaCodecList.ALL_CODECS);
- for (MediaCodecInfo info : list.getCodecInfos()) {
- if (encoder != info.isEncoder()) {
- continue;
- }
-
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mimeType)) {
- LOG.info("Found codec for mimeType=" + mimeType + " codec=" + info.getName());
- return true;
- }
- }
- }
- return false;
- }
-
- public static MediaCodecInfo getMediaCodecInfo(String mimeType, boolean isEncoder) {
- MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- for (MediaCodecInfo info : list.getCodecInfos()) {
- if (isEncoder != info.isEncoder()) {
- continue;
- }
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mimeType)) {
- return info;
- }
- }
- }
- return null;
- }
-
- public static boolean hasH264(boolean encoder) {
- return hasCodecForMimeType("video/avc", encoder);
- }
-
- public static boolean hasHEVC(boolean encoder) {
- return hasCodecForMimeType("video/hevc", encoder);
- }
-
- public static boolean hasH263(boolean encoder) {
- return hasCodecForMimeType("video/3gpp", encoder);
- }
-
- public static boolean hasMpeg4(boolean encoder) {
- return hasCodecForMimeType("video/mp4v-es", encoder);
- }
-
- public static boolean hasVP8(boolean encoder) {
- return hasCodecForMimeType("video/x-vnd.on2.vp8", encoder);
- }
-
- public static boolean hasVP9(boolean encoder) {
- return hasCodecForMimeType("video/x-vnd.on2.vp9", encoder);
- }
public boolean hasAudioOutput() {
return getInstrumentation().getTargetContext().getPackageManager()
@@ -377,23 +300,12 @@
}
public boolean isTv() {
- PackageManager packageManager = getInstrumentation().getTargetContext().getPackageManager();
- return packageManager.hasSystemFeature("android.hardware.type.television") ||
- packageManager.hasSystemFeature("android.software.leanback");
+ PackageManager pm = getInstrumentation().getTargetContext().getPackageManager();
+ return pm.hasSystemFeature(pm.FEATURE_TELEVISION)
+ && pm.hasSystemFeature(pm.FEATURE_LEANBACK);
}
- private static boolean isFormatSupported(
- String mimeType, int w, int h, int frameRate, boolean isEncoder) {
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- MediaFormat format = MediaFormat.createVideoFormat(mimeType, w, h);
- format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
- String codec = isEncoder
- ? mcl.findEncoderForFormat(format)
- : mcl.findDecoderForFormat(format);
- return (codec != null);
- }
-
- public static boolean isDecodeFormatSupported(String mimeType, int w, int h, int frameRate) {
- return isFormatSupported(mimeType, w, h, frameRate, false /* isEncoder */);
+ public boolean checkTv() {
+ return MediaUtils.check(isTv(), "not a TV");
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 318aac2..78b5cfd 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -17,9 +17,8 @@
import android.content.pm.PackageManager;
+import android.cts.util.MediaUtils;
import android.hardware.Camera;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
@@ -309,36 +308,40 @@
}
public void testRecordingAudioInRawFormats() throws Exception {
- if (hasAmrnb()) {
- testRecordAudioInRawFormat(
+ int testsRun = 0;
+ if (hasAmrNb()) {
+ testsRun += testRecordAudioInRawFormat(
MediaRecorder.OutputFormat.AMR_NB,
MediaRecorder.AudioEncoder.AMR_NB);
}
- if (hasAmrwb()) {
- testRecordAudioInRawFormat(
+ if (hasAmrWb()) {
+ testsRun += testRecordAudioInRawFormat(
MediaRecorder.OutputFormat.AMR_WB,
MediaRecorder.AudioEncoder.AMR_WB);
}
- if (hasAcc()) {
- testRecordAudioInRawFormat(
+ if (hasAac()) {
+ testsRun += testRecordAudioInRawFormat(
MediaRecorder.OutputFormat.AAC_ADTS,
MediaRecorder.AudioEncoder.AAC);
}
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no audio codecs or microphone");
+ }
}
- private void testRecordAudioInRawFormat(
+ private int testRecordAudioInRawFormat(
int fileFormat, int codec) throws Exception {
-
if (!hasMicrophone()) {
- return;
+ return 0; // skip
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(fileFormat);
mMediaRecorder.setOutputFile(OUTPUT_PATH);
mMediaRecorder.setAudioEncoder(codec);
recordMedia(MAX_FILE_SIZE, mOutFile);
+ return 1;
}
public void testGetAudioSourceMax() throws Exception {
@@ -354,10 +357,8 @@
}
public void testRecorderAudio() throws Exception {
- if (!hasMicrophone()) {
- return;
- }
- if (!hasAmrnb()) {
+ if (!hasMicrophone() || !hasAmrNb()) {
+ MediaUtils.skipTest("no audio codecs or microphone");
return;
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -372,10 +373,8 @@
}
public void testOnInfoListener() throws Exception {
- if (!hasMicrophone()) {
- return;
- }
- if (!hasAmrnb()) {
+ if (!hasMicrophone() || !hasAmrNb()) {
+ MediaUtils.skipTest("no audio codecs or microphone");
return;
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
@@ -389,10 +388,8 @@
}
public void testSetMaxDuration() throws Exception {
- if (!hasMicrophone()) {
- return;
- }
- if (!hasAmrnb()) {
+ if (!hasMicrophone() || !hasAmrNb()) {
+ MediaUtils.skipTest("no audio codecs or microphone");
return;
}
testSetMaxDuration(RECORD_TIME_LONG_MS, RECORDED_DUR_TOLERANCE_MS);
@@ -430,14 +427,15 @@
}
public void testSetMaxFileSize() throws Exception {
- if (!hasMicrophone() || !hasCamera()) {
- return;
- }
testSetMaxFileSize(512 * 1024, 50 * 1024);
}
private void testSetMaxFileSize(
long fileSize, long tolerance) throws Exception {
+ if (!hasMicrophone() || !hasCamera() || !hasAmrNb() || !hasH264()) {
+ MediaUtils.skipTest("no microphone, camera, or codecs");
+ return;
+ }
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
@@ -466,10 +464,8 @@
}
public void testOnErrorListener() throws Exception {
- if (!hasMicrophone()) {
- return;
- }
- if (!hasAmrnb()) {
+ if (!hasMicrophone() || !hasAmrNb()) {
+ MediaUtils.skipTest("no audio codecs or microphone");
return;
}
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
@@ -505,31 +501,19 @@
PackageManager.FEATURE_MICROPHONE);
}
- private static boolean hasCodecForMimeType(String mimeType, boolean encoder) {
- MediaCodecList list = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
- for (MediaCodecInfo info : list.getCodecInfos()) {
- if (encoder != info.isEncoder()) {
- continue;
- }
-
- for (String type : info.getSupportedTypes()) {
- if (type.equalsIgnoreCase(mimeType)) {
- return true;
- }
- }
- }
- return false;
+ private static boolean hasAmrNb() {
+ return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_NB);
}
- private static boolean hasAmrnb() {
- return hasCodecForMimeType(MediaFormat.MIMETYPE_AUDIO_AMR_NB, false);
+ private static boolean hasAmrWb() {
+ return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AMR_WB);
}
- private static boolean hasAmrwb() {
- return hasCodecForMimeType(MediaFormat.MIMETYPE_AUDIO_AMR_WB, false);
+ private static boolean hasAac() {
+ return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_AUDIO_AAC);
}
- private static boolean hasAcc() {
- return hasCodecForMimeType(MediaFormat.MIMETYPE_AUDIO_AAC, false);
+ private static boolean hasH264() {
+ return MediaUtils.hasEncoder(MediaFormat.MIMETYPE_VIDEO_AVC);
}
}
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index 81c5113..aed0029 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -20,6 +20,7 @@
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.cts.util.MediaUtils;
import android.media.MediaCodec;
import android.media.MediaCodec.BufferInfo;
import android.media.MediaCodecInfo;
@@ -91,7 +92,7 @@
testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
- testExtractor(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
+ testExtractor(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz);
testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -183,24 +184,27 @@
public void testDecoder() throws Exception {
- testDecoder(R.raw.sinesweepogg);
- testDecoder(R.raw.sinesweepmp3lame);
- testDecoder(R.raw.sinesweepmp3smpb);
- testDecoder(R.raw.sinesweepm4a);
- testDecoder(R.raw.sinesweepflac);
- testDecoder(R.raw.sinesweepwav);
+ int testsRun =
+ testDecoder(R.raw.sinesweepogg) +
+ testDecoder(R.raw.sinesweepmp3lame) +
+ testDecoder(R.raw.sinesweepmp3smpb) +
+ testDecoder(R.raw.sinesweepm4a) +
+ testDecoder(R.raw.sinesweepflac) +
+ testDecoder(R.raw.sinesweepwav) +
- testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
- testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
- testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
- testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
- testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+ testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
+ testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
+ testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+ testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
+ testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no decoders found");
+ }
}
- private void testDecoder(int res) throws Exception {
- if (!supportsPlayback(res)) {
- Log.i(TAG, "SKIPPING testDecoder() resid=" + res + " Unsupported decorder.");
- return;
+ private int testDecoder(int res) throws Exception {
+ if (!MediaUtils.hasCodecsForResource(mContext, res)) {
+ return 0; // skip
}
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
@@ -215,6 +219,7 @@
Log.i("@@@", Arrays.toString(ndata));
assertEquals("number of samples differs", jdata.length, ndata.length);
assertTrue("different decoded data", Arrays.equals(jdata, ndata));
+ return 1;
}
private static int[] getDecodedData(FileDescriptor fd, long offset, long size)
@@ -378,17 +383,25 @@
throws IOException;
public void testVideoPlayback() throws Exception {
- testVideoPlayback(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
- testVideoPlayback(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
- testVideoPlayback(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_44100hz);
- testVideoPlayback(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
- testVideoPlayback(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+ int testsRun =
+ testVideoPlayback(
+ R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
+ testVideoPlayback(
+ R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
+ testVideoPlayback(
+ R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+ testVideoPlayback(
+ R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
+ testVideoPlayback(
+ R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
+ if (testsRun == 0) {
+ MediaUtils.skipTest("no decoders found");
+ }
}
- private void testVideoPlayback(int res) throws Exception {
- if (!supportsPlayback(res)) {
- Log.i(TAG, "SKIPPING testVideoPlayback() resid=" + res + " Unsupported decorder.");
- return;
+ private int testVideoPlayback(int res) throws Exception {
+ if (!MediaUtils.checkCodecsForResource(mContext, res)) {
+ return 0; // skip
}
AssetFileDescriptor fd = mResources.openRawResourceFd(res);
@@ -396,6 +409,7 @@
boolean ret = testPlaybackNative(mActivity.getSurfaceHolder().getSurface(),
fd.getParcelFileDescriptor().getFd(), fd.getStartOffset(), fd.getLength());
assertTrue("native playback error", ret);
+ return 1;
}
private static native boolean testPlaybackNative(Surface surface,
@@ -406,10 +420,10 @@
}
private void testMuxer(int res, boolean webm) throws Exception {
- if (!supportsPlayback(res)) {
- Log.i(TAG, "SKIPPING testMuxer() resid=" + res + " Unsupported decorder.");
- return;
+ if (!MediaUtils.checkCodecsForResource(mContext, res)) {
+ return; // skip
}
+
AssetFileDescriptor infd = mResources.openRawResourceFd(res);
File base = mContext.getExternalFilesDir(null);
diff --git a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
index 6198d5f..748cda7 100644
--- a/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/StreamingMediaPlayerTest.java
@@ -15,6 +15,8 @@
*/
package android.media.cts;
+import android.cts.util.MediaUtils;
+import android.media.MediaFormat;
import android.media.MediaPlayer;
import android.os.Looper;
import android.os.SystemClock;
@@ -23,7 +25,6 @@
import java.io.IOException;
-
/**
* Tests of MediaPlayer streaming capabilities.
*/
@@ -64,9 +65,8 @@
*/
// Streaming HTTP video from YouTube
public void testHTTP_H263_AMR_Video1() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "Skipping testHTTP_H263_AMR_Video1(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
@@ -77,9 +77,8 @@
+ "&key=test_key1&user=android-device-test", 176, 144);
}
public void testHTTP_H263_AMR_Video2() throws Exception {
- if (!hasH263(false)) {
- Log.i(TAG, "Skipping testHTTP_H263_AMR_Video2(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_H263, MediaFormat.MIMETYPE_AUDIO_AMR_NB)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
@@ -91,9 +90,8 @@
}
public void testHTTP_MPEG4SP_AAC_Video1() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testHTTP_MPEG4SP_AAC_Video1(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
@@ -104,9 +102,8 @@
+ "&key=test_key1&user=android-device-test", 176, 144);
}
public void testHTTP_MPEG4SP_AAC_Video2() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testHTTP_MPEG4SP_AAC_Video2(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
@@ -118,9 +115,8 @@
}
public void testHTTP_H264Base_AAC_Video1() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testHTTP_H264Base_AAC_Video1(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
@@ -131,9 +127,8 @@
+ "&key=test_key1&user=android-device-test", 640, 360);
}
public void testHTTP_H264Base_AAC_Video2() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testHTTP_H264Base_AAC_Video2(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
playVideoTest("http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
@@ -146,9 +141,8 @@
// Streaming HLS video from YouTube
public void testHLS() throws Exception {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testHLS(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
// Play stream for 60 seconds
@@ -202,9 +196,8 @@
stream_url = stream_url + "?" + CtsTestServer.NOLENGTH_POSTFIX;
}
- if (!supportsPlayback(stream_url)) {
- Log.i(TAG, "Failed to find codec for: '" + stream_url + "'. Skipping test.");
- return;
+ if (!MediaUtils.checkCodecsForPath(mContext, stream_url)) {
+ return; // skip
}
mMediaPlayer.setDataSource(stream_url);
@@ -294,25 +287,22 @@
}
public void testPlayHlsStream() throws Throwable {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testPlayHlsStream(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
localHlsTest("hls.m3u8", false, false);
}
public void testPlayHlsStreamWithQueryString() throws Throwable {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testPlayHlsStreamWithQueryString(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
localHlsTest("hls.m3u8", true, false);
}
public void testPlayHlsStreamWithRedirect() throws Throwable {
- if (!hasH264(false)) {
- Log.i(TAG, "Skipping testPlayHlsStreamWithRedirect(): No codec found.");
- return;
+ if (!MediaUtils.checkDecoder(MediaFormat.MIMETYPE_VIDEO_AVC)) {
+ return; // skip
}
localHlsTest("hls.m3u8", false, true);
}
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
index f78f5f8..4b5e0f0 100644
--- a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -37,12 +37,14 @@
import android.util.Pair;
import android.util.Range;
import android.util.Size;
+import android.view.Surface;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
@@ -57,6 +59,96 @@
private final boolean DEBUG = false;
+ class VideoStorage {
+ private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
+ private MediaFormat mFormat;
+
+ public VideoStorage() {
+ mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
+ }
+
+ public void setFormat(MediaFormat format) {
+ mFormat = format;
+ }
+
+ public void addBuffer(ByteBuffer buffer, BufferInfo info) {
+ ByteBuffer savedBuffer = ByteBuffer.allocate(info.size);
+ savedBuffer.put(buffer);
+ BufferInfo savedInfo = new BufferInfo();
+ savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
+ mStream.addLast(Pair.create(savedBuffer, savedInfo));
+ }
+
+ private void play(MediaCodec decoder, Surface surface) {
+ decoder.reset();
+ final Object condition = new Object();
+ final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
+ decoder.setCallback(new MediaCodec.Callback() {
+ public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
+ codec.releaseOutputBuffer(ix, info.size > 0);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ synchronized (condition) {
+ condition.notifyAll();
+ }
+ }
+ }
+ public void onInputBufferAvailable(MediaCodec codec, int ix) {
+ if (it.hasNext()) {
+ Pair<ByteBuffer, BufferInfo> el = it.next();
+ el.first.clear();
+ codec.getInputBuffer(ix).put(el.first);
+ BufferInfo info = el.second;
+ codec.queueInputBuffer(
+ ix, 0, info.size, info.presentationTimeUs, info.flags);
+ }
+ }
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ Log.i(TAG, "got codec exception", e);
+ fail("received codec error during decode" + e);
+ }
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.i(TAG, "got output format " + format);
+ }
+ });
+ decoder.configure(mFormat, surface, null /* crypto */, 0 /* flags */);
+ decoder.start();
+ synchronized (condition) {
+ try {
+ condition.wait();
+ } catch (InterruptedException e) {
+ fail("playback interrupted");
+ }
+ }
+ decoder.stop();
+ }
+
+ public void playAll(Surface surface) {
+ if (mFormat == null) {
+ Log.i(TAG, "no stream to play");
+ return;
+ }
+ String mime = mFormat.getString(MediaFormat.KEY_MIME);
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ MediaCodec codec = null;
+ try {
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ if (!caps.isFormatSupported(mFormat)) {
+ continue;
+ }
+ codec = MediaCodec.createByCodecName(info.getName());
+ } catch (IllegalArgumentException | IOException e) {
+ continue;
+ }
+ play(codec, surface);
+ codec.release();
+ }
+ }
+ }
+
abstract class VideoProcessorBase extends MediaCodec.Callback {
private static final String TAG = "VideoProcessorBase";
@@ -71,6 +163,10 @@
protected MediaFormat mDecFormat;
protected MediaCodec mDecoder, mEncoder;
+ private VideoStorage mEncodedStream;
+ protected int mFrameRate = 0;
+ protected int mBitRate = 0;
+
protected void open(String path) throws IOException {
mExtractor = new MediaExtractor();
if (path.startsWith("android.resource://")) {
@@ -89,6 +185,7 @@
break;
}
}
+ mEncodedStream = new VideoStorage();
assertTrue("file " + path + " has no video", mTrackIndex >= 0);
}
@@ -107,7 +204,7 @@
mDecoder.setCallback(this);
mEncoder.setCallback(this);
- MediaCodecInfo.VideoCapabilities encCaps =
+ VideoCapabilities encCaps =
mEncoder.getCodecInfo().getCapabilitiesForType(outMime).getVideoCapabilities();
if (!encCaps.isSizeSupported(width, height)) {
Log.i(TAG, videoEncName + " does not support size: " + width + "x" + height);
@@ -119,14 +216,24 @@
{
int maxWidth = encCaps.getSupportedWidths().getUpper();
int maxHeight = encCaps.getSupportedHeightsFor(maxWidth).getUpper();
- int maxRate =
- encCaps.getSupportedFrameRatesFor(maxWidth, maxHeight).getUpper().intValue();
- outFmt.setInteger(MediaFormat.KEY_FRAME_RATE, Math.min(30, maxRate));
- int bitRate = encCaps.getBitrateRange().clamp(
+ int frameRate = mFrameRate;
+ if (frameRate <= 0) {
+ int maxRate =
+ encCaps.getSupportedFrameRatesFor(maxWidth, maxHeight)
+ .getUpper().intValue();
+ frameRate = Math.min(30, maxRate);
+ }
+ outFmt.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+
+ int bitRate = mBitRate;
+ if (bitRate <= 0) {
+ bitRate = encCaps.getBitrateRange().clamp(
(int)(encCaps.getBitrateRange().getUpper() /
Math.sqrt(maxWidth * maxHeight / width / height)));
- Log.d(TAG, "max rate = " + maxRate + ", bit rate = " + bitRate);
+ }
outFmt.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+
+ Log.d(TAG, "frame rate = " + frameRate + ", bit rate = " + bitRate);
}
outFmt.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
outFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
@@ -189,6 +296,7 @@
if (DEBUG) Log.v(TAG, "encoder received output #" + ix
+ " (sz=" + info.size + ", f=" + info.flags
+ ", ts=" + info.presentationTimeUs + ")");
+ mEncodedStream.addBuffer(mEncoder.getOutputBuffer(ix), info);
if (!mCompleted) {
mEncoder.releaseOutputBuffer(ix, false);
if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
@@ -201,6 +309,19 @@
}
}
+ protected void saveEncoderFormat(MediaFormat format) {
+ mEncodedStream.setFormat(format);
+ }
+
+ public void playBack(Surface surface) {
+ mEncodedStream.playAll(surface);
+ }
+
+ public void setFrameAndBitRates(int frameRate, int bitRate) {
+ mFrameRate = frameRate;
+ mBitRate = bitRate;
+ }
+
public abstract boolean processLoop(
String path, String outMime, String videoEncName,
int width, int height, boolean optional);
@@ -379,6 +500,9 @@
@Override
public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+ if (mediaCodec == mEncoder) {
+ saveEncoderFormat(mediaFormat);
+ }
}
// next methods are synchronized on mCondition
@@ -595,6 +719,9 @@
@Override
public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+ if (mediaCodec == mEncoder) {
+ saveEncoderFormat(mediaFormat);
+ }
}
}
@@ -769,18 +896,38 @@
return test(width, height, true /* optional */, flexYUV);
}
+ public boolean testDetailed(
+ int width, int height, int frameRate, int bitRate, boolean flexYUV) {
+ return test(width, height, frameRate, bitRate, true /* optional */, flexYUV);
+ }
+
+ public boolean testSupport(int width, int height, int frameRate, int bitRate) {
+ return mCaps.areSizeAndRateSupported(width, height, frameRate) &&
+ mCaps.getBitrateRange().contains(bitRate);
+ }
+
private boolean test(int width, int height, boolean optional, boolean flexYUV) {
+ return test(width, height, 0 /* frameRate */, 0 /* bitRate */, optional, flexYUV);
+ }
+
+ private boolean test(int width, int height, int frameRate, int bitRate,
+ boolean optional, boolean flexYUV) {
Log.i(TAG, "testing " + mMime + " on " + mName + " for " + width + "x" + height
+ (flexYUV ? " flexYUV" : " surface"));
VideoProcessorBase processor =
flexYUV ? new VideoProcessor() : new SurfaceVideoProcessor();
- // We are using a resource URL as an example
- return processor.processLoop(
- SOURCE_URL, mMime, mName, width, height, optional);
- }
+ processor.setFrameAndBitRates(frameRate, bitRate);
+ // We are using a resource URL as an example
+ boolean success = processor.processLoop(
+ SOURCE_URL, mMime, mName, width, height, optional);
+ if (success) {
+ processor.playBack(getActivity().getSurfaceHolder().getSurface());
+ }
+ return success;
+ }
}
private Encoder[] googH265() { return goog(MediaFormat.MIMETYPE_VIDEO_HEVC); }
@@ -805,6 +952,21 @@
return encoders(mime, false /* goog */);
}
+ private Encoder[] combineArray(Encoder[] a, Encoder[] b) {
+ Encoder[] all = new Encoder[a.length + b.length];
+ System.arraycopy(a, 0, all, 0, a.length);
+ System.arraycopy(b, 0, all, a.length, b.length);
+ return all;
+ }
+
+ private Encoder[] h264() {
+ return combineArray(googH264(), otherH264());
+ }
+
+ private Encoder[] vp8() {
+ return combineArray(googVP8(), otherVP8());
+ }
+
private Encoder[] encoders(String mime, boolean goog) {
MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
ArrayList<Encoder> result = new ArrayList<Encoder>();
@@ -1164,6 +1326,89 @@
public void testOtherVP9Flex1080p() { specific(otherVP9(), 1920, 1080, true /* flex */); }
public void testOtherVP9Surf1080p() { specific(otherVP9(), 1920, 1080, false /* flex */); }
+ // Tests encoder profiles required by CDD.
+ // H264
+ public void testH264LowQualitySDSupport() {
+ support(h264(), 320, 240, 20, 384 * 1000);
+ }
+
+ public void testH264HighQualitySDSupport() {
+ support(h264(), 720, 480, 30, 2 * 1000000);
+ }
+
+ public void testH264FlexQVGA20fps384kbps() {
+ detailed(h264(), 320, 240, 20, 384 * 1000, true /* flex */);
+ }
+
+ public void testH264SurfQVGA20fps384kbps() {
+ detailed(h264(), 320, 240, 20, 384 * 1000, false /* flex */);
+ }
+
+ public void testH264Flex480p30fps2Mbps() {
+ detailed(h264(), 720, 480, 30, 2 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf480p30fps2Mbps() {
+ detailed(h264(), 720, 480, 30, 2 * 1000000, false /* flex */);
+ }
+
+ public void testH264Flex720p30fps4Mbps() {
+ detailed(h264(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf720p30fps4Mbps() {
+ detailed(h264(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+ }
+
+ public void testH264Flex1080p30fps10Mbps() {
+ detailed(h264(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf1080p30fps10Mbps() {
+ detailed(h264(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+ }
+
+ // VP8
+ public void testVP8LowQualitySDSupport() {
+ support(vp8(), 320, 180, 30, 800 * 1000);
+ }
+
+ public void testVP8HighQualitySDSupport() {
+ support(vp8(), 640, 360, 30, 2 * 1000000);
+ }
+
+ public void testVP8Flex180p30fps800kbps() {
+ detailed(vp8(), 320, 180, 30, 800 * 1000, true /* flex */);
+ }
+
+ public void testVP8Surf180p30fps800kbps() {
+ detailed(vp8(), 320, 180, 30, 800 * 1000, false /* flex */);
+ }
+
+ public void testVP8Flex360p30fps2Mbps() {
+ detailed(vp8(), 640, 360, 30, 2 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf360p30fps2Mbps() {
+ detailed(vp8(), 640, 360, 30, 2 * 1000000, false /* flex */);
+ }
+
+ public void testVP8Flex720p30fps4Mbps() {
+ detailed(vp8(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf720p30fps4Mbps() {
+ detailed(vp8(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+ }
+
+ public void testVP8Flex1080p30fps10Mbps() {
+ detailed(vp8(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf1080p30fps10Mbps() {
+ detailed(vp8(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+ }
+
private void minmin(Encoder[] encoders, boolean flexYUV) {
extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, false /* near */);
}
@@ -1244,4 +1489,43 @@
MediaUtils.skipTest("duplicate or unsupported resolution");
}
}
+
+ /* test size, frame rate and bit rate */
+ private void detailed(
+ Encoder[] encoders, int width, int height, int frameRate, int bitRate,
+ boolean flexYUV) {
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ boolean skipped = true;
+ for (Encoder encoder : encoders) {
+ if (encoder.testSupport(width, height, frameRate, bitRate)) {
+ skipped = false;
+ encoder.testDetailed(width, height, frameRate, bitRate, flexYUV);
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("unsupported resolution and rate");
+ }
+ }
+
+ /* test size and rate are supported */
+ private void support(Encoder[] encoders, int width, int height, int frameRate, int bitRate) {
+ boolean supported = false;
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ for (Encoder encoder : encoders) {
+ if (encoder.testSupport(width, height, frameRate, bitRate)) {
+ supported = true;
+ break;
+ }
+ }
+ if (!supported) {
+ fail("unsupported format " + width + "x" + height + " " +
+ frameRate + "fps " + bitRate + "bps");
+ }
+ }
}
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/NativeMediaTest.java b/tests/tests/mediastress/src/android/mediastress/cts/NativeMediaTest.java
index 2119d75..05145f5 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/NativeMediaTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/NativeMediaTest.java
@@ -17,11 +17,11 @@
import android.app.Instrumentation;
import android.content.Intent;
+import android.cts.util.MediaUtils;
import android.media.CamcorderProfile;
+import android.media.MediaFormat;
import android.media.MediaRecorder.AudioEncoder;
import android.media.MediaRecorder.VideoEncoder;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
import android.os.Environment;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
@@ -30,31 +30,11 @@
public class NativeMediaTest extends ActivityInstrumentationTestCase2<NativeMediaActivity> {
private static final String TAG = "NativeMediaTest";
- private static final String MIME_TYPE = "video/h264";
+ private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
private static final int VIDEO_CODEC = VideoEncoder.H264;
private static final int NUMBER_PLAY_PAUSE_REPEATITIONS = 10;
private static final long PLAY_WAIT_TIME_MS = 4000;
- private static boolean hasCodec(String mimeType) {
- int numCodecs = MediaCodecList.getCodecCount();
-
- for (int i = 0; i < numCodecs; i++) {
- MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
-
- if (!codecInfo.isEncoder()) {
- continue;
- }
-
- String[] types = codecInfo.getSupportedTypes();
- for (int j = 0; j < types.length; j++) {
- if (types[j].equalsIgnoreCase(mimeType)) {
- return true;
- }
- }
- }
- return false;
- }
-
public NativeMediaTest() {
super(NativeMediaActivity.class);
}
@@ -77,9 +57,8 @@
private void runPlayTest(int quality) throws InterruptedException {
// Don't run the test if the codec isn't supported.
- if (!hasCodec(MIME_TYPE)) {
- Log.w(TAG, "Codec " + MIME_TYPE + " not supported.");
- return;
+ if (!MediaUtils.checkDecoder(MIME_TYPE)) {
+ return; // skip
}
// Don't run the test if the quality level isn't supported.
if (quality != 0) {
diff --git a/tests/tests/net/src/android/net/cts/DnsTest.java b/tests/tests/net/src/android/net/cts/DnsTest.java
index 879a962..0377d04 100644
--- a/tests/tests/net/src/android/net/cts/DnsTest.java
+++ b/tests/tests/net/src/android/net/cts/DnsTest.java
@@ -16,6 +16,10 @@
package android.net.cts;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -34,6 +38,7 @@
private static final boolean DBG = false;
private static final String TAG = "DnsTest";
+ private static final String PROXY_NETWORK_TYPE = "PROXY";
/**
* @return true on success
@@ -71,6 +76,14 @@
// We should have at least one of the addresses to connect!
assertTrue(foundV4 || foundV6);
+ // Skip the rest of the test if the active network for watch is PROXY.
+ // TODO: Check NetworkInfo type in addition to type name once ag/601257 is merged.
+ if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)
+ && activeNetworkInfoIsProxy()) {
+ Log.i(TAG, "Skipping test because the active network type name is PROXY.");
+ return;
+ }
+
try {
addrs = InetAddress.getAllByName("ipv6.google.com");
} catch (UnknownHostException e) {}
@@ -241,4 +254,15 @@
Log.e(TAG, "bad URL in testDnsPerf: " + e.toString());
}
}
+
+ private boolean activeNetworkInfoIsProxy() {
+ ConnectivityManager cm = (ConnectivityManager)
+ getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ if (PROXY_NETWORK_TYPE.equals(info.getTypeName())) {
+ return true;
+ }
+
+ return false;
+ }
}
diff --git a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
index 49fc59c..e9bfb43 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -130,6 +130,7 @@
// Check the response is an echo reply.
byte[] response = new byte[bytesRead];
+ responseBuffer.flip();
responseBuffer.get(response, 0, bytesRead);
assertEquals((byte) 0x81, response[0]);
diff --git a/tests/tests/opengl/src/android/opengl/cts/ProgramTest.java b/tests/tests/opengl/src/android/opengl/cts/ProgramTest.java
index 85009d2..dab2ef1 100644
--- a/tests/tests/opengl/src/android/opengl/cts/ProgramTest.java
+++ b/tests/tests/opengl/src/android/opengl/cts/ProgramTest.java
@@ -32,7 +32,9 @@
intent.putExtra(OpenGLES20NativeActivityOne.EXTRA_VIEW_TYPE, viewType);
intent.putExtra(OpenGLES20NativeActivityOne.EXTRA_VIEW_INDEX, viewIndex);
setActivityIntent(intent);
- return getActivity();
+ OpenGLES20ActivityOne activity = getActivity();
+ assertTrue(activity.waitForFrameDrawn());
+ return activity;
}
public void test_glAttachShader_program() throws Throwable {
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 6069ee6..3002ca3 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -28,7 +28,8 @@
public class BuildVersionTest extends TestCase {
private static final String LOG_TAG = "BuildVersionTest";
- private static final Set<String> EXPECTED_RELEASES = new HashSet<String>(Arrays.asList("5.0"));
+ private static final Set<String> EXPECTED_RELEASES =
+ new HashSet<String>(Arrays.asList("5.0.1", "5.0.2"));
private static final int EXPECTED_SDK = 21;
private static final String EXPECTED_BUILD_VARIANT = "user";
private static final String EXPECTED_TAG = "release-keys";
diff --git a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
index 516db56..538472e 100644
--- a/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
+++ b/tests/tests/print/src/android/print/cts/PrintDocumentAdapterContractTest.java
@@ -59,6 +59,9 @@
public class PrintDocumentAdapterContractTest extends BasePrintTest {
public void testNoPrintOptionsOrPrinterChange() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -175,6 +178,9 @@
}
public void testNoPrintOptionsOrPrinterChangeCanceled() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -265,6 +271,9 @@
}
public void testPrintOptionsChangeAndNoPrinterChange() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -439,6 +448,9 @@
}
public void testPrintOptionsChangeAndPrinterChange() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -594,6 +606,9 @@
public void testPrintOptionsChangeAndNoPrinterChangeAndContentChange()
throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -718,6 +733,9 @@
}
public void testNewPrinterSupportsSelectedPrintOptions() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -820,6 +838,9 @@
}
public void testNothingChangesAllPagesWrittenFirstTime() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -941,6 +962,9 @@
}
public void testCancelLongRunningLayout() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1015,6 +1039,9 @@
}
public void testCancelLongRunningWrite() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1111,6 +1138,9 @@
}
public void testFailedLayout() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1177,6 +1207,9 @@
}
public void testFailedWrite() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1259,6 +1292,9 @@
}
public void testRequestedPagesNotWritten() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1348,6 +1384,9 @@
}
public void testLayoutCallbackNotCalled() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
@@ -1411,6 +1450,9 @@
}
public void testWriteCallbackNotCalled() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Configure the print services.
FirstPrintService.setCallbacks(createFirstMockPrintServiceCallbacks());
SecondPrintService.setCallbacks(createSecondMockPrintServiceCallbacks());
diff --git a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
index b092044..7ea09e5 100644
--- a/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
+++ b/tests/tests/print/src/android/print/cts/PrinterDiscoverySessionLifecycleTest.java
@@ -61,6 +61,9 @@
private static final String SECOND_PRINTER_LOCAL_ID = "second_printer";
public void testNormalLifecycle() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Create the session callbacks that we will be checking.
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
createFirstMockPrinterDiscoverySessionCallbacks();
@@ -155,6 +158,9 @@
}
public void testStartPrinterDiscoveryWithHistoricalPrinters() throws Exception {
+ if (!supportsPrinting()) {
+ return;
+ }
// Create the session callbacks that we will be checking.
final PrinterDiscoverySessionCallbacks firstSessionCallbacks =
createFirstMockPrinterDiscoverySessionCallbacks();
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index a8f547b..2f5edd0 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -352,6 +352,7 @@
Events.SYNC_DATA2,
Events.SYNC_DATA3,
Events.SYNC_DATA4,
+ Events.MUTATORS,
};
// @formatter:on
@@ -3275,6 +3276,67 @@
CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
}
+ @MediumTest
+ public void testMutatorSetCorrectly() {
+ String account = "ec_account";
+ String packageName = "com.android.cts.provider";
+ int seed = 0;
+
+ // Clean up just in case
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+
+ String mutator;
+ Cursor cursor;
+ ContentValues values = new ContentValues();
+ final long calendarId = createAndVerifyCalendar(account, seed++, null);
+
+ // Verify mutator is set to the package, via:
+ // Create:
+ final long eventId = createAndVerifyEvent(account, seed, calendarId, false, null);
+ final Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertEquals(packageName, mutator);
+
+ // Edit:
+ // First clear the mutator column
+ values.clear();
+ values.putNull(Events.MUTATORS);
+ mContentResolver.update(asSyncAdapter(uri, account, CTS_TEST_TYPE), values, null, null);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertNull(mutator);
+ // Now edit the event and verify the mutator column
+ values.clear();
+ values.put(Events.TITLE, "New title");
+ mContentResolver.update(uri, values, null, null);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertEquals(packageName, mutator);
+
+ // Clean up the event
+ assertEquals(1, EventHelper.deleteEventAsSyncAdapter(mContentResolver, uri, account));
+
+ // Delete:
+ // First create as sync adapter
+ final long eventId2 = createAndVerifyEvent(account, seed, calendarId, true, null);
+ final Uri uri2 = ContentUris.withAppendedId(Events.CONTENT_URI, eventId2);
+ // Now delete the event and verify
+ values.clear();
+ values.put(Events.MUTATORS, packageName);
+ removeAndVerifyEvent(uri2, values, account);
+
+
+ // delete the calendar
+ removeAndVerifyCalendar(account, calendarId);
+ }
+
/**
* Acquires the set of instances that appear between the specified start and end points.
*
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
index 1880132..8faeb22 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
@@ -24,11 +24,17 @@
float cf1[] = {0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f};
float cf2[] = {0.f, -1.f, 0.f, -1.f, 5.f, -1.f, 0.f, -1.f, 0.f};
+ float irCoeff1 = 3.1415927f;
+ float irCoeff2 = -irCoeff1;
+ float cf3[] = {0.f, irCoeff1, 0.f, irCoeff2, 1.f, irCoeff2, 0.f, irCoeff1, 0.f};
+
Element e = makeElement(dt, vecSize);
System.gc();
makeBuffers(w, h, e);
+ mVerify.set_gAllowedIntError(1);
+
ScriptIntrinsicConvolve3x3 si = ScriptIntrinsicConvolve3x3.create(mRS, e);
si.setCoefficients(cf1);
si.setInput(mAllocSrc);
@@ -116,6 +122,44 @@
}
//android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
+ si.setCoefficients(cf3);
+ sr.set_gCoeffs(cf3);
+ si.forEach(mAllocRef, sc);
+ if (dt == Element.DataType.UNSIGNED_8) {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_convolve_U4(mAllocDst, sc);
+ break;
+ case 3:
+ sr.forEach_convolve_U3(mAllocDst, sc);
+ break;
+ case 2:
+ sr.forEach_convolve_U2(mAllocDst, sc);
+ break;
+ case 1:
+ sr.forEach_convolve_U1(mAllocDst, sc);
+ break;
+ }
+ } else {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_convolve_F4(mAllocDst, sc);
+ break;
+ case 3:
+ sr.forEach_convolve_F3(mAllocDst, sc);
+ break;
+ case 2:
+ sr.forEach_convolve_F2(mAllocDst, sc);
+ break;
+ case 1:
+ sr.forEach_convolve_F1(mAllocDst, sc);
+ break;
+ }
+ }
+ //android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
+ mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
mRS.finish();
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
index ee92651..0753c62 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
@@ -85,13 +85,24 @@
-1.f, 0.f, 0.f, 0.f, -1.f,
-1.f, -1.f, -1.f, -1.f, -1.f};
+ float irCoeff1 = 3.1415927f;
+ float irCoeff2 = -irCoeff1;
+ float cf3[] = {irCoeff1, -1.f, -1.f, -1.f, irCoeff2,
+ irCoeff1, 0.f, 0.f, 0.f, irCoeff2,
+ irCoeff1, 0.f, 7.f, 0.f, irCoeff2,
+ irCoeff1, 0.f, 0.f, 0.f, irCoeff2,
+ irCoeff1, -1.f, -1.f, -1.f, irCoeff2};
+
Element e = makeElement(dt, vecSize);
makeBuffers(w, h, e);
+ mVerify.set_gAllowedIntError(1);
+
ScriptIntrinsicConvolve5x5 si = ScriptIntrinsicConvolve5x5.create(mRS, e);
ScriptC_intrinsic_convolve5x5 sr = new ScriptC_intrinsic_convolve5x5(mRS);
test5(sr, si, e, cf1, "test convolve", 1, w, h, sc);
test5(sr, si, e, cf2, "test convolve", 2, w, h, sc);
+ test5(sr, si, e, cf3, "test convolve", 3, w, h, sc);
}
public void test_U8_4() {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
index 9f9aa2b..966dbdd 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
@@ -66,7 +66,7 @@
+ convert_float4(rsGetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23]
+ convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar4(p0);
}
@@ -113,7 +113,7 @@
+ convert_float3(rsGetElementAt_uchar3(gIn, x3, y4)) * gCoeffs[23]
+ convert_float3(rsGetElementAt_uchar3(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar3(p0);
}
@@ -160,7 +160,7 @@
+ convert_float2(rsGetElementAt_uchar2(gIn, x3, y4)) * gCoeffs[23]
+ convert_float2(rsGetElementAt_uchar2(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar2(p0);
}
@@ -207,7 +207,7 @@
+ (float)(rsGetElementAt_uchar(gIn, x3, y4)) * gCoeffs[23]
+ (float)(rsGetElementAt_uchar(gIn, x4, y4)) * gCoeffs[24];
- return clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ return clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
}
float4 __attribute__((kernel)) convolve_F4(uint32_t x, uint32_t y) {
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index da098f3..de7c15e 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -28,9 +28,11 @@
"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",
+ "D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0",
"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",
+ "69:69:56:2E:40:80:F4:24:A1:E7:19:9F:14:BA:F3:EE:58:AB:6A:BB",
"92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F",
"75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
"40:9D:4B:D9:17:B5:5C:27:B6:9B:64:CB:98:22:44:0D:CD:09:B8:89",
@@ -49,6 +51,7 @@
"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",
+ "1F:24:C6:30:CD:A4:18:EF:20:69:FF:AD:4F:DD:5F:46:3A:1B:69:AA",
"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",
"74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
@@ -56,6 +59,7 @@
"3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F",
"A3:F1:33:3F:E2:42:BF:CF:C5:D1:4E:8F:39:42:98:40:68:10:D1:A0",
"5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
+ "2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E",
"A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36",
"59:22:A1:E1:5A:EA:16:35:21:F8:98:39:6A:46:46:B0:44:1B:0F:A9",
"D4:DE:20:D0:5E:66:FC:53:FE:1A:50:88:2C:78:DB:28:52:CA:E4:74",
@@ -155,6 +159,7 @@
"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",
+ "AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
"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",
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
index 661b175..7b1d84c 100644
--- a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -52,51 +52,6 @@
}
/**
- * Tests the SmsManager.updateMmsDownloadStatus() API. This makes a call to
- * updateMmsDownloadStatus() API and expects a SecurityException since the test apk is not
- * signed by a certificate on the SIM.
- */
- public void testUpdateMmsDownloadStatus() {
- try {
- if (isSimCardPresent()) {
- SmsManager.getDefault().updateMmsDownloadStatus(null, 0, 0, null);
- fail("Expected SecurityException. App doesn't have carrier privileges.");
- }
- } catch (SecurityException expected) {
- }
- }
-
- /**
- * Tests the SmsManager.updateMmsSendStatus() API. This makes a call to updateMmsSendStatus()
- * API and expects a SecurityException since the test apk is not signed by a certificate on the
- * SIM.
- */
- public void testUpdateMmsSendStatus() {
- try {
- if (isSimCardPresent()) {
- SmsManager.getDefault().updateMmsSendStatus(null, 0, TEST_PDU, 0, null);
- fail("Expected SecurityException. App doesn't have carrier privileges.");
- }
- } catch (SecurityException expected) {
- }
- }
-
- /**
- * Tests the SmsManager.updateSmsSendStatus() API. This makes a call to updateSmsSendStatus()
- * API and expects a SecurityException since the test apk is not signed by a certificate on the
- * SIM.
- */
- public void testUpdateSmsSendStatus() {
- try {
- if (isSimCardPresent()) {
- SmsManager.getDefault().updateSmsSendStatus(0, false);
- fail("Expected SecurityException. App doesn't have carrier privileges.");
- }
- } catch (SecurityException expected) {
- }
- }
-
- /**
* Tests the TelephonyManager.setLine1NumberForDisplay() API. This makes a call to
* setLine1NumberForDisplay() API and expects a SecurityException since the test apk is not
* signed by a certificate on the SIM.
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index dc5d30a..d2b3ddf 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -75,6 +75,16 @@
android:resource="@xml/stub_tv_input_service" />
</service>
+ <service android:name="android.media.tv.cts.HardwareSessionTest$HardwareProxyTvInputService"
+ android:permission="android.permission.BIND_TV_INPUT">
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+ <meta-data android:name="android.media.tv.input"
+ android:resource="@xml/stub_tv_input_service" />
+ </service>
+
+
<activity android:name="android.media.tv.cts.TvViewStubActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
new file mode 100644
index 0000000..75cf28d
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.TvView;
+import android.media.tv.cts.HardwareSessionTest.HardwareProxyTvInputService.CountingSession;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.cts.tv.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test {@link android.media.tv.TvInputService.HardwareSession}.
+ */
+public class HardwareSessionTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+ /** The maximum time to wait for an operation. */
+ private static final long TIME_OUT = 15000L;
+
+ private TvView mTvView;
+ private Activity mActivity;
+ private Instrumentation mInstrumentation;
+ private TvInputManager mManager;
+ private TvInputInfo mStubInfo;
+ private final List<TvInputInfo> mPassthroughInputList = new ArrayList<>();
+
+ public HardwareSessionTest() {
+ super(TvViewStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ mActivity = getActivity();
+ mInstrumentation = getInstrumentation();
+ mTvView = (TvView) mActivity.findViewById(R.id.tvview);
+ mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
+ for (TvInputInfo info : mManager.getTvInputList()) {
+ if (info.getServiceInfo().name.equals(HardwareProxyTvInputService.class.getName())) {
+ mStubInfo = info;
+ }
+ if (info.isPassthroughInput()) {
+ mPassthroughInputList.add(info);
+ }
+ }
+ assertNotNull(mStubInfo);
+ }
+
+ public void testHardwareProxyTvInputService() throws Throwable {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ for (final TvInputInfo info : mPassthroughInputList) {
+ verifyCommandTuneAndHardwareVideoAvailable(info);
+ }
+ }
+
+ public void verifyCommandTuneAndHardwareVideoAvailable(TvInputInfo passthroughInfo) throws
+ Throwable {
+ HardwareProxyTvInputService.sHardwareInputId = passthroughInfo.getId();
+ Uri fakeChannelUri = TvContract.buildChannelUri(0);
+ mTvView.tune(mStubInfo.getId(), fakeChannelUri);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = HardwareProxyTvInputService.sSession;
+ return session != null && session.mTuneCount > 0
+ && session.mHardwareVideoAvailableCount > 0;
+ }
+ }.run();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTvView.reset();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ HardwareProxyTvInputService.sSession = null;
+ }
+
+ public static class HardwareProxyTvInputService extends TvInputService {
+ static String sHardwareInputId;
+ static CountingSession sSession;
+
+ @Override
+ public Session onCreateSession(String inputId) {
+ sSession = new CountingSession(this);
+ return sSession;
+ }
+
+ public static class CountingSession extends HardwareSession {
+ public volatile int mTuneCount;
+ public volatile int mHardwareVideoAvailableCount;
+
+ CountingSession(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onRelease() {
+ }
+
+ @Override
+ public void onSetCaptionEnabled(boolean enabled) {
+ }
+
+ @Override
+ public String getHardwareInputId() {
+ return sHardwareInputId;
+ }
+
+ @Override
+ public void onSetStreamVolume(float volume) {
+ }
+
+ @Override
+ public boolean onTune(Uri channelUri) {
+ mTuneCount++;
+ return true;
+ }
+
+ @Override
+ public void onHardwareVideoAvailable() {
+ mHardwareVideoAvailableCount++;
+ }
+ }
+ }
+}
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
index 1cd1b21..68c9cd1 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_layout.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/test_root"
android:layout_width="200px"
android:layout_height="200px"
android:clipChildren="false">
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
index 0f254d4..71f4b0c 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_square.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="100px"
- android:layout_height="100px"
- android:background="@drawable/blue_padded_square"/>
+ android:id="@+id/test_root"
+ android:layout_width="100px"
+ android:layout_height="100px"
+ android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index e64c4e9..b570df8 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -13,11 +13,11 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
+ android:id="@+id/test_root"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<View android:layout_width="100px"
android:layout_height="100px"
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 1ae3e38..2d2d189 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
+ android:id="@+id/test_root"
+ android:layout_width="180px"
+ android:layout_height="180px"
android:background="#f00" />
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java
index 978dc0b..36be5f0f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/ExactComparer.java
@@ -41,9 +41,11 @@
for (int x = 0 ; x < width ; x++) {
int index = indexFromXAndY(x, y, stride, offset);
if (ideal[index] != given[index]) {
- Log.d(TAG, "Failure on position x = " + x + " y = " + y);
- Log.d(TAG, "Expected color : " + Integer.toHexString(ideal[index]) +
- " given color : " + Integer.toHexString(given[index]));
+ if (count < 50) {
+ Log.d(TAG, "Failure on position x = " + x + " y = " + y);
+ Log.d(TAG, "Expected color : " + Integer.toHexString(ideal[index]) +
+ " given color : " + Integer.toHexString(given[index]));
+ }
count++;
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index ab809f4..0bdcc9b 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -38,30 +38,33 @@
public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
- boolean res = true;
+ int failCount = 0;
int[] differenceMap = new int[bitmap.length];
for (int y = 0 ; y < height ; y++) {
for (int x = 0 ; x < width ; x++) {
int index = indexFromXAndY(x, y, stride, offset);
int expectedColor = getExpectedColor(x, y);
if (!verifyPixel(bitmap[index], expectedColor)) {
- Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
- + " received : " + Integer.toHexString(bitmap[index])
- + " at position (" + x + "," + y + ")");
- res = false;
+ if (failCount < 50) {
+ Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+ + " received : " + Integer.toHexString(bitmap[index])
+ + " at position (" + x + "," + y + ")");
+ }
+ failCount++;
differenceMap[index] = FAIL_COLOR;
} else {
differenceMap[index] = PASS_COLOR;
}
}
}
- if (!res) {
+ boolean success = failCount == 0;
+ if (!success) {
mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
mDifferenceBitmap.setPixels(differenceMap, offset, stride, 0, 0,
ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
}
- return res;
+ return success;
}
protected boolean verifyPixel(int color, int expectedColor) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 0ba0f69..4667ee9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -15,32 +15,26 @@
*/
package android.uirendering.cts.testclasses;
+import android.graphics.Color;
+import android.graphics.Rect;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.bitmapverifiers.RectVerifier;
import com.android.cts.uirendering.R;
import android.test.suitebuilder.annotation.SmallTest;
-import android.uirendering.cts.bitmapcomparers.BitmapComparer;
-import android.uirendering.cts.bitmapcomparers.ExactComparer;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
-
-/**
- * Created to see how custom views made with XML and programatic code will work.
- */
public class LayoutTests extends ActivityTestBase {
- private BitmapComparer mBitmapComparer;
-
- public LayoutTests() {
- mBitmapComparer = new ExactComparer();
- }
-
@SmallTest
public void testSimpleRedLayout() {
- createTest().addLayout(R.layout.simple_red_layout, null).runWithComparer(mBitmapComparer);
+ createTest().addLayout(R.layout.simple_red_layout, null, false).runWithVerifier(
+ new ColorVerifier(Color.RED));
}
@SmallTest
public void testSimpleRectLayout() {
- createTest().addLayout(R.layout.simple_rect_layout, null).runWithComparer(mBitmapComparer);
+ createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
+ new RectVerifier(Color.WHITE, Color.BLUE, new Rect(0, 0, 100, 100)));
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index 1acdc20..da2db48 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -62,7 +62,7 @@
};
// TODO: attempt to reduce
- static final int TOLERANCE = 10;
+ static final int TOLERANCE = 16;
static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 052b251..386c015 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -38,7 +38,7 @@
*/
public abstract class ActivityTestBase extends
ActivityInstrumentationTestCase2<DrawActivity> {
- public static final String TAG_NAME = "ActivityTestBase";
+ public static final String TAG = "ActivityTestBase";
public static final boolean DEBUG = false;
public static final boolean USE_RS = false;
public static final int TEST_WIDTH = 180;
@@ -97,7 +97,7 @@
for (TestCase testCase : testCases) {
if (!testCase.wasTestRan) {
- Log.w(TAG_NAME, getName() + " not all of the tests were ran");
+ Log.w(TAG, getName() + " not all of the tests ran");
break;
}
}
@@ -115,6 +115,13 @@
getActivity().runOnUiThread(finishRunnable);
}
+ static int[] getBitmapPixels(Bitmap bitmap) {
+ int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(),
+ 0, 0, bitmap.getWidth(), bitmap.getHeight());
+ return pixels;
+ }
+
public Bitmap takeScreenshot() {
getInstrumentation().waitForIdleSync();
Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
@@ -124,7 +131,7 @@
bitmap2 = bitmap1;
bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
count++;
- } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(bitmap2.mBuffer, bitmap1.mBuffer));
+ } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
return bitmap1;
}
@@ -213,6 +220,11 @@
* every test case is tested against it.
*/
public void runWithComparer(BitmapComparer bitmapComparer) {
+ if (getActivity().getOnWatch()) {
+ Log.d(TAG, getName() + "skipped");
+ return;
+ }
+
if (mTestCases.size() == 0) {
throw new IllegalStateException("Need at least one test to run");
}
@@ -231,6 +243,11 @@
* the verifier given.
*/
public void runWithVerifier(BitmapVerifier bitmapVerifier) {
+ if (getActivity().getOnWatch()) {
+ Log.d(TAG, getName() + "skipped");
+ return;
+ }
+
if (mTestCases.size() == 0) {
throw new IllegalStateException("Need at least one test to run");
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 4d4a012..166b6ff 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -17,15 +17,16 @@
import android.annotation.Nullable;
import android.app.Activity;
-import android.content.Context;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
-import android.util.AttributeSet;
import android.view.View;
import android.view.ViewTreeObserver;
import android.webkit.WebView;
+import com.android.cts.uirendering.R;
+
/**
* A generic activity that uses a view specified by the user.
*/
@@ -36,16 +37,17 @@
private Handler mHandler;
private View mView;
+ private boolean mOnWatch;
public void onCreate(Bundle bundle){
super.onCreate(bundle);
mHandler = new RenderSpecHandler();
+ int uiMode = getResources().getConfiguration().uiMode;
+ mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
}
- @Override
- public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
- mView = parent;
- return super.onCreateView(parent, name, context, attrs);
+ public boolean getOnWatch() {
+ return mOnWatch;
}
public void enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
@@ -85,6 +87,10 @@
switch (message.what) {
case LAYOUT_MSG: {
setContentView(message.arg1);
+ mView = findViewById(R.id.test_root);
+ if (mView == null) {
+ throw new IllegalStateException("test_root failed to inflate");
+ }
} break;
case CANVAS_MSG: {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
index 41e255b..8dd98b0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/util/BitmapDumper.java
@@ -99,9 +99,25 @@
saveFile(className, testName, SINGULAR_FILE_NAME, bitmap);
}
+ private static void logIfBitmapSolidColor(String bitmapName, Bitmap bitmap) {
+ int firstColor = bitmap.getPixel(0, 0);
+ for (int x = 0; x < bitmap.getWidth(); x++) {
+ for (int y = 0; y < bitmap.getHeight(); y++) {
+ if (bitmap.getPixel(x, y) != firstColor) {
+ return;
+ }
+ }
+ }
+
+ Log.w(TAG, String.format("%s entire bitmap color is %x", bitmapName, firstColor));
+ }
+
private static void saveFile(String className, String testName, String fileName, Bitmap bitmap) {
- Log.d(TAG, "Saving file : " + testName + "_" + fileName + " in directory : " + className);
- File file = new File(CAPTURE_SUB_DIRECTORY + className, testName + "_" + fileName);
+ String bitmapName = testName + "_" + fileName;
+ Log.d(TAG, "Saving file : " + bitmapName + " in directory : " + className);
+ logIfBitmapSolidColor(bitmapName, bitmap);
+
+ File file = new File(CAPTURE_SUB_DIRECTORY + className, bitmapName);
FileOutputStream fileStream = null;
try {
fileStream = new FileOutputStream(file);
diff --git a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
index fb44334..e8f0cab 100644
--- a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
@@ -144,7 +144,7 @@
public void testGuessFileName() {
String url = "ftp://example.url/test";
- assertEquals("test.jpeg", URLUtil.guessFileName(url, null, "image/jpeg"));
+ assertEquals("test.jpg", URLUtil.guessFileName(url, null, "image/jpeg"));
assertEquals("test.bin", URLUtil.guessFileName(url, null, "application/octet-stream"));
}
diff --git a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
index 41018a9..9589fec 100644
--- a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
@@ -30,28 +30,10 @@
* Stub activity for testing {@link PopupWindow}
*/
public class MockPopupWindowCtsActivity extends Activity {
- private boolean isFirstRun = true;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Window window = getWindow();
- final View decor = window.getDecorView();
- decor.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
- @Override
- public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
- if (isFirstRun) {
- if (insets.isRound()) {
- decor.setPadding(decor.getPaddingLeft(), decor.getPaddingTop(),
- decor.getPaddingRight(),
- decor.getPaddingBottom() + insets.getSystemWindowInsetBottom());
- }
- isFirstRun = false;
- setContentView(R.layout.popupwindow);
- }
- return insets.consumeSystemWindowInsets();
- }
- });
+ setContentView(R.layout.popupwindow);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index e1742c8..c14bb03 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -273,18 +273,21 @@
}
public void testShowAtLocation() {
- int[] viewInWindowXY = new int[2];
- int[] viewOnScreenXY = new int[2];
+ int[] popupContentViewInWindowXY = new int[2];
+ int[] popupContentViewOnScreenXY = new int[2];
mPopupWindow = createPopupWindow(createPopupContent());
+ // Do not attach within the decor; we will be measuring location
+ // with regard to screen coordinates.
+ mPopupWindow.setAttachedInDecor(false);
final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
final int xOff = 10;
final int yOff = 21;
assertFalse(mPopupWindow.isShowing());
- mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
- assertEquals(0, viewInWindowXY[0]);
- assertEquals(0, viewInWindowXY[1]);
+ mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+ assertEquals(0, popupContentViewInWindowXY[0]);
+ assertEquals(0, popupContentViewInWindowXY[1]);
mInstrumentation.runOnMainSync(new Runnable() {
public void run() {
@@ -294,12 +297,12 @@
mInstrumentation.waitForIdleSync();
assertTrue(mPopupWindow.isShowing());
- mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
- mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY);
- assertTrue(viewInWindowXY[0] >= 0);
- assertTrue(viewInWindowXY[1] >= 0);
- assertEquals(viewInWindowXY[0] + xOff, viewOnScreenXY[0]);
- assertEquals(viewInWindowXY[1] + yOff, viewOnScreenXY[1]);
+ mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+ mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY);
+ assertTrue(popupContentViewInWindowXY[0] >= 0);
+ assertTrue(popupContentViewInWindowXY[1] >= 0);
+ assertEquals(popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]);
+ assertEquals(popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]);
dismissPopup();
}
@@ -453,6 +456,9 @@
mInstrumentation.runOnMainSync(new Runnable() {
public void run() {
mPopupWindow = createPopupWindow(createPopupContent());
+ // Do not attach within the decor; we will be measuring location
+ // with regard to screen coordinates.
+ mPopupWindow.setAttachedInDecor(false);
}
});
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index c8211f6..5259736 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -404,8 +404,8 @@
// exceptional
try {
tableLayout.addView(null);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
}
@@ -445,8 +445,8 @@
try {
tableLayout.addView(null, -1);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
}
@@ -477,8 +477,8 @@
try {
tableLayout.addView(null, new TableLayout.LayoutParams(200, 300));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
try {
@@ -540,8 +540,8 @@
try {
tableLayout.addView(null, -1, new TableLayout.LayoutParams(200, 300));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
try {
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
index 1eb4acb..62c268d 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoConstants.java
@@ -68,4 +68,5 @@
public static final String SERIAL_NUMBER = "deviceID";
public static final String STORAGE_DEVICES = "storage_devices";
public static final String MULTI_USER = "multi_user";
+ public static final String ENCRYPTED = "encrypted";
}
diff --git a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
index 19349e5..52ddfe9 100644
--- a/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
+++ b/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/DeviceInfoInstrument.java
@@ -149,6 +149,9 @@
// Multi-user support
addResult(MULTI_USER, getMultiUserInfo());
+ // Encrypted
+ addResult(ENCRYPTED, getEncrypted());
+
finish(Activity.RESULT_OK, mResults);
}
@@ -394,4 +397,29 @@
return "unknown";
}
+
+ private static String getProperty(String property)
+ throws IOException {
+ Process process = new ProcessBuilder("getprop", property).start();
+ Scanner scanner = null;
+ String line = "";
+ try {
+ scanner = new Scanner(process.getInputStream());
+ line = scanner.nextLine();
+ } finally {
+ if (scanner != null) {
+ scanner.close();
+ }
+ }
+ return line;
+ }
+
+ private int getEncrypted() {
+ try {
+ return "encrypted".equals(getProperty("ro.crypto.state")) ? 1 : 0;
+ } catch (IOException e) {
+ }
+
+ return 0;
+ }
}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index e7ab217..7e8a19e 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
@Option(name="cts-install-path", description="the path to the cts installation to use")
private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
- public static final String CTS_BUILD_VERSION = "5.0_r1.92";
+ public static final String CTS_BUILD_VERSION = "5.0_r1.94";
/**
* {@inheritDoc}