Merge "CTS Tests for DreamService#onWindowStartingActionMode" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index ae6c16c..c66341d 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -167,7 +167,6 @@
CtsUtilTestCases \
CtsViewTestCases \
CtsWebkitTestCases \
- CtsWebGLTestCases \
CtsWidgetTestCases
# All APKs that need to be scanned by the coverage utilities.
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index f6d2e2d..bc77a62 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -70,7 +70,8 @@
else:
return float(r["numerator"]) / float(r["denominator"])
-def manual_capture_request(sensitivity, exp_time, linear_tonemap=False):
+def manual_capture_request(
+ sensitivity, exp_time, linear_tonemap=False, props=None):
"""Return a capture request with everything set to manual.
Uses identity/unit color correction, and the default tonemap curve.
@@ -82,6 +83,9 @@
with.
linear_tonemap: [Optional] whether a linear tonemap should be used
in this request.
+ props: [Optional] the object returned from
+ its.device.get_camera_properties(). Must present when
+ linear_tonemap is True.
Returns:
The default manual capture request, ready to be passed to the
@@ -105,10 +109,20 @@
"android.shading.mode": 1
}
if linear_tonemap:
- req["android.tonemap.mode"] = 0
- req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
- req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
- req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
+ assert(props is not None)
+ #CONTRAST_CURVE mode
+ if 0 in props["android.tonemap.availableToneMapModes"]:
+ req["android.tonemap.mode"] = 0
+ req["android.tonemap.curveRed"] = [0.0,0.0, 1.0,1.0]
+ req["android.tonemap.curveGreen"] = [0.0,0.0, 1.0,1.0]
+ req["android.tonemap.curveBlue"] = [0.0,0.0, 1.0,1.0]
+ #GAMMA_VALUE mode
+ elif 3 in props["android.tonemap.availableToneMapModes"]:
+ req["android.tonemap.mode"] = 3
+ req["android.tonemap.gamma"] = 1.0
+ else:
+ print "Linear tonemap is not supported"
+ assert(False)
return req
def auto_capture_request():
diff --git a/apps/CameraITS/tests/scene1/test_crop_region_raw.py b/apps/CameraITS/tests/scene1/test_crop_region_raw.py
index 189e987..7973755 100644
--- a/apps/CameraITS/tests/scene1/test_crop_region_raw.py
+++ b/apps/CameraITS/tests/scene1/test_crop_region_raw.py
@@ -64,7 +64,7 @@
# Use a manual request with a linear tonemap so that the YUV and RAW
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
- req = its.objects.manual_capture_request(s,e, True)
+ req = its.objects.manual_capture_request(s,e, True, props)
cap1_raw, cap1_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
# Capture with a crop region.
diff --git a/apps/CameraITS/tests/scene1/test_jpeg.py b/apps/CameraITS/tests/scene1/test_jpeg.py
index 25c2038..7bc038d 100644
--- a/apps/CameraITS/tests/scene1/test_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_jpeg.py
@@ -33,7 +33,7 @@
its.caps.per_frame_control(props))
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
# YUV
size = its.objects.get_available_output_sizes("yuv", props)[0]
diff --git a/apps/CameraITS/tests/scene1/test_latching.py b/apps/CameraITS/tests/scene1/test_latching.py
index 3bc4356..176f01b 100644
--- a/apps/CameraITS/tests/scene1/test_latching.py
+++ b/apps/CameraITS/tests/scene1/test_latching.py
@@ -45,20 +45,20 @@
b_means = []
reqs = [
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s*2,e, True),
- its.objects.manual_capture_request(s*2,e, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s, e*2, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s*2,e, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s, e*2, True),
- its.objects.manual_capture_request(s, e, True),
- its.objects.manual_capture_request(s, e*2, True),
- its.objects.manual_capture_request(s, e*2, True),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s*2,e, True, props),
+ its.objects.manual_capture_request(s*2,e, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s, e*2, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s*2,e, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s, e*2, True, props),
+ its.objects.manual_capture_request(s, e, True, props),
+ its.objects.manual_capture_request(s, e*2, True, props),
+ its.objects.manual_capture_request(s, e*2, True, props),
]
caps = cam.do_capture(reqs, fmt)
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1/test_locked_burst.py
index 958fc72..6552c73 100644
--- a/apps/CameraITS/tests/scene1/test_locked_burst.py
+++ b/apps/CameraITS/tests/scene1/test_locked_burst.py
@@ -32,7 +32,8 @@
NAME = os.path.basename(__file__).split(".")[0]
BURST_LEN = 8
- SPREAD_THRESH = 0.005
+ SPREAD_THRESH_MANUAL_SENSOR = 0.005
+ SPREAD_THRESH = 0.03
FPS_MAX_DIFF = 2.0
with its.device.ItsSession() as cam:
@@ -67,8 +68,10 @@
for means in [r_means, g_means, b_means]:
spread = max(means) - min(means)
print "Patch mean spread", spread, \
- " (min/max: ", min(means), "/", max(means), ")"
- assert(spread < SPREAD_THRESH)
+ " (min/max: ", min(means), "/", max(means), ")"
+ threshold = SPREAD_THRESH_MANUAL_SENSOR \
+ if its.caps.manual_sensor(props) else SPREAD_THRESH
+ assert(spread < threshold)
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_param_color_correction.py b/apps/CameraITS/tests/scene1/test_param_color_correction.py
index b7fdc7b..09b3707 100644
--- a/apps/CameraITS/tests/scene1/test_param_color_correction.py
+++ b/apps/CameraITS/tests/scene1/test_param_color_correction.py
@@ -42,7 +42,7 @@
# Baseline request
e, s = its.target.get_target_exposure_combos(cam)["midSensitivity"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
req["android.colorCorrection.mode"] = 0
# Transforms:
diff --git a/apps/CameraITS/tests/scene1/test_param_exposure_time.py b/apps/CameraITS/tests/scene1/test_param_exposure_time.py
index e6078d9..0c0aab1 100644
--- a/apps/CameraITS/tests/scene1/test_param_exposure_time.py
+++ b/apps/CameraITS/tests/scene1/test_param_exposure_time.py
@@ -39,7 +39,7 @@
e,s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
for i,e_mult in enumerate([0.8, 0.9, 1.0, 1.1, 1.2]):
- req = its.objects.manual_capture_request(s, e * e_mult, True)
+ req = its.objects.manual_capture_request(s, e * e_mult, True, props)
cap = cam.do_capture(req)
img = its.image.convert_capture_to_rgb_image(cap)
its.image.write_image(
diff --git a/apps/CameraITS/tests/scene1/test_param_flash_mode.py b/apps/CameraITS/tests/scene1/test_param_flash_mode.py
index aae56aa..38f864f 100644
--- a/apps/CameraITS/tests/scene1/test_param_flash_mode.py
+++ b/apps/CameraITS/tests/scene1/test_param_flash_mode.py
@@ -39,7 +39,7 @@
# linear tonemap.
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
e /= 4
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
for f in [0,1,2]:
req["android.flash.mode"] = f
diff --git a/apps/CameraITS/tests/scene1/test_param_shading_mode.py b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
new file mode 100644
index 0000000..65b7e97
--- /dev/null
+++ b/apps/CameraITS/tests/scene1/test_param_shading_mode.py
@@ -0,0 +1,109 @@
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import its.caps
+import its.device
+import its.objects
+import matplotlib
+import numpy
+import os
+import os.path
+import pylab
+
+def main():
+ """Test that the android.shading.mode param is applied.
+
+ Switching shading modes and checks that the lens shading maps are
+ modified as expected.
+ """
+ NAME = os.path.basename(__file__).split(".")[0]
+
+ NUM_SHADING_MODE_SWITCH_LOOPS = 3
+ THRESHOLD_DIFF_RATIO = 0.15
+
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+
+ its.caps.skip_unless(its.caps.per_frame_control(props))
+
+ assert(props.has_key("android.lens.info.shadingMapSize") and
+ props["android.lens.info.shadingMapSize"] != None)
+
+ num_map_gains = props["android.lens.info.shadingMapSize"]["width"] * \
+ props["android.lens.info.shadingMapSize"]["height"] * 4
+
+ # Test 1: Switching shading modes several times and verify:
+ # 1. Lens shading maps with mode OFF are all 1.0
+ # 2. Lens shading maps with mode FAST are similar after switching
+ # shading modes.
+ # 3. Lens shading maps with mode HIGH_QUALITY are similar after
+ # switching shading modes.
+ cam.do_3a();
+
+ # Get the reference lens shading maps for OFF, FAST, and HIGH_QUALITY
+ # in different sessions.
+ # reference_maps[mode]
+ reference_maps = [[] for mode in range(3)]
+ reference_maps[0] = [1.0] * num_map_gains
+ for mode in range(1, 3):
+ req = its.objects.auto_capture_request();
+ req["android.statistics.lensShadingMapMode"] = 1
+ req["android.shading.mode"] = mode
+ reference_maps[mode] = cam.do_capture(req)["metadata"] \
+ ["android.statistics.lensShadingMap"]
+
+ # Get the lens shading maps while switching modes in one session.
+ reqs = []
+ for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
+ for mode in range(3):
+ req = its.objects.auto_capture_request();
+ req["android.statistics.lensShadingMapMode"] = 1
+ req["android.shading.mode"] = mode
+ reqs.append(req);
+
+ caps = cam.do_capture(reqs)
+
+ # shading_maps[mode][loop]
+ shading_maps = [[[] for loop in range(NUM_SHADING_MODE_SWITCH_LOOPS)]
+ for mode in range(3)]
+
+ # Get the shading maps out of capture results
+ for i in range(len(caps)):
+ shading_maps[i % 3][i / 3] = \
+ caps[i]["metadata"]["android.statistics.lensShadingMap"]
+
+ # Draw the maps
+ for mode in range(3):
+ for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
+ pylab.clf()
+ pylab.plot(range(num_map_gains), shading_maps[mode][i], 'r')
+ pylab.plot(range(num_map_gains), reference_maps[mode], 'g')
+ pylab.xlim([0, num_map_gains])
+ pylab.ylim([0.9, 4.0])
+ matplotlib.pyplot.savefig("%s_ls_maps_mode_%d_loop_%d.png" %
+ (NAME, mode, i))
+
+ print "Verifying lens shading maps with mode OFF are all 1.0"
+ for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
+ assert(numpy.allclose(shading_maps[0][i], reference_maps[0]))
+
+ for mode in range(1, 3):
+ print "Verifying lens shading maps with mode", mode, "are similar"
+ for i in range(NUM_SHADING_MODE_SWITCH_LOOPS):
+ assert(numpy.allclose(shading_maps[mode][i],
+ reference_maps[mode],
+ THRESHOLD_DIFF_RATIO))
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tests/scene1/test_tonemap_sequence.py b/apps/CameraITS/tests/scene1/test_tonemap_sequence.py
index 18ca506..7c87ca2 100644
--- a/apps/CameraITS/tests/scene1/test_tonemap_sequence.py
+++ b/apps/CameraITS/tests/scene1/test_tonemap_sequence.py
@@ -40,7 +40,7 @@
means = []
# Capture 3 manual shots with a linear tonemap.
- req = its.objects.manual_capture_request(sens, exp_time, True)
+ req = its.objects.manual_capture_request(sens, exp_time, True, props)
for i in [0,1,2]:
cap = cam.do_capture(req)
img = its.image.convert_capture_to_rgb_image(cap)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py b/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py
index 1b278ef..0c428fc 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_jpeg_all.py
@@ -35,7 +35,7 @@
# Use a manual request with a linear tonemap so that the YUV and JPEG
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
rgbs = []
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 6daa243..9ce8d76 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -37,7 +37,7 @@
# Use a manual request with a linear tonemap so that the YUV and JPEG
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
cap_yuv, cap_jpeg = cam.do_capture(req, [fmt_yuv, fmt_jpeg])
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index eb01c1a..f13801b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -36,7 +36,7 @@
# Use a manual request with a linear tonemap so that the YUV and RAW
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index 910a8ea..e52946d 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -36,7 +36,7 @@
# Use a manual request with a linear tonemap so that the YUV and RAW
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
cap_raw, cap_yuv = cam.do_capture(req,
[{"format":"raw10"}, {"format":"yuv"}])
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
index bbd9144..c5c3c73 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw12.py
@@ -36,7 +36,7 @@
# Use a manual request with a linear tonemap so that the YUV and RAW
# should look the same (once converted by the its.image module).
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
- req = its.objects.manual_capture_request(s, e, True)
+ req = its.objects.manual_capture_request(s, e, True, props)
cap_raw, cap_yuv = cam.do_capture(req,
[{"format":"raw12"}, {"format":"yuv"}])
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 37f1f90..b6a7b71 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -26,6 +26,7 @@
LOCAL_SRC_FILES := $(call all-java-files-under, src) $(call all-Iaidl-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := android-ex-camera2 \
+ android-support-v4 \
compatibility-common-util-devicesidelib_v2 \
cts-sensors-tests \
ctstestrunner \
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5380601..4226bf2 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1306,10 +1306,23 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_REMOVE" />
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_INSTALL_APK" />
<action android:name="com.android.cts.verifier.managedprovisioning.action.CHECK_INTENT_FILTERS" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_AUDIO" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
+ <provider
+ android:name="android.support.v4.content.FileProvider"
+ android:authorities="com.android.cts.verifier.managedprovisioning.fileprovider"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/filepaths" />
+ </provider>
+
<activity android:name=".managedprovisioning.ByodIconSamplerActivity">
<intent-filter>
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_SAMPLE_ICON" />
diff --git a/apps/CtsVerifier/res/layout/byod_present_media.xml b/apps/CtsVerifier/res/layout/byod_present_media.xml
new file mode 100644
index 0000000..f6c7eb3
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/byod_present_media.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <ImageView android:id="@+id/imageView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:scaleType="fitCenter"
+ android:minHeight="300dp"
+ android:minWidth="300dp"
+ android:visibility="gone"/>
+
+ <VideoView android:id="@+id/videoView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:minHeight="300dp"
+ android:minWidth="300dp"
+ android:layout_gravity="center"
+ android:visibility="gone"/>
+
+ <Button android:id="@+id/playButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/provisioning_byod_play"
+ android:visibility="gone"/>
+
+ <Button android:id="@+id/dismissButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0"
+ android:text="@string/provisioning_byod_dismiss_result_dialog"/>
+
+</LinearLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 1b0ca68..9f8f4c1 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -1152,6 +1152,39 @@
2. Verify that the installation of the package is refused.
</string>
+ <string name="provisioning_byod_capture_image_support">Camera support cross profile image capture</string>
+ <string name="provisioning_byod_capture_image_support_info">
+ This test verifies that images can be captured from the managed profile using the primary profile camera.\n
+ 1. Capture a picture using the camera.\n
+ 2. Verify that the captured picture is shown.\n
+ 3. Click on the close button.
+ </string>
+ <string name="provisioning_byod_capture_video_support">Camera support cross profile video capture</string>
+ <string name="provisioning_byod_capture_video_support_info">
+ This test verifies that videos can be captured from the managed profile using the primary profile camera.\n
+ 1. Capture a video using the camera.\n
+ 2. Click on the play button.\n
+ 3. Verify that the captured video is played.\n
+ 4. Click on the close button.
+ </string>
+ <string name="provisioning_byod_capture_audio_support">Sound recorder support cross profile audio capture</string>
+ <string name="provisioning_byod_capture_audio_support_info">
+ This test verifies that audio can be captured from the managed profile using the primary profile sound recorder.\n
+ 1. Capture audio.\n
+ 2. Click on the play button.\n
+ 3. Verify that the captured audio is played.\n
+ 4. Click on the close button.\n
+ </string>
+ <string name="provisioning_byod_dismiss_result_dialog">Close</string>
+ <string name="provisioning_byod_play">Play</string>
+ <string name="provisioning_byod_verify_image_title">Verify captured image</string>
+ <string name="provisioning_byod_verify_video_title">Verify captured video</string>
+ <string name="provisioning_byod_verify_audio_title">Verify captured audio</string>
+ <string name="provisioning_byod_no_image_capture_resolver">No image capture app present. Skip test.</string>
+ <string name="provisioning_byod_no_video_capture_resolver">No video capture app present. Skip test.</string>
+ <string name="provisioning_byod_no_audio_capture_resolver">No audio capture app present. Skip test.</string>
+ <string name="provisioning_byod_capture_media_error">Error while capturing media from managed profile.</string>
+
<!-- Strings for DeskClock -->
<string name="deskclock_tests">Alarms and Timers Tests</string>
<string name="deskclock_tests_info">
@@ -1464,6 +1497,14 @@
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_input_discover_test_go_to_epg">
+ Press the \"Launch EPG\" button, and locate the channel named \"Dummy\".
+ </string>
+ <string name="tv_input_discover_test_verify_epg">
+ Do you see the programs named \"Dummy Program\" and its description
+ "Dummy Program Description" in the EPG?
+ </string>
+ <string name="tv_input_discover_test_yes">Yes</string>
<string name="tv_parental_control_test">Live Channels app parental control test</string>
<string name="tv_parental_control_test_info">
@@ -1491,14 +1532,15 @@
</string>
<string name="tv_launch_tv_app">Launch Live Channels</string>
+ <string name="tv_launch_epg">Launch EPG</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">Live Channels app closed captions and multi-audio 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.
+ This test verifies that the default Live Channels app invokes proper mulitple tracks 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.
diff --git a/apps/CtsVerifier/res/xml/filepaths.xml b/apps/CtsVerifier/res/xml/filepaths.xml
new file mode 100644
index 0000000..2d555a2
--- /dev/null
+++ b/apps/CtsVerifier/res/xml/filepaths.xml
@@ -0,0 +1,3 @@
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
+ <files-path path="images/" name="images" />
+</paths>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
index 628ff3e..e41c6d0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -82,6 +82,9 @@
private TestItem mCredSettingsVisibleTest;
private TestItem mPrintSettingsVisibleTest;
private TestItem mIntentFiltersTest;
+ private TestItem mCrossProfileImageCaptureSupportTest;
+ private TestItem mCrossProfileVideoCaptureSupportTest;
+ private TestItem mCrossProfileAudioCaptureSupportTest;
private int mCurrentTestPosition;
@@ -256,6 +259,50 @@
mTests.add(mDisableNonMarketTest);
mTests.add(mEnableNonMarketTest);
mTests.add(mIntentFiltersTest);
+
+ if (canResolveIntent(ByodHelperActivity.getCaptureImageIntent())) {
+ // Capture image intent can be resolved in primary profile, so test.
+ mCrossProfileImageCaptureSupportTest = new TestItem(this,
+ R.string.provisioning_byod_capture_image_support,
+ R.string.provisioning_byod_capture_image_support_info,
+ new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE));
+ mTests.add(mCrossProfileImageCaptureSupportTest);
+ } else {
+ // Capture image intent cannot be resolved in primary profile, so skip test.
+ Toast.makeText(ByodFlowTestActivity.this,
+ R.string.provisioning_byod_no_image_capture_resolver, Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ if (canResolveIntent(ByodHelperActivity.getCaptureVideoIntent())) {
+ // Capture video intent can be resolved in primary profile, so test.
+ mCrossProfileVideoCaptureSupportTest = new TestItem(this,
+ R.string.provisioning_byod_capture_video_support,
+ R.string.provisioning_byod_capture_video_support_info,
+ new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_VIDEO));
+ mTests.add(mCrossProfileVideoCaptureSupportTest);
+ } else {
+ // Capture video intent cannot be resolved in primary profile, so skip test.
+ Toast.makeText(ByodFlowTestActivity.this,
+ R.string.provisioning_byod_no_video_capture_resolver, Toast.LENGTH_SHORT)
+ .show();
+ }
+
+ /* TODO: reinstate when bug b/20131958 is fixed
+ if (canResolveIntent(ByodHelperActivity.getCaptureAudioIntent())) {
+ // Capture audio intent can be resolved in primary profile, so test.
+ mCrossProfileAudioCaptureSupportTest = new TestItem(this,
+ R.string.provisioning_byod_capture_audio_support,
+ R.string.provisioning_byod_capture_audio_support_info,
+ new Intent(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_AUDIO));
+ mTests.add(mCrossProfileAudioCaptureSupportTest);
+ } else {
+ // Capture audio intent cannot be resolved in primary profile, so skip test.
+ Toast.makeText(ByodFlowTestActivity.this,
+ R.string.provisioning_byod_no_audio_capture_resolver, Toast.LENGTH_SHORT)
+ .show();
+ }
+ */
}
@Override
@@ -266,6 +313,11 @@
test.performTest(this);
}
+ // Return whether the intent can be resolved in the current profile
+ private boolean canResolveIntent(Intent intent) {
+ return intent.resolveActivity(getPackageManager()) != null;
+ }
+
private void showManualTestDialog(final TestItem test) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index 13af890..d8a3387 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.admin.DevicePolicyManager;
+import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -26,24 +27,31 @@
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
+import android.provider.MediaStore;
import android.provider.Settings;
+import android.support.v4.content.FileProvider;
import android.util.Log;
import android.widget.Toast;
import static android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS;
+import java.io.File;
+import java.util.ArrayList;
+
import com.android.cts.verifier.R;
import com.android.cts.verifier.managedprovisioning.ByodFlowTestActivity.TestResult;
+import com.android.cts.verifier.managedprovisioning.ByodPresentMediaDialog.DialogCallback;
/**
* A helper activity from the managed profile side that responds to requests from CTS verifier in
* primary user. Profile owner APIs are accessible inside this activity (given this activity is
* started within the work profile). Its current functionalities include making sure the profile
- * owner is setup correctly, and removing the work profile upon request.
+ * owner is setup correctly, removing the work profile upon request, and verifying the image and
+ * video capture functionality.
*
* Note: We have to use a dummy activity because cross-profile intents only work for activities.
*/
-public class ByodHelperActivity extends Activity {
+public class ByodHelperActivity extends Activity implements DialogCallback {
static final String TAG = "ByodHelperActivity";
// Primary -> managed intent: query if the profile owner has been set up.
@@ -54,6 +62,12 @@
public static final String ACTION_REMOVE_PROFILE_OWNER = "com.android.cts.verifier.managedprovisioning.BYOD_REMOVE";
// Managed -> managed intent: provisioning completed successfully
public static final String ACTION_PROFILE_PROVISIONED = "com.android.cts.verifier.managedprovisioning.BYOD_PROVISIONED";
+ // Primage -> managed intent: request to capture and check an image
+ public static final String ACTION_CAPTURE_AND_CHECK_IMAGE = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_IMAGE";
+ // Primage -> managed intent: request to capture and check a video
+ public static final String ACTION_CAPTURE_AND_CHECK_VIDEO = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_VIDEO";
+ // Primage -> managed intent: request to capture and check an audio recording
+ public static final String ACTION_CAPTURE_AND_CHECK_AUDIO = "com.android.cts.verifier.managedprovisioning.BYOD_CAPTURE_AND_CHECK_AUDIO";
public static final String EXTRA_PROVISIONED = "extra_provisioned";
@@ -68,6 +82,9 @@
public static final int RESULT_FAILED = RESULT_FIRST_USER;
private static final int REQUEST_INSTALL_PACKAGE = 1;
+ private static final int REQUEST_IMAGE_CAPTURE = 2;
+ private static final int REQUEST_VIDEO_CAPTURE = 3;
+ private static final int REQUEST_AUDIO_CAPTURE = 4;
private static final String ORIGINAL_SETTINGS_NAME = "original settings";
private Bundle mOriginalSettings;
@@ -75,6 +92,11 @@
private ComponentName mAdminReceiverComponent;
private DevicePolicyManager mDevicePolicyManager;
+ private Uri mImageUri;
+ private Uri mVideoUri;
+
+ private ArrayList<File> mTempFiles = new ArrayList<File>();
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -132,6 +154,40 @@
new IntentFiltersTestHelper(this).checkCrossProfileIntentFilters(
IntentFiltersTestHelper.FLAG_INTENTS_FROM_MANAGED);
setResult(intentFiltersSetForManagedIntents? RESULT_OK : RESULT_FAILED, null);
+ } else if (action.equals(ACTION_CAPTURE_AND_CHECK_IMAGE)) {
+ Intent captureImageIntent = getCaptureImageIntent();
+ mImageUri = getTempUri("image.jpg");
+ captureImageIntent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri);
+ if (captureImageIntent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(captureImageIntent, REQUEST_IMAGE_CAPTURE);
+ } else {
+ Log.e(TAG, "Capture image intent could not be resolved in managed profile.");
+ showToast(R.string.provisioning_byod_capture_media_error);
+ finish();
+ }
+ return;
+ } else if (action.equals(ACTION_CAPTURE_AND_CHECK_VIDEO)) {
+ Intent captureVideoIntent = getCaptureVideoIntent();
+ mVideoUri = getTempUri("video.mp4");
+ captureVideoIntent.putExtra(MediaStore.EXTRA_OUTPUT, mVideoUri);
+ if (captureVideoIntent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(captureVideoIntent, REQUEST_VIDEO_CAPTURE);
+ } else {
+ Log.e(TAG, "Capture video intent could not be resolved in managed profile.");
+ showToast(R.string.provisioning_byod_capture_media_error);
+ finish();
+ }
+ return;
+ } else if (action.equals(ACTION_CAPTURE_AND_CHECK_AUDIO)) {
+ Intent captureAudioIntent = getCaptureAudioIntent();
+ if (captureAudioIntent.resolveActivity(getPackageManager()) != null) {
+ startActivityForResult(captureAudioIntent, REQUEST_AUDIO_CAPTURE);
+ } else {
+ Log.e(TAG, "Capture audio intent could not be resolved in managed profile.");
+ showToast(R.string.provisioning_byod_capture_media_error);
+ finish();
+ }
+ return;
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -157,6 +213,36 @@
finish();
break;
}
+ case REQUEST_IMAGE_CAPTURE: {
+ if (resultCode == RESULT_OK) {
+ ByodPresentMediaDialog.newImageInstance(mImageUri)
+ .show(getFragmentManager(), "ViewImageDialogFragment");
+ } else {
+ // Failed capturing image.
+ finish();
+ }
+ break;
+ }
+ case REQUEST_VIDEO_CAPTURE: {
+ if (resultCode == RESULT_OK) {
+ ByodPresentMediaDialog.newVideoInstance(mVideoUri)
+ .show(getFragmentManager(), "PlayVideoDialogFragment");
+ } else {
+ // Failed capturing video.
+ finish();
+ }
+ break;
+ }
+ case REQUEST_AUDIO_CAPTURE: {
+ if (resultCode == RESULT_OK) {
+ ByodPresentMediaDialog.newAudioInstance(data.getData())
+ .show(getFragmentManager(), "PlayAudioDialogFragment");
+ } else {
+ // Failed capturing audio.
+ finish();
+ }
+ break;
+ }
default: {
Log.wtf(TAG, "Unknown requestCode " + requestCode + "; data = " + data);
break;
@@ -164,6 +250,39 @@
}
}
+ @Override
+ protected void onDestroy() {
+ cleanUpTempUris();
+ super.onDestroy();
+ }
+
+ public static Intent getCaptureImageIntent() {
+ return new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
+ }
+
+ public static Intent getCaptureVideoIntent() {
+ return new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
+ }
+
+ public static Intent getCaptureAudioIntent() {
+ return new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION);
+ }
+
+ private Uri getTempUri(String fileName) {
+ final File file = new File(getFilesDir() + File.separator + "images"
+ + File.separator + fileName);
+ file.getParentFile().mkdirs(); //if the folder doesn't exists it is created
+ mTempFiles.add(file);
+ return FileProvider.getUriForFile(this,
+ "com.android.cts.verifier.managedprovisioning.fileprovider", file);
+ }
+
+ private void cleanUpTempUris() {
+ for (File file : mTempFiles) {
+ file.delete();
+ }
+ }
+
private boolean isProfileOwner() {
return mDevicePolicyManager.isAdminActive(mAdminReceiverComponent) &&
mDevicePolicyManager.isProfileOwnerApp(mAdminReceiverComponent.getPackageName());
@@ -193,4 +312,9 @@
String message = getString(messageId);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
+
+ @Override
+ public void onDialogClose() {
+ finish();
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
new file mode 100644
index 0000000..b3f126b
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodPresentMediaDialog.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.managedprovisioning;
+
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.media.MediaPlayer;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.Toast;
+import android.widget.VideoView;
+
+import com.android.cts.verifier.R;
+
+import java.io.IOException;
+
+/**
+ * This dialog shows/plays an image, video or audio uri.
+ */
+public class ByodPresentMediaDialog extends DialogFragment {
+ static final String TAG = "ByodPresentMediaDialog";
+
+ private static final String KEY_VIDEO_URI = "video";
+ private static final String KEY_IMAGE_URI = "image";
+ private static final String KEY_AUDIO_URI = "audio";
+
+ /**
+ * Get a dialogFragment showing an image.
+ */
+ public static ByodPresentMediaDialog newImageInstance(Uri uri) {
+ ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+ Bundle args = new Bundle();
+ args.putParcelable(KEY_IMAGE_URI, uri);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ /**
+ * Get a dialogFragment playing a video.
+ */
+ public static ByodPresentMediaDialog newVideoInstance(Uri uri) {
+ ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+ Bundle args = new Bundle();
+ args.putParcelable(KEY_VIDEO_URI, uri);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ /**
+ * Get a dialogFragment playing audio.
+ */
+ public static ByodPresentMediaDialog newAudioInstance(Uri uri) {
+ ByodPresentMediaDialog dialog = new ByodPresentMediaDialog();
+ Bundle args = new Bundle();
+ args.putParcelable(KEY_AUDIO_URI, uri);
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final Dialog dialog = new Dialog(getActivity());
+ dialog.setContentView(R.layout.byod_present_media);
+
+ Button dismissButton = (Button) dialog.findViewById(R.id.dismissButton);
+ dismissButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ dismiss();
+ ((DialogCallback) getActivity()).onDialogClose();
+ }
+ });
+
+ Bundle arguments = getArguments();
+
+ // Initially all video and image specific UI is invisible.
+ if (arguments.containsKey(KEY_VIDEO_URI)) {
+ // Show video UI.
+ dialog.setTitle(getString(R.string.provisioning_byod_verify_video_title));
+
+ Uri uri = (Uri) getArguments().getParcelable(KEY_VIDEO_URI);
+ final VideoView videoView = (VideoView) dialog.findViewById(R.id.videoView);
+ videoView.setVisibility(View.VISIBLE);
+ videoView.setVideoURI(uri);
+
+ Button playButton = (Button) dialog.findViewById(R.id.playButton);
+ playButton.setVisibility(View.VISIBLE);
+ playButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ videoView.start();
+ }
+ });
+ } else if (arguments.containsKey(KEY_IMAGE_URI)) {
+ // Show image UI.
+ dialog.setTitle(getString(R.string.provisioning_byod_verify_image_title));
+
+ Uri uri = (Uri) getArguments().getParcelable(KEY_IMAGE_URI);
+ ImageView imageView = (ImageView) dialog.findViewById(R.id.imageView);
+ imageView.setVisibility(View.VISIBLE);
+ imageView.setImageURI(uri);
+ } else if (arguments.containsKey(KEY_AUDIO_URI)) {
+ // Show audio playback UI.
+ dialog.setTitle(getString(R.string.provisioning_byod_verify_audio_title));
+
+ Uri uri = (Uri) getArguments().getParcelable(KEY_AUDIO_URI);
+ final MediaPlayer mediaPlayer = new MediaPlayer();
+ final Button playButton = (Button) dialog.findViewById(R.id.playButton);
+ playButton.setVisibility(View.VISIBLE);
+ playButton.setEnabled(false);
+
+ try {
+ mediaPlayer.setDataSource(getActivity(), uri);
+ mediaPlayer.prepare();
+ } catch (IllegalArgumentException|SecurityException|IllegalStateException
+ |IOException e) {
+ Log.e(TAG, "Cannot play given audio with media player.", e);
+ Toast.makeText(getActivity(), R.string.provisioning_byod_capture_media_error,
+ Toast.LENGTH_SHORT).show();
+ getActivity().finish();
+ }
+
+ mediaPlayer.setOnPreparedListener(new OnPreparedListener() {
+ @Override
+ public void onPrepared(MediaPlayer mp) {
+ playButton.setEnabled(true);
+ playButton.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ mediaPlayer.start();
+ }
+ });
+ }
+ });
+ }
+
+ return dialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ ((DialogCallback) getActivity()).onDialogClose();
+ }
+
+ public interface DialogCallback {
+ public abstract void onDialogClose();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 58c068f..e95752e 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -50,6 +50,9 @@
filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
filter.addAction(ByodHelperActivity.ACTION_CHECK_INTENT_FILTERS);
+ filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_IMAGE);
+ filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_VIDEO);
+ filter.addAction(ByodHelperActivity.ACTION_CAPTURE_AND_CHECK_AUDIO);
filter.addAction(CrossProfileTestActivity.ACTION_CROSS_PROFILE);
filter.addAction(WorkNotificationTestActivity.ACTION_WORK_NOTIFICATION);
filter.addAction(WorkNotificationTestActivity.ACTION_CLEAR_WORK_NOTIFICATION);
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 f4460de..e7d1d79 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputService.java
@@ -33,15 +33,11 @@
import android.media.tv.TvTrackInfo;
import android.net.Uri;
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;
@@ -78,9 +74,9 @@
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")
+ static final TvTrackInfo sKorSubtitleTrack =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle_kor")
+ .setLanguage("kor")
.build();
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@@ -179,7 +175,7 @@
mTracks.add(sEngAudioTrack);
mTracks.add(sSpaAudioTrack);
mTracks.add(sEngSubtitleTrack);
- mTracks.add(sSpaSubtitleTrack);
+ mTracks.add(sKorSubtitleTrack);
}
@Override
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 81a8edc..1d3fd40 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -21,18 +21,26 @@
import android.content.ContentValues;
import android.database.Cursor;
import android.media.tv.TvContract;
+import android.media.tv.TvContract.Programs;
import android.media.tv.TvInputInfo;
import android.net.Uri;
import android.os.Bundle;
import android.util.Pair;
import android.view.View;
+import java.util.ArrayList;
+
public class MockTvInputSetupActivity extends Activity {
private static final String TAG = "MockTvInputSetupActivity";
private static final String CHANNEL_NUMBER = "999-0";
private static final String CHANNEL_NAME = "Dummy";
+ private static final String PROGRAM_TITLE = "Dummy Program";
+ private static final String PROGRAM_DESCRIPTION = "Dummy Program Description";
+ private static final long PROGRAM_LENGTH_MILLIS = 60 * 60 * 1000;
+ private static final int PROGRAM_COUNT = 24;
+
private static Object sLock = new Object();
private static Pair<View, Runnable> sLaunchCallback = null;
@@ -55,6 +63,8 @@
return;
}
}
+
+ // Add a channel.
ContentValues values = new ContentValues();
values.put(TvContract.Channels.COLUMN_INPUT_ID, inputId);
values.put(TvContract.Channels.COLUMN_DISPLAY_NUMBER, CHANNEL_NUMBER);
@@ -62,9 +72,27 @@
Uri channelUri = getContentResolver().insert(uri, values);
// If the channel's ID happens to be zero, we add another and delete the one.
if (ContentUris.parseId(channelUri) == 0) {
- getContentResolver().insert(uri, values);
getContentResolver().delete(channelUri, null, null);
+ channelUri = getContentResolver().insert(uri, values);
}
+
+ // Add Programs.
+ values = new ContentValues();
+ values.put(Programs.COLUMN_CHANNEL_ID, ContentUris.parseId(channelUri));
+ values.put(Programs.COLUMN_TITLE, PROGRAM_TITLE);
+ values.put(Programs.COLUMN_SHORT_DESCRIPTION, PROGRAM_DESCRIPTION);
+ long nowMs = System.currentTimeMillis();
+ long startTimeMs = nowMs - nowMs % PROGRAM_LENGTH_MILLIS;
+ ArrayList<ContentValues> list = new ArrayList<>();
+ for (int i = 0; i < PROGRAM_COUNT; ++i) {
+ values.put(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs);
+ values.put(Programs.COLUMN_END_TIME_UTC_MILLIS,
+ startTimeMs + PROGRAM_LENGTH_MILLIS);
+ startTimeMs += PROGRAM_LENGTH_MILLIS;
+ list.add(new ContentValues(values));
+ }
+ getContentResolver().bulkInsert(Programs.CONTENT_URI, list.toArray(
+ new ContentValues[0]));
} finally {
Pair<View, Runnable> launchCallback = null;
synchronized (sLock) {
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 3d17a1a..4d12d52 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -30,7 +30,9 @@
private static final String TAG = "TvInputDiscoveryTestActivity";
private static final Intent TV_APP_INTENT = new Intent(Intent.ACTION_VIEW,
- TvContract.buildChannelUri(0));
+ TvContract.Channels.CONTENT_URI);
+ private static final Intent EPG_INTENT = new Intent(Intent.ACTION_VIEW,
+ TvContract.Programs.CONTENT_URI);
private static final long TIMEOUT_MS = 5l * 60l * 1000l; // 5 mins.
@@ -39,6 +41,8 @@
private View mTuneToChannelItem;
private View mVerifyTuneItem;
private View mVerifyOverlayViewItem;
+ private View mGoToEpgItem;
+ private View mVerifyEpgItem;
private boolean mTuneVerified;
private boolean mOverlayViewVerified;
@@ -63,6 +67,7 @@
setButtonEnabled(mTuneToChannelItem, true);
}
});
+ startActivity(TV_APP_INTENT);
} else if (containsButton(mTuneToChannelItem, v)) {
final Runnable failCallback = new Runnable() {
@Override
@@ -78,7 +83,7 @@
setPassState(mVerifyTuneItem, true);
mTuneVerified = true;
- updatePassState(postTarget, failCallback);
+ goToNextState(postTarget, failCallback);
}
});
MockTvInputService.expectOverlayView(postTarget, new Runnable() {
@@ -88,11 +93,18 @@
setPassState(mVerifyOverlayViewItem, true);
mOverlayViewVerified = true;
- updatePassState(postTarget, failCallback);
+ goToNextState(postTarget, failCallback);
}
});
+ startActivity(TV_APP_INTENT);
+ } else if (containsButton(mGoToEpgItem, v)) {
+ startActivity(EPG_INTENT);
+ setPassState(mGoToEpgItem, true);
+ setButtonEnabled(mVerifyEpgItem, true);
+ } else if (containsButton(mVerifyEpgItem, v)) {
+ setPassState(mVerifyEpgItem, true);
+ getPassButton().setEnabled(true);
}
- startActivity(TV_APP_INTENT);
}
@Override
@@ -106,12 +118,16 @@
mVerifyTuneItem = createAutoItem(R.string.tv_input_discover_test_verify_tune);
mVerifyOverlayViewItem = createAutoItem(
R.string.tv_input_discover_test_verify_overlay_view);
+ mGoToEpgItem = createUserItem(R.string.tv_input_discover_test_go_to_epg,
+ R.string.tv_launch_epg, this);
+ mVerifyEpgItem = createUserItem(R.string.tv_input_discover_test_verify_epg,
+ R.string.tv_input_discover_test_yes, this);
}
- private void updatePassState(View postTarget, Runnable failCallback) {
+ private void goToNextState(View postTarget, Runnable failCallback) {
if (mTuneVerified && mOverlayViewVerified) {
postTarget.removeCallbacks(failCallback);
- getPassButton().setEnabled(true);
+ setButtonEnabled(mGoToEpgItem, true);
}
}
diff --git a/build/test_executable.mk b/build/test_executable.mk
index 3a2cb8e..979f59e 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -25,6 +25,7 @@
LOCAL_CXX_STL := libc++
include $(BUILD_EXECUTABLE)
+include $(BUILD_CTS_MODULE_TEST_CONFIG)
cts_executable_bin :=
$(foreach fp, $(ALL_MODULES.$(LOCAL_MODULE).BUILT) $(ALL_MODULES.$(LOCAL_MODULE)$(TARGET_2ND_ARCH_MODULE_SUFFIX).BUILT),\
@@ -38,6 +39,7 @@
$(cts_executable_xml): PRIVATE_LIST_EXECUTABLE := $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)_list
$(cts_executable_xml): $(HOST_OUT_EXECUTABLES)/$(LOCAL_MODULE)_list
$(cts_executable_xml): $(cts_executable_bin)
+$(cts_executable_xml): $(cts_module_test_config)
$(cts_executable_xml): $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES)) $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_NATIVE_TEST_SCANNER) $(CTS_XML_GENERATOR)
$(hide) echo Generating test description for native package $(PRIVATE_TEST_PACKAGE)
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
@@ -52,4 +54,4 @@
-o $@
# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
-$(my_register_name) : $(cts_executable_bin) $(cts_executable_xml)
+$(my_register_name) : $(cts_executable_bin) $(cts_executable_xml) $(cts_module_test_config)
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
index fc468d0..6f71830 100644
--- a/build/test_gtest_package.mk
+++ b/build/test_gtest_package.mk
@@ -24,6 +24,7 @@
LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_CTS_SUPPORT_PACKAGE)
+include $(BUILD_CTS_MODULE_TEST_CONFIG)
cts_package_xml := $(CTS_TESTCASES_OUT)/$(LOCAL_PACKAGE_NAME).xml
$(cts_package_xml): PRIVATE_PATH := $(LOCAL_PATH)
@@ -33,6 +34,7 @@
$(cts_package_xml): PRIVATE_TEST_LIST := $(LOCAL_PATH)/$(LOCAL_MODULE)_list.txt
$(cts_package_xml): $(LOCAL_PATH)/$(LOCAL_MODULE)_list.txt
$(cts_package_xml): $(cts_support_apks)
+$(cts_package_xml): $(cts_module_test_config)
$(cts_package_xml): $(addprefix $(LOCAL_PATH)/,$(LOCAL_SRC_FILES)) $(CTS_NATIVE_TEST_SCANNER) $(CTS_XML_GENERATOR)
$(hide) echo Generating test description for wrapped native package $(PRIVATE_EXECUTABLE)
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
@@ -48,4 +50,4 @@
-o $@
# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
-$(my_register_name) : $(cts_package_xml)
+$(my_register_name) : $(cts_package_xml) $(cts_module_test_config)
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 7e86ac9..7fdefb5 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -18,6 +18,7 @@
#
include $(BUILD_HOST_JAVA_LIBRARY)
+include $(BUILD_CTS_MODULE_TEST_CONFIG)
cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
$(cts_library_jar): $(LOCAL_BUILT_MODULE)
@@ -33,6 +34,7 @@
$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
$(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
$(cts_library_xml): $(cts_library_jar)
+$(cts_library_xml): $(cts_module_test_config)
$(cts_library_xml): $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
$(hide) echo Generating test description for host library $(PRIVATE_LIBRARY)
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
@@ -48,4 +50,4 @@
-o $@
# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
-$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml) $(cts_module_test_config)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index b573d25..a191d72 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -20,6 +20,7 @@
LOCAL_DEX_PREOPT := false
include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_CTS_MODULE_TEST_CONFIG)
cts_library_jar := $(CTS_TESTCASES_OUT)/$(LOCAL_MODULE).jar
$(cts_library_jar): $(LOCAL_BUILT_MODULE)
@@ -37,6 +38,7 @@
$(cts_library_xml): PRIVATE_LIBRARY := $(LOCAL_MODULE)
$(cts_library_xml): PRIVATE_JAR_PATH := $(LOCAL_MODULE).jar
$(cts_library_xml): $(cts_library_jar)
+$(cts_library_xml): $(cts_module_test_config)
$(cts_library_xml): $(CTS_EXPECTATIONS) $(CTS_UNSUPPORTED_ABIS) $(CTS_JAVA_TEST_SCANNER_DOCLET) $(CTS_JAVA_TEST_SCANNER) $(CTS_XML_GENERATOR)
$(hide) echo Generating test description for uiautomator library $(PRIVATE_LIBRARY)
$(hide) mkdir -p $(CTS_TESTCASES_OUT)
@@ -55,4 +57,4 @@
-o $@
# Have the module name depend on the cts files; so the cts files get generated when you run mm/mmm/mma/mmma.
-$(my_register_name) : $(cts_library_jar) $(cts_library_xml)
+$(my_register_name) : $(cts_library_jar) $(cts_library_xml) $(cts_module_test_config)
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java
new file mode 100644
index 0000000..8e63543
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/SilentPackageInstallerTest.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.deviceowner;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * This class tests silent package install and uninstall by a device owner.
+ */
+public class SilentPackageInstallerTest extends BaseDeviceOwnerTest {
+ private static final String TEST_APP_LOCATION = "/data/local/tmp/CtsSimpleApp.apk";
+ private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+ private static final int PACKAGE_INSTALLER_TIMEOUT_MS = 60000; // 60 seconds
+ private static final String ACTION_INSTALL_COMMIT =
+ "com.android.cts.deviceowner.INTENT_PACKAGE_INSTALL_COMMIT";
+
+ private PackageManager mPackageManager;
+ private PackageInstaller mPackageInstaller;
+ private PackageInstaller.Session mSession;
+ private boolean mCallbackReceived;
+
+ private final Object mPackageInstallerTimeoutLock = new Object();
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ assertEquals(PackageInstaller.STATUS_SUCCESS, intent.getIntExtra(
+ PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE));
+ assertEquals(TEST_APP_PKG, intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME));
+ mContext.unregisterReceiver(this);
+ synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackReceived = true;
+ mPackageInstallerTimeoutLock.notify();
+ }
+ }
+ };
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mPackageManager = mContext.getPackageManager();
+ mPackageInstaller = mPackageManager.getPackageInstaller();
+ assertNotNull(mPackageInstaller);
+ mCallbackReceived = false;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ mContext.unregisterReceiver(mBroadcastReceiver);
+ } catch (IllegalArgumentException e) {
+ // ignore
+ }
+ if (mSession != null) {
+ mSession.abandon();
+ }
+ super.tearDown();
+ }
+
+ public void testSilentInstallUninstall() throws Exception {
+ // check that app is not already installed
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+
+ // install the app
+ installPackage(TEST_APP_LOCATION);
+ synchronized (mPackageInstallerTimeoutLock) {
+ try {
+ mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ }
+ assertTrue(mCallbackReceived);
+ }
+ assertTrue(isPackageInstalled(TEST_APP_PKG));
+
+ // uninstall the app again
+ synchronized (mPackageInstallerTimeoutLock) {
+ mCallbackReceived = false;
+ mPackageInstaller.uninstall(TEST_APP_PKG, getCommitCallback(0));
+ try {
+ mPackageInstallerTimeoutLock.wait(PACKAGE_INSTALLER_TIMEOUT_MS);
+ } catch (InterruptedException e) {
+ }
+ assertTrue(mCallbackReceived);
+ }
+ assertFalse(isPackageInstalled(TEST_APP_PKG));
+ }
+
+ private void installPackage(String packageLocation) throws Exception {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ int sessionId = mPackageInstaller.createSession(params);
+ mSession = mPackageInstaller.openSession(sessionId);
+
+ File file = new File(packageLocation);
+ InputStream in = new FileInputStream(file);
+ OutputStream out = mSession.openWrite("SilentPackageInstallerTest", 0, file.length());
+ byte[] buffer = new byte[65536];
+ int c;
+ while ((c = in.read(buffer)) != -1) {
+ out.write(buffer, 0, c);
+ }
+ mSession.fsync(out);
+ out.close();
+ mSession.commit(getCommitCallback(sessionId));
+ mSession.close();
+ }
+
+ private IntentSender getCommitCallback(int sessionId) {
+ // Create an intent-filter and register the receiver
+ String action = ACTION_INSTALL_COMMIT + "." + sessionId;
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(action);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ // Create a PendingIntent and use it to generate the IntentSender
+ Intent broadcastIntent = new Intent(action);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext,
+ sessionId,
+ broadcastIntent,
+ PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ return pendingIntent.getIntentSender();
+ }
+
+ private boolean isPackageInstalled(String packageName) {
+ try {
+ PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0);
+ return pi != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
index 093a402..b76d9e4 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
@@ -28,6 +28,7 @@
import android.os.Build;
import android.os.RemoteException;
import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.Photo;
import android.provider.ContactsContract.PhoneLookup;
@@ -44,10 +45,19 @@
private static final String TEST_ACCOUNT_NAME = "CTS";
private static final String TEST_ACCOUNT_TYPE = "com.android.cts.test";
+ // details of a sample primary contact
private static final String PRIMARY_CONTACT_DISPLAY_NAME = "Primary";
private static final String PRIMARY_CONTACT_PHONE = "00000001";
+ private static final String PRIMARY_CONTACT_EMAIL = "one@primary.com";
+ // details of a sample managed contact
private static final String MANAGED_CONTACT_DISPLAY_NAME = "Managed";
private static final String MANAGED_CONTACT_PHONE = "6891999";
+ private static final String MANAGED_CONTACT_EMAIL = "one@managed.com";
+ // details of a sample primary and a sample managed contact, with the same phone & email
+ private static final String PRIMARY_CONTACT_DISPLAY_NAME_2 = "PrimaryShared";
+ private static final String MANAGED_CONTACT_DISPLAY_NAME_2 = "ManagedShared";
+ private static final String SHARED_CONTACT_PHONE = "00000002";
+ private static final String SHARED_CONTACT_EMAIL = "shared@shared.com";
private DevicePolicyManager mDevicePolicyManager;
private ContentResolver mResolver;
@@ -85,11 +95,12 @@
.getSystemService(Context.DEVICE_POLICY_SERVICE);
}
- public void testPrimaryProfilePhoneLookup_insertedAndfound() throws RemoteException,
+ public void testPrimaryProfilePhoneAndEmailLookup_insertedAndfound() throws RemoteException,
OperationApplicationException, NotFoundException, IOException {
assertFalse(isManagedProfile());
// Do not insert to primary contact
- insertContact(PRIMARY_CONTACT_DISPLAY_NAME, PRIMARY_CONTACT_PHONE, 0);
+ insertContact(PRIMARY_CONTACT_DISPLAY_NAME, PRIMARY_CONTACT_PHONE,
+ PRIMARY_CONTACT_EMAIL, 0);
ContactInfo contactInfo = getContactInfo(PRIMARY_CONTACT_PHONE);
assertNotNull(contactInfo);
@@ -97,13 +108,23 @@
assertFalse(contactInfo.hasPhotoUri());
assertFalse(contactInfo.hasPhotoId());
assertFalse(isEnterpriseContactId(contactInfo.contactId));
+
+ contactInfo = getContactInfoFromEmail(PRIMARY_CONTACT_EMAIL);
+ assertNotNull(contactInfo);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+
}
- public void testManagedProfilePhoneLookup_insertedAndfound() throws RemoteException,
+ public void testManagedProfilePhoneAndEmailLookup_insertedAndfound() throws RemoteException,
OperationApplicationException, NotFoundException, IOException {
assertTrue(isManagedProfile());
// Insert ic_contact_picture as photo in managed contact
- insertContact(MANAGED_CONTACT_DISPLAY_NAME, MANAGED_CONTACT_PHONE,
+ insertContact(MANAGED_CONTACT_DISPLAY_NAME,
+ MANAGED_CONTACT_PHONE,
+ MANAGED_CONTACT_EMAIL,
com.android.cts.managedprofile.R.raw.ic_contact_picture);
ContactInfo contactInfo = getContactInfo(MANAGED_CONTACT_PHONE);
@@ -112,6 +133,56 @@
assertTrue(contactInfo.hasPhotoUri());
assertTrue(contactInfo.hasPhotoId());
assertFalse(isEnterpriseContactId(contactInfo.contactId));
+
+ contactInfo = getContactInfoFromEmail(MANAGED_CONTACT_EMAIL);
+ assertNotNull(contactInfo);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound() throws
+ RemoteException, OperationApplicationException, NotFoundException, IOException {
+ assertFalse(isManagedProfile());
+ insertContact(PRIMARY_CONTACT_DISPLAY_NAME_2, SHARED_CONTACT_PHONE,
+ SHARED_CONTACT_EMAIL,
+ com.android.cts.managedprofile.R.raw.ic_contact_picture);
+
+ ContactInfo contactInfo = getContactInfo(SHARED_CONTACT_PHONE);
+ assertNotNull(contactInfo);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+
+ contactInfo = getContactInfoFromEmail(SHARED_CONTACT_EMAIL);
+ assertNotNull(contactInfo);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound() throws
+ RemoteException, OperationApplicationException, NotFoundException, IOException {
+ assertTrue(isManagedProfile());
+ insertContact(MANAGED_CONTACT_DISPLAY_NAME_2, SHARED_CONTACT_PHONE,
+ SHARED_CONTACT_EMAIL, 0);
+
+ ContactInfo contactInfo = getContactInfo(SHARED_CONTACT_PHONE);
+ assertNotNull(contactInfo);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+
+ contactInfo = getContactInfoFromEmail(SHARED_CONTACT_EMAIL);
+ assertNotNull(contactInfo);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
}
public void testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact() {
@@ -124,7 +195,66 @@
assertTrue(isEnterpriseContactId(contactInfo.contactId));
}
- public void testPrimaryProfilePhoneLookup_canAccessPrimaryContact() {
+ public void testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(MANAGED_CONTACT_EMAIL);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ // Cannot get photo id in ENTERPRISE_CONTENT_FILTER_URI
+ assertFalse(contactInfo.hasPhotoId());
+ assertTrue(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(SHARED_CONTACT_PHONE);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(SHARED_CONTACT_EMAIL);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(SHARED_CONTACT_PHONE);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(SHARED_CONTACT_EMAIL);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME_2, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+
+ public void testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getContactInfo(MANAGED_CONTACT_PHONE);
+ assertNull(contactInfo);
+ }
+
+ public void testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getContactInfoFromEmail(MANAGED_CONTACT_EMAIL);
+ assertNull(contactInfo);
+ }
+
+ public void testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact() {
assertFalse(isManagedProfile());
ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
@@ -133,7 +263,16 @@
assertFalse(isEnterpriseContactId(contactInfo.contactId));
}
- public void testManagedProfilePhoneLookup_canAccessEnterpriseContact() {
+ public void testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(PRIMARY_CONTACT_EMAIL);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact() {
assertTrue(isManagedProfile());
ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
@@ -142,15 +281,48 @@
assertFalse(isEnterpriseContactId(contactInfo.contactId));
}
- public void testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact() {
- assertFalse(isManagedProfile());
- ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+ public void testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(MANAGED_CONTACT_EMAIL);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+ assertNull(contactInfo);
+ }
+
+ public void testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(PRIMARY_CONTACT_EMAIL);
assertNull(contactInfo);
}
public void testManagedProfilePhoneLookup_canNotAccessPrimaryContact() {
assertTrue(isManagedProfile());
- ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+ ContactInfo contactInfo = getContactInfo(PRIMARY_CONTACT_PHONE);
+ assertNull(contactInfo);
+ }
+
+ public void testManagedProfileEmailLookup_canNotAccessPrimaryContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getContactInfoFromEmail(PRIMARY_CONTACT_EMAIL);
+ assertNull(contactInfo);
+ }
+
+ public void testPrimaryProfileEnterpriseEmailLookup_canNotAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfoFromEmail(MANAGED_CONTACT_EMAIL);
+ assertNull(contactInfo);
+ }
+
+ public void testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
assertNull(contactInfo);
}
@@ -186,9 +358,13 @@
return mDevicePolicyManager.isProfileOwnerApp(adminPackage);
}
- private void insertContact(String displayName, String phoneNumber, int photoResId)
- throws RemoteException,
- OperationApplicationException, NotFoundException, IOException {
+ private void insertContact(String displayName, String phoneNumber, int photoResId) throws
+ RemoteException, OperationApplicationException, NotFoundException, IOException {
+ insertContact(displayName, phoneNumber, null, photoResId);
+ }
+
+ private void insertContact(String displayName, String phoneNumber, String email, int photoResId)
+ throws RemoteException, OperationApplicationException, NotFoundException, IOException {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation
.newInsert(ContactsContract.RawContacts.CONTENT_URI)
@@ -216,6 +392,18 @@
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE,
Phone.TYPE_MOBILE)
.build());
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS,
+ email)
+ .withValue(ContactsContract.CommonDataKinds.Email.TYPE,
+ Email.TYPE_WORK)
+ .build());
+
if (photoResId != 0) {
InputStream phoneInputStream = mContext.getResources().openRawResource(photoResId);
byte[] rawPhoto = getByteFromStream(phoneInputStream);
@@ -256,17 +444,56 @@
return result;
}
+ private ContactInfo getContactInfoFromEmailUri(Uri emailLookupUri, String email) {
+ Uri uri = Uri.withAppendedPath(emailLookupUri, Uri.encode(email));
+ Cursor cursor = mResolver.query(uri,
+ new String[] {
+ Email.CONTACT_ID,
+ Email.DISPLAY_NAME_PRIMARY,
+ Email.PHOTO_URI,
+ Email.PHOTO_ID,
+ Email.PHOTO_THUMBNAIL_URI,
+ }, null, null, null);
+ if (cursor == null) {
+ return null;
+ }
+ ContactInfo result = null;
+ if (cursor.moveToFirst()) {
+ result = new ContactInfo(
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Email.CONTACT_ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Email.DISPLAY_NAME_PRIMARY)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Email.PHOTO_URI)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Email.PHOTO_THUMBNAIL_URI)),
+ cursor.getString(cursor.getColumnIndexOrThrow(
+ Email.PHOTO_ID)));
+ }
+ cursor.close();
+ return result;
+ }
+
private ContactInfo getContactInfo(String phoneNumber) {
return getContactInfoFromUri(PhoneLookup.CONTENT_FILTER_URI,
phoneNumber);
}
+ private ContactInfo getContactInfoFromEmail(String email) {
+ return getContactInfoFromEmailUri(Email.CONTENT_LOOKUP_URI, email);
+ }
+
private ContactInfo getEnterpriseContactInfo(String phoneNumber) {
return getContactInfoFromUri(
PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
phoneNumber);
}
+ private ContactInfo getEnterpriseContactInfoFromEmail(String email) {
+ return getContactInfoFromEmailUri(Email.ENTERPRISE_CONTENT_LOOKUP_URI, email);
+ }
+
private void removeAllTestContactsInProfile() {
ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI)
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index c0e6479..7e0d9595 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -54,7 +54,7 @@
protected static final String ADMIN_RECEIVER_TEST_CLASS =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
- private CtsBuildHelper mCtsBuild;
+ protected CtsBuildHelper mCtsBuild;
private HashSet<String> mAvailableFeatures;
protected boolean mHasFeature;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index b84e9fe..65bb877 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -20,6 +20,8 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
+import java.io.File;
+
/**
* Set of tests for Device Owner use cases.
*/
@@ -28,6 +30,10 @@
private static final String DEVICE_OWNER_PKG = "com.android.cts.deviceowner";
private static final String DEVICE_OWNER_APK = "CtsDeviceOwnerApp.apk";
+ private static final String TEST_APP_APK = "CtsSimpleApp.apk";
+ private static final String TEST_APP_PKG = "com.android.cts.launcherapps.simpleapp";
+ private static final String TEST_APP_LOCATION = "/data/local/tmp/";
+
private static final String ADMIN_RECEIVER_TEST_CLASS =
DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
private static final String CLEAR_DEVICE_OWNER_TEST_CLASS =
@@ -81,6 +87,19 @@
executeDeviceOwnerTest("ScreenCaptureDisabledTest");
}
+ public void testSilentPackageInstaller() throws Exception {
+ final File apk = mCtsBuild.getTestApp(TEST_APP_APK);
+ try {
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ assertTrue(getDevice().pushFile(apk, TEST_APP_LOCATION + apk.getName()));
+ executeDeviceOwnerTest("SilentPackageInstallerTest");
+ } finally {
+ String command = "rm " + TEST_APP_LOCATION + apk.getName();
+ String commandOutput = getDevice().executeShellCommand(command);
+ getDevice().uninstallPackage(TEST_APP_PKG);
+ }
+ }
+
private void executeDeviceOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
index f8c2e7d..43f1f5a 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsProfileTest.java
@@ -32,10 +32,7 @@
@Override
protected void setUp() throws Exception {
super.setUp();
-
- // We need multi user to be supported in order to create a profile of the user owner.
- mHasFeature = mHasFeature && (getMaxNumberOfUsersSupported() > 1);
-
+ mHasFeature = mHasFeature && hasDeviceFeature("android.software.managed_users");
if (mHasFeature) {
removeTestUsers();
installTestApps();
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 5c0126d..bb8cd3f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -20,6 +20,8 @@
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.log.LogUtil.CLog;
+import junit.framework.AssertionFailedError;
+
/**
* Set of tests for Managed Profile use cases.
*/
@@ -45,7 +47,7 @@
super.setUp();
// We need multi user to be supported in order to create a profile of the user owner.
- mHasFeature = mHasFeature && (getMaxNumberOfUsersSupported() > 1) && hasDeviceFeature(
+ mHasFeature = mHasFeature && hasDeviceFeature(
"android.software.managed_users");
if (mHasFeature) {
@@ -91,11 +93,16 @@
assertFalse(listUsers().contains(mUserId));
}
- public void testMaxUsersStrictlyMoreThanOne() throws Exception {
- if (hasDeviceFeature("android.software.managed_users")) {
- assertTrue("Device must support more than 1 user "
- + "if android.software.managed_users feature is available",
- getMaxNumberOfUsersSupported() > 1);
+ public void testMaxOneManagedProfile() throws Exception {
+ int newUserId = -1;
+ try {
+ newUserId = createManagedProfile();
+ } catch (AssertionFailedError expected) {
+ }
+ if (newUserId > 0) {
+ removeUser(newUserId);
+ fail(mHasFeature ? "Device must allow creating only one managed profile"
+ : "Device must not allow creating a managed profile");
}
}
@@ -247,39 +254,111 @@
try {
// Insert Primary profile Contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testPrimaryProfilePhoneLookup_insertedAndfound", 0));
+ "testPrimaryProfilePhoneAndEmailLookup_insertedAndfound", 0));
// Insert Managed profile Contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testManagedProfilePhoneLookup_insertedAndfound", mUserId));
+ "testManagedProfilePhoneAndEmailLookup_insertedAndfound", mUserId));
+ // Insert a primary contact with same phone & email as other enterprise contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileDuplicatedPhoneEmailContact_insertedAndfound", 0));
+ // Insert a enterprise contact with same phone & email as other primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileDuplicatedPhoneEmailContact_insertedAndfound", mUserId));
+
// Set cross profile caller id to enabled
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testSetCrossProfileCallerIdDisabled_false", mUserId));
- // Managed user can use ENTERPRISE_CONTENT_FILTER_URI
- // To access managed contacts but not primary contacts
+ // Primary user cannot use ordinary phone/email lookup api to access managed contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testManagedProfilePhoneLookup_canAccessEnterpriseContact", mUserId));
+ "testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", 0));
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
-
- // Primary user can use ENTERPRISE_CONTENT_FILTER_URI
- // To access both primary and managed contacts
+ "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", 0));
+ // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessPrimaryContact", 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessPrimaryContact", 0));
+ // Primary user can use ENTERPRISE_CONTENT_FILTER_URI to access managed profile contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact", 0));
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
- "testPrimaryProfilePhoneLookup_canAccessPrimaryContact", 0));
+ "testPrimaryProfileEnterpriseEmailLookup_canAccessEnterpriseContact", 0));
+ // When there exist contacts with the same phone/email in primary & enterprise,
+ // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access the primary contact.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
+ 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
+ 0));
+
+ // Managed user cannot use ordinary phone/email lookup api to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mUserId));
+ // Managed user can use ENTERPRISE_CONTENT_FILTER_URI to access enterprise contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookup_canAccessEnterpriseContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookup_canAccessEnterpriseContact", mUserId));
+ // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact", mUserId));
+ // When there exist contacts with the same phone/email in primary & enterprise,
+ // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access the enterprise contact.
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
+ mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
+ mUserId));
// Set cross profile caller id to disabled
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testSetCrossProfileCallerIdDisabled_true", mUserId));
- // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to access managed contacts
+ // Primary user cannot use ordinary phone/email lookup api to access managed contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact", 0));
- // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEmailLookup_canNotAccessEnterpriseContact", 0));
+ // Primary user cannot use ENTERPRISE_CONTENT_FILTER_URI to access managed contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canNotAccessEnterpriseContact", 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookup_canNotAccessEnterpriseContact", 0));
+ // When there exist contacts with the same phone/email in primary & enterprise,
+ // primary user can use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterpriseEmailLookupDuplicated_canAccessPrimaryContact",
+ 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookupDuplicated_canAccessPrimaryContact",
+ 0));
+
+ // Managed user cannot use ordinary phone/email lookup api to access primary contacts
assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
"testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEmailLookup_canNotAccessPrimaryContact", mUserId));
+ // Managed user cannot use ENTERPRISE_CONTENT_FILTER_URI to access primary contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookup_canNotAccessPrimaryContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookup_canNotAccessPrimaryContact", mUserId));
+ // When there exist contacts with the same phone/email in primary & enterprise,
+ // managed user can use ENTERPRISE_CONTENT_FILTER_URI to access enterprise contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterpriseEmailLookupDuplicated_canAccessEnterpriseContact",
+ mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfileEnterprisePhoneLookupDuplicated_canAccessEnterpriseContact",
+ mUserId));
} finally {
// Clean up in managed profile and primary profile
runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index 6e2c90e..a0d3167 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -106,32 +106,27 @@
/* obtain sepolicy file from running device */
devicePolicyFile = File.createTempFile("sepolicy", ".tmp");
devicePolicyFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/sys/fs/selinux/policy",
- devicePolicyFile.getAbsolutePath());
+ mDevice.pullFile("/sys/fs/selinux/policy", devicePolicyFile);
/* obtain seapp_contexts file from running device */
deviceSeappFile = File.createTempFile("seapp_contexts", ".tmp");
deviceSeappFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/seapp_contexts",
- deviceSeappFile.getAbsolutePath());
+ mDevice.pullFile("/seapp_contexts", deviceSeappFile);
/* obtain file_contexts file from running device */
deviceFcFile = File.createTempFile("file_contexts", ".tmp");
deviceFcFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/file_contexts",
- deviceFcFile.getAbsolutePath());
+ mDevice.pullFile("/file_contexts", deviceFcFile);
/* obtain property_contexts file from running device */
devicePcFile = File.createTempFile("property_contexts", ".tmp");
devicePcFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/property_contexts",
- devicePcFile.getAbsolutePath());
+ mDevice.pullFile("/property_contexts", devicePcFile);
/* obtain service_contexts file from running device */
deviceSvcFile = File.createTempFile("service_contexts", ".tmp");
deviceSvcFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/service_contexts",
- deviceSvcFile.getAbsolutePath());
+ mDevice.pullFile("/service_contexts", deviceSvcFile);
/* retrieve the AOSP *_contexts files from jar */
aospSeappFile = copyResourceToTempFile("/general_seapp_contexts");
diff --git a/hostsidetests/theme/app/Android.mk b/hostsidetests/theme/app/Android.mk
index 1be2983..70623cb 100644
--- a/hostsidetests/theme/app/Android.mk
+++ b/hostsidetests/theme/app/Android.mk
@@ -26,8 +26,6 @@
LOCAL_PROGUARD_ENABLED := disabled
-LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
-
LOCAL_SRC_FILES := $(call all-java-files-under, src)
#Flags to tell the Android Asset Packaging Tool not to strip for some densities
diff --git a/hostsidetests/theme/app/AndroidManifest.xml b/hostsidetests/theme/app/AndroidManifest.xml
index 2f8fb3b..81a4d9d 100755
--- a/hostsidetests/theme/app/AndroidManifest.xml
+++ b/hostsidetests/theme/app/AndroidManifest.xml
@@ -24,7 +24,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>
<uses-library android:name="android.test.runner" />
- <activity android:name=".HoloDeviceActivity" >
+ <activity android:name=".HoloDeviceActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -37,13 +37,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
- <activity android:name=".CaptureActivity" />
</application>
- <!-- self-instrumenting test package. -->
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.theme.app"
- android:label="Generates Theme reference images"/>
-
</manifest>
-
diff --git a/hostsidetests/theme/app/res/layout/holo_test.xml b/hostsidetests/theme/app/res/layout/holo_test.xml
index 0aef953..3eed4ba 100644
--- a/hostsidetests/theme/app/res/layout/holo_test.xml
+++ b/hostsidetests/theme/app/res/layout/holo_test.xml
@@ -15,6 +15,8 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
+ android:focusable="true"
+ android:keepScreenOn="true"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.theme.app.ReferenceViewGroup
diff --git a/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java b/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java
deleted file mode 100644
index d241ff6..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/CaptureActivity.java
+++ /dev/null
@@ -1,87 +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.theme.app;
-
-import android.app.Activity;
-import android.content.Intent;
-import android.os.Bundle;
-
-import java.util.concurrent.CountDownLatch;
-
-/**
- * Iterates through all themes and all layouts, starting the Activity to capture the images.
- */
-public class CaptureActivity extends Activity {
-
- private static final int REQUEST_CODE = 1;
-
- private static final int NUM_THEMES = 24;
-
- private static final int NUM_LAYOUTS = 47;
-
- private final CountDownLatch mLatch = new CountDownLatch(1);
-
- private int mCurrentTheme = 0;
-
- private int mCurrentLayout = 0;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- generateNextImage();
- }
-
- /**
- * Starts the activity to generate the next image.
- */
- private void generateNextImage() {
- Intent intent = new Intent(this, HoloDeviceActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- intent.putExtra(HoloDeviceActivity.EXTRA_THEME, mCurrentTheme);
- intent.putExtra(HoloDeviceActivity.EXTRA_LAYOUT, mCurrentLayout);
- startActivityForResult(intent, REQUEST_CODE);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (requestCode == REQUEST_CODE) {
- if (resultCode == RESULT_OK) {
- mCurrentLayout++;
- if (mCurrentLayout >= NUM_LAYOUTS) {
- mCurrentLayout = 0;
- mCurrentTheme++;
- }
- if (mCurrentTheme < NUM_THEMES) {
- generateNextImage();
- } else {
- finish();
- }
- } else {
- finish();
- }
- }
- }
-
- public void finish() {
- mLatch.countDown();
- super.finish();
- }
-
- public void waitForCompletion() throws InterruptedException {
- mLatch.await();
- }
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java b/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java
deleted file mode 100644
index 7e2b2c9..0000000
--- a/hostsidetests/theme/app/src/android/theme/app/CaptureHolo.java
+++ /dev/null
@@ -1,37 +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.theme.app;
-
-import android.app.KeyguardManager;
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-
-public class CaptureHolo extends ActivityInstrumentationTestCase2<CaptureActivity> {
-
- public CaptureHolo() {
- super(CaptureActivity.class);
- }
-
- public void testCaptureHolo() throws Exception {
- setActivityInitialTouchMode(true);
- CaptureActivity activity = getActivity();
- KeyguardManager keyguardManager =
- (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
- keyguardManager.newKeyguardLock("holo_capture").disableKeyguard();
- activity.waitForCompletion();
- }
-}
diff --git a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
index 3939979..8ae9fc8 100644
--- a/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
+++ b/hostsidetests/theme/app/src/android/theme/app/HoloDeviceActivity.java
@@ -36,6 +36,7 @@
import android.util.Log;
import android.view.View;
import android.widget.CheckBox;
+import android.widget.DatePicker;
import android.widget.LinearLayout;
import java.io.File;
@@ -50,65 +51,88 @@
public static final String EXTRA_THEME = "holo_theme_extra";
- public static final String EXTRA_LAYOUT = "holo_layout_extra";
-
- public static final String EXTRA_TIMEOUT = "holo_timeout_extra";
-
private static final String TAG = HoloDeviceActivity.class.getSimpleName();
- private static final int TIMEOUT = 1 * 1000;//1 sec
+ /**
+ * The duration of the CalendarView adjustement to settle to its final position.
+ */
+ private static final long CALENDAR_VIEW_ADJUSTMENT_DURATION = 540;
- private View mView;
-
- private String mName;
-
- private Bitmap mBitmap;
+ private Theme mTheme;
private ReferenceViewGroup mViewGroup;
+ private int mLayoutIndex;
+
@Override
- public void onCreate(Bundle icicle) {
+ protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
- setUpUi(getIntent());
+
+ mTheme = THEMES[getIntent().getIntExtra(EXTRA_THEME, 0)];
+ setTheme(mTheme.mId);
+ setContentView(R.layout.holo_test);
+ mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
}
@Override
- public void onNewIntent(Intent intent) {
- super.onNewIntent(intent);
- setUpUi(intent);
+ protected void onResume() {
+ super.onResume();
+ setNextLayout();
+ }
+
+ @Override
+ protected void onPause() {
+ if (!isFinishing()) {
+ // The Activity got paused for some reasons, for finish it as the host won't move on to
+ // the next theme otherwise.
+ Log.w(TAG, "onPause called without a call to finish().");
+ finish();
+ }
+ super.onPause();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mLayoutIndex != LAYOUTS.length) {
+ Log.w(TAG, "Not all layouts got rendered: " + mLayoutIndex);
+ }
+ Log.i(TAG, "OKAY:" + mTheme.mName);
+ super.onDestroy();
}
/**
- * Configures the UI with the given intent
+ * Sets the next layout in the UI.
*/
- private void setUpUi(Intent intent) {
- final Theme theme = themes[intent.getIntExtra(EXTRA_THEME, 0)];
- final Layout layout = layouts[intent.getIntExtra(EXTRA_LAYOUT, 0)];
- final int timeout = intent.getIntExtra(EXTRA_TIMEOUT, TIMEOUT);
-
- setTheme(theme.mId);
- setContentView(R.layout.holo_test);
-
- mViewGroup = (ReferenceViewGroup) findViewById(R.id.reference_view_group);
-
- mView = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
- mViewGroup.addView(mView);
- if (layout.mModifier != null) {
- layout.mModifier.modifyView(mView);
+ private void setNextLayout() {
+ if (mLayoutIndex >= LAYOUTS.length) {
+ finish();
+ return;
}
- mViewGroup.measure(0, 0);
- mViewGroup.layout(0, 0, mViewGroup.getMeasuredWidth(), mViewGroup.getMeasuredHeight());
- mView.setFocusable(false);
- mName = String.format("%s_%s", theme.mName, layout.mName);
+ final Layout layout = LAYOUTS[mLayoutIndex++];
+ final String layoutName = String.format("%s_%s", mTheme.mName, layout.mName);
- final Handler handler = new Handler();
- handler.postDelayed(new Runnable() {
+ mViewGroup.removeAllViews();
+ final View view = getLayoutInflater().inflate(layout.mId, mViewGroup, false);
+ if (layout.mModifier != null) {
+ layout.mModifier.modifyView(view);
+ }
+ mViewGroup.addView(view);
+ view.setFocusable(false);
+
+ final Runnable generateBitmapRunnable = new Runnable() {
@Override
public void run() {
- new GenerateBitmapTask().execute();
+ new GenerateBitmapTask(view, layoutName).execute();
}
- }, timeout);
- setResult(RESULT_CANCELED);//On success will be changed to OK
+ };
+
+ if (view instanceof DatePicker) {
+ // DatePicker uses a CalendarView that has a non-configurable adjustment duration of
+ // 540ms
+ view.postDelayed(generateBitmapRunnable, CALENDAR_VIEW_ADJUSTMENT_DURATION);
+ } else {
+ view.post(generateBitmapRunnable);
+ }
}
/**
@@ -117,12 +141,14 @@
*/
private class GenerateBitmapTask extends AsyncTask<Void, Void, Boolean> {
- @Override
- protected void onPreExecute() {
- final View v = mView;
- mBitmap = Bitmap.createBitmap(v.getWidth(), v.getHeight(), Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(mBitmap);
- v.draw(canvas);
+ private final View mView;
+
+ private final String mName;
+
+ public GenerateBitmapTask(final View view, final String name) {
+ super();
+ mView = view;
+ mName = name;
}
@Override
@@ -131,6 +157,16 @@
Log.i(TAG, "External storage for saving bitmaps is not mounted");
return false;
}
+ if (mView.getWidth() == 0 || mView.getHeight() == 0) {
+ Log.w(TAG, "Unable to draw View due to incorrect size: " + mName);
+ return false;
+ }
+
+ final Bitmap bitmap = Bitmap.createBitmap(
+ mView.getWidth(), mView.getHeight(), Bitmap.Config.ARGB_8888);
+ final Canvas canvas = new Canvas(bitmap);
+
+ mView.draw(canvas);
final File dir = new File(Environment.getExternalStorageDirectory(), "cts-holo-assets");
dir.mkdirs();
boolean success = false;
@@ -139,27 +175,23 @@
FileOutputStream stream = null;
try {
stream = new FileOutputStream(file);
- mBitmap.compress(CompressFormat.PNG, 100, stream);
+ success = bitmap.compress(CompressFormat.PNG, 100, stream);
} finally {
if (stream != null) {
stream.close();
}
}
- success = true;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
} finally {
- mBitmap.recycle();
- mBitmap = null;
+ bitmap.recycle();
}
return success;
}
@Override
protected void onPostExecute(Boolean success) {
- Log.i(TAG, (success ? "OKAY" : "ERROR") + ":" + mName);
- setResult(RESULT_OK);
- finish();
+ setNextLayout();
}
}
@@ -178,7 +210,7 @@
}
}
- private static final Theme[] themes = {
+ private static final Theme[] THEMES = {
new Theme(android.R.style.Theme_Holo,
"holo"),
new Theme(android.R.style.Theme_Holo_Dialog,
@@ -247,7 +279,7 @@
}
}
- private static final Layout[] layouts = {
+ private static final Layout[] LAYOUTS = {
new Layout(R.layout.button, "button", null),
new Layout(R.layout.button, "button_pressed", new ViewPressedModifier()),
new Layout(R.layout.checkbox, "checkbox", null),
diff --git a/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java b/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
index 077d8d7..8d2461b 100644
--- a/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
+++ b/hostsidetests/theme/app/src/android/theme/app/ReferenceViewGroup.java
@@ -72,10 +72,6 @@
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
- if (!changed) {
- return;
- }
-
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
diff --git a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
index da94b15..8326b1f 100644
--- a/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
+++ b/hostsidetests/theme/src/android/theme/cts/ThemeHostTest.java
@@ -52,8 +52,6 @@
private static final String TAG = ThemeHostTest.class.getSimpleName();
- private static final int CAPTURE_TIMEOUT = 500;//0.5sec in ms
-
private static final int ADB_TIMEOUT = 60 * 60 * 1000;//60mins in ms
/** The package name of the APK. */
@@ -69,6 +67,8 @@
private static final String START_CMD = String.format(
"am start -W -a android.intent.action.MAIN -n %s/%s.%s", PACKAGE, PACKAGE, CLASS);
+ private static final String CLEAR_GENERATED_CMD = "rm -rf /sdcard/cts-holo-assets/*.png";
+
private static final String STOP_CMD = String.format("am force-stop %s", PACKAGE);
private static final String HARDWARE_TYPE_CMD = "dumpsys | grep android.hardware.type";
@@ -87,10 +87,6 @@
// Intent extra keys
private static final String EXTRA_THEME = "holo_theme_extra";
- private static final String EXTRA_LAYOUT = "holo_layout_extra";
-
- private static final String EXTRA_TIMEOUT = "holo_timeout_extra";
-
private static final String[] THEMES = {
"holo",
"holo_dialog",
@@ -211,7 +207,8 @@
String[] options = {AbiUtils.createAbiFlag(mAbi.getName())};
// Install the APK on the device.
mDevice.installPackage(app, false, options);
-
+ // Remove previously generated images.
+ mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
final String densityProp;
if (mDevice.getSerialNumber().startsWith("emulator-")) {
@@ -261,6 +258,8 @@
mExecutionService.shutdown();
// Remove the APK.
mDevice.uninstallPackage(PACKAGE);
+ // Remove generated images.
+ mDevice.executeShellCommand(CLEAR_GENERATED_CMD);
super.tearDown();
}
@@ -272,7 +271,6 @@
return;
}
-
if (mReferences.isEmpty()) {
Log.logAndDisplay(LogLevel.INFO, TAG,
"Skipped HoloThemes test due to no reference images");
@@ -282,20 +280,18 @@
int numTasks = 0;
for (int i = 0; i < NUM_THEMES; i++) {
final String themeName = THEMES[i];
+ runCapture(i, themeName);
for (int j = 0; j < NUM_LAYOUTS; j++) {
final String name = String.format("%s_%s", themeName, LAYOUTS[j]);
- if (runCapture(i, j, name)) {
- final File ref = mReferences.get(name + ".png");
- if (!ref.exists()) {
- Log.logAndDisplay(LogLevel.INFO, TAG,
- "Skipping theme test due to missing reference for reference image " + name);
- continue;
- }
- mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
- numTasks++;
- } else {
- Log.logAndDisplay(LogLevel.ERROR, TAG, "Capture failed: " + name);
+ final File ref = mReferences.get(name + ".png");
+ if (!ref.exists()) {
+ Log.logAndDisplay(LogLevel.INFO, TAG,
+ "Skipping theme test due to missing reference for reference image " +
+ name);
+ continue;
}
+ mCompletionService.submit(new ComparisonTask(mDevice, ref, name));
+ numTasks++;
}
}
int failures = 0;
@@ -305,11 +301,9 @@
assertTrue(failures + " failures in theme test", failures == 0);
}
- private boolean runCapture(int themeId, int layoutId, String imageName) throws Exception {
+ private void runCapture(int themeId, String themeName) throws Exception {
final StringBuilder sb = new StringBuilder(START_CMD);
sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_THEME, themeId));
- sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_LAYOUT, layoutId));
- sb.append(String.format(INTENT_INTEGER_EXTRA, EXTRA_TIMEOUT, CAPTURE_TIMEOUT));
final String startCommand = sb.toString();
// Clear logcat
mDevice.executeAdbCommand("logcat", "-c");
@@ -318,9 +312,8 @@
// Start activity
mDevice.executeShellCommand(startCommand);
- boolean success = false;
boolean waiting = true;
- while (waiting) {
+ do {
// Dump logcat.
final String logs = mDevice.executeAdbCommand(
"logcat", "-v", "brief", "-d", CLASS + ":I", "*:S");
@@ -331,20 +324,14 @@
if (line.startsWith("I/" + CLASS)) {
final String[] lineSplit = line.split(":");
final String s = lineSplit[1].trim();
- final String imageNameGenerated = lineSplit[2].trim();
- if (s.equals("OKAY") && imageNameGenerated.equals(imageName)) {
- success = true;
- waiting = false;
- } else if (s.equals("ERROR") && imageNameGenerated.equals(imageName)) {
- success = false;
+ final String themeNameGenerated = lineSplit[2].trim();
+ if (s.equals("OKAY") && themeNameGenerated.equals(themeName)) {
waiting = false;
}
}
}
in.close();
- }
-
- return success;
+ } while (waiting);
}
private static String getDensityBucket(int density) {
diff --git a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
index adcc06f..2933b0b 100644
--- a/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
+++ b/libs/deviceutillegacy/src/android/webkit/cts/WebViewOnUiThread.java
@@ -319,11 +319,11 @@
});
}
- public void postMessageToMainFrame(final WebMessage message, final Uri targetOrigin) {
+ public void postWebMessage(final WebMessage message, final Uri targetOrigin) {
runOnUiThread(new Runnable() {
@Override
public void run() {
- mWebView.postMessageToMainFrame(message, targetOrigin);
+ mWebView.postWebMessage(message, targetOrigin);
}
});
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
index 38a753d..4f549f8 100644
--- a/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
+++ b/tests/JobScheduler/src/android/jobscheduler/MockJobService.java
@@ -46,7 +46,7 @@
public boolean onStartJob(JobParameters params) {
Log.i(TAG, "Test job executing: " + params.getJobId());
- TestEnvironment.getTestEnvironment().notifyExecution(params.getJobId());
+ TestEnvironment.getTestEnvironment().notifyExecution(params);
return false; // No work to do.
}
@@ -63,10 +63,10 @@
public static final class TestEnvironment {
private static TestEnvironment kTestEnvironment;
- public static final int INVALID_JOB_ID = -1;
+ //public static final int INVALID_JOB_ID = -1;
private CountDownLatch mLatch;
- private int mExecutedJobId;
+ private JobParameters mExecutedJobParameters;
public static TestEnvironment getTestEnvironment() {
if (kTestEnvironment == null) {
@@ -75,6 +75,10 @@
return kTestEnvironment;
}
+ public JobParameters getLastJobParameters() {
+ return mExecutedJobParameters;
+ }
+
/**
* Block the test thread, waiting on the JobScheduler to execute some previously scheduled
* job on this service.
@@ -93,9 +97,9 @@
return !mLatch.await(DEFAULT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
}
- private void notifyExecution(int jobId) {
- Log.d(TAG, "Job executed:" + jobId);
- mExecutedJobId = jobId;
+ private void notifyExecution(JobParameters params) {
+ Log.d(TAG, "Job executed:" + params.getJobId());
+ mExecutedJobParameters = params;
mLatch.countDown();
}
@@ -111,7 +115,7 @@
/** Called in each testCase#setup */
public void setUp() {
mLatch = null;
- mExecutedJobId = INVALID_JOB_ID;
+ mExecutedJobParameters = null;
}
}
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
index ed9cadd..40b67c8 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TimingConstraintsTest.java
@@ -17,6 +17,7 @@
import android.annotation.TargetApi;
import android.app.job.JobInfo;
+import android.app.job.JobParameters;
/**
* Schedules jobs with various timing constraints and ensures that they are executed when
@@ -26,6 +27,8 @@
public class TimingConstraintsTest extends ConstraintTest {
private static final int TIMING_JOB_ID = TimingConstraintsTest.class.hashCode() + 0;
private static final int CANCEL_JOB_ID = TimingConstraintsTest.class.hashCode() + 1;
+ private static final int EXPIRED_JOB_ID = TimingConstraintsTest.class.hashCode() + 2;
+ private static final int UNEXPIRED_JOB_ID = TimingConstraintsTest.class.hashCode() + 3;
public void testScheduleOnce() throws Exception {
JobInfo oneTimeJob = new JobInfo.Builder(TIMING_JOB_ID, kJobServiceComponent)
@@ -63,4 +66,44 @@
assertTrue("Cancel failed: job executed when it shouldn't have.",
kTestEnvironment.awaitTimeout());
}
+
+ /**
+ * Ensure that when a job is executed because its deadline has expired, that
+ * {@link JobParameters#isOverrideDeadlineExpired()} returns the correct value.
+ */
+ public void testJobParameters_expiredDeadline() throws Exception {
+
+ JobInfo deadlineJob =
+ new JobInfo.Builder(EXPIRED_JOB_ID, kJobServiceComponent)
+ .setOverrideDeadline(2000L)
+ .build();
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(deadlineJob);
+ assertTrue("Failed to execute deadline job", kTestEnvironment.awaitExecution());
+ assertTrue("Job that had its deadline expire didn't have" +
+ " JobParameters#isOverrideDeadlineExpired=true",
+ kTestEnvironment.getLastJobParameters().isOverrideDeadlineExpired());
+ }
+
+
+ /**
+ * Ensure that when a job is executed and its deadline hasn't expired, that
+ * {@link JobParameters#isOverrideDeadlineExpired()} returns the correct value.
+ */
+ public void testJobParameters_unexpiredDeadline() throws Exception {
+
+ JobInfo deadlineJob =
+ new JobInfo.Builder(UNEXPIRED_JOB_ID, kJobServiceComponent)
+ .setMinimumLatency(500L)
+ .setRequiresCharging(true)
+ .build();
+ kTestEnvironment.setExpectedExecutions(1);
+ mJobScheduler.schedule(deadlineJob);
+ // Run everything by pretending the device was just plugged in.
+ sendExpediteStableChargingBroadcast();
+ assertTrue("Failed to execute non-deadline job", kTestEnvironment.awaitExecution());
+ assertFalse("Job that ran early (unexpired) didn't have" +
+ " JobParameters#isOverrideDeadlineExpired=false",
+ kTestEnvironment.getLastJobParameters().isOverrideDeadlineExpired());
+ }
}
\ No newline at end of file
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 1e29e10..ecb7050 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -89,31 +89,6 @@
bug: 17993121
},
{
- description: "A few WebGL tests are known to fail in WebView",
- names: [
- "android.webgl.cts.WebGLTest#test_conformance_extensions_oes_texture_float_with_video_html",
- "android.webgl.cts.WebGLTest#test_conformance_renderbuffers_framebuffer_object_attachment_html",
- "android.webgl.cts.WebGLTest#test_conformance_rendering_multisample_corruption_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgb565_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba4444_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba5551_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_texture_npot_html",
- "android.webgl.cts.WebGLTest#test_conformance_textures_texture_npot_video_html",
- "android.webgl.cts.WebGLTest#test_conformance_glsl_misc_empty_main_vert_html",
- "android.webgl.cts.WebGLTest#test_conformance_glsl_misc_gl_position_unset_vert_html",
- "android.webgl.cts.WebGLTest#test_conformance_misc_webgl_specific_html"
- ],
- bug: 17748398
-},
-{
- description: "WebGL test uniformMatrixBadArgs is too strict. Disabled until it's fixed upstream.",
- names: [
- "android.webgl.cts.WebGLTest#test_conformance_more_functions_uniformMatrixBadArgs_html"
- ],
- bug: 18638404
-},
-{
description: "permissions for the API previously used in the test has changed, making it impossible to pass",
names: [
"android.openglperf.cts.GlAppSwitchTest#testGlActivitySwitchingFast",
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
index e633f1f..998a005 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -219,7 +219,8 @@
hasTestProcess = true;
}
}
- assertTrue(hasSystemProcess && hasTestProcess);
+ // For security reasons the system process is not exposed.
+ assertTrue(!hasSystemProcess && hasTestProcess);
for (RunningAppProcessInfo ra : list) {
if (ra.processName.equals("com.android.cts.app.stub:remote")) {
diff --git a/tests/tests/deqp/gles31-temporary-failures.txt b/tests/tests/deqp/gles31-temporary-failures.txt
index d921091..c3986eb 100644
--- a/tests/tests/deqp/gles31-temporary-failures.txt
+++ b/tests/tests/deqp/gles31-temporary-failures.txt
@@ -387,6 +387,38 @@
dEQP-GLES31.functional.copy_image.compressed.viewclass_etc2_rgb.srgb8_etc2_srgb8_etc2#cubemap_to_cubemap
dEQP-GLES31.functional.copy_image.compressed.viewclass_etc2_rgb.srgb8_etc2_srgb8_etc2#cubemap_to_texture2d
dEQP-GLES31.functional.copy_image.compressed.viewclass_etc2_rgb.srgb8_etc2_srgb8_etc2#texture2d_to_cubemap
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x10_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x10_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x5_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x5_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x6_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x6_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x8_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_10x8_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_12x10_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_12x10_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_12x12_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_12x12_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_4x4_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_4x4_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_5x4_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_5x4_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_5x5_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_5x5_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_6x5_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_6x5_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_6x6_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_6x6_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x5_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x5_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x6_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x6_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x8_khr_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_astc_8x8_khr_rgba32ui#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_etc2_eac_rgba32i#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_etc2_eac_rgba32i#texture2d_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_etc2_eac_rgba32ui#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.srgb8_alpha8_etc2_eac_rgba32ui#texture2d_to_renderbuffer
dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.rg11_eac_rgba32f#cubemap_to_cubemap
dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.rg11_eac_rgba32f#cubemap_to_texture2d
dEQP-GLES31.functional.copy_image.mixed.viewclass_128_bits_mixed.rg11_eac_rgba32f#cubemap_to_texture2d_array
@@ -3240,6 +3272,30 @@
dEQP-GLES31.functional.copy_image.mixed.viewclass_64_bits_mixed.signed_r11_eac_rgba16ui#texture2d_to_texture2d
dEQP-GLES31.functional.copy_image.mixed.viewclass_64_bits_mixed.signed_r11_eac_rgba16ui#texture2d_to_texture2d_array
dEQP-GLES31.functional.copy_image.mixed.viewclass_64_bits_mixed.signed_r11_eac_rgba16ui#texture2d_to_texture3d
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_24_bits.srgb8_rgb8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.r11f_g11f_b10f_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.r32f_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.r32i_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.r32ui_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rg16f_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rg16i_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rg16ui_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgb10_a2_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgb10_a2ui_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgb9_e5_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgba8_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgba8i_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.rgba8ui_srgb8_alpha8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_r32i#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_r32ui#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rg16i#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rg16ui#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rgb10_a2#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rgb10_a2ui#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rgba8#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rgba8i#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_rgba8ui#cubemap_to_renderbuffer
+dEQP-GLES31.functional.copy_image.non_compressed.viewclass_32_bits.srgb8_alpha8_srgb8_alpha8#cubemap_to_renderbuffer
dEQP-GLES31.functional.copy_image.non_compressed.viewclass_128_bits.rgba32f_rgba32f#cubemap_to_cubemap
dEQP-GLES31.functional.copy_image.non_compressed.viewclass_128_bits.rgba32f_rgba32f#cubemap_to_texture2d
dEQP-GLES31.functional.copy_image.non_compressed.viewclass_128_bits.rgba32f_rgba32f#cubemap_to_texture2d_array
@@ -6697,20 +6753,24 @@
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_common#common_color_mask_buffer_color_mask
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_common#common_separate_blend_func_buffer_blend_func
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_common#common_separate_blend_func_buffer_separate_blend_func
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_eq_buffer_advanced_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_enable_buffer_enable
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_disable_buffer_disable
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_disable_buffer_enable
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_enable_buffer_disable
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_eq_buffer_blend_eq
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_eq_buffer_separate_blend_eq
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_func_buffer_blend_func
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_func_buffer_separate_blend_func
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_color_mask_buffer_color_mask
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_disable_buffer_disable
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_enable_buffer_disable
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_enable_buffer_enable
-dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_eq_buffer_advanced_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_eq_buffer_advanced_blend_eq
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_eq_buffer_blend_eq
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_eq_buffer_separate_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_eq_buffer_advanced_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_advanced_blend_eq_buffer_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_advanced_blend_eq_buffer_separate_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_advanced_blend_eq_buffer_advanced_blend_eq
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_func_buffer_blend_func
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_blend_func_buffer_separate_blend_func
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_func_buffer_blend_func
dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_separate_blend_func_buffer_separate_blend_func
+dEQP-GLES31.functional.draw_buffers_indexed.overwrite_indexed#common_color_mask_buffer_color_mask
dEQP-GLES31.functional.draw_buffers_indexed.random.max_implementation_draw_buffers#0
dEQP-GLES31.functional.draw_buffers_indexed.random.max_implementation_draw_buffers#1
dEQP-GLES31.functional.draw_buffers_indexed.random.max_implementation_draw_buffers#14
@@ -8687,6 +8747,10 @@
dEQP-GLES31.functional.shaders.uniform_block.invalid#repeated_block_vertex
dEQP-GLES31.functional.shaders.uniform_block.invalid#too_long_block_name_fragment
dEQP-GLES31.functional.shaders.uniform_block.invalid#too_long_block_name_vertex
+dEQP-GLES31.functional.shaders.uniform_block.invalid#global_layout_std430_fragment
+dEQP-GLES31.functional.shaders.uniform_block.invalid#global_layout_std430_vertex
+dEQP-GLES31.functional.shaders.uniform_block.invalid#structure_definition_fragment
+dEQP-GLES31.functional.shaders.uniform_block.invalid#structure_definition_vertex
dEQP-GLES31.functional.shaders.uniform_block.valid#member_layout_all_8_times_fragment
dEQP-GLES31.functional.shaders.uniform_block.valid#member_layout_all_8_times_vertex
dEQP-GLES31.functional.shaders.uniform_block.valid#member_layout_all_fragment
diff --git a/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java b/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
index fdef54c..2657d15 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ColorMatrixColorFilterTest.java
@@ -70,7 +70,10 @@
paint.setColor(Color.RED);
bitmap.eraseColor(Color.TRANSPARENT);
canvas.drawPoint(0, 0, paint);
- assertColor(Color.argb(128, 255, 0, 64), bitmap.getPixel(0, 0));
+ // the bitmap stores the result in premul colors and we read out an
+ // unpremultiplied result, which causes us to need a bigger tolerance in
+ // this case (due to the fact that scaling by 1/255 is not exact).
+ assertColor(Color.argb(128, 255, 0, 64), bitmap.getPixel(0, 0), 2);
paint.setColor(Color.CYAN);
canvas.drawPoint(0, 0, paint);
// blue gets clipped
@@ -89,9 +92,13 @@
}
private void assertColor(int expected, int actual) {
- assertEquals(Color.red(expected), Color.red(actual), TOLERANCE);
- assertEquals(Color.green(expected), Color.green(actual), TOLERANCE);
- assertEquals(Color.blue(expected), Color.blue(actual), TOLERANCE);
- assertEquals(Color.alpha(expected), Color.alpha(actual), TOLERANCE);
+ assertColor(expected, actual, TOLERANCE);
+ }
+
+ private void assertColor(int expected, int actual, int tolerance) {
+ assertEquals(Color.red(expected), Color.red(actual), tolerance);
+ assertEquals(Color.green(expected), Color.green(actual), tolerance);
+ assertEquals(Color.blue(expected), Color.blue(actual), tolerance);
+ assertEquals(Color.alpha(expected), Color.alpha(actual), tolerance);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
index da8ea65..5a66fa7 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -32,6 +32,7 @@
import java.util.List;
import java.util.ArrayList;
+import java.util.Arrays;
public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
private static final String TAG = "BurstCaptureTest";
@@ -74,7 +75,8 @@
final Size previewSize = mOrderedPreviewSizes.get(0);
// Get maximum YUV_420_888 size
- final Size stillSize = getMaxPreviewSize(cameraId, mCameraManager);
+ final Size stillSize = getSortedSizesForFormat(
+ cameraId, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
// Find max pipeline depth and sync latency
final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
@@ -89,24 +91,34 @@
config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, stillSize);
// Find suitable target FPS range - as high as possible
- Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
- int minBurstFps = (int) Math.floor(1e9 / minStillFrameDuration);
- Range<Integer> targetRange = null;
- for (Range<Integer> candidateRange : fpsRanges) {
- if (candidateRange.getLower() >= minBurstFps) {
- if (targetRange == null) {
- targetRange = candidateRange;
- } else if (candidateRange.getLower() > targetRange.getLower()) {
- targetRange = candidateRange;
- } else if (candidateRange.getUpper() > targetRange.getUpper()) {
- targetRange = candidateRange;
- }
+ List<Range<Integer> > fpsRanges = Arrays.asList(
+ mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+ Range<Integer> targetRange = mStaticInfo.getAeMaxTargetFpsRange();
+ // Add 0.05 here so Fps like 29.99 evaluated to 30
+ int minBurstFps = (int) Math.floor(1e9 / minStillFrameDuration + 0.05f);
+ boolean foundConstantMaxYUVRange = false;
+ boolean foundYUVStreamingRange = false;
+
+ for (Range<Integer> fpsRange : fpsRanges) {
+ if (fpsRange.getLower() == minBurstFps && fpsRange.getUpper() == minBurstFps) {
+ foundConstantMaxYUVRange = true;
}
+ if (fpsRange.getLower() <= 15 && fpsRange.getUpper() == minBurstFps) {
+ foundYUVStreamingRange = true;
+ }
+ }
+
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ assertTrue(String.format("Cam %s: Target FPS range of (%d, %d) must be supported",
+ cameraId, minBurstFps, minBurstFps), foundConstantMaxYUVRange);
+ assertTrue(String.format(
+ "Cam %s: Target FPS range of (x, %d) where x <= 15 must be supported",
+ cameraId, minBurstFps), foundYUVStreamingRange);
}
assertTrue(String.format("Cam %s: No target FPS range found with minimum FPS above " +
" 1/minFrameDuration (%d fps, duration %d ns) for full-resolution YUV",
- cameraId, minBurstFps, minStillFrameDuration),
- targetRange != null);
+ cameraId, minBurstFps, minStillFrameDuration),
+ targetRange.getLower() >= minBurstFps);
Log.i(TAG, String.format("Selected frame rate range %d - %d for YUV burst",
targetRange.getLower(), targetRange.getUpper()));
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 53ca31f..be80eea 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -23,6 +23,7 @@
import static android.hardware.camera2.CaptureRequest.*;
import android.content.Context;
+import android.graphics.SurfaceTexture;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
@@ -380,11 +381,7 @@
closeSession();
}
finally {
- try {
-
- } finally {
- closeDevice(mCameraIds[i], mCameraMockListener);
- }
+ closeDevice(mCameraIds[i], mCameraMockListener);
}
}
}
@@ -581,6 +578,146 @@
}
}
+ /**
+ * Verify basic semantics and error conditions of the prepare call.
+ *
+ */
+ public void testPrepare() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ openDevice(mCameraIds[i], mCameraMockListener);
+ waitForDeviceState(STATE_OPENED, CAMERA_OPEN_TIMEOUT_MS);
+
+ prepareTestByCamera();
+ }
+ finally {
+ closeDevice(mCameraIds[i], mCameraMockListener);
+ }
+ }
+ }
+
+ private void prepareTestByCamera() throws Exception {
+ final int PREPARE_TIMEOUT_MS = 10000;
+
+ mSessionMockListener = spy(new BlockingSessionCallback());
+
+ SurfaceTexture output1 = new SurfaceTexture(1);
+ Surface output1Surface = new Surface(output1);
+ SurfaceTexture output2 = new SurfaceTexture(2);
+ Surface output2Surface = new Surface(output2);
+
+ List<Surface> outputSurfaces = new ArrayList<>(
+ Arrays.asList(output1Surface, output2Surface));
+ mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
+
+ mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+
+ // Try basic prepare
+
+ mSession.prepare(output1Surface);
+
+ verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
+ .onSurfacePrepared(eq(mSession), eq(output1Surface));
+
+ // Should not complain if preparing already prepared stream
+
+ mSession.prepare(output1Surface);
+
+ verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(2))
+ .onSurfacePrepared(eq(mSession), eq(output1Surface));
+
+ // Check surface not included in session
+
+ SurfaceTexture output3 = new SurfaceTexture(3);
+ Surface output3Surface = new Surface(output3);
+ try {
+ mSession.prepare(output3Surface);
+ fail("Preparing surface not part of session must throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Ensure second prepare also works
+
+ mSession.prepare(output2Surface);
+
+ verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
+ .onSurfacePrepared(eq(mSession), eq(output2Surface));
+
+ // Use output1
+
+ CaptureRequest.Builder r = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ r.addTarget(output1Surface);
+
+ mSession.capture(r.build(), null, null);
+
+ try {
+ mSession.prepare(output1Surface);
+ fail("Preparing already-used surface must throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Create new session with outputs 1 and 3, ensure output1Surface still can't be prepared
+ // again
+
+ mSessionMockListener = spy(new BlockingSessionCallback());
+
+ outputSurfaces = new ArrayList<>(
+ Arrays.asList(output1Surface, output3Surface));
+ mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
+
+ mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+
+ try {
+ mSession.prepare(output1Surface);
+ fail("Preparing surface used in previous session must throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Use output3, wait for result, then make sure prepare still doesn't work
+
+ r = mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ r.addTarget(output3Surface);
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ mSession.capture(r.build(), resultListener, mHandler);
+
+ resultListener.getCaptureResult(CAPTURE_RESULT_TIMEOUT_MS);
+
+ try {
+ mSession.prepare(output3Surface);
+ fail("Preparing already-used surface must throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ // Create new session with outputs 1 and 2, ensure output2Surface can be prepared again
+
+ mSessionMockListener = spy(new BlockingSessionCallback());
+
+ outputSurfaces = new ArrayList<>(
+ Arrays.asList(output1Surface, output2Surface));
+ mCamera.createCaptureSession(outputSurfaces, mSessionMockListener, mHandler);
+
+ mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+
+ mSession.prepare(output2Surface);
+
+ verify(mSessionMockListener, timeout(PREPARE_TIMEOUT_MS).times(1))
+ .onSurfacePrepared(eq(mSession), eq(output2Surface));
+
+ try {
+ mSession.prepare(output1Surface);
+ fail("Preparing surface used in previous session must throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ }
+
+
private void invalidRequestCaptureTestByCamera() throws Exception {
if (VERBOSE) Log.v(TAG, "invalidRequestCaptureTestByCamera");
@@ -888,7 +1025,7 @@
mSession = mSessionMockListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
waitForSessionState(SESSION_CONFIGURED, SESSION_CONFIGURE_TIMEOUT_MS);
waitForSessionState(SESSION_READY, SESSION_READY_TIMEOUT_MS);
-}
+ }
private void waitForDeviceState(int state, long timeoutMs) {
mCameraMockListener.waitForState(state, timeoutMs);
@@ -1298,6 +1435,10 @@
if (mStaticInfo.areKeysAvailable(TONEMAP_MODE)) {
mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE);
+ mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
+ CaptureRequest.TONEMAP_MODE_GAMMA_VALUE);
+ mCollector.expectKeyValueNotEquals(request, TONEMAP_MODE,
+ CaptureRequest.TONEMAP_MODE_PRESET_CURVE);
}
if (mStaticInfo.areKeysAvailable(STATISTICS_LENS_SHADING_MAP_MODE)) {
mCollector.expectKeyValueNotNull(request, STATISTICS_LENS_SHADING_MAP_MODE);
@@ -1333,7 +1474,7 @@
// OK
} else if (template == CameraDevice.TEMPLATE_ZERO_SHUTTER_LAG &&
!mStaticInfo.isCapabilitySupported(CameraCharacteristics.
- REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING)) {
+ REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING)) {
// OK.
} else if (sLegacySkipTemplates.contains(template) &&
mStaticInfo.isHardwareLevelLegacy()) {
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 27ff6d1..77a0c8e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -422,38 +422,23 @@
new BlockingStateCallback(mockFailListener);
mCameraManager.openCamera(ids[i], successListener, mHandler);
-
- try {
- mCameraManager.openCamera(ids[i], failListener,
- mHandler);
- } catch (CameraAccessException e) {
- // Optional (but common). Camera might fail asynchronously only.
- // Don't assert here, otherwise, all subsequent tests will fail because the
- // opened camera is never closed.
- mCollector.expectEquals(
- "If second camera open fails immediately, must be due to"
- + "camera being busy for ID: " + ids[i],
- CameraAccessException.CAMERA_ERROR, e.getReason());
- }
+ mCameraManager.openCamera(ids[i], failListener,
+ mHandler);
successListener.waitForState(BlockingStateCallback.STATE_OPENED,
CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
- // Have to get the successCamera here, otherwise, it won't be
- // closed if STATE_ERROR timeout exception occurs.
ArgumentCaptor<CameraDevice> argument =
ArgumentCaptor.forClass(CameraDevice.class);
verify(mockSuccessListener, atLeastOnce()).onOpened(argument.capture());
+ verify(mockSuccessListener, atLeastOnce()).onDisconnected(argument.capture());
- failListener.waitForState(BlockingStateCallback.STATE_ERROR,
+ failListener.waitForState(BlockingStateCallback.STATE_OPENED,
CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+ verify(mockFailListener, atLeastOnce()).onOpened(argument.capture());
successCamera = verifyCameraStateOpened(
- ids[i], mockSuccessListener);
+ ids[i], mockFailListener);
- verify(mockFailListener)
- .onError(
- and(notNull(CameraDevice.class), not(eq(successCamera))),
- eq(StateCallback.ERROR_CAMERA_IN_USE));
verifyNoMoreInteractions(mockFailListener);
} finally {
if (successCamera != null) {
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 08f628f..d39ff1f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -109,13 +109,8 @@
public static ImageReader makeImageReader(Size size, int format, int maxNumImages,
ImageReader.OnImageAvailableListener listener, Handler handler) {
ImageReader reader;
- if (format == ImageFormat.PRIVATE) {
- reader = ImageReader.newOpaqueInstance(size.getWidth(), size.getHeight(),
- maxNumImages);
- } else {
- reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), format,
- maxNumImages);
- }
+ reader = ImageReader.newInstance(size.getWidth(), size.getHeight(), format,
+ maxNumImages);
reader.setOnImageAvailableListener(listener, handler);
if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size);
return reader;
@@ -132,9 +127,9 @@
*/
public static ImageWriter makeImageWriter(
Surface inputSurface, int maxImages,
- ImageWriter.ImageListener listener, Handler handler) {
+ ImageWriter.OnImageReleasedListener listener, Handler handler) {
ImageWriter writer = ImageWriter.newInstance(inputSurface, maxImages);
- writer.setImageListener(listener, handler);
+ writer.setOnImageReleasedListener(listener, handler);
return writer;
}
@@ -286,11 +281,11 @@
}
}
- public static class SimpleImageWriterListener implements ImageWriter.ImageListener {
+ public static class SimpleImageWriterListener implements ImageWriter.OnImageReleasedListener {
private final Semaphore mImageReleasedSema = new Semaphore(0);
private final ImageWriter mWriter;
@Override
- public void onInputImageReleased(ImageWriter writer) {
+ public void onImageReleased(ImageWriter writer) {
if (writer != mWriter) {
return;
}
@@ -565,19 +560,26 @@
throws CameraAccessException {
BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
camera.createCaptureSession(outputSurfaces, sessionListener, handler);
+ CameraCaptureSession session =
+ sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+ assertFalse("Camera session should not be a reprocessable session",
+ session.isReprocessable());
- return sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+ return session;
}
- public static CameraCaptureSession configureReprocessibleCameraSession(CameraDevice camera,
+ public static CameraCaptureSession configureReprocessableCameraSession(CameraDevice camera,
InputConfiguration inputConfiguration, List<Surface> outputSurfaces,
CameraCaptureSession.StateCallback listener, Handler handler)
throws CameraAccessException {
BlockingSessionCallback sessionListener = new BlockingSessionCallback(listener);
- camera.createReprocessibleCaptureSession(inputConfiguration, outputSurfaces,
+ camera.createReprocessableCaptureSession(inputConfiguration, outputSurfaces,
sessionListener, handler);
+ CameraCaptureSession session =
+ sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+ assertTrue("Camera session should be a reprocessable session", session.isReprocessable());
- return sessionListener.waitAndGetSession(SESSION_CONFIGURE_TIMEOUT_MS);
+ return session;
}
public static <T> void assertArrayNotEmpty(T arr, String message) {
@@ -1270,8 +1272,9 @@
if (src.getFormat() != dst.getFormat()) {
throw new IllegalArgumentException("Src and dst images should have the same format");
}
- if (src.isOpaque() || dst.isOpaque()) {
- throw new IllegalArgumentException("Opaque image is not copyable");
+ if (src.getFormat() == ImageFormat.PRIVATE ||
+ dst.getFormat() == ImageFormat.PRIVATE) {
+ throw new IllegalArgumentException("PRIVATE format images are not copyable");
}
// TODO: check the owner of the dst image, it must be from ImageWriter, other source may
@@ -1298,9 +1301,9 @@
* Checks whether the two images are strongly equal.
* </p>
* <p>
- * Two images are strongly equal if and only if the data, formats, sizes, and
- * timestamps are same. For opaque images ({@link Image#isOpaque()} returns
- * true), the image data is not not accessible thus the data comparison is
+ * Two images are strongly equal if and only if the data, formats, sizes,
+ * and timestamps are same. For {@link ImageFormat#PRIVATE PRIVATE} format
+ * images, the image data is not not accessible thus the data comparison is
* effectively skipped as the number of planes is zero.
* </p>
* <p>
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 68c8077..f3acf4c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -1389,73 +1389,70 @@
return;
}
- SimpleCaptureCallback listener;
CaptureRequest.Builder requestBuilder =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
-
- Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
-
int[] toneMapModes = mStaticInfo.getAvailableToneMapModesChecked();
for (int mode : toneMapModes) {
- requestBuilder.set(CaptureRequest.TONEMAP_MODE, mode);
if (VERBOSE) {
Log.v(TAG, "Testing tonemap mode " + mode);
}
- if (mode == CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE) {
- TonemapCurve tcLinear = new TonemapCurve(
- TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR);
- requestBuilder.set(CaptureRequest.TONEMAP_CURVE, tcLinear);
- // Create a new listener for each run to avoid the results from one run spill
- // into another run.
- listener = new SimpleCaptureCallback();
- startPreview(requestBuilder, maxPreviewSz, listener);
- waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
- verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
- TONEMAP_CURVE_LINEAR);
+ requestBuilder.set(CaptureRequest.TONEMAP_MODE, mode);
+ switch (mode) {
+ case CaptureRequest.TONEMAP_MODE_CONTRAST_CURVE:
+ TonemapCurve toneCurve = new TonemapCurve(TONEMAP_CURVE_LINEAR,
+ TONEMAP_CURVE_LINEAR, TONEMAP_CURVE_LINEAR);
+ requestBuilder.set(CaptureRequest.TONEMAP_CURVE, toneCurve);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
- TonemapCurve tcSrgb = new TonemapCurve(
- TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB);
- requestBuilder.set(CaptureRequest.TONEMAP_CURVE, tcSrgb);
- // Create a new listener for each run to avoid the results from one run spill
- // into another run.
- listener = new SimpleCaptureCallback();
- startPreview(requestBuilder, maxPreviewSz, listener);
- waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
- verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
- TONEMAP_CURVE_SRGB);
- } else {
- // Create a new listener for each run to avoid the results from one run spill
- // into another run.
- listener = new SimpleCaptureCallback();
- startPreview(requestBuilder, maxPreviewSz, listener);
- waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
- verifyToneMapModeResults(listener, NUM_FRAMES_VERIFIED, mode,
- /*inputToneCurve*/null);
+ toneCurve = new TonemapCurve(TONEMAP_CURVE_SRGB,
+ TONEMAP_CURVE_SRGB, TONEMAP_CURVE_SRGB);
+ requestBuilder.set(CaptureRequest.TONEMAP_CURVE, toneCurve);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ break;
+ case CaptureRequest.TONEMAP_MODE_GAMMA_VALUE:
+ requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 1.0f);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 2.2f);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ requestBuilder.set(CaptureRequest.TONEMAP_GAMMA, 5.0f);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ break;
+ case CaptureRequest.TONEMAP_MODE_PRESET_CURVE:
+ requestBuilder.set(CaptureRequest.TONEMAP_PRESET_CURVE,
+ CaptureRequest.TONEMAP_PRESET_CURVE_REC709);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ requestBuilder.set(CaptureRequest.TONEMAP_PRESET_CURVE,
+ CaptureRequest.TONEMAP_PRESET_CURVE_SRGB);
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ break;
+ default:
+ testToneMapMode(NUM_FRAMES_VERIFIED, requestBuilder);
+ break;
}
}
- stopPreview();
+
}
/**
- * Verify tonemap results.
- * <p>
- * Assumes R,G,B channels use the same tone curve
- * </p>
+ * Test tonemap mode with speficied request settings
*
- * @param listener The capture listener used to get the capture results
* @param numFramesVerified Number of results to be verified
- * @param tonemapMode Tonemap mode to verify
- * @param inputToneCurve Tonemap curve used by all 3 channels, ignored when
- * map mode is not CONTRAST_CURVE.
+ * @param requestBuilder the request builder of settings to be tested
*/
- private void verifyToneMapModeResults(SimpleCaptureCallback listener, int numFramesVerified,
- int tonemapMode, float[] inputToneCurve) {
+ private void testToneMapMode (int numFramesVerified,
+ CaptureRequest.Builder requestBuilder) throws Exception {
final int MIN_TONEMAP_CURVE_POINTS = 2;
final Float ZERO = new Float(0);
final Float ONE = new Float(1.0f);
+ SimpleCaptureCallback listener = new SimpleCaptureCallback();
+ int tonemapMode = requestBuilder.get(CaptureRequest.TONEMAP_MODE);
+ Size maxPreviewSz = mOrderedPreviewSizes.get(0); // Max preview size.
+ startPreview(requestBuilder, maxPreviewSz, listener);
+ waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+
int maxCurvePoints = mStaticInfo.getMaxTonemapCurvePointChecked();
for (int i = 0; i < numFramesVerified; i++) {
CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
@@ -1477,6 +1474,14 @@
* between request and result, as they may have different array
* size.
*/
+ } else if (tonemapMode == CaptureResult.TONEMAP_MODE_GAMMA_VALUE) {
+ mCollector.expectEquals("Capture result gamma value should match request",
+ requestBuilder.get(CaptureRequest.TONEMAP_GAMMA),
+ result.get(CaptureResult.TONEMAP_GAMMA));
+ } else if (tonemapMode == CaptureResult.TONEMAP_MODE_PRESET_CURVE) {
+ mCollector.expectEquals("Capture result preset curve should match request",
+ requestBuilder.get(CaptureRequest.TONEMAP_PRESET_CURVE),
+ result.get(CaptureResult.TONEMAP_PRESET_CURVE));
}
// Tonemap curve result availability and basic sanity check for all modes.
@@ -1493,6 +1498,7 @@
mCollector.expectInRange("Tonemap curve blue length is out of range",
mapBlue.length, MIN_TONEMAP_CURVE_POINTS, maxCurvePoints * 2);
}
+ stopPreview();
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
index ec9e41b..2430dd0 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureResultTest.java
@@ -440,17 +440,21 @@
// Keys only present when corresponding control is on are being
// verified in its own functional test
- // Only present when tone mapping mode is CONTRAST_CURVE
+ // Only present in certain tonemap mode. Test in CaptureRequestTest.
waiverKeys.add(CaptureResult.TONEMAP_CURVE);
+ waiverKeys.add(CaptureResult.TONEMAP_GAMMA);
+ waiverKeys.add(CaptureResult.TONEMAP_PRESET_CURVE);
// Only present when test pattern mode is SOLID_COLOR.
// TODO: verify this key in test pattern test later
waiverKeys.add(CaptureResult.SENSOR_TEST_PATTERN_DATA);
// Only present when STATISTICS_LENS_SHADING_MAP_MODE is ON
waiverKeys.add(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
- // Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON
+ // Only present when STATISTICS_INFO_AVAILABLE_HOT_PIXEL_MAP_MODES is ON
waiverKeys.add(CaptureResult.STATISTICS_HOT_PIXEL_MAP);
- // Only present when face detection is on
+ // Only present when face detection is on
waiverKeys.add(CaptureResult.STATISTICS_FACES);
+ // Only present in reprocessing capture result.
+ waiverKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
//Keys not required if RAW is not supported
if (!mStaticInfo.isCapabilitySupported(
@@ -460,6 +464,13 @@
waiverKeys.add(CaptureResult.SENSOR_NOISE_PROFILE);
}
+ //Keys for depth output capability
+ if (!mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT)) {
+ waiverKeys.add(CaptureResult.LENS_POSE_ROTATION);
+ waiverKeys.add(CaptureResult.LENS_POSE_TRANSLATION);
+ }
+
if (mStaticInfo.getAeMaxRegionsChecked() == 0) {
waiverKeys.add(CaptureResult.CONTROL_AE_REGIONS);
}
@@ -687,6 +698,8 @@
resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
+ resultKeys.add(CaptureResult.LENS_POSE_ROTATION);
+ resultKeys.add(CaptureResult.LENS_POSE_TRANSLATION);
resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
resultKeys.add(CaptureResult.LENS_STATE);
resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
@@ -712,7 +725,10 @@
resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP_MODE);
resultKeys.add(CaptureResult.TONEMAP_CURVE);
resultKeys.add(CaptureResult.TONEMAP_MODE);
+ resultKeys.add(CaptureResult.TONEMAP_GAMMA);
+ resultKeys.add(CaptureResult.TONEMAP_PRESET_CURVE);
resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
+ resultKeys.add(CaptureResult.REPROCESS_EFFECTIVE_EXPOSURE_FACTOR);
return resultKeys;
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 00a5d66..38332a1 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -87,7 +87,7 @@
private static final int YUV_REPROCESS =
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
private static final int OPAQUE_REPROCESS =
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING;
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
@Override
public void setContext(Context context) {
@@ -204,11 +204,13 @@
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_RANGE , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_COMPENSATION_STEP , LEGACY , BC );
+ expectKeyAvailable(c, CameraCharacteristics.CONTROL_AE_LOCK_AVAILABLE , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AF_AVAILABLE_MODES , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_EFFECTS , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_SCENE_MODES , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AVAILABLE_VIDEO_STABILIZATION_MODES , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_AVAILABLE_MODES , LEGACY , BC );
+ expectKeyAvailable(c, CameraCharacteristics.CONTROL_AWB_LOCK_AVAILABLE , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AE , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AF , LEGACY , BC );
expectKeyAvailable(c, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB , LEGACY , BC );
@@ -335,6 +337,10 @@
BlackLevelPattern blackLevel = mCollector.expectKeyValueNotNull(c,
CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN);
if (blackLevel != null) {
+ String blackLevelPatternString = blackLevel.toString();
+ if (VERBOSE) {
+ Log.v(TAG, "Black level pattern: " + blackLevelPatternString);
+ }
int[] blackLevelPattern = new int[BlackLevelPattern.COUNT];
blackLevel.copyTo(blackLevelPattern, /*offset*/0);
Integer whitelevel = c.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
@@ -491,7 +497,7 @@
boolean supportYUV = arrayContains(capabilities,
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING);
boolean supportOpaque = arrayContains(capabilities,
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING);
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING);
StreamConfigurationMap configs =
c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Integer maxNumInputStreams =
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
index d9e5cdf..b081660 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageWriterTest.java
@@ -28,7 +28,6 @@
import android.media.Image.Plane;
import android.media.ImageReader;
import android.media.ImageWriter;
-import android.os.ConditionVariable;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
@@ -51,7 +50,7 @@
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
// Max number of images can be accessed simultaneously from ImageReader.
private static final int MAX_NUM_IMAGES = 3;
- private static final int CAMERA_OPAQUE_FORMAT = ImageFormat.PRIVATE;
+ private static final int CAMERA_PRIVATE_FORMAT = ImageFormat.PRIVATE;
private ImageReader mReaderForWriter;
private ImageWriter mWriter;
@@ -125,7 +124,7 @@
try {
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
- readerWriterFormatTestByCamera(CAMERA_OPAQUE_FORMAT);
+ readerWriterFormatTestByCamera(CAMERA_PRIVATE_FORMAT);
} finally {
closeDevice(id);
}
@@ -158,7 +157,7 @@
assertNotNull("Surface from ImageReader shouldn't be null", surface);
mWriter = ImageWriter.newInstance(surface, MAX_NUM_IMAGES);
SimpleImageWriterListener writerImageListener = new SimpleImageWriterListener(mWriter);
- mWriter.setImageListener(writerImageListener, mHandler);
+ mWriter.setOnImageReleasedListener(writerImageListener, mHandler);
// Start capture: capture 2 images.
List<Surface> outputSurfaces = new ArrayList<Surface>();
@@ -182,17 +181,17 @@
Image outputImage = null;
assertTrue("ImageWriter max images should be " + MAX_NUM_IMAGES,
mWriter.getMaxImages() == MAX_NUM_IMAGES);
- if (format == CAMERA_OPAQUE_FORMAT) {
- assertTrue("First ImageReader should be opaque",
- mReader.isOpaque());
- assertTrue("Second ImageReader should be opaque",
- mReaderForWriter.isOpaque());
- assertTrue("Format of first ImageReader should be opaque",
- mReader.getImageFormat() == CAMERA_OPAQUE_FORMAT);
- assertTrue(" Format of second ImageReader should be opaque",
- mReaderForWriter.getImageFormat() == CAMERA_OPAQUE_FORMAT);
- assertTrue(" Format of ImageWriter should be opaque",
- mWriter.getFormat() == CAMERA_OPAQUE_FORMAT);
+ if (format == CAMERA_PRIVATE_FORMAT) {
+ assertTrue("First ImageReader format should be PRIVATE",
+ mReader.getImageFormat() == CAMERA_PRIVATE_FORMAT);
+ assertTrue("Second ImageReader should be PRIVATE",
+ mReaderForWriter.getImageFormat() == CAMERA_PRIVATE_FORMAT);
+ assertTrue("Format of first ImageReader should be PRIVATE",
+ mReader.getImageFormat() == CAMERA_PRIVATE_FORMAT);
+ assertTrue(" Format of second ImageReader should be PRIVATE",
+ mReaderForWriter.getImageFormat() == CAMERA_PRIVATE_FORMAT);
+ assertTrue(" Format of ImageWriter should be PRIVATE",
+ mWriter.getFormat() == CAMERA_PRIVATE_FORMAT);
// Validate 2 images
validateOpaqueImages(maxSize, listenerForCamera, listenerForWriter, captureListener,
@@ -307,7 +306,7 @@
private void validateOpaqueImage(Image image, String msg, Size imageSize,
CaptureResult result) {
assertNotNull("Opaque image Capture result should not be null", result != null);
- mCollector.expectImageProperties(msg + "Opaque ", image, CAMERA_OPAQUE_FORMAT,
+ mCollector.expectImageProperties(msg + "Opaque ", image, CAMERA_PRIVATE_FORMAT,
imageSize, result.get(CaptureResult.SENSOR_TIMESTAMP));
mCollector.expectTrue(msg + "Opaque image number planes should be zero",
image.getPlanes().length == 0);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
index 56eee0e..3f54a39 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -626,7 +626,7 @@
InputConfiguration inputConfig = new InputConfiguration(maxInputSize.getWidth(),
maxInputSize.getHeight(), inputFormat);
mSessionListener = new BlockingSessionCallback();
- mSession = CameraTestUtils.configureReprocessibleCameraSession(
+ mSession = CameraTestUtils.configureReprocessableCameraSession(
mCamera, inputConfig, outSurfaces, mSessionListener, mHandler);
// 3. Create ImageWriter for input
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 005d948..ed43b06 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -24,6 +24,7 @@
import android.util.Size;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.media.CamcorderProfile;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
@@ -46,6 +47,7 @@
import java.io.File;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -82,6 +84,7 @@
private static final int MAX_NUM_FRAME_DROP_INTERVAL_ALLOWED = 4;
private List<Size> mSupportedVideoSizes;
private Surface mRecordingSurface;
+ private Surface mPersistentSurface;
private MediaRecorder mMediaRecorder;
private String mOutMediaFileName;
private int mVideoFrameRate;
@@ -98,19 +101,7 @@
super.tearDown();
}
- /**
- * <p>
- * Test basic camera recording.
- * </p>
- * <p>
- * This test covers the typical basic use case of camera recording.
- * MediaRecorder is used to record the audio and video, CamcorderProfile is
- * used to configure the MediaRecorder. It goes through the pre-defined
- * CamcorderProfile list, test each profile configuration and validate the
- * recorded video. Preview is set to the video size.
- * </p>
- */
- public void testBasicRecording() throws Exception {
+ private void doBasicRecording() throws Exception {
for (int i = 0; i < mCameraIds.length; i++) {
try {
Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
@@ -130,6 +121,43 @@
/**
* <p>
+ * Test basic camera recording.
+ * </p>
+ * <p>
+ * This test covers the typical basic use case of camera recording.
+ * MediaRecorder is used to record the audio and video, CamcorderProfile is
+ * used to configure the MediaRecorder. It goes through the pre-defined
+ * CamcorderProfile list, test each profile configuration and validate the
+ * recorded video. Preview is set to the video size.
+ * </p>
+ */
+ public void testBasicRecording() throws Exception {
+ doBasicRecording();
+ }
+
+ /**
+ * <p>
+ * Test basic camera recording from a persistent input surface.
+ * </p>
+ * <p>
+ * This test is similar to testBasicRecording except that MediaRecorder records
+ * from a persistent input surface that's used across multiple recording sessions.
+ * </p>
+ */
+ public void testRecordingFromPersistentSurface() throws Exception {
+ mPersistentSurface = MediaCodec.createPersistentInputSurface();
+ assertNotNull("Failed to create persistent input surface!", mPersistentSurface);
+
+ try {
+ doBasicRecording();
+ } finally {
+ mPersistentSurface.release();
+ mPersistentSurface = null;
+ }
+ }
+
+ /**
+ * <p>
* Test camera recording for all supported sizes by using MediaRecorder.
* </p>
* <p>
@@ -368,7 +396,7 @@
Range<Integer> maxRange = availableFpsRanges[0];
boolean foundRange = false;
for (Range<Integer> range : availableFpsRanges) {
- if (range.getLower() == range.getUpper() && range.getLower() >= maxRange.getLower()) {
+ if (range.getLower().equals(range.getUpper()) && range.getLower() >= maxRange.getLower()) {
foundRange = true;
maxRange = range;
}
@@ -439,8 +467,10 @@
*/
private void basicRecordingTestByCamera(int[] camcorderProfileList) throws Exception {
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+ List<Range<Integer> > fpsRanges = Arrays.asList(
+ mStaticInfo.getAeAvailableTargetFpsRangesChecked());
+ int cameraId = Integer.valueOf(mCamera.getId());
for (int profileId : camcorderProfileList) {
- int cameraId = Integer.valueOf(mCamera.getId());
if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
allowedUnsupported(cameraId, profileId)) {
continue;
@@ -448,6 +478,7 @@
CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+ Range<Integer> fpsRange = new Range(profile.videoFrameRate, profile.videoFrameRate);
if (mStaticInfo.isHardwareLevelLegacy() &&
(videoSz.getWidth() > maxPreviewSize.getWidth() ||
videoSz.getHeight() > maxPreviewSize.getHeight())) {
@@ -457,6 +488,9 @@
assertTrue("Video size " + videoSz.toString() + " for profile ID " + profileId +
" must be one of the camera device supported video size!",
mSupportedVideoSizes.contains(videoSz));
+ assertTrue("Frame rate range " + fpsRange + " (for profile ID " + profileId +
+ ") must be one of the camera device available FPS range!",
+ fpsRanges.contains(fpsRange));
if (VERBOSE) {
Log.v(TAG, "Testing camera recording with video size " + videoSz.toString());
@@ -631,6 +665,13 @@
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+ if (mStaticInfo.isHardwareLevelLegacy() &&
+ (videoSz.getWidth() > maxPreviewSize.getWidth() ||
+ videoSz.getHeight() > maxPreviewSize.getHeight())) {
+ // Skip. Legacy mode can only do recording up to max preview size
+ continue;
+ }
+
// For LEGACY, find closest supported smaller or equal JPEG size to the current video
// size; if no size is smaller than the video, pick the smallest JPEG size. The assert
// for video size above guarantees that for LIMITED or FULL, we select videoSz here.
@@ -812,8 +853,14 @@
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setProfile(profile);
mMediaRecorder.setOutputFile(mOutMediaFileName);
+ if (mPersistentSurface != null) {
+ mMediaRecorder.setInputSurface(mPersistentSurface);
+ mRecordingSurface = mPersistentSurface;
+ }
mMediaRecorder.prepare();
- mRecordingSurface = mMediaRecorder.getSurface();
+ if (mPersistentSurface == null) {
+ mRecordingSurface = mMediaRecorder.getSurface();
+ }
assertNotNull("Recording surface must be non-null!", mRecordingSurface);
mVideoFrameRate = profile.videoFrameRate;
mVideoSize = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
@@ -837,8 +884,14 @@
mMediaRecorder.setVideoSize(sz.getWidth(), sz.getHeight());
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+ if (mPersistentSurface != null) {
+ mMediaRecorder.setInputSurface(mPersistentSurface);
+ mRecordingSurface = mPersistentSurface;
+ }
mMediaRecorder.prepare();
- mRecordingSurface = mMediaRecorder.getSurface();
+ if (mPersistentSurface == null) {
+ mRecordingSurface = mMediaRecorder.getSurface();
+ }
assertNotNull("Recording surface must be non-null!", mRecordingSurface);
mVideoFrameRate = videoFrameRate;
mVideoSize = sz;
@@ -901,7 +954,7 @@
} else {
// TODO: need implement MediaCodec path.
}
- if (mRecordingSurface != null) {
+ if (mPersistentSurface == null && mRecordingSurface != null) {
mRecordingSurface.release();
mRecordingSurface = null;
}
@@ -1018,7 +1071,7 @@
));
}
- durationMs = (int) (nextTS - currentTS) / 1000000;
+ durationMs = (nextTS - currentTS) / 1000000.0;
mCollector.expectTrue(
String.format(
"Video %dx%d Frame drop detected after video snapshot: " +
@@ -1032,7 +1085,7 @@
if (durationMs >= expectedDurationMs * 2) {
Log.w(TAG, String.format(
"Video %dx%d Frame drop detected after video snapshot: " +
- "duration %dms (expected %dms)",
+ "duration %fms (expected %fms)",
mVideoSize.getWidth(), mVideoSize.getHeight(),
durationMs, expectedDurationMs
));
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 7d6ab55..94cbbf7 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -247,7 +247,7 @@
setupImageReaders(inputSize, inputFormat, reprocessOutputSize,
reprocessOutputFormat, /*maxImages*/1);
- setupReprocessibleSession(/*previewSurface*/null, /*numImageWriterImages*/1);
+ setupReprocessableSession(/*previewSurface*/null, /*numImageWriterImages*/1);
TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
/*inputResult*/null);
@@ -258,7 +258,7 @@
// recreate the session
closeReprossibleSession();
- setupReprocessibleSession(/*previewSurface*/null, /*numImageWriterImages*/1);
+ setupReprocessableSession(/*previewSurface*/null, /*numImageWriterImages*/1);
try {
TotalCaptureResult reprocessResult;
// issue and wait on reprocess capture request
@@ -429,7 +429,7 @@
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
totalNumBurst);
- setupReprocessibleSession(mPreviewSurface, /*numImageWriterImages*/numBurst);
+ setupReprocessableSession(mPreviewSurface, /*numImageWriterImages*/numBurst);
if (enablePreview) {
startPreview(mPreviewSurface);
@@ -498,7 +498,7 @@
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
numBurst);
- setupReprocessibleSession(mPreviewSurface, numBurst);
+ setupReprocessableSession(mPreviewSurface, numBurst);
if (enablePreview) {
startPreview(mPreviewSurface);
@@ -556,7 +556,7 @@
setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
/*maxImages*/1);
- setupReprocessibleSession(mPreviewSurface, /*numImageWriterImages*/1);
+ setupReprocessableSession(mPreviewSurface, /*numImageWriterImages*/1);
if (enablePreview) {
startPreview(mPreviewSurface);
@@ -635,11 +635,11 @@
}
/**
- * Set up a reprocessible session and create an ImageWriter with the sessoin's input surface.
+ * Set up a reprocessable session and create an ImageWriter with the sessoin's input surface.
*/
- private void setupReprocessibleSession(Surface previewSurface, int numImageWriterImages)
+ private void setupReprocessableSession(Surface previewSurface, int numImageWriterImages)
throws Exception {
- // create a reprocessible capture session
+ // create a reprocessable capture session
List<Surface> outSurfaces = new ArrayList<Surface>();
outSurfaces.add(mFirstImageReader.getSurface());
if (!mShareOneImageReader) {
@@ -651,6 +651,10 @@
InputConfiguration inputConfig = new InputConfiguration(mFirstImageReader.getWidth(),
mFirstImageReader.getHeight(), mFirstImageReader.getImageFormat());
+ String inputConfigString = inputConfig.toString();
+ if (VERBOSE) {
+ Log.v(TAG, "InputConfiguration: " + inputConfigString);
+ }
assertTrue(String.format("inputConfig is wrong: %dx%d format %d. Expect %dx%d format %d",
inputConfig.getWidth(), inputConfig.getHeight(), inputConfig.getFormat(),
mFirstImageReader.getWidth(), mFirstImageReader.getHeight(),
@@ -660,7 +664,7 @@
inputConfig.getFormat() == mFirstImageReader.getImageFormat());
mSessionListener = new BlockingSessionCallback();
- mSession = configureReprocessibleCameraSession(mCamera, inputConfig, outSurfaces,
+ mSession = configureReprocessableCameraSession(mCamera, inputConfig, outSurfaces,
mSessionListener, mHandler);
// create an ImageWriter
@@ -669,11 +673,11 @@
numImageWriterImages);
mImageWriterListener = new SimpleImageWriterListener(mImageWriter);
- mImageWriter.setImageListener(mImageWriterListener, mHandler);
+ mImageWriter.setOnImageReleasedListener(mImageWriterListener, mHandler);
}
/**
- * Close the reprocessible session and ImageWriter.
+ * Close the reprocessable session and ImageWriter.
*/
private void closeReprossibleSession() {
mInputSurface = null;
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 61860a7..3d5ceaa 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -49,6 +49,7 @@
*/
public class RobustnessTest extends Camera2AndroidTestCase {
private static final String TAG = "RobustnessTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final int CONFIGURE_TIMEOUT = 5000; //ms
private static final int CAPTURE_TIMEOUT = 1000; //ms
@@ -223,6 +224,11 @@
MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id);
final StaticMetadata staticInfo = new StaticMetadata(cc);
+ String streamConfigurationMapString =
+ cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).toString();
+ if (VERBOSE) {
+ Log.v(TAG, "StreamConfigurationMap: " + streamConfigurationMapString);
+ }
openDevice(id);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
index 3076d09..0dba61e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -321,6 +321,7 @@
// Legacy mode always doesn't support these requirements
Boolean contrastCurveModeSupported = false;
+ Boolean gammaAndPresetModeSupported = false;
Boolean offColorAberrationModeSupported = false;
if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
int[] tonemapModes = mStaticInfo.getAvailableToneMapModesChecked();
@@ -329,6 +330,10 @@
Arrays.asList(CameraTestUtils.toObject(tonemapModes));
contrastCurveModeSupported =
modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE);
+ gammaAndPresetModeSupported =
+ modeList.contains(CameraMetadata.TONEMAP_MODE_GAMMA_VALUE) &&
+ modeList.contains(CameraMetadata.TONEMAP_MODE_PRESET_CURVE);
+
int[] colorAberrationModes =
mStaticInfo.getAvailableColorAberrationModesChecked();
modeList = (colorAberrationModes.length == 0) ?
@@ -337,8 +342,12 @@
offColorAberrationModeSupported =
modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF);
}
+ Boolean tonemapModeQualified =
+ contrastCurveModeSupported || gammaAndPresetModeSupported;
additionalRequirements.add(new Pair<String, Boolean>(
- "Tonemap mode must include CONTRAST_CURVE", contrastCurveModeSupported));
+ "Tonemap mode must include {CONTRAST_CURVE} and/or " +
+ "{GAMMA_VALUE, PRESET_CURVE}",
+ tonemapModeQualified));
additionalRequirements.add(new Pair<String, Boolean>(
"Color aberration mode must include OFF", offColorAberrationModeSupported));
additionalRequirements.add(new Pair<String, Boolean>(
@@ -380,7 +389,7 @@
break;
case REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING:
- case REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING:
+ case REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
// Tested in ExtendedCameraCharacteristicsTest
return;
default:
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
index 01da4c8..7d377d6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -18,6 +18,8 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
+import android.graphics.ImageFormat;
+import android.view.Surface;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCaptureSession.CaptureCallback;
import android.hardware.camera2.CameraDevice;
@@ -29,6 +31,7 @@
import android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
import android.util.Log;
+import android.util.Pair;
import android.util.Range;
import org.mockito.ArgumentCaptor;
@@ -36,6 +39,7 @@
import static org.mockito.Mockito.*;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -118,6 +122,192 @@
}
/**
+ * Test to verify the {@link CameraCaptureSession#prepare} method works correctly, and has the
+ * expected effects on performance.
+ *
+ * - Ensure that prepare() results in onSurfacePrepared() being invoked
+ * - Ensure that prepare() does not cause preview glitches while operating
+ * - Ensure that starting to use a newly-prepared output does not cause additional
+ * preview glitches to occur
+ */
+ public void testPreparePerformance() throws Throwable {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ openDevice(mCameraIds[i]);
+
+ preparePerformanceTestByCamera(mCameraIds[i]);
+ }
+ finally {
+ closeDevice();
+ }
+ }
+ }
+
+ private void preparePerformanceTestByCamera(String cameraId) throws Exception {
+ final int MAX_IMAGES_TO_PREPARE = 10;
+ final int UNKNOWN_LATENCY_RESULT_WAIT = 5;
+ final int MAX_RESULTS_TO_WAIT = 10;
+ final int FRAMES_FOR_AVERAGING = 100;
+ final int PREPARE_TIMEOUT_MS = 10000; // 10 s
+ final float PREPARE_FRAME_RATE_BOUNDS = 0.05f; // fraction allowed difference
+ final float PREPARE_PEAK_RATE_BOUNDS = 0.5f; // fraction allowed difference
+
+ Size maxYuvSize = getSupportedPreviewSizes(cameraId, mCameraManager, null).get(0);
+ Size maxPreviewSize = mOrderedPreviewSizes.get(0);
+
+ // Don't need image data, just drop it right away to minimize overhead
+ ImageDropperListener imageListener = new ImageDropperListener();
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+ CaptureRequest.Builder previewRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ // Configure outputs and session
+
+ updatePreviewSurface(maxPreviewSize);
+
+ createImageReader(maxYuvSize, ImageFormat.YUV_420_888, MAX_IMAGES_TO_PREPARE, imageListener);
+
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ outputSurfaces.add(mPreviewSurface);
+ outputSurfaces.add(mReaderSurface);
+
+ CameraCaptureSession.StateCallback mockSessionListener =
+ mock(CameraCaptureSession.StateCallback.class);
+
+ mSession = configureCameraSession(mCamera, outputSurfaces, mockSessionListener, mHandler);
+
+ previewRequest.addTarget(mPreviewSurface);
+ Range<Integer> maxFpsTarget = mStaticInfo.getAeMaxTargetFpsRange();
+ previewRequest.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE, maxFpsTarget);
+
+ mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+
+ // Converge AE
+ waitForAeStable(resultListener, UNKNOWN_LATENCY_RESULT_WAIT);
+
+ if (mStaticInfo.isAeLockSupported()) {
+ // Lock AE if possible to improve stability
+ previewRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+ waitForResultValue(resultListener, CaptureResult.CONTROL_AE_STATE,
+ CaptureResult.CONTROL_AE_STATE_LOCKED, MAX_RESULTS_TO_WAIT);
+ }
+
+ // Measure frame rate for a bit
+ Pair<Long, Long> frameDurationStats =
+ measureMeanFrameInterval(resultListener, FRAMES_FOR_AVERAGING, /*prevTimestamp*/ 0);
+
+ Log.i(TAG, String.format("Frame interval avg during normal preview: %f ms, peak %f ms",
+ frameDurationStats.first / 1e6, frameDurationStats.second / 1e6));
+
+ // Drain results, do prepare
+ resultListener.drain();
+
+ mSession.prepare(mReaderSurface);
+
+ verify(mockSessionListener,
+ timeout(PREPARE_TIMEOUT_MS).times(1)).
+ onSurfacePrepared(eq(mSession), eq(mReaderSurface));
+
+ // Calculate frame rate during prepare
+
+ int resultsReceived = (int) resultListener.getTotalNumFrames();
+ if (resultsReceived > 2) {
+ // Only verify frame rate if there are a couple of results
+ Pair<Long, Long> whilePreparingFrameDurationStats =
+ measureMeanFrameInterval(resultListener, resultsReceived, /*prevTimestamp*/ 0);
+
+ Log.i(TAG, String.format("Frame interval during prepare avg: %f ms, peak %f ms",
+ whilePreparingFrameDurationStats.first / 1e6,
+ whilePreparingFrameDurationStats.second / 1e6));
+
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ mCollector.expectTrue(
+ String.format("Camera %s: Preview peak frame interval affected by prepare " +
+ "call: preview avg frame duration: %f ms, peak during prepare: %f ms",
+ cameraId,
+ frameDurationStats.first / 1e6,
+ whilePreparingFrameDurationStats.second / 1e6),
+ (whilePreparingFrameDurationStats.second <=
+ frameDurationStats.first * (1 + PREPARE_PEAK_RATE_BOUNDS)));
+ mCollector.expectTrue(
+ String.format("Camera %s: Preview average frame interval affected by prepare " +
+ "call: preview avg frame duration: %f ms, during prepare: %f ms",
+ cameraId,
+ frameDurationStats.first / 1e6,
+ whilePreparingFrameDurationStats.first / 1e6),
+ (whilePreparingFrameDurationStats.first <=
+ frameDurationStats.first * (1 + PREPARE_FRAME_RATE_BOUNDS)));
+ }
+ }
+
+ resultListener.drain();
+
+ // Get at least one more preview result without prepared target
+ CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+ long prevTimestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+
+ // Now use the prepared stream and ensure there are no hiccups from using it
+ previewRequest.addTarget(mReaderSurface);
+
+ mSession.setRepeatingRequest(previewRequest.build(), resultListener, mHandler);
+
+ Pair<Long, Long> preparedFrameDurationStats =
+ measureMeanFrameInterval(resultListener, MAX_IMAGES_TO_PREPARE*2, prevTimestamp);
+
+ Log.i(TAG, String.format("Frame interval with prepared stream added avg: %f ms, peak %f ms",
+ preparedFrameDurationStats.first / 1e6,
+ preparedFrameDurationStats.second / 1e6));
+
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ mCollector.expectTrue(
+ String.format("Camera %s: Preview peak frame interval affected by use of new " +
+ " stream: preview avg frame duration: %f ms, peak with new stream: %f ms",
+ cameraId,
+ frameDurationStats.first / 1e6, preparedFrameDurationStats.second / 1e6),
+ (preparedFrameDurationStats.second <=
+ frameDurationStats.first * (1 + PREPARE_PEAK_RATE_BOUNDS)));
+ mCollector.expectTrue(
+ String.format("Camera %s: Preview average frame interval affected by use of new " +
+ "stream: preview avg frame duration: %f ms, with new stream: %f ms",
+ cameraId,
+ frameDurationStats.first / 1e6, preparedFrameDurationStats.first / 1e6),
+ (preparedFrameDurationStats.first <=
+ frameDurationStats.first * (1 + PREPARE_FRAME_RATE_BOUNDS)));
+ }
+ }
+
+ /**
+ * Measure the inter-frame interval based on SENSOR_TIMESTAMP for frameCount frames from the
+ * provided capture listener. If prevTimestamp is positive, it is used for the first interval
+ * calculation; otherwise, the first result is used to establish the starting time.
+ *
+ * Returns the mean interval in the first pair entry, and the largest interval in the second
+ * pair entry
+ */
+ Pair<Long, Long> measureMeanFrameInterval(SimpleCaptureCallback resultListener, int frameCount,
+ long prevTimestamp) throws Exception {
+ long summedIntervals = 0;
+ long maxInterval = 0;
+ int measurementCount = frameCount - ((prevTimestamp > 0) ? 0 : 1);
+
+ for (int i = 0; i < frameCount; i++) {
+ CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+ long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
+ if (prevTimestamp > 0) {
+ long interval = timestamp - prevTimestamp;
+ if (interval > maxInterval) maxInterval = interval;
+ summedIntervals += interval;
+ }
+ prevTimestamp = timestamp;
+ }
+ return new Pair<Long, Long>(summedIntervals / measurementCount, maxInterval);
+ }
+
+
+ /**
* Test preview fps range for all supported ranges. The exposure time are frame duration are
* validated.
*/
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 538864d..b63541b 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
@@ -678,13 +678,8 @@
List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
checkTrueForKey(key, " Camera devices must always support FAST mode",
modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
- if (isCapabilitySupported(
- CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING)) {
- checkTrueForKey(key, "MANUAL_POST_PROCESSING supported camera devices must support"
- + "CONTRAST_CURVE mode",
- modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) &&
- modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
- }
+ // Qualification check for MANUAL_POSTPROCESSING capability is in
+ // StaticMetadataTest#testCapabilities
if (isHardwareLevelLimitedOrBetter()) {
// FAST and HIGH_QUALITY mode must be both present or both not present
@@ -698,7 +693,7 @@
}
checkElementDistinct(key, modeList);
checkArrayValuesInRange(key, modes, CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE,
- CameraMetadata.TONEMAP_MODE_HIGH_QUALITY);
+ CameraMetadata.TONEMAP_MODE_PRESET_CURVE);
return modes;
}
@@ -711,16 +706,23 @@
public int getMaxTonemapCurvePointChecked() {
Key<Integer> key = CameraCharacteristics.TONEMAP_MAX_CURVE_POINTS;
Integer count = getValueFromKeyNonNull(key);
+ List<Integer> modeList =
+ Arrays.asList(CameraTestUtils.toObject(getAvailableToneMapModesChecked()));
+ boolean tonemapCurveOutputSupported =
+ modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE) ||
+ modeList.contains(CameraMetadata.TONEMAP_MODE_GAMMA_VALUE) ||
+ modeList.contains(CameraMetadata.TONEMAP_MODE_PRESET_CURVE);
if (count == null) {
+ if (tonemapCurveOutputSupported) {
+ Assert.fail("Tonemap curve output is supported but MAX_CURVE_POINTS is null");
+ }
return 0;
}
- List<Integer> modeList =
- Arrays.asList(CameraTestUtils.toObject(getAvailableToneMapModesChecked()));
- if (modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE)) {
- checkTrueForKey(key, "Full-capability camera device must support maxCurvePoints "
- + ">= " + TONEMAP_MAX_CURVE_POINTS_AT_LEAST,
+ if (tonemapCurveOutputSupported) {
+ checkTrueForKey(key, "Tonemap curve output supported camera device must support "
+ + "maxCurvePoints >= " + TONEMAP_MAX_CURVE_POINTS_AT_LEAST,
count >= TONEMAP_MAX_CURVE_POINTS_AT_LEAST);
}
@@ -1282,9 +1284,17 @@
int fpsRangeLength = fpsRanges.length;
int minFps, maxFps;
long maxFrameDuration = getMaxFrameDurationChecked();
+ boolean foundConstant30Range = false;
+ boolean foundPreviewStreamingRange = false;
for (int i = 0; i < fpsRangeLength; i += 1) {
minFps = fpsRanges[i].getLower();
maxFps = fpsRanges[i].getUpper();
+ if (minFps == 30 && maxFps == 30) {
+ foundConstant30Range = true;
+ }
+ if (minFps <= 15 && maxFps >= 30) {
+ foundPreviewStreamingRange = true;
+ }
checkTrueForKey(key, " min fps must be no larger than max fps!",
minFps > 0 && maxFps >= minFps);
long maxDuration = (long) (1e9 / minFps);
@@ -1292,11 +1302,38 @@
" the frame duration %d for min fps %d must smaller than maxFrameDuration %d",
maxDuration, minFps, maxFrameDuration), maxDuration <= maxFrameDuration);
}
-
+ checkTrueForKey(key, String.format(" (30, 30) must be included"), foundConstant30Range);
+ checkTrueForKey(key, String.format(
+ " (min, max) where min <= 15 and max >= 30 must be included"),
+ foundPreviewStreamingRange);
return fpsRanges;
}
/**
+ * Get the highest supported target FPS range.
+ * Prioritizes maximizing the min FPS, then the max FPS without lowering min FPS.
+ */
+ public Range<Integer> getAeMaxTargetFpsRange() {
+ Range<Integer>[] fpsRanges = getAeAvailableTargetFpsRangesChecked();
+
+ Range<Integer> targetRange = fpsRanges[0];
+ // Assume unsorted list of target FPS ranges, so use two passes, first maximize min FPS
+ for (Range<Integer> candidateRange : fpsRanges) {
+ if (candidateRange.getLower() > targetRange.getLower()) {
+ targetRange = candidateRange;
+ }
+ }
+ // Then maximize max FPS while not lowering min FPS
+ for (Range<Integer> candidateRange : fpsRanges) {
+ if (candidateRange.getLower() >= targetRange.getLower() &&
+ candidateRange.getUpper() > targetRange.getUpper()) {
+ targetRange = candidateRange;
+ }
+ }
+ return targetRange;
+ }
+
+ /**
* Get max frame duration.
*
* @return 0 if maxFrameDuration is null
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
index e7f1e7a..78370b3 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2AndroidTestCase.java
@@ -294,13 +294,8 @@
ImageReader.OnImageAvailableListener listener) throws Exception {
ImageReader reader = null;
- if (format == ImageFormat.PRIVATE) {
- // Create opaque ImageReader
- reader = ImageReader.newOpaqueInstance(size.getWidth(), size.getHeight(), maxNumImages);
- } else {
- reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
- format, maxNumImages);
- }
+ reader = ImageReader.newInstance(size.getWidth(), size.getHeight(),
+ format, maxNumImages);
reader.setOnImageAvailableListener(listener, mHandler);
if (VERBOSE) Log.v(TAG, "Created ImageReader size " + size.toString());
@@ -462,15 +457,6 @@
// Expected.
}
- // Image#isOpaque test
- try {
- closedImage.isOpaque();
- fail("Image should throw IllegalStateException when calling isOpaque"
- + " after the image is closed");
- } catch (IllegalStateException e) {
- // Expected.
- }
-
// Image#getCropRect test
try {
closedImage.getCropRect();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
index c6c0250..3ca696b 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2SurfaceViewTestCase.java
@@ -707,7 +707,7 @@
CheckLevel.ASSERT, /*collector*/ null);
int cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING;
if (format == ImageFormat.PRIVATE) {
- cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING;
+ cap = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING;
}
return info.isCapabilitySupported(cap);
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java
index a923844..33c8955 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorSpecTest.java
@@ -25,11 +25,6 @@
import javax.security.auth.x500.X500Principal;
public class KeyPairGeneratorSpecTest extends AndroidTestCase {
- private static final X500Principal DEFAULT_CERT_SUBJECT = new X500Principal("CN=fake");
- private static final BigInteger DEFAULT_CERT_SERIAL_NUMBER = new BigInteger("1");
- private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1980
- private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
-
private static final String TEST_ALIAS_1 = "test1";
private static final X500Principal TEST_DN_1 = new X500Principal("CN=test1");
@@ -110,44 +105,56 @@
}
}
- public void testBuilder_MissingSubjectDN_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSerialNumber(SERIAL_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build();
- assertEquals(DEFAULT_CERT_SUBJECT, spec.getSubjectDN());
+ public void testBuilder_MissingSubjectDN_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when subject is missing");
+ } catch (IllegalArgumentException expected) {
+ }
}
- public void testBuilder_MissingSerialNumber_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setStartDate(NOW)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build();
- assertEquals(DEFAULT_CERT_SERIAL_NUMBER, spec.getSerialNumber());
+ public void testBuilder_MissingSerialNumber_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setStartDate(NOW)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when serialNumber is missing");
+ } catch (IllegalArgumentException expected) {
+ }
}
- public void testBuilder_MissingStartDate_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(SERIAL_1)
- .setEndDate(NOW_PLUS_10_YEARS)
- .build();
- assertEquals(DEFAULT_CERT_NOT_BEFORE, spec.getStartDate());
+ public void testBuilder_MissingStartDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setEndDate(NOW_PLUS_10_YEARS)
+ .build();
+ fail("Should throw IllegalArgumentException when startDate is missing");
+ } catch (IllegalArgumentException expected) {
+ }
}
- public void testBuilder_MissingEndDate_Success() throws Exception {
- KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
- .setAlias(TEST_ALIAS_1)
- .setSubject(TEST_DN_1)
- .setSerialNumber(SERIAL_1)
- .setStartDate(NOW)
- .build();
- assertEquals(DEFAULT_CERT_NOT_AFTER, spec.getEndDate());
+ public void testBuilder_MissingEndDate_Failure() throws Exception {
+ try {
+ new KeyPairGeneratorSpec.Builder(getContext())
+ .setAlias(TEST_ALIAS_1)
+ .setSubject(TEST_DN_1)
+ .setSerialNumber(SERIAL_1)
+ .setStartDate(NOW)
+ .build();
+ fail("Should throw IllegalArgumentException when endDate is missing");
+ } catch (IllegalArgumentException expected) {
+ }
}
public void testBuilder_EndBeforeStart_Failure() throws Exception {
diff --git a/tests/tests/location/src/android/location/cts/CriteriaTest.java b/tests/tests/location/src/android/location/cts/CriteriaTest.java
index d812965f..422f561 100644
--- a/tests/tests/location/src/android/location/cts/CriteriaTest.java
+++ b/tests/tests/location/src/android/location/cts/CriteriaTest.java
@@ -113,6 +113,19 @@
assertTrue(criteria.isAltitudeRequired());
}
+ public void testAccessBearingAccuracy() {
+ Criteria criteria = new Criteria();
+
+ criteria.setBearingAccuracy(Criteria.ACCURACY_LOW);
+ assertEquals(Criteria.ACCURACY_LOW, criteria.getBearingAccuracy());
+
+ criteria.setBearingAccuracy(Criteria.ACCURACY_HIGH);
+ assertEquals(Criteria.ACCURACY_HIGH, criteria.getBearingAccuracy());
+
+ criteria.setBearingAccuracy(Criteria.NO_REQUIREMENT);
+ assertEquals(Criteria.NO_REQUIREMENT, criteria.getBearingAccuracy());
+ }
+
public void testAccessBearingRequired() {
Criteria criteria = new Criteria();
@@ -133,6 +146,35 @@
assertTrue(criteria.isCostAllowed());
}
+ public void testAccessHorizontalAccuracy() {
+ Criteria criteria = new Criteria();
+
+ criteria.setHorizontalAccuracy(Criteria.ACCURACY_LOW);
+ assertEquals(Criteria.ACCURACY_LOW, criteria.getHorizontalAccuracy());
+
+ criteria.setHorizontalAccuracy(Criteria.ACCURACY_MEDIUM);
+ assertEquals(Criteria.ACCURACY_MEDIUM, criteria.getHorizontalAccuracy());
+
+ criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
+ assertEquals(Criteria.ACCURACY_HIGH, criteria.getHorizontalAccuracy());
+
+ criteria.setHorizontalAccuracy(Criteria.NO_REQUIREMENT);
+ assertEquals(Criteria.NO_REQUIREMENT, criteria.getHorizontalAccuracy());
+ }
+
+ public void testAccessSpeedAccuracy() {
+ Criteria criteria = new Criteria();
+
+ criteria.setSpeedAccuracy(Criteria.ACCURACY_LOW);
+ assertEquals(Criteria.ACCURACY_LOW, criteria.getSpeedAccuracy());
+
+ criteria.setSpeedAccuracy(Criteria.ACCURACY_HIGH);
+ assertEquals(Criteria.ACCURACY_HIGH, criteria.getSpeedAccuracy());
+
+ criteria.setSpeedAccuracy(Criteria.NO_REQUIREMENT);
+ assertEquals(Criteria.NO_REQUIREMENT, criteria.getSpeedAccuracy());
+ }
+
public void testAccessSpeedRequired() {
Criteria criteria = new Criteria();
@@ -143,6 +185,19 @@
assertTrue(criteria.isSpeedRequired());
}
+ public void testAccessVerticalAccuracy() {
+ Criteria criteria = new Criteria();
+
+ criteria.setVerticalAccuracy(Criteria.ACCURACY_LOW);
+ assertEquals(Criteria.ACCURACY_LOW, criteria.getVerticalAccuracy());
+
+ criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);
+ assertEquals(Criteria.ACCURACY_HIGH, criteria.getVerticalAccuracy());
+
+ criteria.setVerticalAccuracy(Criteria.NO_REQUIREMENT);
+ assertEquals(Criteria.NO_REQUIREMENT, criteria.getVerticalAccuracy());
+ }
+
public void testWriteToParcel() {
Criteria criteria = new Criteria();
criteria.setAltitudeRequired(true);
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index a985aee..3168335 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -27,6 +27,7 @@
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
+import android.location.GpsStatus.NmeaListener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -537,6 +538,358 @@
}
}
+ public void testSingleUpdateWithLocationListenerAndLooper() throws InterruptedException {
+ double latitude1 = 60;
+ double longitude1 = 20;
+ double latitude2 = 40;
+ double longitude2 = 30;
+ double latitude3 = 10;
+ double longitude3 = 50;
+ final MockLocationListener listener = new MockLocationListener();
+
+ // update location and notify listener
+ HandlerThread handlerThread = new HandlerThread("testLocationUpdates4");
+ handlerThread.start();
+ mManager.requestSingleUpdate(TEST_MOCK_PROVIDER_NAME, listener, handlerThread.getLooper());
+
+ updateLocation(latitude1, longitude1);
+ assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+ Location location = listener.getLocation();
+ assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
+
+ // Any further location change doesn't trigger an update.
+ updateLocation(latitude2, longitude2);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+
+ // update location without notifying listener
+ mManager.removeUpdates(listener);
+ listener.reset();
+ updateLocation(latitude3, longitude3);
+ assertFalse(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+
+ try {
+ mManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, (LocationListener) null,
+ Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestSingleUpdate((String) null, listener, Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param provider is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (LocationListener) null );
+ fail("Should throw IllegalArgumentException if listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
+ public void testLocationUpdatesWithCriteriaAndPendingIntent() throws InterruptedException {
+ double latitude1 = 10;
+ double longitude1 = 20;
+ double latitude2 = 30;
+ double longitude2 = 40;
+
+ registerIntentReceiver();
+ mockFusedLocation();
+
+ // Update location and receive broadcast.
+ Criteria criteria = createLocationCriteria();
+ mManager.requestLocationUpdates(0, 0 , criteria, mPendingIntent);
+ updateFusedLocation(latitude1, longitude1);
+ waitForReceiveBroadcast();
+
+ assertNotNull(mIntentReceiver.getLastReceivedIntent());
+ Location location = (Location) mIntentReceiver.getLastReceivedIntent().getExtras()
+ .get(LocationManager.KEY_LOCATION_CHANGED);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertTrue(location.hasAccuracy());
+ assertEquals(1.0f, location.getAccuracy());
+ assertEquals(true, location.isFromMockProvider());
+
+ // Update location without receiving broadcast.
+ mManager.removeUpdates(mPendingIntent);
+ mIntentReceiver.clearReceivedIntents();
+ updateFusedLocation(latitude2, longitude2);
+ waitForReceiveBroadcast();
+ assertNull(mIntentReceiver.getLastReceivedIntent());
+
+ // Missing arguments throw exceptions.
+ try {
+ mManager.requestLocationUpdates(0, 0, criteria, (PendingIntent) null);
+ fail("Should throw IllegalArgumentException if param intent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestLocationUpdates(0, 0, null, mPendingIntent);
+ fail("Should throw IllegalArgumentException if param criteria is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (PendingIntent) null );
+ fail("Should throw IllegalArgumentException if param PendingIntent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ unmockFusedLocation();
+ }
+
+ public void testSingleUpdateWithCriteriaAndPendingIntent() throws InterruptedException {
+ double latitude1 = 10;
+ double longitude1 = 20;
+ double latitude2 = 30;
+ double longitude2 = 40;
+ double latitude3 = 50;
+ double longitude3 = 60;
+
+ registerIntentReceiver();
+ mockFusedLocation();
+
+ // Update location and receive broadcast.
+ Criteria criteria = createLocationCriteria();
+ mManager.requestSingleUpdate(criteria, mPendingIntent);
+ updateFusedLocation(latitude1, longitude1);
+ waitForReceiveBroadcast();
+
+ assertNotNull(mIntentReceiver.getLastReceivedIntent());
+ Location location = (Location) mIntentReceiver.getLastReceivedIntent().getExtras()
+ .get(LocationManager.KEY_LOCATION_CHANGED);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertTrue(location.hasAccuracy());
+ assertEquals(1.0f, location.getAccuracy());
+ assertEquals(true, location.isFromMockProvider());
+
+ // Any further location change doesn't trigger an update.
+ updateFusedLocation(latitude2, longitude2);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+
+ // Update location without receiving broadcast.
+ mManager.removeUpdates(mPendingIntent);
+ mIntentReceiver.clearReceivedIntents();
+ updateFusedLocation(latitude3, longitude3);
+ waitForReceiveBroadcast();
+ assertNull(mIntentReceiver.getLastReceivedIntent());
+
+ // Missing arguments throw exceptions.
+ try {
+ mManager.requestSingleUpdate(criteria, (PendingIntent) null);
+ fail("Should throw IllegalArgumentException if param intent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestSingleUpdate((Criteria) null, mPendingIntent);
+ fail("Should throw IllegalArgumentException if param criteria is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (PendingIntent) null );
+ fail("Should throw IllegalArgumentException if param PendingIntent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ unmockFusedLocation();
+ }
+
+ public void testLocationUpdatesWithCriteriaAndLocationListenerAndLooper()
+ throws InterruptedException {
+ double latitude1 = 40;
+ double longitude1 = 10;
+ double latitude2 = 20;
+ double longitude2 = 30;
+ final MockLocationListener listener = new MockLocationListener();
+ mockFusedLocation();
+
+ // update location and notify listener
+ HandlerThread handlerThread = new HandlerThread("testLocationUpdates1");
+ handlerThread.start();
+ Criteria criteria = createLocationCriteria();
+ mManager.requestLocationUpdates(0, 0, criteria, listener, handlerThread.getLooper());
+
+ updateFusedLocation(latitude1, longitude1);
+ assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+ Location location = listener.getLocation();
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertTrue(location.hasAccuracy());
+ assertEquals(1.0f, location.getAccuracy());
+ assertEquals(true, location.isFromMockProvider());
+
+ // update location without notifying listener
+ mManager.removeUpdates(listener);
+ listener.reset();
+ updateFusedLocation(latitude2, longitude2);
+ assertFalse(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+
+ // Missing arguments throw exceptions.
+ try {
+ mManager.requestLocationUpdates(0, 0, criteria, (LocationListener) null,
+ Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestLocationUpdates(0, 0, null, listener, Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param criteria is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (LocationListener) null );
+ fail("Should throw IllegalArgumentException if listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ unmockFusedLocation();
+ }
+
+ public void testSingleUpdateWithCriteriaAndLocationListenerAndLooper()
+ throws InterruptedException {
+ double latitude1 = 40;
+ double longitude1 = 10;
+ double latitude2 = 20;
+ double longitude2 = 30;
+ double latitude3 = 60;
+ double longitude3 = 50;
+ final MockLocationListener listener = new MockLocationListener();
+ mockFusedLocation();
+
+ // update location and notify listener
+ HandlerThread handlerThread = new HandlerThread("testLocationUpdates2");
+ handlerThread.start();
+ Criteria criteria = createLocationCriteria();
+ mManager.requestSingleUpdate(criteria, listener, handlerThread.getLooper());
+
+ updateFusedLocation(latitude1, longitude1);
+ assertTrue(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+ Location location = listener.getLocation();
+ assertEquals(FUSED_PROVIDER_NAME, location.getProvider());
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertTrue(location.hasAccuracy());
+ assertEquals(1.0f, location.getAccuracy());
+ assertEquals(true, location.isFromMockProvider());
+
+ // Any further location change doesn't trigger an update.
+ updateFusedLocation(latitude2, longitude2);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+
+ // update location without notifying listener
+ mManager.removeUpdates(listener);
+ listener.reset();
+ updateFusedLocation(latitude3, longitude3);
+ assertFalse(listener.hasCalledOnLocationChanged(TEST_TIME_OUT));
+
+ // Missing arguments throw exceptions.
+ try {
+ mManager.requestLocationUpdates(0, 0, criteria, (LocationListener) null,
+ Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestLocationUpdates(0, 0, null, listener, Looper.myLooper());
+ fail("Should throw IllegalArgumentException if param criteria is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (LocationListener) null );
+ fail("Should throw IllegalArgumentException if listener is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ unmockFusedLocation();
+ }
+
+ public void testSingleUpdateWithPendingIntent() throws InterruptedException {
+ double latitude1 = 20;
+ double longitude1 = 40;
+ double latitude2 = 30;
+ double longitude2 = 50;
+ double latitude3 = 10;
+ double longitude3 = 60;
+
+ // update location and receive broadcast.
+ registerIntentReceiver();
+ mManager.requestLocationUpdates(TEST_MOCK_PROVIDER_NAME, 0, 0, mPendingIntent);
+ updateLocation(latitude1, longitude1);
+ waitForReceiveBroadcast();
+
+ assertNotNull(mIntentReceiver.getLastReceivedIntent());
+ Location location = mManager.getLastKnownLocation(TEST_MOCK_PROVIDER_NAME);
+ assertEquals(TEST_MOCK_PROVIDER_NAME, location.getProvider());
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+ assertEquals(true, location.isFromMockProvider());
+
+ // Any further location change doesn't trigger an update.
+ updateLocation(latitude2, longitude2);
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+
+ // update location without receiving broadcast.
+ mManager.removeUpdates(mPendingIntent);
+ mIntentReceiver.clearReceivedIntents();
+ updateLocation(latitude3, longitude3);
+ waitForReceiveBroadcast();
+ assertEquals(latitude1, location.getLatitude());
+ assertEquals(longitude1, location.getLongitude());
+
+ try {
+ mManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0,
+ (PendingIntent) null);
+ fail("Should throw IllegalArgumentException if param intent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.requestLocationUpdates(null, 0, 0, mPendingIntent);
+ fail("Should throw IllegalArgumentException if param provider is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+
+ try {
+ mManager.removeUpdates( (PendingIntent) null );
+ fail("Should throw IllegalArgumentException if intent is null!");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
+
public void testAddProximityAlert() {
Intent i = new Intent();
i.setAction("android.location.cts.TEST_GET_GPS_STATUS_ACTION");
@@ -546,6 +899,17 @@
mManager.removeProximityAlert(pi);
}
+
+ @UiThreadTest
+ public void testNmeaListener() {
+ MockNmeaListener listener = new MockNmeaListener();
+ mManager.addNmeaListener(listener);
+ mManager.removeNmeaListener(listener);
+
+ mManager.addNmeaListener(null);
+ mManager.removeNmeaListener(null);
+ }
+
public void testIsProviderEnabled() {
// this test assumes enabled TEST_MOCK_PROVIDER_NAME was created in setUp.
assertNotNull(mManager.getProvider(TEST_MOCK_PROVIDER_NAME));
@@ -840,6 +1204,21 @@
updateLocation(TEST_MOCK_PROVIDER_NAME, latitude, longitude);
}
+ private void updateFusedLocation(final double latitude, final double longitude) {
+ updateLocation(FUSED_PROVIDER_NAME, latitude, longitude);
+ }
+
+ private Criteria createLocationCriteria() {
+ Criteria criteria = new Criteria();
+ criteria.setAccuracy(Criteria.ACCURACY_FINE);
+ criteria.setPowerRequirement(Criteria.POWER_MEDIUM);
+ criteria.setAltitudeRequired(false);
+ criteria.setBearingRequired(false);
+ criteria.setCostAllowed(false);
+ criteria.setSpeedRequired(false);
+ return criteria;
+ }
+
private void mockFusedLocation() {
addTestProvider(FUSED_PROVIDER_NAME);
}
@@ -1003,6 +1382,23 @@
}
}
+ private static class MockNmeaListener implements NmeaListener {
+ private boolean mIsNmeaReceived;
+
+ @Override
+ public void onNmeaReceived(long timestamp, String nmea) {
+ mIsNmeaReceived = true;
+ }
+
+ public boolean isNmeaRecevied() {
+ return mIsNmeaReceived;
+ }
+
+ public void reset() {
+ mIsNmeaReceived = false;
+ }
+ }
+
private static class MockGpsStatusListener implements Listener {
private boolean mHasCallOnGpsStatusChanged;
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index e913f05..cdc0e60 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -52,6 +52,21 @@
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
</intent-filter>
</activity>
+ <activity android:name="android.media.cts.ResourceManagerStubActivity"
+ android:label="ResourceManagerStubActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+ <activity android:name="android.media.cts.ResourceManagerTestActivity1"
+ android:label="ResourceManagerTestActivity1"
+ android:process=":mediaCodecTestProcess1">
+ </activity>
+ <activity android:name="android.media.cts.ResourceManagerTestActivity2"
+ android:label="ResourceManagerTestActivity2"
+ android:process=":mediaCodecTestProcess2">
+ </activity>
<activity android:name="android.media.cts.RingtonePickerActivity"
android:label="RingtonePickerActivity">
<intent-filter>
@@ -65,6 +80,11 @@
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</service>
+ <service android:name="android.media.cts.StubMediaBrowserService">
+ <intent-filter>
+ <action android:name="android.media.browse.MediaBrowserService" />
+ </intent-filter>
+ </service>
</application>
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index dbb609d..1fb3ea7 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -862,6 +862,15 @@
mCodec.configure(format, mSurface.getSurface(), null /* crypto */, 0 /* flags */);
Log.i(TAG, "start");
mCodec.start();
+
+ // inject some minimal setOutputSurface test
+ // TODO: change this test to also change the surface midstream
+ try {
+ mCodec.setOutputSurface(null);
+ fail("should not be able to set surface to NULL");
+ } catch (IllegalArgumentException e) {}
+ mCodec.setOutputSurface(mSurface.getSurface());
+
mInputBuffers = mCodec.getInputBuffers();
mOutputBuffers = mCodec.getOutputBuffers();
Log.i(TAG, "configured " + mInputBuffers.length + " input[" +
diff --git a/tests/tests/media/src/android/media/cts/AudioHelper.java b/tests/tests/media/src/android/media/cts/AudioHelper.java
index 6f3d4d0..efee024 100644
--- a/tests/tests/media/src/android/media/cts/AudioHelper.java
+++ b/tests/tests/media/src/android/media/cts/AudioHelper.java
@@ -17,6 +17,7 @@
package android.media.cts;
import java.nio.ByteBuffer;
+
import org.junit.Assert;
import android.media.AudioAttributes;
@@ -29,6 +30,43 @@
// Used for statistics and loopers in listener tests.
// See AudioRecordTest.java and AudioTrack_ListenerTest.java.
public class AudioHelper {
+
+ // create sine waves or chirps for data arrays
+ public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
+ final double frequency, double sweep) {
+ final double rad = 2 * Math.PI * frequency / sampleRate;
+ byte[] vai = new byte[bufferSamples];
+ sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
+ for (int j = 0; j < vai.length; j++) {
+ int unsigned = (int)(Math.sin(j * (rad + j * sweep)) * Byte.MAX_VALUE)
+ + Byte.MAX_VALUE & 0xFF;
+ vai[j] = (byte) unsigned;
+ }
+ return vai;
+ }
+
+ public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
+ final double frequency, double sweep) {
+ final double rad = 2 * Math.PI * frequency / sampleRate;
+ short[] vai = new short[bufferSamples];
+ sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
+ for (int j = 0; j < vai.length; j++) {
+ vai[j] = (short)(Math.sin(j * (rad + j * sweep)) * Short.MAX_VALUE);
+ }
+ return vai;
+ }
+
+ public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
+ final double frequency, double sweep) {
+ final double rad = 2 * Math.PI * frequency / sampleRate;
+ float[] vaf = new float[bufferSamples];
+ sweep = Math.PI * sweep / ((double)sampleRate * vaf.length);
+ for (int j = 0; j < vaf.length; j++) {
+ vaf[j] = (float)(Math.sin(j * (rad + j * sweep)));
+ }
+ return vaf;
+ }
+
public static int frameSizeFromFormat(AudioFormat format) {
return format.getChannelCount()
* format.getBytesPerSample(format.getEncoding());
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index d390c14..56d2b68 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -25,7 +25,9 @@
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioRecord.OnRecordPositionUpdateListener;
+import android.media.AudioTrack;
import android.media.MediaRecorder;
+import android.media.MediaSyncEvent;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -414,16 +416,153 @@
assertEquals(TEST_NAME + ": state", expectedState, observedState);
}
+ public void testSynchronizedRecord() throws Exception {
+ if (!hasMicrophone()) {
+ return;
+ }
+ final String TEST_NAME = "testSynchronizedRecord";
+ AudioTrack track = null;
+ AudioRecord record = null;
+
+ try {
+ // 1. create a static AudioTrack.
+ final int PLAYBACK_TIME_IN_MS = 2000; /* ms duration. */
+ final int PLAYBACK_SAMPLE_RATE = 8000; /* in hz */
+ AudioFormat format = new AudioFormat.Builder()
+ .setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
+ .setEncoding(AudioFormat.ENCODING_PCM_8BIT)
+ .setSampleRate(PLAYBACK_SAMPLE_RATE)
+ .build();
+ final int frameCount = AudioHelper.frameCountFromMsec(PLAYBACK_TIME_IN_MS, format);
+ final int frameSize = AudioHelper.frameSizeFromFormat(format);
+ track = new AudioTrack.Builder()
+ .setAudioFormat(format)
+ .setBufferSizeInBytes(frameCount * frameSize)
+ .setTransferMode(AudioTrack.MODE_STATIC)
+ .build();
+ // create float array and write it
+ final int sampleCount = frameCount * format.getChannelCount();
+ byte[] vab = AudioHelper.createSoundDataInByteArray(
+ sampleCount, PLAYBACK_SAMPLE_RATE, 600 /* frequency */, 0 /* sweep */);
+ assertEquals(TEST_NAME, vab.length,
+ track.write(vab, 0 /* offsetInBytes */, vab.length,
+ AudioTrack.WRITE_NON_BLOCKING));
+ final int trackSessionId = track.getAudioSessionId();
+
+ // 2. create an AudioRecord to sync off of AudioTrack completion.
+ final int RECORD_TIME_IN_MS = 2000;
+ final int RECORD_ENCODING = AudioFormat.ENCODING_PCM_16BIT;
+ final int RECORD_CHANNEL_MASK = AudioFormat.CHANNEL_IN_STEREO;
+ final int RECORD_SAMPLE_RATE = 44100;
+ record = new AudioRecord.Builder()
+ .setAudioFormat(new AudioFormat.Builder()
+ .setSampleRate(RECORD_SAMPLE_RATE)
+ .setChannelMask(RECORD_CHANNEL_MASK)
+ .setEncoding(RECORD_ENCODING)
+ .build())
+ .build();
+ // AudioRecord creation may have silently failed, check state now
+ assertEquals(TEST_NAME, AudioRecord.STATE_INITIALIZED, record.getState());
+
+ // 3. create a MediaSyncEvent
+ // This MediaSyncEvent checks playback completion of an AudioTrack
+ // (or MediaPlayer, or ToneGenerator) based on its audio session id.
+ //
+ // Note: when synchronizing record from a MediaSyncEvent
+ // (1) You need to be "close" to the end of the associated AudioTrack.
+ // If the track does not complete in 30 seconds, recording begins regardless.
+ // (actual delay limit may vary).
+ //
+ // (2) Track completion may be triggered by pause() as well as stop()
+ // or when a static AudioTrack completes playback.
+ //
+ final int eventType = MediaSyncEvent.SYNC_EVENT_PRESENTATION_COMPLETE;
+ MediaSyncEvent event = MediaSyncEvent.createEvent(eventType)
+ .setAudioSessionId(trackSessionId);
+ assertEquals(TEST_NAME, trackSessionId, event.getAudioSessionId());
+ assertEquals(TEST_NAME, eventType, event.getType());
+
+ // 4. now set the AudioTrack playing and start the recording synchronized
+ track.play();
+ // start recording. Recording state turns to RECORDSTATE_RECORDING immediately
+ // but the data read() only occurs after the AudioTrack completes.
+ record.startRecording(event);
+ assertEquals(TEST_NAME,
+ AudioRecord.RECORDSTATE_RECORDING, record.getRecordingState());
+ long startTime = System.currentTimeMillis();
+
+ // 5. get record data.
+ // For our tests, we could set test duration by timed sleep or by # frames received.
+ // Since we don't know *exactly* when AudioRecord actually begins recording,
+ // we end the test by # frames read.
+ final int numChannels =
+ AudioFormat.channelCountFromInChannelMask(RECORD_CHANNEL_MASK);
+ final int bytesPerSample = AudioFormat.getBytesPerSample(RECORD_ENCODING);
+ final int bytesPerFrame = numChannels * bytesPerSample;
+ // careful about integer overflow in the formula below:
+ final int targetSamples =
+ (int)((long)RECORD_TIME_IN_MS * RECORD_SAMPLE_RATE * numChannels / 1000);
+ final int BUFFER_FRAMES = 512;
+ final int BUFFER_SAMPLES = BUFFER_FRAMES * numChannels;
+
+ // After starting, there is no guarantee when the first frame of data is read.
+ long firstSampleTime = 0;
+ int samplesRead = 0;
+
+ // For 16 bit data, use shorts
+ short[] shortData = new short[BUFFER_SAMPLES];
+ while (samplesRead < targetSamples) {
+ // the first time through, we read a single frame.
+ // this sets the recording anchor position.
+ int amount = samplesRead == 0 ? numChannels :
+ Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+ int ret = record.read(shortData, 0, amount);
+ assertEquals(TEST_NAME, amount, ret);
+ if (samplesRead == 0 && ret > 0) {
+ firstSampleTime = System.currentTimeMillis();
+ }
+ samplesRead += ret;
+ // sanity check: elapsed time cannot be more than a second
+ // than what we expect.
+ assertTrue(System.currentTimeMillis() - startTime <=
+ PLAYBACK_TIME_IN_MS + RECORD_TIME_IN_MS + 1000);
+ }
+
+ // 6. We've read all the frames, now check the timing.
+ final long endTime = System.currentTimeMillis();
+ //Log.d(TEST_NAME, "first sample time " + (firstSampleTime - startTime)
+ // + " test time " + (endTime - firstSampleTime));
+ //
+ // Verify recording starts within 400 ms of AudioTrack completion (typical 180ms)
+ // Verify recording completes within 50 ms of expected test time (typical 20ms)
+ assertEquals(TEST_NAME, PLAYBACK_TIME_IN_MS, firstSampleTime - startTime, 400);
+ assertEquals(TEST_NAME, RECORD_TIME_IN_MS, endTime - firstSampleTime, 50);
+
+ record.stop();
+ assertEquals(TEST_NAME, AudioRecord.RECORDSTATE_STOPPED, record.getRecordingState());
+ } finally {
+ if (record != null) {
+ record.release();
+ record = null;
+ }
+ if (track != null) {
+ track.release();
+ track = null;
+ }
+ }
+ }
+
private AudioRecord createAudioRecord(
int audioSource, int sampleRateInHz,
int channelConfig, int audioFormat, int bufferSizeInBytes,
boolean auditRecording, boolean isChannelIndex) {
+ final AudioRecord record;
if (auditRecording) {
- return new AudioHelper.AudioRecordAudit(
+ record = new AudioHelper.AudioRecordAudit(
audioSource, sampleRateInHz, channelConfig,
audioFormat, bufferSizeInBytes, isChannelIndex);
} else if (isChannelIndex) {
- return new AudioRecord.Builder()
+ record = new AudioRecord.Builder()
.setAudioFormat(new AudioFormat.Builder()
.setChannelIndexMask(channelConfig)
.setEncoding(audioFormat)
@@ -432,9 +571,23 @@
.setBufferSizeInBytes(bufferSizeInBytes)
.build();
} else {
- return new AudioRecord(audioSource, sampleRateInHz, channelConfig,
+ record = new AudioRecord(audioSource, sampleRateInHz, channelConfig,
audioFormat, bufferSizeInBytes);
}
+
+ // did we get the AudioRecord we expected?
+ final AudioFormat format = record.getFormat();
+ assertEquals(isChannelIndex ? channelConfig : AudioFormat.CHANNEL_INVALID,
+ format.getChannelIndexMask());
+ assertEquals(isChannelIndex ? AudioFormat.CHANNEL_INVALID : channelConfig,
+ format.getChannelMask());
+ assertEquals(audioFormat, format.getEncoding());
+ assertEquals(sampleRateInHz, format.getSampleRate());
+ final int frameSize =
+ format.getChannelCount() * AudioFormat.getBytesPerSample(audioFormat);
+ // our native frame count cannot be smaller than our minimum buffer size request.
+ assertTrue(record.getBufferSizeInFrames() * frameSize >= bufferSizeInBytes);
+ return record;
}
private void doTest(String reportName, boolean localRecord, boolean customHandler,
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 403d714..dfb83eb 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1391,6 +1391,7 @@
frameCount /= 2;
}
assertTrue(TEST_NAME, track.getNativeFrameCount() >= frameCount);
+ assertEquals(TEST_NAME, track.getNativeFrameCount(), track.getBufferSizeInFrames());
}
public void testReloadStaticData() throws Exception {
@@ -1404,7 +1405,8 @@
// -------- initialization --------------
int bufferSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
- byte data[] = createSoundDataInByteArray(bufferSize, TEST_SR, 1024);
+ byte data[] = AudioHelper.createSoundDataInByteArray(
+ bufferSize, TEST_SR, 1024 /* frequency */, 0 /* sweep */);
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT,
bufferSize, TEST_MODE);
// -------- test --------------
@@ -1422,56 +1424,6 @@
track.release();
}
- public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
- final double frequency, double sweep) {
- final double rad = 2 * Math.PI * frequency / sampleRate;
- byte[] vai = new byte[bufferSamples];
- sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
- for (int j = 0; j < vai.length; j++) {
- int unsigned = (int)(Math.sin(j * (rad + j * sweep)) * Byte.MAX_VALUE)
- + Byte.MAX_VALUE & 0xFF;
- vai[j] = (byte) unsigned;
- }
- return vai;
- }
-
- public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
- final double frequency, double sweep) {
- final double rad = 2 * Math.PI * frequency / sampleRate;
- short[] vai = new short[bufferSamples];
- sweep = Math.PI * sweep / ((double)sampleRate * vai.length);
- for (int j = 0; j < vai.length; j++) {
- vai[j] = (short)(Math.sin(j * (rad + j * sweep)) * Short.MAX_VALUE);
- }
- return vai;
- }
-
- public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
- final double frequency, double sweep) {
- final double rad = 2 * Math.PI * frequency / sampleRate;
- float[] vaf = new float[bufferSamples];
- sweep = Math.PI * sweep / ((double)sampleRate * vaf.length);
- for (int j = 0; j < vaf.length; j++) {
- vaf[j] = (float)(Math.sin(j * (rad + j * sweep)));
- }
- return vaf;
- }
-
- public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
- final double frequency) {
- return createSoundDataInByteArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
- }
-
- public static short[] createSoundDataInShortArray(int bufferSamples, final int sampleRate,
- final double frequency) {
- return createSoundDataInShortArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
- }
-
- public static float[] createSoundDataInFloatArray(int bufferSamples, final int sampleRate,
- final double frequency) {
- return createSoundDataInFloatArray(bufferSamples, sampleRate, frequency, 0 /*sweep*/);
- }
-
public void testPlayStaticData() throws Exception {
if (!hasAudioOutput()) {
Log.w(TAG,"AUDIO_OUTPUT feature not found. This system might not have a valid "
@@ -1529,7 +1481,7 @@
// only need to write once to the static track
switch (TEST_FORMAT) {
case AudioFormat.ENCODING_PCM_8BIT: {
- byte data[] = createSoundDataInByteArray(
+ byte data[] = AudioHelper.createSoundDataInByteArray(
bufferSamples, TEST_SR,
testFrequency, TEST_SWEEP);
assertEquals(TEST_NAME,
@@ -1537,7 +1489,7 @@
track.write(data, 0 /*offsetInBytes*/, data.length));
} break;
case AudioFormat.ENCODING_PCM_16BIT: {
- short data[] = createSoundDataInShortArray(
+ short data[] = AudioHelper.createSoundDataInShortArray(
bufferSamples, TEST_SR,
testFrequency, TEST_SWEEP);
assertEquals(TEST_NAME,
@@ -1545,7 +1497,7 @@
track.write(data, 0 /*offsetInBytes*/, data.length));
} break;
case AudioFormat.ENCODING_PCM_FLOAT: {
- float data[] = createSoundDataInFloatArray(
+ float data[] = AudioHelper.createSoundDataInFloatArray(
bufferSamples, TEST_SR,
testFrequency, TEST_SWEEP);
assertEquals(TEST_NAME,
@@ -1611,6 +1563,7 @@
};
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ final float TEST_SWEEP = 0; // sine wave only
for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
double frequency = 400; // frequency changes for each test
@@ -1649,12 +1602,12 @@
// We choose a value here which simulates double buffer writes.
final int buffers = 2; // double buffering mode
final int samplesPerWrite =
- (track.getNativeFrameCount() / buffers) * channelCount;
+ (track.getBufferSizeInFrames() / buffers) * channelCount;
switch (TEST_FORMAT) {
case AudioFormat.ENCODING_PCM_8BIT: {
- byte data[] = createSoundDataInByteArray(
+ byte data[] = AudioHelper.createSoundDataInByteArray(
sourceSamples, TEST_SR,
- testFrequency);
+ testFrequency, TEST_SWEEP);
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples);
@@ -1663,9 +1616,9 @@
}
} break;
case AudioFormat.ENCODING_PCM_16BIT: {
- short data[] = createSoundDataInShortArray(
+ short data[] = AudioHelper.createSoundDataInShortArray(
sourceSamples, TEST_SR,
- testFrequency);
+ testFrequency, TEST_SWEEP);
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples);
@@ -1674,9 +1627,9 @@
}
} break;
case AudioFormat.ENCODING_PCM_FLOAT: {
- float data[] = createSoundDataInFloatArray(
+ float data[] = AudioHelper.createSoundDataInFloatArray(
sourceSamples, TEST_SR,
- testFrequency);
+ testFrequency, TEST_SWEEP);
while (written < data.length) {
int samples = Math.min(data.length - written, samplesPerWrite);
int ret = track.write(data, written, samples,
@@ -1719,6 +1672,7 @@
};
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ final float TEST_SWEEP = 0; // sine wave only
for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
double frequency = 800; // frequency changes for each test
@@ -1732,10 +1686,19 @@
int bufferSize = 12 * minBufferSize;
int bufferSamples = bufferSize
/ AudioFormat.getBytesPerSample(TEST_FORMAT);
+
+ // create audio track and confirm settings
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
TEST_CONF, TEST_FORMAT, minBufferSize, TEST_MODE);
- assertTrue(TEST_NAME,
- track.getState() == AudioTrack.STATE_INITIALIZED);
+ assertEquals(TEST_NAME + ": state",
+ AudioTrack.STATE_INITIALIZED, track.getState());
+ assertEquals(TEST_NAME + ": sample rate",
+ TEST_SR, track.getSampleRate());
+ assertEquals(TEST_NAME + ": channel mask",
+ TEST_CONF, track.getChannelConfiguration());
+ assertEquals(TEST_NAME + ": encoding",
+ TEST_FORMAT, track.getAudioFormat());
+
ByteBuffer bb = (useDirect == 1)
? ByteBuffer.allocateDirect(bufferSize)
: ByteBuffer.allocate(bufferSize);
@@ -1744,24 +1707,24 @@
// -------- test --------------
switch (TEST_FORMAT) {
case AudioFormat.ENCODING_PCM_8BIT: {
- byte data[] = createSoundDataInByteArray(
+ byte data[] = AudioHelper.createSoundDataInByteArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
bb.put(data);
bb.flip();
} break;
case AudioFormat.ENCODING_PCM_16BIT: {
- short data[] = createSoundDataInShortArray(
+ short data[] = AudioHelper.createSoundDataInShortArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
ShortBuffer sb = bb.asShortBuffer();
sb.put(data);
bb.limit(sb.limit() * 2);
} break;
case AudioFormat.ENCODING_PCM_FLOAT: {
- float data[] = createSoundDataInFloatArray(
+ float data[] = AudioHelper.createSoundDataInFloatArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
FloatBuffer fb = bb.asFloatBuffer();
fb.put(data);
bb.limit(fb.limit() * 4);
@@ -1823,6 +1786,7 @@
AudioTrack.WRITE_BLOCKING,
AudioTrack.WRITE_NON_BLOCKING,
};
+ final float TEST_SWEEP = 0;
for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
for (int TEST_CONF : TEST_CONF_ARRAY) {
@@ -1854,24 +1818,24 @@
switch (TEST_FORMAT) {
case AudioFormat.ENCODING_PCM_8BIT: {
- byte data[] = createSoundDataInByteArray(
+ byte data[] = AudioHelper.createSoundDataInByteArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
bb.put(data);
bb.flip();
} break;
case AudioFormat.ENCODING_PCM_16BIT: {
- short data[] = createSoundDataInShortArray(
+ short data[] = AudioHelper.createSoundDataInShortArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
ShortBuffer sb = bb.asShortBuffer();
sb.put(data);
bb.limit(sb.limit() * 2);
} break;
case AudioFormat.ENCODING_PCM_FLOAT: {
- float data[] = createSoundDataInFloatArray(
+ float data[] = AudioHelper.createSoundDataInFloatArray(
bufferSamples, TEST_SR,
- frequency);
+ frequency, TEST_SWEEP);
FloatBuffer fb = bb.asFloatBuffer();
fb.put(data);
bb.limit(fb.limit() * 4);
@@ -2081,7 +2045,8 @@
TEST_FORMAT, bufferSizeInBytes, TEST_MODE);
// create byte array and write it
- byte[] vai = createSoundDataInByteArray(bufferSizeInBytes, TEST_SR, 600);
+ byte[] vai = AudioHelper.createSoundDataInByteArray(bufferSizeInBytes, TEST_SR,
+ 600 /* frequency */, 0 /* sweep */);
assertEquals(vai.length, track.write(vai, 0 /* offsetInBytes */, vai.length));
// sweep up test and sweep down test
@@ -2149,7 +2114,8 @@
// create float array and write it
final int sampleCount = frameCount * format.getChannelCount();
- float[] vaf = createSoundDataInFloatArray(sampleCount, TEST_SR, 600);
+ float[] vaf = AudioHelper.createSoundDataInFloatArray(
+ sampleCount, TEST_SR, 600 /* frequency */, 0 /* sweep */);
assertEquals(vaf.length, track.write(vaf, 0 /* offsetInFloats */, vaf.length,
AudioTrack.WRITE_NON_BLOCKING));
@@ -2161,6 +2127,39 @@
{ {1.0f, 0.5f}, {1.0f, 2.0f} }, // pitch by SR conversion (chirp)
};
+ // sanity test that playback params works as expected
+ PlaybackParams params = new PlaybackParams().allowDefaults();
+ assertEquals(TEST_NAME, 1.0f, params.getSpeed());
+ assertEquals(TEST_NAME, 1.0f, params.getPitch());
+ assertEquals(TEST_NAME,
+ params.AUDIO_FALLBACK_MODE_DEFAULT,
+ params.getAudioFallbackMode());
+ track.setPlaybackParams(params); // OK
+ params.setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
+ assertEquals(TEST_NAME,
+ params.AUDIO_FALLBACK_MODE_FAIL, params.getAudioFallbackMode());
+ params.setPitch(0.0f);
+ try {
+ track.setPlaybackParams(params);
+ fail("IllegalArgumentException should be thrown on out of range data");
+ } catch (IllegalArgumentException e) {
+ ; // expect this is invalid
+ }
+ // on failure, the AudioTrack params should not change.
+ PlaybackParams paramCheck = track.getPlaybackParams();
+ assertEquals(TEST_NAME,
+ paramCheck.AUDIO_FALLBACK_MODE_DEFAULT, paramCheck.getAudioFallbackMode());
+ assertEquals(TEST_NAME,
+ 1.0f, paramCheck.getPitch());
+
+ // now try to see if we can do extreme pitch correction that should probably be muted.
+ params.setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_MUTE);
+ assertEquals(TEST_NAME,
+ params.AUDIO_FALLBACK_MODE_MUTE, params.getAudioFallbackMode());
+ params.setPitch(0.1f);
+ track.setPlaybackParams(params); // OK
+
+ // now do our actual playback
final int TEST_TIME_MS = 2000;
final int TEST_DELTA_MS = 100;
final int testSteps = TEST_TIME_MS / TEST_DELTA_MS;
@@ -2238,6 +2237,7 @@
}
*/
+ /* MockAudioTrack allows testing of protected getNativeFrameCount() and setState(). */
private class MockAudioTrack extends AudioTrack {
public MockAudioTrack(int streamType, int sampleRateInHz, int channelConfig,
diff --git a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
index 353dbcb..e059e36 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrack_ListenerTest.java
@@ -119,19 +119,29 @@
listener = new MockOnPlaybackPositionUpdateListener(track);
}
- byte[] vai = AudioTrackTest.createSoundDataInByteArray(bufferSizeInBytes, TEST_SR, 1024);
+ byte[] vai = AudioHelper.createSoundDataInByteArray(
+ bufferSizeInBytes, TEST_SR, 1024 /* frequency */, 0 /* sweep */);
int markerPeriods = Math.max(3, mFrameCount * markerPeriodsPerSecond / TEST_SR);
mMarkerPeriodInFrames = mFrameCount / markerPeriods;
markerPeriods = mFrameCount / mMarkerPeriodInFrames; // recalculate due to round-down
mMarkerPosition = mMarkerPeriodInFrames;
+
+ // check that we can get and set notification marker position
+ assertEquals(0, track.getNotificationMarkerPosition());
assertEquals(AudioTrack.SUCCESS,
track.setNotificationMarkerPosition(mMarkerPosition));
+ assertEquals(mMarkerPosition, track.getNotificationMarkerPosition());
+
int updatePeriods = Math.max(3, mFrameCount * periodsPerSecond / TEST_SR);
final int updatePeriodInFrames = mFrameCount / updatePeriods;
updatePeriods = mFrameCount / updatePeriodInFrames; // recalculate due to round-down
+
+ // we set the notification period before running for better period positional accuracy.
+ // check that we can get and set notification periods
+ assertEquals(0, track.getPositionNotificationPeriod());
assertEquals(AudioTrack.SUCCESS,
track.setPositionNotificationPeriod(updatePeriodInFrames));
- // set NotificationPeriod before running to ensure better period positional accuracy.
+ assertEquals(updatePeriodInFrames, track.getPositionNotificationPeriod());
if (mode == AudioTrack.MODE_STATIC && TEST_LOOP_FACTOR > 1) {
track.setLoopPoints(0, vai.length, TEST_LOOP_FACTOR - 1);
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
new file mode 100644
index 0000000..0e1621d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import android.media.AudioDeviceCallback;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import android.test.AndroidTestCase;
+
+/**
+ * TODO: Insert description here. (generated by pmclean)
+ */
+public class EnumDevicesTest extends AndroidTestCase {
+ private AudioManager mAudioManager;
+
+ boolean mAddCallbackCalled = false;
+ boolean mRemoveCallbackCalled = false;
+
+ static {
+ // We're going to use a Handler
+ Looper.prepare();
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // get the AudioManager
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull(mAudioManager);
+ }
+
+ public void test_getDevices() {
+ AudioDeviceInfo[] deviceList;
+
+ // test an empty flags set
+ deviceList = mAudioManager.getDevices(0);
+ assertTrue(deviceList != null);
+ assertTrue(deviceList.length == 0);
+
+ int numOutputDevices = 0;
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // test OUTPUTS
+ deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ assertTrue(deviceList != null);
+ numOutputDevices = deviceList.length;
+ assertTrue(numOutputDevices != 0);
+
+ // all should be "sinks"
+ for(int index = 0; index < numOutputDevices; index++) {
+ assertTrue(deviceList[index].isSink());
+ }
+ }
+
+ int numInputDevices = 0;
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ // test INPUTS
+ deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+ assertTrue(deviceList != null);
+ numInputDevices = deviceList.length;
+ assertTrue(numInputDevices != 0);
+
+ // all should be "sources"
+ for(int index = 0; index < numInputDevices; index++) {
+ assertTrue(deviceList[index].isSource());
+ }
+ }
+
+ // INPUTS & OUTPUTS
+ if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) &&
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
+ assertTrue(deviceList.length == (numOutputDevices + numInputDevices));
+ }
+ }
+
+ private class EmptyDeviceCallback extends AudioDeviceCallback {
+ public void onAudioDevicesAdded(AudioDeviceInfo[] addedDevices) {
+ mAddCallbackCalled = true;
+ }
+
+ public void onAudioDevicesRemoved(AudioDeviceInfo[] removedDevices) {
+ mRemoveCallbackCalled = true;
+ }
+ }
+
+ public void test_deviceCallback() {
+ mAudioManager.registerAudioDeviceCallback(null,null);
+ assertTrue(true);
+
+ AudioDeviceCallback callback = new EmptyDeviceCallback();
+ mAudioManager.registerAudioDeviceCallback(callback, null);
+ assertTrue(true);
+
+ mAudioManager.registerAudioDeviceCallback(callback, new Handler());
+ assertTrue(true);
+ }
+
+ //TODO - Need tests for device connect/disconnect callbacks
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
new file mode 100644
index 0000000..b53aa92
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.content.ComponentName;
+import android.cts.util.PollingCheck;
+import android.media.browse.MediaBrowser;
+import android.test.InstrumentationTestCase;
+
+/**
+ * Test {@link android.media.browse.MediaBrowser}.
+ */
+public class MediaBrowserTest extends InstrumentationTestCase {
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 1000L;
+ private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+ "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+ private final StubConnectionCallback mConnectionCallback = new StubConnectionCallback();
+
+ private MediaBrowser mMediaBrowser;
+
+ public void testMediaBrowser() {
+ mConnectionCallback.resetCounts();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ assertEquals(false, mMediaBrowser.isConnected());
+
+ connectMediaBrowserService();
+ assertEquals(true, mMediaBrowser.isConnected());
+
+ assertEquals(TEST_BROWSER_SERVICE, mMediaBrowser.getServiceComponent());
+ assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mMediaBrowser.getRoot());
+ assertEquals(StubMediaBrowserService.EXTRAS_VALUE,
+ mMediaBrowser.getExtras().getString(StubMediaBrowserService.EXTRAS_KEY));
+ assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+ mMediaBrowser.getSessionToken());
+
+ mMediaBrowser.disconnect();
+ assertEquals(false, mMediaBrowser.isConnected());
+ }
+
+ public void testConnectTwice() {
+ mConnectionCallback.resetCounts();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ try {
+ mMediaBrowser.connect();
+ fail();
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ public void testGetServiceComponentBeforeConnection() {
+ mConnectionCallback.resetCounts();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ try {
+ ComponentName serviceComponent = mMediaBrowser.getServiceComponent();
+ fail();
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ private void createMediaBrowser(final ComponentName component) {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+ component, mConnectionCallback, null);
+ }
+ });
+ }
+
+ private void connectMediaBrowserService() {
+ mMediaBrowser.connect();
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mConnectionCallback.mConnectedCount > 0;
+ }
+ }.run();
+ }
+
+ private static class StubConnectionCallback extends MediaBrowser.ConnectionCallback {
+ volatile int mConnectedCount;
+ volatile int mConnectionFailedCount;
+ volatile int mConnectionSuspendedCount;
+
+ public void resetCounts() {
+ mConnectedCount = 0;
+ mConnectionFailedCount = 0;
+ mConnectionSuspendedCount = 0;
+ }
+
+ @Override
+ public void onConnected() {
+ mConnectedCount++;
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ mConnectionFailedCount++;
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ mConnectionSuspendedCount++;
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java b/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
index 52fd395..a197cca 100644
--- a/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaDrmMockTest.java
@@ -17,10 +17,10 @@
package android.media.cts;
import android.media.MediaDrm;
-import android.media.MediaDrm.ProvisionRequest;
+import android.media.MediaDrm.CryptoSession;
import android.media.MediaDrm.KeyRequest;
import android.media.MediaDrm.KeyStatus;
-import android.media.MediaDrm.CryptoSession;
+import android.media.MediaDrm.ProvisionRequest;
import android.media.MediaDrmException;
import android.media.NotProvisionedException;
import android.media.ResourceBusyException;
@@ -245,7 +245,7 @@
optionalParameters);
assertTrue(Arrays.equals(request.getData(), testRequest));
assertTrue(request.getDefaultUrl().equals(testDefaultUrl));
- assertEquals(request.getRequestType(), MediaDrm.REQUEST_TYPE_INITIAL);
+ assertEquals(request.getRequestType(), MediaDrm.KeyRequest.REQUEST_TYPE_INITIAL);
assertTrue(Arrays.equals(initData, md.getPropertyByteArray("mock-initdata")));
assertTrue(mimeType.equals(md.getPropertyString("mock-mimetype")));
@@ -278,7 +278,7 @@
null);
assertTrue(Arrays.equals(request.getData(), testRequest));
assertTrue(request.getDefaultUrl().equals(testDefaultUrl));
- assertEquals(request.getRequestType(), MediaDrm.REQUEST_TYPE_INITIAL);
+ assertEquals(request.getRequestType(), MediaDrm.KeyRequest.REQUEST_TYPE_INITIAL);
assertTrue(Arrays.equals(initData, md.getPropertyByteArray("mock-initdata")));
assertTrue(mimeType.equals(md.getPropertyString("mock-mimetype")));
@@ -310,7 +310,7 @@
null);
assertTrue(Arrays.equals(request.getData(), testRequest));
assertTrue(request.getDefaultUrl().equals(testDefaultUrl));
- assertEquals(request.getRequestType(), MediaDrm.REQUEST_TYPE_RENEWAL);
+ assertEquals(request.getRequestType(), MediaDrm.KeyRequest.REQUEST_TYPE_RENEWAL);
assertTrue(Arrays.equals(initData, md.getPropertyByteArray("mock-initdata")));
assertTrue(mimeType.equals(md.getPropertyString("mock-mimetype")));
@@ -340,7 +340,7 @@
null);
assertTrue(Arrays.equals(request.getData(), testRequest));
assertTrue(request.getDefaultUrl().equals(testDefaultUrl));
- assertEquals(request.getRequestType(), MediaDrm.REQUEST_TYPE_RELEASE);
+ assertEquals(request.getRequestType(), MediaDrm.KeyRequest.REQUEST_TYPE_RELEASE);
assertTrue(mimeType.equals(md.getPropertyString("mock-mimetype")));
assertTrue(md.getPropertyString("mock-keytype").equals("2"));
@@ -862,7 +862,7 @@
assertTrue(mGotEvent);
}
- public void testKeysChange() throws Exception {
+ public void testKeyStatusChange() throws Exception {
if (!isMockPluginInstalled()) {
return;
}
@@ -893,30 +893,30 @@
synchronized(mLock) {
mLock.notify();
- mMediaDrm.setOnKeysChangeListener(new MediaDrm.OnKeysChangeListener() {
+ mMediaDrm.setOnKeyStatusChangeListener(new MediaDrm.OnKeyStatusChangeListener() {
@Override
- public void onKeysChange(MediaDrm md, byte[] sessionId,
+ public void onKeyStatusChange(MediaDrm md, byte[] sessionId,
List<KeyStatus> keyInformation, boolean hasNewUsableKey) {
synchronized(mLock) {
- Log.d(TAG,"testKeysChange.onKeysChange");
+ Log.d(TAG,"testKeyStatusChange.onKeyStatusChange");
assertTrue(md == mMediaDrm);
assertTrue(Arrays.equals(sessionId, expected_sessionId));
try {
KeyStatus keyStatus = keyInformation.get(0);
assertTrue(Arrays.equals(keyStatus.getKeyId(), "key1".getBytes()));
- assertTrue(keyStatus.getStatusCode() == MediaDrm.KEY_STATUS_USABLE);
+ assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_USABLE);
keyStatus = keyInformation.get(1);
assertTrue(Arrays.equals(keyStatus.getKeyId(), "key2".getBytes()));
- assertTrue(keyStatus.getStatusCode() == MediaDrm.KEY_STATUS_EXPIRED);
+ assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_EXPIRED);
keyStatus = keyInformation.get(2);
assertTrue(Arrays.equals(keyStatus.getKeyId(), "key3".getBytes()));
- assertTrue(keyStatus.getStatusCode() == MediaDrm.KEY_STATUS_OUTPUT_NOT_ALLOWED);
+ assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_OUTPUT_NOT_ALLOWED);
keyStatus = keyInformation.get(3);
assertTrue(Arrays.equals(keyStatus.getKeyId(), "key4".getBytes()));
- assertTrue(keyStatus.getStatusCode() == MediaDrm.KEY_STATUS_PENDING);
+ assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_PENDING);
keyStatus = keyInformation.get(4);
assertTrue(Arrays.equals(keyStatus.getKeyId(), "key5".getBytes()));
- assertTrue(keyStatus.getStatusCode() == MediaDrm.KEY_STATUS_INTERNAL_ERROR);
+ assertTrue(keyStatus.getStatusCode() == MediaDrm.KeyStatus.STATUS_INTERNAL_ERROR);
assertTrue(hasNewUsableKey);
mGotEvent = true;
} catch (IndexOutOfBoundsException e) {
diff --git a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
index 7ca498f..9db54ff 100644
--- a/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaExtractorTest.java
@@ -83,7 +83,7 @@
public void testExtractorFailsIfMediaDataSourceReturnsAnError() throws Exception {
TestMediaDataSource dataSource = getDataSourceFor(R.raw.testvideo);
- dataSource.returnFromReadAt(-1);
+ dataSource.returnFromReadAt(-2);
try {
mExtractor.setDataSource(dataSource);
fail("Expected IOException.");
diff --git a/tests/tests/media/src/android/media/cts/MediaItemTest.java b/tests/tests/media/src/android/media/cts/MediaItemTest.java
new file mode 100644
index 0000000..4eefaa7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaItemTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.MediaDescription;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.browse.MediaBrowser.MediaItem}.
+ */
+public class MediaItemTest extends AndroidTestCase {
+ private static final String DESCRIPTION = "test_description";
+ private static final String MEDIA_ID = "test_media_id";
+ private static final String TITLE = "test_title";
+ private static final String SUBTITLE = "test_subtitle";
+
+ public void testBrowsableMediaItem() {
+ MediaDescription description = new MediaDescription.Builder()
+ .setDescription(DESCRIPTION).setMediaId(MEDIA_ID)
+ .setTitle(TITLE).setSubtitle(SUBTITLE).build();
+ MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_BROWSABLE);
+
+ assertEquals(description.toString(), mediaItem.getDescription().toString());
+ assertEquals(MEDIA_ID, mediaItem.getMediaId());
+ assertEquals(MediaItem.FLAG_BROWSABLE, mediaItem.getFlags());
+ assertTrue(mediaItem.isBrowsable());
+ assertFalse(mediaItem.isPlayable());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ mediaItem.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ assertEquals(mediaItem.getFlags(), p.readInt());
+ assertEquals(description.toString(),
+ MediaDescription.CREATOR.createFromParcel(p).toString());
+ p.recycle();
+ }
+
+ public void testPlayableMediaItem() {
+ MediaDescription description = new MediaDescription.Builder()
+ .setDescription(DESCRIPTION).setMediaId(MEDIA_ID)
+ .setTitle(TITLE).setSubtitle(SUBTITLE).build();
+ MediaItem mediaItem = new MediaItem(description, MediaItem.FLAG_PLAYABLE);
+
+ assertEquals(description.toString(), mediaItem.getDescription().toString());
+ assertEquals(MEDIA_ID, mediaItem.getMediaId());
+ assertEquals(MediaItem.FLAG_PLAYABLE, mediaItem.getFlags());
+ assertFalse(mediaItem.isBrowsable());
+ assertTrue(mediaItem.isPlayable());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ mediaItem.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ assertEquals(mediaItem.getFlags(), p.readInt());
+ assertEquals(description.toString(),
+ MediaDescription.CREATOR.createFromParcel(p).toString());
+ p.recycle();
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
index 622c0ec..562656b 100644
--- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java
@@ -146,7 +146,7 @@
public void testRetrieveFailsIfMediaDataSourceReturnsAnError() throws Exception {
TestMediaDataSource dataSource = setDataSourceCallback(R.raw.testvideo);
- dataSource.returnFromReadAt(-1);
+ dataSource.returnFromReadAt(-2);
assertTrue(mRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE) == null);
}
}
diff --git a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
index 67eeca0..0f664a5 100644
--- a/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaMuxerTest.java
@@ -104,6 +104,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
// Throws exception b/c 2 video tracks were added.
@@ -115,6 +117,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
// Throws exception b/c 2 audio tracks were added.
@@ -125,6 +129,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
// Throws exception b/c 3 tracks were added.
@@ -137,6 +143,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
// Throws exception b/c no tracks was added.
@@ -146,6 +154,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
// Throws exception b/c a wrong format.
@@ -155,6 +165,8 @@
fail("should throw IllegalStateException.");
} catch (IllegalStateException e) {
// expected
+ } finally {
+ muxer.release();
}
new File(outputFile).delete();
}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
index 32fbfb5..640083f 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerFlakyNetworkTest.java
@@ -23,6 +23,8 @@
import android.os.SystemClock;
import android.webkit.cts.CtsTestServer;
+import com.android.cts.util.TimeoutReq;
+
import org.apache.http.HttpServerConnection;
import org.apache.http.impl.DefaultHttpServerConnection;
@@ -67,30 +69,37 @@
super.tearDown();
}
+ @TimeoutReq(minutes = 5)
public void test_S0P0() throws Throwable {
doPlayStreams(0, 0);
}
+ @TimeoutReq(minutes = 10)
public void test_S1P000005() throws Throwable {
doPlayStreams(1, 0.000005f);
}
+ @TimeoutReq(minutes = 10)
public void test_S2P00001() throws Throwable {
doPlayStreams(2, 0.00001f);
}
+ @TimeoutReq(minutes = 10)
public void test_S3P00001() throws Throwable {
doPlayStreams(3, 0.00001f);
}
+ @TimeoutReq(minutes = 10)
public void test_S4P00001() throws Throwable {
doPlayStreams(4, 0.00001f);
}
+ @TimeoutReq(minutes = 10)
public void test_S5P00001() throws Throwable {
doPlayStreams(5, 0.00001f);
}
+ @TimeoutReq(minutes = 10)
public void test_S6P00002() throws Throwable {
doPlayStreams(6, 0.00002f);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
index 661b815..18cd353 100644
--- a/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaPlayerTest.java
@@ -26,10 +26,13 @@
import android.media.MediaDataSource;
import android.media.MediaExtractor;
import android.media.MediaFormat;
+import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnErrorListener;
import android.media.MediaRecorder;
-import android.media.MediaMetadataRetriever;
+import android.media.MediaTimestamp;
+import android.media.PlaybackParams;
+import android.media.SyncParams;
import android.media.TimedText;
import android.media.audiofx.AudioEffect;
import android.media.audiofx.Visualizer;
@@ -68,6 +71,8 @@
private static final int RECORDED_VIDEO_WIDTH = 176;
private static final int RECORDED_VIDEO_HEIGHT = 144;
private static final long RECORDED_DURATION_MS = 3000;
+ private static final float FLOAT_TOLERANCE = .0001f;
+
private Vector<Integer> mTimedTextTrackIndex = new Vector<Integer>();
private int mSelectedTimedTextIndex;
private Monitor mOnTimedTextCalled = new Monitor();
@@ -840,15 +845,22 @@
mMediaPlayer.setDisplay(mActivity.getSurfaceHolder());
mMediaPlayer.prepare();
+ SyncParams sync = new SyncParams().allowDefaults();
+ mMediaPlayer.setSyncParams(sync);
+ sync = mMediaPlayer.getSyncParams();
+
float[] rates = { 0.25f, 0.5f, 1.0f, 2.0f };
for (float playbackRate : rates) {
mMediaPlayer.seekTo(0);
Thread.sleep(1000);
int playTime = 4000; // The testing clip is about 10 second long.
- mMediaPlayer.setPlaybackRate(playbackRate,
- MediaPlayer.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ mMediaPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
mMediaPlayer.start();
Thread.sleep(playTime);
+ PlaybackParams pbp = mMediaPlayer.getPlaybackParams();
+ assertEquals(
+ playbackRate, pbp.getSpeed(),
+ FLOAT_TOLERANCE + playbackRate * sync.getTolerance());
assertTrue("MediaPlayer should still be playing", mMediaPlayer.isPlaying());
int playedMediaDurationMs = mMediaPlayer.getCurrentPosition();
@@ -858,10 +870,58 @@
+ ", play time is " + playTime + " vs expected " + playedMediaDurationMs);
}
mMediaPlayer.pause();
+ pbp = mMediaPlayer.getPlaybackParams();
+ assertEquals(0.f, pbp.getSpeed(), FLOAT_TOLERANCE);
}
mMediaPlayer.stop();
}
+ public void testGetTimestamp() throws Exception {
+ final int toleranceUs = 100000;
+ final float playbackRate = 1.0f;
+ if (!checkLoadResource(
+ R.raw.video_480x360_mp4_h264_1000kbps_30fps_aac_stereo_128kbps_44100hz)) {
+ return; // skip
+ }
+
+ mMediaPlayer.setDisplay(mActivity.getSurfaceHolder());
+ mMediaPlayer.prepare();
+ mMediaPlayer.start();
+ mMediaPlayer.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
+ Thread.sleep(SLEEP_TIME); // let player get into stable state.
+ long nt1 = System.nanoTime();
+ MediaTimestamp ts1 = mMediaPlayer.getTimestamp();
+ long nt2 = System.nanoTime();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ assertEquals("MediaPlayer had error in clockRate " + ts1.getMediaClockRate(),
+ playbackRate, ts1.getMediaClockRate(), 0.001f);
+ assertTrue("The nanoTime of Media timestamp should be taken when getTimestamp is called.",
+ nt1 <= ts1.nanoTime && ts1.nanoTime <= nt2);
+
+ mMediaPlayer.pause();
+ ts1 = mMediaPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ assertTrue("Media player should have play rate of 0.0f when paused",
+ ts1.getMediaClockRate() == 0.0f);
+
+ mMediaPlayer.seekTo(0);
+ mMediaPlayer.start();
+ Thread.sleep(SLEEP_TIME); // let player get into stable state.
+ int playTime = 4000; // The testing clip is about 10 second long.
+ ts1 = mMediaPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts1 != null);
+ Thread.sleep(playTime);
+ MediaTimestamp ts2 = mMediaPlayer.getTimestamp();
+ assertTrue("Media player should return a valid time stamp", ts2 != null);
+ assertTrue("The clockRate should not be changed.",
+ ts1.getMediaClockRate() == ts2.getMediaClockRate());
+ assertEquals("MediaPlayer had error in timestamp.",
+ ts1.getAnchorMediaTimeUs() + (long)(playTime * ts1.getMediaClockRate() * 1000),
+ ts2.getAnchorMediaTimeUs(), toleranceUs);
+
+ mMediaPlayer.stop();
+ }
+
public void testLocalVideo_MP4_H264_480x360_500kbps_25fps_AAC_Stereo_128kbps_44110Hz()
throws Exception {
playVideoTest(
@@ -1589,7 +1649,7 @@
mMediaPlayer.setDataSource(dataSource);
mMediaPlayer.prepare();
- dataSource.returnFromReadAt(-1);
+ dataSource.returnFromReadAt(-2);
mMediaPlayer.start();
assertTrue(mOnErrorCalled.waitForSignal());
}
diff --git a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 78b5cfd..b6ee1db 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -18,10 +18,16 @@
import android.content.pm.PackageManager;
import android.cts.util.MediaUtils;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
import android.hardware.Camera;
+import android.media.EncoderCapabilities;
+import android.media.MediaCodec;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaRecorder;
+import android.media.EncoderCapabilities.VideoEncoderCap;
import android.media.MediaRecorder.OnErrorListener;
import android.media.MediaRecorder.OnInfoListener;
import android.media.MediaMetadataRetriever;
@@ -38,6 +44,7 @@
import java.io.FileOutputStream;
import java.lang.InterruptedException;
import java.lang.Runnable;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -62,6 +69,12 @@
private static final int MAX_DURATION_MSEC = 2000;
private static final float LATITUDE = 0.0000f;
private static final float LONGITUDE = -180.0f;
+ private static final int NORMAL_FPS = 30;
+ private static final int TIME_LAPSE_FPS = 5;
+ private static final int SLOW_MOTION_FPS = 120;
+ private static final List<VideoEncoderCap> mVideoEncoders =
+ EncoderCapabilities.getVideoEncoders();
+
private boolean mOnInfoCalled;
private boolean mOnErrorCalled;
private File mOutFile;
@@ -138,8 +151,10 @@
@Override
protected void tearDown() throws Exception {
- mMediaRecorder.release();
- mMediaRecorder = null;
+ if (mMediaRecorder != null) {
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ }
if (mOutFile != null && mOutFile.exists()) {
mOutFile.delete();
}
@@ -477,6 +492,441 @@
assertFalse(mOnErrorCalled);
}
+ private void setupRecorder(String filename, boolean useSurface, boolean hasAudio)
+ throws Exception {
+ int codec = MediaRecorder.VideoEncoder.H264;
+ int frameRate = getMaxFrameRateForCodec(codec);
+ if (mMediaRecorder == null) {
+ mMediaRecorder = new MediaRecorder();
+ }
+
+ if (!useSurface) {
+ mCamera = Camera.open(0);
+ Camera.Parameters params = mCamera.getParameters();
+ frameRate = params.getPreviewFrameRate();
+ mCamera.unlock();
+ mMediaRecorder.setCamera(mCamera);
+ mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
+ }
+
+ mMediaRecorder.setVideoSource(useSurface ?
+ MediaRecorder.VideoSource.SURFACE : MediaRecorder.VideoSource.CAMERA);
+
+ if (hasAudio) {
+ mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+ }
+
+ mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
+ mMediaRecorder.setOutputFile(filename);
+
+ mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
+ mMediaRecorder.setVideoFrameRate(frameRate);
+ mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
+
+ if (hasAudio) {
+ mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
+ }
+ }
+
+ private Surface tryGetSurface(boolean shouldThrow) throws Exception {
+ Surface surface = null;
+ try {
+ surface = mMediaRecorder.getSurface();
+ assertFalse("failed to throw IllegalStateException", shouldThrow);
+ } catch (IllegalStateException e) {
+ assertTrue("threw unexpected exception: " + e, shouldThrow);
+ }
+ return surface;
+ }
+
+ private boolean validateGetSurface(boolean useSurface) {
+ Log.v(TAG,"validateGetSurface, useSurface=" + useSurface);
+ if (!useSurface && !hasCamera()) {
+ // pass if testing camera source but no hardware
+ return true;
+ }
+ Surface surface = null;
+ boolean success = true;
+ try {
+ setupRecorder(OUTPUT_PATH, useSurface, false /* hasAudio */);
+
+ /* Test: getSurface() before prepare()
+ * should throw IllegalStateException
+ */
+ surface = tryGetSurface(true /* shouldThow */);
+
+ mMediaRecorder.prepare();
+
+ /* Test: getSurface() after prepare()
+ * should succeed for surface source
+ * should fail for camera source
+ */
+ surface = tryGetSurface(!useSurface);
+
+ mMediaRecorder.start();
+
+ /* Test: getSurface() after start()
+ * should succeed for surface source
+ * should fail for camera source
+ */
+ surface = tryGetSurface(!useSurface);
+
+ try {
+ mMediaRecorder.stop();
+ } catch (Exception e) {
+ // stop() could fail if the recording is empty, as we didn't render anything.
+ // ignore any failure in stop, we just want it stopped.
+ }
+
+ /* Test: getSurface() after stop()
+ * should throw IllegalStateException
+ */
+ surface = tryGetSurface(true /* shouldThow */);
+ } catch (Exception e) {
+ Log.d(TAG, e.toString());
+ success = false;
+ } finally {
+ // reset to clear states, as stop() might have failed
+ mMediaRecorder.reset();
+
+ if (mCamera != null) {
+ mCamera.release();
+ mCamera = null;
+ }
+ if (surface != null) {
+ surface.release();
+ surface = null;
+ }
+ }
+
+ return success;
+ }
+
+ private void trySetInputSurface(Surface surface) throws Exception {
+ boolean testBadArgument = (surface == null);
+ try {
+ mMediaRecorder.setInputSurface(testBadArgument ? new Surface() : surface);
+ fail("failed to throw exception");
+ } catch (IllegalArgumentException e) {
+ // OK only if testing bad arg
+ assertTrue("threw unexpected exception: " + e, testBadArgument);
+ } catch (IllegalStateException e) {
+ // OK only if testing error case other than bad arg
+ assertFalse("threw unexpected exception: " + e, testBadArgument);
+ }
+ }
+
+ private boolean validatePersistentSurface(boolean errorCase) {
+ Log.v(TAG, "validatePersistentSurface, errorCase=" + errorCase);
+
+ Surface surface = MediaCodec.createPersistentInputSurface();
+ if (surface == null) {
+ return false;
+ }
+ Surface dummy = null;
+
+ boolean success = true;
+ try {
+ setupRecorder(OUTPUT_PATH, true /* useSurface */, false /* hasAudio */);
+
+ if (errorCase) {
+ /*
+ * Test: should throw if called with non-persistent surface
+ */
+ trySetInputSurface(null);
+ } else {
+ /*
+ * Test: should succeed if called with a persistent surface before prepare()
+ */
+ mMediaRecorder.setInputSurface(surface);
+ }
+
+ /*
+ * Test: getSurface() should fail before prepare
+ */
+ dummy = tryGetSurface(true /* shouldThow */);
+
+ mMediaRecorder.prepare();
+
+ /*
+ * Test: setInputSurface() should fail after prepare
+ */
+ trySetInputSurface(surface);
+
+ /*
+ * Test: getSurface() should fail if setInputSurface() succeeded
+ */
+ dummy = tryGetSurface(!errorCase /* shouldThow */);
+
+ mMediaRecorder.start();
+
+ /*
+ * Test: setInputSurface() should fail after start
+ */
+ trySetInputSurface(surface);
+
+ /*
+ * Test: getSurface() should fail if setInputSurface() succeeded
+ */
+ dummy = tryGetSurface(!errorCase /* shouldThow */);
+
+ try {
+ mMediaRecorder.stop();
+ } catch (Exception e) {
+ // stop() could fail if the recording is empty, as we didn't render anything.
+ // ignore any failure in stop, we just want it stopped.
+ }
+
+ /*
+ * Test: getSurface() should fail after stop
+ */
+ dummy = tryGetSurface(true /* shouldThow */);
+ } catch (Exception e) {
+ Log.d(TAG, e.toString());
+ success = false;
+ } finally {
+ // reset to clear states, as stop() might have failed
+ mMediaRecorder.reset();
+
+ if (mCamera != null) {
+ mCamera.release();
+ mCamera = null;
+ }
+ if (surface != null) {
+ surface.release();
+ surface = null;
+ }
+ if (dummy != null) {
+ dummy.release();
+ dummy = null;
+ }
+ }
+
+ return success;
+ }
+
+ public void testGetSurfaceApi() {
+ if (!hasH264()) {
+ MediaUtils.skipTest("no codecs");
+ return;
+ }
+
+ if (hasCamera()) {
+ // validate getSurface() with CAMERA source
+ assertTrue(validateGetSurface(false /* useSurface */));
+ }
+
+ // validate getSurface() with SURFACE source
+ assertTrue(validateGetSurface(true /* useSurface */));
+ }
+
+ public void testPersistentSurfaceApi() {
+ if (!hasH264()) {
+ MediaUtils.skipTest("no codecs");
+ return;
+ }
+
+ // test valid use case
+ assertTrue(validatePersistentSurface(false /* errorCase */));
+
+ // test invalid use case
+ assertTrue(validatePersistentSurface(true /* errorCase */));
+ }
+
+ private static int getMaxFrameRateForCodec(int codec) {
+ for (VideoEncoderCap cap : mVideoEncoders) {
+ if (cap.mCodec == codec) {
+ return cap.mMaxFrameRate < NORMAL_FPS ? cap.mMaxFrameRate : NORMAL_FPS;
+ }
+ }
+ fail("didn't find max FPS for codec");
+ return -1;
+ }
+
+ private boolean recordFromSurface(
+ String filename,
+ int captureRate,
+ boolean hasAudio,
+ Surface persistentSurface) {
+ Log.v(TAG, "recordFromSurface");
+ Surface surface = null;
+ try {
+ setupRecorder(filename, true /* useSurface */, hasAudio);
+
+ int sleepTimeMs;
+ if (captureRate > 0) {
+ mMediaRecorder.setCaptureRate(captureRate);
+ sleepTimeMs = 1000 / captureRate;
+ } else {
+ sleepTimeMs = 1000 / getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H264);
+ }
+
+ if (persistentSurface != null) {
+ Log.v(TAG, "using persistent surface");
+ surface = persistentSurface;
+ mMediaRecorder.setInputSurface(surface);
+ }
+
+ mMediaRecorder.prepare();
+
+ if (persistentSurface == null) {
+ surface = mMediaRecorder.getSurface();
+ }
+
+ Paint paint = new Paint();
+ paint.setTextSize(16);
+ paint.setColor(Color.RED);
+ int i;
+
+ /* Test: draw 10 frames at 30fps before start
+ * these should be dropped and not causing malformed stream.
+ */
+ for(i = 0; i < 10; i++) {
+ Canvas canvas = surface.lockCanvas(null);
+ int background = (i * 255 / 99);
+ canvas.drawARGB(255, background, background, background);
+ String text = "Frame #" + i;
+ canvas.drawText(text, 50, 50, paint);
+ surface.unlockCanvasAndPost(canvas);
+ Thread.sleep(sleepTimeMs);
+ }
+
+ Log.v(TAG, "start");
+ mMediaRecorder.start();
+
+ /* Test: draw another 90 frames at 30fps after start */
+ for(i = 10; i < 100; i++) {
+ Canvas canvas = surface.lockCanvas(null);
+ int background = (i * 255 / 99);
+ canvas.drawARGB(255, background, background, background);
+ String text = "Frame #" + i;
+ canvas.drawText(text, 50, 50, paint);
+ surface.unlockCanvasAndPost(canvas);
+ Thread.sleep(sleepTimeMs);
+ }
+
+ Log.v(TAG, "stop");
+ mMediaRecorder.stop();
+ } catch (Exception e) {
+ Log.v(TAG, "record video failed: " + e.toString());
+ return false;
+ } finally {
+ // We need to test persistent surface across multiple MediaRecorder
+ // instances, so must destroy mMediaRecorder here.
+ if (mMediaRecorder != null) {
+ mMediaRecorder.release();
+ mMediaRecorder = null;
+ }
+
+ // release surface if not using persistent surface
+ if (persistentSurface == null && surface != null) {
+ surface.release();
+ surface = null;
+ }
+ }
+ return true;
+ }
+
+ private boolean checkCaptureFps(String filename, int captureRate) {
+ MediaMetadataRetriever retriever = new MediaMetadataRetriever();
+
+ retriever.setDataSource(filename);
+
+ // verify capture rate meta key is present and correct
+ String captureFps = retriever.extractMetadata(
+ MediaMetadataRetriever.METADATA_KEY_CAPTURE_FRAMERATE);
+
+ if (captureFps == null) {
+ Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is missing");
+ return false;
+ }
+
+ if (Math.abs(Float.parseFloat(captureFps) - captureRate) > 0.001) {
+ Log.d(TAG, "METADATA_KEY_CAPTURE_FRAMERATE is incorrect: "
+ + captureFps + "vs. " + captureRate);
+ return false;
+ }
+
+ // verify other meta keys here if necessary
+ return true;
+ }
+
+ private boolean testRecordFromSurface(boolean persistent, boolean timelapse) {
+ Log.v(TAG, "testRecordFromSurface: " +
+ "persistent=" + persistent + ", timelapse=" + timelapse);
+ boolean success = false;
+ Surface surface = null;
+ int noOfFailure = 0;
+ try {
+ if (persistent) {
+ surface = MediaCodec.createPersistentInputSurface();
+ }
+
+ for (int k = 0; k < 2; k++) {
+ String filename = (k == 0) ? OUTPUT_PATH : OUTPUT_PATH2;
+ boolean hasAudio = false;
+ int captureRate = 0;
+
+ if (timelapse) {
+ // if timelapse/slow-mo, k chooses between low/high capture fps
+ captureRate = (k == 0) ? TIME_LAPSE_FPS : SLOW_MOTION_FPS;
+ } else {
+ // otherwise k chooses between no-audio and audio
+ hasAudio = (k == 0) ? false : true;
+ }
+
+ if (hasAudio && (!hasMicrophone() || !hasAmrNb())) {
+ // audio test waived if no audio support
+ continue;
+ }
+
+ Log.v(TAG, "testRecordFromSurface - round " + k);
+ success = recordFromSurface(filename, captureRate, hasAudio, surface);
+ if (success) {
+ checkTracksAndDuration(0, true /* hasVideo */, hasAudio, filename);
+
+ // verify capture fps meta key
+ if (timelapse && !checkCaptureFps(filename, captureRate)) {
+ noOfFailure++;
+ }
+ }
+ if (!success) {
+ noOfFailure++;
+ }
+ }
+ } catch (Exception e) {
+ Log.v(TAG, e.toString());
+ noOfFailure++;
+ } finally {
+ if (surface != null) {
+ Log.v(TAG, "releasing persistent surface");
+ surface.release();
+ surface = null;
+ }
+ }
+ return (noOfFailure == 0);
+ }
+
+ // Test recording from surface source with/without audio)
+ public void testSurfaceRecording() {
+ assertTrue(testRecordFromSurface(false /* persistent */, false /* timelapse */));
+ }
+
+ // Test recording from persistent surface source with/without audio
+ public void testPersistentSurfaceRecording() {
+ assertTrue(testRecordFromSurface(true /* persistent */, false /* timelapse */));
+ }
+
+ // Test timelapse recording from surface without audio
+ public void testSurfaceRecordingTimeLapse() {
+ assertTrue(testRecordFromSurface(false /* persistent */, true /* timelapse */));
+ }
+
+ // Test timelapse recording from persisent surface without audio
+ public void testPersistentSurfaceRecordingTimeLapse() {
+ assertTrue(testRecordFromSurface(true /* persistent */, true /* timelapse */));
+ }
+
private void recordMedia(long maxFileSize, File outFile) throws Exception {
mMediaRecorder.setMaxFileSize(maxFileSize);
mMediaRecorder.prepare();
diff --git a/tests/tests/media/src/android/media/cts/MediaSyncTest.java b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
index 96bed7d..6f9e2a2 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
@@ -31,6 +31,8 @@
import android.media.MediaFormat;
import android.media.MediaSync;
import android.media.MediaTimestamp;
+import android.media.PlaybackParams;
+import android.media.SyncParams;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
import android.view.Surface;
@@ -60,6 +62,7 @@
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
private final int APPLICATION_AUDIO_PERIOD_MS = 200;
private final int TEST_MAX_SPEED = 2;
+ private static final float FLOAT_TOLERANCE = .00001f;
private Context mContext;
private Resources mResources;
@@ -151,12 +154,12 @@
}
/**
- * Tests setPlaybackRate is handled correctly for wrong rate.
+ * Tests setPlaybackParams is handled correctly for wrong rate.
*/
- public void testSetPlaybackRateFail() throws InterruptedException {
+ public void testSetPlaybackParamsFail() throws InterruptedException {
final float rate = -1.0f;
try {
- mMediaSync.setPlaybackRate(rate, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(rate));
fail("playback rate " + rate + " is not handled correctly");
} catch (IllegalArgumentException e) {
}
@@ -170,7 +173,7 @@
mMediaSync.setAudioTrack(mAudioTrack);
try {
- mMediaSync.setPlaybackRate(rate, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(rate));
fail("With audio track set, playback rate " + rate
+ " is not handled correctly");
} catch (IllegalArgumentException e) {
@@ -178,13 +181,15 @@
}
/**
- * Tests setPlaybackRate is handled correctly for good rate without audio track set.
+ * Tests setPlaybackParams is handled correctly for good rate without audio track set.
* The case for good rate with audio track set is tested in testPlaybackRate*.
*/
- public void testSetPlaybackRateSucceed() throws InterruptedException {
+ public void testSetPlaybackParamsSucceed() throws InterruptedException {
final float rate = (float)TEST_MAX_SPEED;
try {
- mMediaSync.setPlaybackRate(rate, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(rate));
+ PlaybackParams pbp = mMediaSync.getPlaybackParams();
+ assertEquals(rate, pbp.getSpeed(), FLOAT_TOLERANCE);
} catch (IllegalArgumentException e) {
fail("playback rate " + rate + " is not handled correctly");
}
@@ -201,6 +206,9 @@
}
}
+ private PlaybackParams PAUSED_RATE = new PlaybackParams().setSpeed(0.f);
+ private PlaybackParams NORMAL_RATE = new PlaybackParams().setSpeed(1.f);
+
private boolean runCheckAudioBuffer(int inputResourceId, int timeOutMs) {
final int NUM_LOOPS = 10;
final Object condition = new Object();
@@ -233,7 +241,7 @@
}
}, null);
- mMediaSync.setPlaybackRate(1.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ mMediaSync.setPlaybackParams(NORMAL_RATE);
synchronized (condition) {
mDecoderAudio.start();
@@ -296,7 +304,7 @@
}
}, null);
- mMediaSync.setPlaybackRate(0.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_DEFAULT);
+ mMediaSync.setPlaybackParams(PAUSED_RATE);
ByteBuffer buffer1 = ByteBuffer.allocate(BUFFER_SIZE);
ByteBuffer buffer2 = ByteBuffer.allocate(BUFFER_SIZE);
@@ -305,7 +313,7 @@
mMediaSync.queueAudio(buffer2, INDEX_AFTER_FLUSH, 0 /* presentationTimeUs */);
synchronized (condition) {
- mMediaSync.setPlaybackRate(1.0f, MediaSync.PLAYBACK_RATE_AUDIO_MODE_DEFAULT);
+ mMediaSync.setPlaybackParams(NORMAL_RATE);
try {
condition.wait(timeOutMs);
@@ -438,7 +446,11 @@
mHasAudio = true;
}
- mMediaSync.setPlaybackRate(playbackRate, MediaSync.PLAYBACK_RATE_AUDIO_MODE_RESAMPLE);
+ SyncParams sync = new SyncParams().allowDefaults();
+ mMediaSync.setSyncParams(sync);
+ sync = mMediaSync.getSyncParams();
+
+ mMediaSync.setPlaybackParams(new PlaybackParams().setSpeed(playbackRate));
synchronized (conditionFirstAudioBuffer) {
if (video) {
@@ -488,7 +500,10 @@
+ ", play time is " + playTimeUs + " vs expected " + mediaDurationUs,
mediaDurationUs,
playTimeUs * playbackRate,
- mediaDurationUs * PLAYBACK_RATE_TOLERANCE_PERCENT / 100
+ // sync.getTolerance() is MediaSync's tolerance of the playback rate, whereas
+ // PLAYBACK_RATE_TOLERANCE_PERCENT / 100 is our test's tolerance.
+ // We need to add both to get an upperbound for allowable error.
+ mediaDurationUs * (sync.getTolerance() + PLAYBACK_RATE_TOLERANCE_PERCENT / 100)
+ TIME_MEASUREMENT_TOLERANCE_US);
}
diff --git a/tests/tests/media/src/android/media/cts/ParamsTest.java b/tests/tests/media/src/android/media/cts/ParamsTest.java
new file mode 100644
index 0000000..4e42004
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ParamsTest.java
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.media.PlaybackParams;
+import android.media.SyncParams;
+import android.test.AndroidTestCase;
+
+/**
+ * General Params tests.
+ *
+ * In particular, check Params objects' behavior.
+ */
+public class ParamsTest extends AndroidTestCase {
+ private static final String TAG = "ParamsTest";
+ private static final float FLOAT_TOLERANCE = .00001f;
+ private static final float MAX_DEFAULT_TOLERANCE = 1/24.f;
+
+ public void testSyncParamsConstants() {
+ assertEquals(0, SyncParams.SYNC_SOURCE_DEFAULT);
+ assertEquals(1, SyncParams.SYNC_SOURCE_SYSTEM_CLOCK);
+ assertEquals(2, SyncParams.SYNC_SOURCE_AUDIO);
+ assertEquals(3, SyncParams.SYNC_SOURCE_VSYNC);
+
+ assertEquals(0, SyncParams.AUDIO_ADJUST_MODE_DEFAULT);
+ assertEquals(1, SyncParams.AUDIO_ADJUST_MODE_STRETCH);
+ assertEquals(2, SyncParams.AUDIO_ADJUST_MODE_RESAMPLE);
+ }
+
+ public void testSyncParamsDefaults() {
+ SyncParams p = new SyncParams();
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+
+ SyncParams q = p.allowDefaults();
+ assertSame(p, q);
+ assertEquals(p.AUDIO_ADJUST_MODE_DEFAULT, p.getAudioAdjustMode());
+ assertEquals(p.SYNC_SOURCE_DEFAULT, p.getSyncSource());
+ assertTrue(p.getTolerance() >= 0.f
+ && p.getTolerance() < MAX_DEFAULT_TOLERANCE + FLOAT_TOLERANCE);
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+ }
+
+ public void testSyncParamsAudioAdjustMode() {
+ // setting this cannot fail
+ SyncParams p = new SyncParams();
+ for (int i : new int[] {
+ SyncParams.AUDIO_ADJUST_MODE_STRETCH,
+ SyncParams.AUDIO_ADJUST_MODE_RESAMPLE,
+ -1 /* invalid */}) {
+ SyncParams q = p.setAudioAdjustMode(i); // verify both initial set and update
+ assertSame(p, q);
+ assertEquals(i, p.getAudioAdjustMode());
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+ }
+ }
+
+ public void testSyncParamsSyncSource() {
+ // setting this cannot fail
+ SyncParams p = new SyncParams();
+ for (int i : new int[] {
+ SyncParams.SYNC_SOURCE_SYSTEM_CLOCK,
+ SyncParams.SYNC_SOURCE_AUDIO,
+ -1 /* invalid */}) {
+ SyncParams q = p.setSyncSource(i); // verify both initial set and update
+ assertSame(p, q);
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ assertEquals(i, p.getSyncSource());
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+ }
+ }
+
+ public void testSyncParamsTolerance() {
+ // this can fail on values not in [0, 1)
+
+ // test good values
+ SyncParams p = new SyncParams();
+ float lastValue = 2.f; /* some initial value to avoid compile error */
+ for (float f : new float[] { 0.f, .1f, .9999f }) {
+ SyncParams q = p.setTolerance(f); // verify both initial set and update
+ assertSame(p, q);
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ assertEquals(f, p.getTolerance(), FLOAT_TOLERANCE);
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+ lastValue = f;
+ }
+
+ // test bad values - these should have no effect
+ boolean update = true;
+ for (float f : new float[] { -.0001f, 1.f }) {
+ try {
+ p.setTolerance(f);
+ fail("set tolerance to " + f);
+ } catch (IllegalArgumentException e) {}
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ if (update) {
+ // if updating, last value should remain
+ assertEquals(lastValue, p.getTolerance(), FLOAT_TOLERANCE);
+ } else {
+ // otherwise, it should remain undefined
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ }
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+
+ // no longer updating in subsequent iterations
+ p = new SyncParams();
+ update = false;
+ }
+ }
+
+ public void testSyncParamsFrameRate() {
+ // setting this cannot fail, but negative values may be normalized to some negative value
+ SyncParams p = new SyncParams();
+ for (float f : new float[] { 0.f, .0001f, 30.f, 300.f, -.0001f, -1.f }) {
+ SyncParams q = p.setFrameRate(f);
+ assertSame(p, q);
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ if (f >= 0) {
+ assertEquals(f, p.getFrameRate(), FLOAT_TOLERANCE);
+ } else {
+ assertTrue(p.getFrameRate() < 0.f);
+ }
+ }
+ }
+
+ public void testSyncParamsMultipleSettings() {
+ {
+ SyncParams p = new SyncParams();
+ p.setAudioAdjustMode(p.AUDIO_ADJUST_MODE_STRETCH);
+ SyncParams q = p.setTolerance(.5f);
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_ADJUST_MODE_STRETCH, p.getAudioAdjustMode());
+ try { fail("got " + p.getSyncSource()); } catch (IllegalStateException e) {}
+ assertEquals(.5f, p.getTolerance(), FLOAT_TOLERANCE);
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+
+ // allowDefaults should not change set values
+ q = p.allowDefaults();
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_ADJUST_MODE_STRETCH, p.getAudioAdjustMode());
+ assertEquals(p.SYNC_SOURCE_DEFAULT, p.getSyncSource());
+ assertEquals(.5f, p.getTolerance(), FLOAT_TOLERANCE);
+ try { fail("got " + p.getFrameRate()); } catch (IllegalStateException e) {}
+ }
+
+ {
+ SyncParams p = new SyncParams();
+ p.setSyncSource(p.SYNC_SOURCE_VSYNC);
+ SyncParams q = p.setFrameRate(25.f);
+ assertSame(p, q);
+
+ try { fail("got " + p.getAudioAdjustMode()); } catch (IllegalStateException e) {}
+ assertEquals(p.SYNC_SOURCE_VSYNC, p.getSyncSource());
+ try { fail("got " + p.getTolerance()); } catch (IllegalStateException e) {}
+ assertEquals(25.f, p.getFrameRate(), FLOAT_TOLERANCE);
+
+ // allowDefaults should not change set values
+ q = p.allowDefaults();
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_ADJUST_MODE_DEFAULT, p.getAudioAdjustMode());
+ assertEquals(p.SYNC_SOURCE_VSYNC, p.getSyncSource());
+ assertTrue(p.getTolerance() >= 0.f
+ && p.getTolerance() < MAX_DEFAULT_TOLERANCE + FLOAT_TOLERANCE);
+ assertEquals(25.f, p.getFrameRate(), FLOAT_TOLERANCE);
+ }
+ }
+
+ public void testPlaybackParamsConstants() {
+ assertEquals(0, PlaybackParams.AUDIO_STRETCH_MODE_DEFAULT);
+ assertEquals(1, PlaybackParams.AUDIO_STRETCH_MODE_VOICE);
+
+ assertEquals(0, PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT);
+ assertEquals(1, PlaybackParams.AUDIO_FALLBACK_MODE_MUTE);
+ assertEquals(2, PlaybackParams.AUDIO_FALLBACK_MODE_FAIL);
+ }
+
+ public void testPlaybackParamsDefaults() {
+ PlaybackParams p = new PlaybackParams();
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+
+ PlaybackParams q = p.allowDefaults();
+ assertSame(p, q);
+ assertEquals(p.AUDIO_FALLBACK_MODE_DEFAULT, p.getAudioFallbackMode());
+ assertEquals(p.AUDIO_STRETCH_MODE_DEFAULT, p.getAudioStretchMode());
+ assertEquals(1.f, p.getPitch(), FLOAT_TOLERANCE);
+ assertEquals(1.f, p.getSpeed(), FLOAT_TOLERANCE);
+ }
+
+ public void testPlaybackParamsAudioFallbackMode() {
+ // setting this cannot fail
+ PlaybackParams p = new PlaybackParams();
+ for (int i : new int[] {
+ PlaybackParams.AUDIO_FALLBACK_MODE_MUTE,
+ PlaybackParams.AUDIO_FALLBACK_MODE_FAIL,
+ -1 /* invalid */}) {
+ PlaybackParams q = p.setAudioFallbackMode(i); // verify both initial set and update
+ assertSame(p, q);
+ assertEquals(i, p.getAudioFallbackMode());
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+ }
+ }
+
+ public void testPlaybackParamsAudioStretchMode() {
+ // setting this cannot fail
+ PlaybackParams p = new PlaybackParams();
+ for (int i : new int[] {
+ PlaybackParams.AUDIO_STRETCH_MODE_DEFAULT,
+ PlaybackParams.AUDIO_STRETCH_MODE_VOICE,
+ -1 /* invalid */}) {
+ PlaybackParams q = p.setAudioStretchMode(i); // verify both initial set and update
+ assertSame(p, q);
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ assertEquals(i, p.getAudioStretchMode());
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+ }
+ }
+
+ public void testPlaybackParamsPitch() {
+ // this can fail on values not in [0, Inf)
+
+ // test good values
+ PlaybackParams p = new PlaybackParams();
+ float lastValue = 2.f; /* some initial value to avoid compile error */
+ for (float f : new float[] { 0.f, .1f, 9999.f }) {
+ PlaybackParams q = p.setPitch(f); // verify both initial set and update
+ assertSame(p, q);
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ assertEquals(f, p.getPitch(), FLOAT_TOLERANCE);
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+ lastValue = f;
+ }
+
+ // test bad values - these should have no effect
+ boolean update = true;
+ for (float f : new float[] { -.0001f, -1.f }) {
+ try {
+ p.setPitch(f);
+ fail("set tolerance to " + f);
+ } catch (IllegalArgumentException e) {}
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ if (update) {
+ // if updating, last value should remain
+ assertEquals(lastValue, p.getPitch(), FLOAT_TOLERANCE);
+ } else {
+ // otherwise, it should remain undefined
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ }
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+
+ // no longer updating in subsequent iterations
+ p = new PlaybackParams();
+ update = false;
+ }
+ }
+
+ public void testPlaybackParamsSpeed() {
+ // setting this cannot fail
+ PlaybackParams p = new PlaybackParams();
+ for (float f : new float[] { 0.f, .0001f, 30.f, 300.f, -.0001f, -1.f, -300.f }) {
+ PlaybackParams q = p.setSpeed(f);
+ assertSame(p, q);
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ assertEquals(f, p.getSpeed(), FLOAT_TOLERANCE);
+ }
+ }
+
+ public void testPlaybackParamsMultipleSettings() {
+ {
+ PlaybackParams p = new PlaybackParams();
+ p.setAudioFallbackMode(p.AUDIO_FALLBACK_MODE_MUTE);
+ PlaybackParams q = p.setPitch(.5f);
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_FALLBACK_MODE_MUTE, p.getAudioFallbackMode());
+ try { fail("got " + p.getAudioStretchMode()); } catch (IllegalStateException e) {}
+ assertEquals(.5f, p.getPitch(), FLOAT_TOLERANCE);
+ try { fail("got " + p.getSpeed()); } catch (IllegalStateException e) {}
+
+ // allowDefaults should not change set values
+ q = p.allowDefaults();
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_FALLBACK_MODE_MUTE, p.getAudioFallbackMode());
+ assertEquals(p.AUDIO_STRETCH_MODE_DEFAULT, p.getAudioStretchMode());
+ assertEquals(.5f, p.getPitch(), FLOAT_TOLERANCE);
+ assertEquals(1.f, p.getSpeed(), FLOAT_TOLERANCE);
+ }
+
+ {
+ PlaybackParams p = new PlaybackParams();
+ p.setAudioStretchMode(p.AUDIO_STRETCH_MODE_VOICE);
+ PlaybackParams q = p.setSpeed(25.f);
+ assertSame(p, q);
+
+ try { fail("got " + p.getAudioFallbackMode()); } catch (IllegalStateException e) {}
+ assertEquals(p.AUDIO_STRETCH_MODE_VOICE, p.getAudioStretchMode());
+ try { fail("got " + p.getPitch()); } catch (IllegalStateException e) {}
+ assertEquals(25.f, p.getSpeed(), FLOAT_TOLERANCE);
+
+ // allowDefaults should not change set values
+ q = p.allowDefaults();
+ assertSame(p, q);
+
+ assertEquals(p.AUDIO_FALLBACK_MODE_DEFAULT, p.getAudioFallbackMode());
+ assertEquals(p.AUDIO_STRETCH_MODE_VOICE, p.getAudioStretchMode());
+ assertEquals(1.f, p.getPitch(), FLOAT_TOLERANCE);
+ assertEquals(25.f, p.getSpeed(), FLOAT_TOLERANCE);
+ }
+ }
+
+
+
+}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
new file mode 100644
index 0000000..214ced4
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import junit.framework.Assert;
+
+public class ResourceManagerStubActivity extends Activity {
+ private static final String TAG = "ResourceManagerStubActivity";
+ private final Object mFinishEvent = new Object();
+ private int[] mRequestCodes = {0, 1};
+ private boolean[] mResults = {false, false};
+ private int mNumResults = 0;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "Activity " + requestCode + " finished.");
+ mResults[requestCode] = (resultCode == RESULT_OK);
+ if (++mNumResults == mResults.length) {
+ synchronized (mFinishEvent) {
+ mFinishEvent.notify();
+ }
+ }
+ }
+
+ public boolean testReclaimResource() throws InterruptedException {
+ Thread thread = new Thread() {
+ @Override
+ public void run() {
+ try {
+ Context context = getApplicationContext();
+ Intent intent1 = new Intent(context, ResourceManagerTestActivity1.class);
+ startActivityForResult(intent1, mRequestCodes[0]);
+ Thread.sleep(2000); // wait for process to launch.
+
+ Intent intent2 = new Intent(context, ResourceManagerTestActivity2.class);
+ startActivityForResult(intent2, mRequestCodes[1]);
+
+ synchronized (mFinishEvent) {
+ mFinishEvent.wait();
+ }
+ } catch(Exception e) {
+ Log.d(TAG, "testReclaimResource got exception " + e.toString());
+ }
+ }
+ };
+ thread.start();
+ thread.join(10000);
+
+ for (int i = 0; i < mResults.length; ++i) {
+ Assert.assertTrue("Result from activity " + i + " is a fail.", mResults[i]);
+ }
+ return true;
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTest.java b/tests/tests/media/src/android/media/cts/ResourceManagerTest.java
new file mode 100644
index 0000000..5170aac
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTest.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+
+public class ResourceManagerTest
+ extends ActivityInstrumentationTestCase2<ResourceManagerStubActivity> {
+
+ public ResourceManagerTest() {
+ super("com.android.cts.media", ResourceManagerStubActivity.class);
+ }
+
+ public void testReclaimResource() throws Exception {
+ Bundle extras = new Bundle();
+ ResourceManagerStubActivity activity = launchActivity(
+ "com.android.cts.media", ResourceManagerStubActivity.class, extras);
+ activity.testReclaimResource();
+ activity.finish();
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java
new file mode 100644
index 0000000..aff3f03
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ResourceManagerTestActivity1 extends ResourceManagerTestActivityBase {
+ private static final int MAX_INSTANCES = 32;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ TAG = "ResourceManagerTestActivity1";
+
+ Log.d(TAG, "onCreate called.");
+ super.onCreate(savedInstanceState);
+ moveTaskToBack(true);
+
+ if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
+ // haven't reached the limit with MAX_INSTANCES, report RESULT_OK directly and
+ // skip additional test.
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ useCodecs();
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java
new file mode 100644
index 0000000..f4c57f5
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.util.Log;
+
+public class ResourceManagerTestActivity2 extends ResourceManagerTestActivityBase {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ TAG = "ResourceManagerTestActivity2";
+
+ Log.d(TAG, "onCreate called.");
+ super.onCreate(savedInstanceState);
+
+ if (allocateCodecs(1) == 1) {
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java
new file mode 100644
index 0000000..9c48fc4
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.app.Activity;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.util.Log;
+import java.io.IOException;
+import java.util.Vector;
+
+public class ResourceManagerTestActivityBase extends Activity {
+ protected String TAG;
+ private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+ private static final String MIME = MediaFormat.MIMETYPE_VIDEO_AVC;
+
+ private Vector<MediaCodec> mCodecs = new Vector<MediaCodec>();
+
+ private class TestCodecCallback extends MediaCodec.Callback {
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ Log.d(TAG, "onInputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ MediaCodec codec, int index, MediaCodec.BufferInfo info) {
+ Log.d(TAG, "onOutputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ Log.d(TAG, "onError " + codec.toString() + " errorCode " + e.getErrorCode());
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.d(TAG, "onOutputFormatChanged " + codec.toString());
+ }
+ }
+
+ private MediaCodec.Callback mCallback = new TestCodecCallback();
+
+ private static MediaFormat getTestFormat(VideoCapabilities vcaps) {
+ int maxWidth = vcaps.getSupportedWidths().getUpper();
+ int maxHeight = vcaps.getSupportedHeightsFor(maxWidth).getUpper();
+ int maxBitrate = vcaps.getBitrateRange().getUpper();
+ int maxFramerate = vcaps.getSupportedFrameRatesFor(maxWidth, maxHeight)
+ .getUpper().intValue();
+
+ MediaFormat format = MediaFormat.createVideoFormat(MIME, maxWidth, maxHeight);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
+ CodecCapabilities.COLOR_FormatYUV420Flexible);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, maxBitrate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, maxFramerate);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ return format;
+ }
+
+ private MediaCodecInfo getTestCodecInfo() {
+ // Use avc decoder for testing.
+ boolean isEncoder = false;
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder() != isEncoder) {
+ continue;
+ }
+ CodecCapabilities caps;
+ try {
+ caps = info.getCapabilitiesForType(MIME);
+ } catch (IllegalArgumentException e) {
+ // mime is not supported
+ continue;
+ }
+ return info;
+ }
+
+ return null;
+ }
+
+ protected int allocateCodecs(int max) {
+ MediaCodecInfo info = getTestCodecInfo();
+ if (info == null) {
+ // skip the test
+ return 0;
+ }
+
+ String name = info.getName();
+ VideoCapabilities vcaps = info.getCapabilitiesForType(MIME).getVideoCapabilities();
+ MediaFormat format = getTestFormat(vcaps);
+ for (int i = 0; i < max; ++i) {
+ try {
+ Log.d(TAG, "Create codec " + name + " #" + i);
+ MediaCodec codec = MediaCodec.createByCodecName(name);
+ codec.setCallback(mCallback);
+ Log.d(TAG, "Configure codec " + format);
+ codec.configure(format, null, null, 0);
+ Log.d(TAG, "Start codec " + format);
+ codec.start();
+ mCodecs.add(codec);
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "IllegalArgumentException " + e.getMessage());
+ break;
+ } catch (IOException e) {
+ Log.d(TAG, "IOException " + e.getMessage());
+ break;
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ break;
+ }
+ }
+
+ return mCodecs.size();
+ }
+
+ private void doUseCodecs() {
+ int current = 0;
+ try {
+ for (current = 0; current < mCodecs.size(); ++current) {
+ mCodecs.get(current).getName();
+ }
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "useCodecs got CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ if (e.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
+ Log.d(TAG, "Remove codec " + current + " from the list");
+ mCodecs.remove(current);
+ setResult(Activity.RESULT_OK);
+ finish();
+ }
+ return;
+ }
+ }
+
+ private Thread mWorkerThread;
+ protected void useCodecs() {
+ mWorkerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ doUseCodecs();
+ }
+ }
+ });
+ mWorkerThread.start();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy called.");
+ super.onDestroy();
+
+ for (int i = 0; i < mCodecs.size(); ++i) {
+ mCodecs.get(i).release();
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
new file mode 100644
index 0000000..fcb61dd
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+
+import android.media.AudioDeviceInfo;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioRecord;
+import android.media.AudioTrack;
+import android.media.MediaRecorder;
+
+import android.test.AndroidTestCase;
+
+import android.util.Log;
+
+/**
+ * TODO: Insert description here. (generated by pmclean)
+ */
+public class RoutingTest extends AndroidTestCase {
+ private static final String TAG = "RoutingTest";
+
+ private AudioManager mAudioManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ // get the AudioManager
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull(mAudioManager);
+ }
+
+ public void test_audioTrack_preferredDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ int bufferSize =
+ AudioTrack.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ AudioTrack audioTrack =
+ new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize,
+ AudioTrack.MODE_STREAM);
+ assertNotNull(audioTrack);
+
+ // None selected (new AudioTrack), so check for default
+ assertNull(audioTrack.getPreferredDevice());
+
+ // resets to default
+ assertTrue(audioTrack.setPreferredDevice(null));
+
+ // test each device
+ AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+ for(int index = 0; index < deviceList.length; index++) {
+ assertTrue(audioTrack.setPreferredDevice(deviceList[index]));
+ assertTrue(audioTrack.getPreferredDevice() == deviceList[index]);
+ }
+
+ // Check defaults again
+ assertTrue(audioTrack.setPreferredDevice(null));
+ assertNull(audioTrack.getPreferredDevice());
+ }
+
+ public void test_audioRecord_preferredDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ int bufferSize =
+ AudioRecord.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT);
+ AudioRecord audioRecord =
+ new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT,
+ 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize);
+ assertNotNull(audioRecord);
+
+ // None selected (new AudioRecord), so check for default
+ assertNull(audioRecord.getPreferredDevice());
+
+ // resets to default
+ assertTrue(audioRecord.setPreferredDevice(null));
+
+ // test each device
+ AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
+ for(int index = 0; index < deviceList.length; index++) {
+ assertTrue(audioRecord.setPreferredDevice(deviceList[index]));
+ assertTrue(audioRecord.getPreferredDevice() == deviceList[index]);
+ }
+
+ // Check defaults again
+ assertTrue(audioRecord.setPreferredDevice(null));
+ assertNull(audioRecord.getPreferredDevice());
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/SoundPoolTest.java b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
index 15d18a0..23c4a7c 100644
--- a/tests/tests/media/src/android/media/cts/SoundPoolTest.java
+++ b/tests/tests/media/src/android/media/cts/SoundPoolTest.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.SoundPool;
import android.test.AndroidTestCase;
@@ -29,6 +30,8 @@
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.InputStream;
+import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicInteger;
abstract class SoundPoolTest extends AndroidTestCase {
@@ -230,6 +233,104 @@
mSoundPool.release();
}
+ public void testAutoPauseResume() throws Exception {
+ // The number of possible SoundPool streams simultaneously active is limited by
+ // track resources. Generally this is no greater than 32, but the actual
+ // amount may be less depending on concurrently running applications.
+ // Here we attempt to create more streams than what is normally possible;
+ // SoundPool should gracefully degrade to play those streams it can.
+ //
+ // Try to keep the maxStreams less than the number required to be active
+ // and certainly less than 20 to be cooperative to other applications.
+ final int TEST_STREAMS = 40;
+ SoundPool soundPool = null;
+ try {
+ soundPool = new SoundPool.Builder()
+ .setAudioAttributes(new AudioAttributes.Builder().build())
+ .setMaxStreams(TEST_STREAMS)
+ .build();
+
+ // get our sounds
+ final int[] sounds = getSounds();
+
+ // set our completion listener
+ final int[] loadIds = new int[TEST_STREAMS];
+ final Object done = new Object();
+ final int[] loaded = new int[1]; // used as a "pointer" to an integer
+ final SoundPool fpool = soundPool; // final reference in scope of try block
+ soundPool.setOnLoadCompleteListener(new SoundPool.OnLoadCompleteListener() {
+ @Override
+ public void onLoadComplete(SoundPool pool, int sampleId, int status) {
+ assertEquals(fpool, pool);
+ assertEquals(0 /* success */, status);
+ synchronized(done) {
+ loadIds[loaded[0]++] = sampleId;
+ if (loaded[0] == loadIds.length) {
+ done.notify();
+ }
+ }
+ }
+ });
+
+ // initiate loading
+ final int[] soundIds = new int[TEST_STREAMS];
+ for (int i = 0; i < soundIds.length; i++) {
+ soundIds[i] = soundPool.load(mContext, sounds[i % sounds.length], PRIORITY);
+ }
+
+ // wait for all sounds to load
+ final long LOAD_TIMEOUT_IN_MS = 10000;
+ final long startTime = System.currentTimeMillis();
+ synchronized(done) {
+ while (loaded[0] != soundIds.length) {
+ final long waitTime =
+ LOAD_TIMEOUT_IN_MS - (System.currentTimeMillis() - startTime);
+ assertTrue(waitTime > 0);
+ done.wait(waitTime);
+ }
+ }
+
+ // verify the Ids match (actually does sorting too)
+ Arrays.sort(loadIds);
+ Arrays.sort(soundIds);
+ assertTrue(Arrays.equals(loadIds, soundIds));
+
+ // play - should hear the following:
+ // 1 second of sound
+ // 1 second of silence
+ // 1 second of sound.
+ int[] streamIds = new int[soundIds.length];
+ for (int i = 0; i < soundIds.length; i++) {
+ streamIds[i] = soundPool.play(soundIds[i],
+ 0.5f /* leftVolume */, 0.5f /* rightVolume */, PRIORITY,
+ -1 /* loop (infinite) */, 1.0f /* rate */);
+ }
+ Thread.sleep(1000 /* millis */);
+ soundPool.autoPause();
+ Thread.sleep(1000 /* millis */);
+ soundPool.autoResume();
+ Thread.sleep(1000 /* millis */);
+
+ // clean up
+ for (int stream : streamIds) {
+ assertTrue(stream != 0);
+ soundPool.stop(stream);
+ }
+ for (int sound : soundIds) {
+ assertEquals(true, soundPool.unload(sound));
+ }
+ // check to see we're really unloaded
+ for (int sound : soundIds) {
+ assertEquals(false, soundPool.unload(sound));
+ }
+ } finally {
+ if (soundPool != null) {
+ soundPool.release();
+ soundPool = null;
+ }
+ }
+ }
+
/**
* Load a sample and wait until it is ready to be played.
* @return The sample ID.
diff --git a/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
new file mode 100644
index 0000000..d559c72
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.media.browse.MediaBrowser.MediaItem;
+import android.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+
+import java.util.List;
+
+/**
+ * Stub implementation of (@link android.service.media.MediaBrowserService}.
+ */
+public class StubMediaBrowserService extends MediaBrowserService {
+ static final String MEDIA_ID_ROOT = "test_media_id_root";
+ static final String EXTRAS_KEY = "test_extras_key";
+ static final String EXTRAS_VALUE = "test_extras_value";
+
+ /* package private */ static MediaSession sSession;
+ private Bundle mExtras;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sSession = new MediaSession(this, "MediaBrowserStubService");
+ setSessionToken(sSession.getSessionToken());
+ }
+
+ @Override
+ public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ mExtras = new Bundle();
+ mExtras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+ return new BrowserRoot(MEDIA_ID_ROOT, mExtras);
+ }
+
+ @Override
+ public void onLoadChildren(final String parentMediaId, final Result<List<MediaItem>> result) {
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/TestMediaDataSource.java b/tests/tests/media/src/android/media/cts/TestMediaDataSource.java
index 87b4c59..a10840b 100644
--- a/tests/tests/media/src/android/media/cts/TestMediaDataSource.java
+++ b/tests/tests/media/src/android/media/cts/TestMediaDataSource.java
@@ -28,7 +28,7 @@
/**
* A MediaDataSource that reads from a byte array for use in tests.
*/
-public class TestMediaDataSource implements MediaDataSource {
+public class TestMediaDataSource extends MediaDataSource {
private static final String TAG = "TestMediaDataSource";
private byte[] mData;
@@ -62,29 +62,30 @@
}
@Override
- public synchronized int readAt(long offset, byte[] buffer, int size) {
+ public synchronized int readAt(long position, byte[] buffer, int offset, int size)
+ throws IOException {
if (mThrowFromReadAt) {
- throw new RuntimeException("Test exception from readAt()");
+ throw new IOException("Test exception from readAt()");
}
if (mReturnFromReadAt != null) {
return mReturnFromReadAt;
}
// Clamp reads past the end of the source.
- if (offset >= mData.length) {
- return 0;
+ if (position >= mData.length) {
+ return -1; // -1 indicates EOF
}
- if (offset + size > mData.length) {
- size -= (offset + size) - mData.length;
+ if (position + size > mData.length) {
+ size -= (position + size) - mData.length;
}
- System.arraycopy(mData, (int)offset, buffer, 0, size);
+ System.arraycopy(mData, (int)position, buffer, offset, size);
return size;
}
@Override
- public synchronized long getSize() {
+ public synchronized long getSize() throws IOException {
if (mThrowFromGetSize) {
- throw new RuntimeException("Test exception from getSize()");
+ throw new IOException("Test exception from getSize()");
}
if (mReturnFromGetSize != null) {
return mReturnFromGetSize;
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 eddb416..c23ad30 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -155,7 +155,7 @@
public void testLoopbackPing() throws ErrnoException, IOException {
// Generate a random ping packet and send it to localhost.
InetAddress ipv6Loopback = InetAddress.getByName(null);
- assertEquals("localhost/::1", ipv6Loopback.toString());
+ assertEquals("::1", ipv6Loopback.getHostAddress());
for (int i = 0; i < NUM_PACKETS; i++) {
byte[] packet = pingPacket((int) (Math.random() * (MAX_SIZE - ICMP_HEADER_SIZE)));
diff --git a/tests/tests/os/jni/Android.mk b/tests/tests/os/jni/Android.mk
index dd3bb51..fab1ec2 100644
--- a/tests/tests/os/jni/Android.mk
+++ b/tests/tests/os/jni/Android.mk
@@ -27,7 +27,30 @@
android_os_cts_TaggedPointer.cpp \
android_os_cts_HardwareName.cpp \
android_os_cts_OSFeatures.cpp \
- android_os_cts_NoExecutePermissionTest.cpp
+ android_os_cts_NoExecutePermissionTest.cpp \
+ android_os_cts_SeccompTest.cpp
+
+# Select the architectures on which seccomp-bpf are supported. This is used to
+# include extra test files that will not compile on architectures where it is
+# not supported.
+ARCH_SUPPORTS_SECCOMP := 0
+ifeq ($(strip $(TARGET_ARCH)),arm)
+ ARCH_SUPPORTS_SECCOMP = 1
+endif
+
+ifeq ($(strip $(TARGET_ARCH)),x86)
+ ARCH_SUPPORTS_SECCOMP = 1
+endif
+
+ifeq ($(strip $(TARGET_ARCH)),x86_64)
+ ARCH_SUPPORTS_SECCOMP = 1
+endif
+
+ifeq ($(ARCH_SUPPORTS_SECCOMP),1)
+ LOCAL_SRC_FILES += seccomp-tests/tests/seccomp_bpf_tests.c
+ # This define controls the behavior of OSFeatures.needsSeccompSupport().
+ LOCAL_CFLAGS += -DARCH_SUPPORTS_SECCOMP
+endif
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
diff --git a/tests/tests/os/jni/CtsOsJniOnLoad.cpp b/tests/tests/os/jni/CtsOsJniOnLoad.cpp
index e4a0dc0..cac750f 100644
--- a/tests/tests/os/jni/CtsOsJniOnLoad.cpp
+++ b/tests/tests/os/jni/CtsOsJniOnLoad.cpp
@@ -29,6 +29,8 @@
extern int register_android_os_cts_NoExecutePermissionTest(JNIEnv*);
+extern int register_android_os_cts_SeccompTest(JNIEnv*);
+
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
@@ -60,5 +62,9 @@
return JNI_ERR;
}
+ if (register_android_os_cts_SeccompTest(env)) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_4;
}
diff --git a/tests/tests/os/jni/android_os_cts_OSFeatures.cpp b/tests/tests/os/jni/android_os_cts_OSFeatures.cpp
index 2ee31b1..153fb27 100644
--- a/tests/tests/os/jni/android_os_cts_OSFeatures.cpp
+++ b/tests/tests/os/jni/android_os_cts_OSFeatures.cpp
@@ -84,8 +84,9 @@
jboolean android_os_cts_OSFeatures_needsSeccompSupport(JNIEnv*, jobject)
{
-#if !defined(__arm__) && !defined(__i386__) && !defined(__x86_64__)
+#if !defined(ARCH_SUPPORTS_SECCOMP)
// Seccomp support is only available for ARM, x86, x86_64.
+ // This define is controlled by the Android.mk.
return false;
#endif
diff --git a/tests/tests/os/jni/android_os_cts_SeccompTest.cpp b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
new file mode 100644
index 0000000..cd1543d
--- /dev/null
+++ b/tests/tests/os/jni/android_os_cts_SeccompTest.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android/log.h>
+#include <jni.h>
+#include <string.h>
+
+#include "seccomp-tests/tests/test_harness.h"
+
+// Forward declare from seccomp_bpf_tests.c.
+extern "C" {
+struct __test_metadata* get_seccomp_test_list();
+}
+
+static const char TAG[] = "SecompBpfTest-Native";
+
+jboolean android_security_cts_SeccompBpfTest_runKernelUnitTest(
+ JNIEnv* env, jobject thiz __unused, jstring name) {
+#if defined(ARCH_SUPPORTS_SECCOMP)
+ const char* nameStr = env->GetStringUTFChars(name, nullptr);
+
+ for (struct __test_metadata* t = get_seccomp_test_list(); t; t = t->next) {
+ if (strcmp(t->name, nameStr) == 0) {
+ __android_log_print(ANDROID_LOG_INFO, TAG, "Start: %s", t->name);
+ __run_test(t);
+ __android_log_print(ANDROID_LOG_INFO, TAG, "%s: %s",
+ t->passed ? "PASS" : "FAIL", t->name);
+ return t->passed;
+ }
+ }
+#endif // ARCH_SUPPORTS_SECCOMP
+
+ return false;
+}
+
+static JNINativeMethod methods[] = {
+ { "runKernelUnitTest", "(Ljava/lang/String;)Z",
+ (void*)android_security_cts_SeccompBpfTest_runKernelUnitTest },
+};
+
+int register_android_os_cts_SeccompTest(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/os/cts/SeccompTest");
+ return env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/os/jni/seccomp-tests/LICENSE b/tests/tests/os/jni/seccomp-tests/LICENSE
new file mode 100644
index 0000000..b9e779f
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2014 The Chromium OS Authors. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/tests/tests/os/jni/seccomp-tests/README b/tests/tests/os/jni/seccomp-tests/README
new file mode 100644
index 0000000..c8cd2ad
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/README
@@ -0,0 +1,4 @@
+seccomp
+-------
+
+Landing place for code relating to seccomp_filter work for the Linux kernel.
diff --git a/tests/tests/os/jni/seccomp-tests/README.android b/tests/tests/os/jni/seccomp-tests/README.android
new file mode 100644
index 0000000..4c5bb83
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/README.android
@@ -0,0 +1,13 @@
+This is the kernel unittest for seccomp-bpf sandboxing.
+
+URL: https://github.com/redpig/seccomp
+Revision: e65c79a14dc2bbb6d8dbf12ebf71905e2253a4b2
+License: BSD
+
+Local modifications:
+- Remove usage of pthread_cancel()
+- Use __android_log_print() instead of fprintf()
+- Rename main() to seccomp_test_main()
+- Add get_seccomp_test_list()
+
+The diff of modifications can be found in local-modifications-android.diff.
diff --git a/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff b/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff
new file mode 100644
index 0000000..288261a
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/local-modifications-android.diff
@@ -0,0 +1,59 @@
+diff --git a/tests/seccomp_bpf_tests.c b/tests/seccomp_bpf_tests.c
+index deb78d1..98b0231 100644
+--- a/tests/seccomp_bpf_tests.c
++++ b/tests/seccomp_bpf_tests.c
+@@ -1457,7 +1457,7 @@ FIXTURE_TEARDOWN(TSYNC) {
+ if (!s->tid)
+ continue;
+ if (pthread_kill(s->tid, 0)) {
+- pthread_cancel(s->tid);
++ //pthread_cancel(s->tid); // ANDROID
+ pthread_join(s->tid, &status);
+ }
+ }
+@@ -1940,4 +1940,10 @@ TEST(syscall_restart) {
+ * - ...
+ */
+
++// ANDROID:begin
++struct __test_metadata* get_seccomp_test_list() {
++ return __test_list;
++}
++// ANDROID:end
++
+ TEST_HARNESS_MAIN
+diff --git a/tests/test_harness.h b/tests/test_harness.h
+index 47ee027..597e40c 100644
+--- a/tests/test_harness.h
++++ b/tests/test_harness.h
+@@ -49,6 +49,8 @@
+ #include <sys/wait.h>
+ #include <unistd.h>
+
++#include <android/log.h> // ANDROID
++
+ /* All exported functionality should be declared through this macro. */
+ #define TEST_API(x) _##x
+
+@@ -206,9 +208,11 @@
+ } while (0)
+
+ /* Unconditional logger for internal use. */
++// ANDROID:begin
+ #define __TH_LOG(fmt, ...) \
+- fprintf(TH_LOG_STREAM, "%s:%d:%s:" fmt "\n", \
++ __android_log_print(ANDROID_LOG_ERROR, "SeccompBpfTest-KernelUnit", "%s:%d:%s:" fmt "\n", \
+ __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__)
++// ANDROID:end
+
+ /* Defines the test function and creates the registration stub. */
+ #define _TEST(test_name) __TEST_IMPL(test_name, -1)
+@@ -292,7 +296,7 @@
+ if (!__constructor_order) \
+ __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \
+ } \
+- int main(int argc, char **argv) { return test_harness_run(argc, argv); }
++ int seccomp_test_main(int argc, char **argv) { return test_harness_run(argc, argv); } // ANDROID
+
+ #define _ASSERT_EQ(_expected, _seen) \
+ __EXPECT(_expected, _seen, ==, 1)
diff --git a/tests/tests/os/jni/seccomp-tests/tests/.gitignore b/tests/tests/os/jni/seccomp-tests/tests/.gitignore
new file mode 100644
index 0000000..cdfc7c6
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/.gitignore
@@ -0,0 +1,3 @@
+sigsegv
+resumption
+seccomp_bpf_tests
diff --git a/tests/tests/os/jni/seccomp-tests/tests/Makefile b/tests/tests/os/jni/seccomp-tests/tests/Makefile
new file mode 100644
index 0000000..88994b3
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/Makefile
@@ -0,0 +1,23 @@
+CFLAGS += -Wall
+EXEC=resumption seccomp_bpf_tests sigsegv
+
+all: $(EXEC)
+
+clean:
+ rm -f $(EXEC)
+
+seccomp_bpf_tests: seccomp_bpf_tests.c test_harness.h
+ $(CC) seccomp_bpf_tests.c -o seccomp_bpf_tests $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -pthread
+
+resumption: resumption.c test_harness.h
+ $(CC) $^ -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -ggdb3
+
+sigsegv: sigsegv.c test_harness.h
+ $(CC) $^ -o $@ $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -ggdb3
+
+run_tests: $(EXEC)
+ ./seccomp_bpf_tests
+ ./resumption
+ ./sigsegv
+
+.PHONY: clean run_tests
diff --git a/tests/tests/os/jni/seccomp-tests/tests/resumption.c b/tests/tests/os/jni/seccomp-tests/tests/resumption.c
new file mode 100644
index 0000000..41795c0
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/resumption.c
@@ -0,0 +1,217 @@
+/* seccomp_bpf_tests.c
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test code for seccomp bpf.
+ */
+
+#include <asm/siginfo.h>
+#define __have_siginfo_t 1
+#define __have_sigval_t 1
+#define __have_sigevent_t 1
+
+#include <linux/filter.h>
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+#include <linux/seccomp.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <syscall.h>
+#define __USE_GNU 1
+#include <sys/ucontext.h>
+#include <sys/mman.h>
+
+#include "test_harness.h"
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+
+#if defined(__i386__)
+#define REG_IP REG_EIP
+#define REG_SP REG_ESP
+#define REG_RESULT REG_EAX
+#define REG_SYSCALL REG_EAX
+#define REG_ARG0 REG_EBX
+#define REG_ARG1 REG_ECX
+#define REG_ARG2 REG_EDX
+#define REG_ARG3 REG_ESI
+#define REG_ARG4 REG_EDI
+#define REG_ARG5 REG_EBP
+#elif defined(__x86_64__)
+#define REG_IP REG_RIP
+#define REG_SP REG_RSP
+#define REG_RESULT REG_RAX
+#define REG_SYSCALL REG_RAX
+#define REG_ARG0 REG_RDI
+#define REG_ARG1 REG_RSI
+#define REG_ARG2 REG_RDX
+#define REG_ARG3 REG_R10
+#define REG_ARG4 REG_R8
+#define REG_ARG5 REG_R9
+#endif
+
+FIXTURE_DATA(TRAP) {
+ struct sock_fprog prog;
+};
+
+/* XXX: will need one per arch, etc.
+ * thankfully _arch can tell us the calling convention!
+ */
+extern void *thunk_ip; /* label for the instruction _after_ syscall */
+static void syscall_thunk(void)
+{
+ asm("syscall; thunk_ip:");
+}
+
+static time_t vsyscall_time(time_t *p)
+{
+ register time_t t asm ("rax");
+ __attribute__((unused)) register time_t *p1 asm ("rdi") = p;
+ __asm__("call 0xffffffffff600400 \n");
+ return t;
+}
+
+
+#if 0
+/* For instance, we could jump here instead. */
+static void compat_thunk(void)
+{
+ asm("int 0x80");
+}
+#endif
+
+FIXTURE_SETUP(TRAP) {
+ /* instruction after the syscall. Will be arch specific, of course. */
+ unsigned long thunk_addr = (unsigned long)&thunk_ip;
+ TH_LOG("Thunk: 0x%lX\n", thunk_addr);
+ {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ /* Whitelist anything you might need in the sigaction */
+#ifdef __NR_sigreturn
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 3, 0),
+#endif
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
+ /* Allow __NR_write so easy logging. */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ /* Check if we're within the thunk. */
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+ offsetof(struct seccomp_data, instruction_pointer)),
+ /* XXX: make this 32-bit friendly. */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[0], 0, 3),
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+ offsetof(struct seccomp_data, instruction_pointer)+sizeof(int)),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ((__u32*)&thunk_addr)[1], 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
+ };
+ memset(&self->prog, 0, sizeof(self->prog));
+ self->prog.filter = malloc(sizeof(filter));
+ ASSERT_NE(NULL, self->prog.filter);
+ memcpy(self->prog.filter, filter, sizeof(filter));
+ self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
+ }
+}
+
+FIXTURE_TEARDOWN(TRAP) {
+ if (self->prog.filter)
+ free(self->prog.filter);
+};
+
+struct arch_sigsys {
+ void *_call_addr; /* calling user insn */
+ int _syscall; /* triggering system call number */
+ unsigned int _arch; /* AUDIT_ARCH_* of syscall */
+};
+
+static void TRAP_action(int nr, siginfo_t *info, void *void_context)
+{
+ ucontext_t *ctx = (ucontext_t *)void_context;
+ char buf[256];
+ int len;
+ int do_ret = 1;
+ struct arch_sigsys *sys = (struct arch_sigsys *)
+#ifdef si_syscall
+ &(info->si_call_addr);
+#else
+ &(info->si_pid);
+#endif
+
+ if (info->si_code != SYS_SECCOMP)
+ return;
+ if (!ctx)
+ return;
+ len = snprintf(buf, sizeof(buf),
+ "@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX\n",
+ (unsigned long)sys->_call_addr,
+ sys->_arch,
+ sys->_syscall,
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG5]);
+ /* Send the soft-fail to our "listener" */
+ syscall(__NR_write, STDOUT_FILENO, buf, len);
+ if (ctx->uc_mcontext.gregs[REG_IP] >= 0xffffffffff600000ULL &&
+ ctx->uc_mcontext.gregs[REG_IP] < 0xffffffffff601000ULL)
+ do_ret = 0;
+ if (do_ret) {
+ /* push [REG_IP] */
+ ctx->uc_mcontext.gregs[REG_SP] -= sizeof(unsigned long);
+ *((unsigned long *)ctx->uc_mcontext.gregs[REG_SP]) =
+ ctx->uc_mcontext.gregs[REG_IP];
+ }
+ /* jmp syscall_thunk */
+ ctx->uc_mcontext.gregs[REG_IP] = (unsigned long)syscall_thunk;
+ return;
+}
+
+TEST_F(TRAP, handler) {
+ int ret;
+ struct sigaction act;
+ pid_t pid;
+ sigset_t mask;
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGSYS);
+
+ act.sa_sigaction = &TRAP_action;
+ act.sa_flags = SA_SIGINFO;
+ ret = sigaction(SIGSYS, &act, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigaction failed");
+ }
+ ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigprocmask failed");
+ }
+
+ /* Get the pid to compare against. */
+ pid = getpid();
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
+ ASSERT_EQ(0, ret);
+
+ /* Call anything! */
+ ret = syscall(__NR_getpid);
+ ASSERT_EQ(pid, ret);
+ ret = syscall(__NR_close, 0);
+ ASSERT_EQ(0, ret);
+ ret = syscall(__NR_close, 0);
+ ASSERT_EQ(-1, ret);
+ printf("The time is %ld\n", vsyscall_time(NULL));
+ ASSERT_LT(0, vsyscall_time(NULL));
+}
+
+TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
new file mode 100644
index 0000000..98b0231
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/seccomp_bpf_tests.c
@@ -0,0 +1,1949 @@
+/* seccomp_bpf_tests.c
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Test code for seccomp bpf.
+ */
+
+#include <asm/siginfo.h>
+#define __have_siginfo_t 1
+#define __have_sigval_t 1
+#define __have_sigevent_t 1
+
+#include <errno.h>
+#include <linux/filter.h>
+#include <sys/prctl.h>
+#include <sys/ptrace.h>
+#include <sys/user.h>
+#include <linux/prctl.h>
+#include <linux/ptrace.h>
+#include <linux/seccomp.h>
+#include <poll.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <linux/elf.h>
+#include <sys/uio.h>
+
+#define _GNU_SOURCE
+#include <unistd.h>
+#include <sys/syscall.h>
+
+#include "test_harness.h"
+
+#ifndef PR_SET_PTRACER
+# define PR_SET_PTRACER 0x59616d61
+#endif
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+
+#ifndef PR_SECCOMP_EXT
+#define PR_SECCOMP_EXT 43
+#endif
+
+#ifndef SECCOMP_EXT_ACT
+#define SECCOMP_EXT_ACT 1
+#endif
+
+#ifndef SECCOMP_EXT_ACT_TSYNC
+#define SECCOMP_EXT_ACT_TSYNC 1
+#endif
+
+#ifndef SECCOMP_MODE_STRICT
+#define SECCOMP_MODE_STRICT 1
+#endif
+
+#ifndef SECCOMP_MODE_FILTER
+#define SECCOMP_MODE_FILTER 2
+#endif
+
+#ifndef SECCOMP_RET_KILL
+#define SECCOMP_RET_KILL 0x00000000U // kill the task immediately
+#define SECCOMP_RET_TRAP 0x00030000U // disallow and force a SIGSYS
+#define SECCOMP_RET_ERRNO 0x00050000U // returns an errno
+#define SECCOMP_RET_TRACE 0x7ff00000U // pass to a tracer or disallow
+#define SECCOMP_RET_ALLOW 0x7fff0000U // allow
+
+/* Masks for the return value sections. */
+#define SECCOMP_RET_ACTION 0x7fff0000U
+#define SECCOMP_RET_DATA 0x0000ffffU
+
+struct seccomp_data {
+ int nr;
+ __u32 arch;
+ __u64 instruction_pointer;
+ __u64 args[6];
+};
+#endif
+
+#define syscall_arg(_n) (offsetof(struct seccomp_data, args[_n]))
+
+#define SIBLING_EXIT_UNKILLED 0xbadbeef
+#define SIBLING_EXIT_FAILURE 0xbadface
+#define SIBLING_EXIT_NEWPRIVS 0xbadfeed
+
+TEST(mode_strict_support) {
+ long ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support CONFIG_SECCOMP");
+ }
+ syscall(__NR_exit, 1);
+}
+
+TEST_SIGNAL(mode_strict_cannot_call_prctl, SIGKILL) {
+ long ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, NULL, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support CONFIG_SECCOMP");
+ }
+ syscall(__NR_prctl, PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, NULL, NULL);
+ EXPECT_FALSE(true) {
+ TH_LOG("Unreachable!");
+ }
+}
+
+/* Note! This doesn't test no new privs behavior */
+TEST(no_new_privs_support) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+}
+
+/* Tests kernel support by checking for a copy_from_user() fault on * NULL. */
+TEST(mode_filter_support) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, NULL, NULL, NULL);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EFAULT, errno) {
+ TH_LOG("Kernel does not support CONFIG_SECCOMP_FILTER!");
+ }
+}
+
+TEST(mode_filter_without_nnp) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_GET_NO_NEW_PRIVS, 0, NULL, 0, 0);
+ ASSERT_LE(0, ret) {
+ TH_LOG("Expected 0 or unsupported for NO_NEW_PRIVS");
+ }
+ errno = 0;
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ /* Succeeds with CAP_SYS_ADMIN, fails without */
+ /* TODO(wad) check caps not euid */
+ if (geteuid()) {
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EACCES, errno);
+ } else {
+ EXPECT_EQ(0, ret);
+ }
+}
+
+#define MAX_INSNS_PER_PATH 32768
+
+TEST(filter_size_limits) {
+ int i;
+ int count = BPF_MAXINSNS + 1;
+ struct sock_filter allow[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_filter *filter;
+ struct sock_fprog prog = { };
+
+ filter = calloc(count, sizeof(*filter));
+ ASSERT_NE(NULL, filter);
+
+ for (i = 0; i < count; i++) {
+ filter[i] = allow[0];
+ }
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ prog.filter = filter;
+ prog.len = count;
+
+ /* Too many filter instructions in a single filter. */
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ ASSERT_NE(0, ret) {
+ TH_LOG("Installing %d insn filter was allowed", prog.len);
+ }
+
+ /* One less is okay, though. */
+ prog.len -= 1;
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Installing %d insn filter wasn't allowed", prog.len);
+ }
+}
+
+TEST(filter_chain_limits) {
+ int i;
+ int count = BPF_MAXINSNS;
+ struct sock_filter allow[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_filter *filter;
+ struct sock_fprog prog = { };
+
+ filter = calloc(count, sizeof(*filter));
+ ASSERT_NE(NULL, filter);
+
+ for (i = 0; i < count; i++) {
+ filter[i] = allow[0];
+ }
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ prog.filter = filter;
+ prog.len = 1;
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ prog.len = count;
+
+ /* Too many total filter instructions. */
+ for (i = 0; i < MAX_INSNS_PER_PATH; i++) {
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ if (ret != 0)
+ break;
+ }
+ ASSERT_NE(0, ret) {
+ TH_LOG("Allowed %d %d-insn filters (total with penalties:%d)",
+ i, count, i * (count + 4));
+ }
+}
+
+TEST(mode_filter_cannot_move_to_strict) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, NULL, 0, 0);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+}
+
+
+TEST(mode_filter_get_seccomp) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
+ EXPECT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_GET_SECCOMP, 0, 0, 0, 0);
+ EXPECT_EQ(2, ret);
+}
+
+
+TEST(ALLOW_all) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+}
+
+TEST(empty_prog) {
+ struct sock_filter filter[] = {
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+}
+
+TEST_SIGNAL(unknown_ret_is_kill_inside, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, 0x10000000U),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+ EXPECT_EQ(0, syscall(__NR_getpid)) {
+ TH_LOG("getpid() shouldn't ever return");
+ }
+}
+
+/* return code >= 0x80000000 is unused. */
+TEST_SIGNAL(unknown_ret_is_kill_above_allow, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, 0x90000000U),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+ EXPECT_EQ(0, syscall(__NR_getpid)) {
+ TH_LOG("getpid() shouldn't ever return");
+ }
+}
+
+TEST_SIGNAL(KILL_all, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+}
+
+TEST_SIGNAL(KILL_one, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_SIGNAL(KILL_one_arg_one, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ /* Only both with lower 32-bit for now. */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(0)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ pid_t pid = getpid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ EXPECT_EQ(pid, syscall(__NR_getpid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid, 0x0C0FFEE));
+}
+
+TEST_SIGNAL(KILL_one_arg_six, SIGSYS) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ /* Only both with lower 32-bit for now. */
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(5)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, 0x0C0FFEE, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ pid_t pid = getpid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ EXPECT_EQ(pid, syscall(__NR_getpid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid, 1, 2, 3, 4, 5, 0x0C0FFEE));
+}
+
+/* TODO(wad) add 64-bit versus 32-bit arg tests. */
+
+TEST(arg_out_of_range) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS, syscall_arg(6)),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(EINVAL, errno);
+}
+
+TEST(ERRNO_one) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | E2BIG),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ EXPECT_EQ(-1, read(0, NULL, 0));
+ EXPECT_EQ(E2BIG, errno);
+}
+
+TEST(ERRNO_one_ok) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* "errno" of 0 is ok. */
+ EXPECT_EQ(0, read(0, NULL, 0));
+}
+
+FIXTURE_DATA(TRAP) {
+ struct sock_fprog prog;
+};
+
+FIXTURE_SETUP(TRAP) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ memset(&self->prog, 0, sizeof(self->prog));
+ self->prog.filter = malloc(sizeof(filter));
+ ASSERT_NE(NULL, self->prog.filter);
+ memcpy(self->prog.filter, filter, sizeof(filter));
+ self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
+}
+
+FIXTURE_TEARDOWN(TRAP) {
+ if (self->prog.filter)
+ free(self->prog.filter);
+};
+
+TEST_F_SIGNAL(TRAP, dfl, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
+ ASSERT_EQ(0, ret);
+ syscall(__NR_getpid);
+}
+
+/* Ensure that SIGSYS overrides SIG_IGN */
+TEST_F_SIGNAL(TRAP, ign, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ signal(SIGSYS, SIG_IGN);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
+ ASSERT_EQ(0, ret);
+ syscall(__NR_getpid);
+}
+
+static struct siginfo TRAP_info;
+static volatile int TRAP_nr;
+static void TRAP_action(int nr, siginfo_t *info, void *void_context)
+{
+ memcpy(&TRAP_info, info, sizeof(TRAP_info));
+ TRAP_nr = nr;
+ return;
+}
+
+TEST_F(TRAP, handler) {
+ int ret, test;
+ struct sigaction act;
+ sigset_t mask;
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGSYS);
+
+ act.sa_sigaction = &TRAP_action;
+ act.sa_flags = SA_SIGINFO;
+ ret = sigaction(SIGSYS, &act, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigaction failed");
+ }
+ ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigprocmask failed");
+ }
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
+ ASSERT_EQ(0, ret);
+ TRAP_nr = 0;
+ memset(&TRAP_info, 0, sizeof(TRAP_info));
+ /* Expect the registers to be rolled back. (nr = error) may vary
+ * based on arch. */
+ ret = syscall(__NR_getpid);
+ /* Silence gcc warning about volatile. */
+ test = TRAP_nr;
+ EXPECT_EQ(SIGSYS, test);
+ struct local_sigsys {
+ void *_call_addr; /* calling user insn */
+ int _syscall; /* triggering system call number */
+ unsigned int _arch; /* AUDIT_ARCH_* of syscall */
+ } *sigsys = (struct local_sigsys *)
+#ifdef si_syscall
+ &(TRAP_info.si_call_addr);
+#else
+ &TRAP_info.si_pid;
+#endif
+ EXPECT_EQ(__NR_getpid, sigsys->_syscall);
+ /* Make sure arch is non-zero. */
+ EXPECT_NE(0, sigsys->_arch);
+ EXPECT_NE(0, (unsigned long)sigsys->_call_addr);
+}
+
+FIXTURE_DATA(precedence) {
+ struct sock_fprog allow;
+ struct sock_fprog trace;
+ struct sock_fprog error;
+ struct sock_fprog trap;
+ struct sock_fprog kill;
+};
+
+FIXTURE_SETUP(precedence) {
+ struct sock_filter allow_insns[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_filter trace_insns[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE),
+ };
+ struct sock_filter error_insns[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO),
+ };
+ struct sock_filter trap_insns[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRAP),
+ };
+ struct sock_filter kill_insns[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 1, 0),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ };
+ memset(self, 0, sizeof(*self));
+#define FILTER_ALLOC(_x) \
+ self->_x.filter = malloc(sizeof(_x##_insns)); \
+ ASSERT_NE(NULL, self->_x.filter); \
+ memcpy(self->_x.filter, &_x##_insns, sizeof(_x##_insns)); \
+ self->_x.len = (unsigned short)(sizeof(_x##_insns)/sizeof(_x##_insns[0]))
+ FILTER_ALLOC(allow);
+ FILTER_ALLOC(trace);
+ FILTER_ALLOC(error);
+ FILTER_ALLOC(trap);
+ FILTER_ALLOC(kill);
+}
+
+FIXTURE_TEARDOWN(precedence) {
+#define FILTER_FREE(_x) if (self->_x.filter) free(self->_x.filter)
+ FILTER_FREE(allow);
+ FILTER_FREE(trace);
+ FILTER_FREE(error);
+ FILTER_FREE(trap);
+ FILTER_FREE(kill);
+}
+
+TEST_F(precedence, allow_ok) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ pid_t res = 0;
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ res = syscall(__NR_getppid);
+ EXPECT_EQ(parent, res);
+}
+
+TEST_F_SIGNAL(precedence, kill_is_highest, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ pid_t res = 0;
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ res = syscall(__NR_getppid);
+ EXPECT_EQ(parent, res);
+ /* getpid() should never return. */
+ res = syscall(__NR_getpid);
+ EXPECT_EQ(0, res);
+}
+
+TEST_F_SIGNAL(precedence, kill_is_highest_in_any_order, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->kill);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_F_SIGNAL(precedence, trap_is_second, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_F_SIGNAL(precedence, trap_is_second_in_any_order, SIGSYS) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trap);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* getpid() should never return. */
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_F(precedence, errno_is_third) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_F(precedence, errno_is_third_in_any_order) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->error);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ EXPECT_EQ(0, syscall(__NR_getpid));
+}
+
+TEST_F(precedence, trace_is_fourth) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* No ptracer */
+ EXPECT_EQ(-1, syscall(__NR_getpid));
+}
+
+TEST_F(precedence, trace_is_fourth_in_any_order) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ pid_t parent = getppid();
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->trace);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->allow);
+ ASSERT_EQ(0, ret);
+ /* Should work just fine. */
+ EXPECT_EQ(parent, syscall(__NR_getppid));
+ /* No ptracer */
+ EXPECT_EQ(-1, syscall(__NR_getpid));
+}
+
+#ifndef PTRACE_O_TRACESECCOMP
+#define PTRACE_O_TRACESECCOMP 0x00000080
+#endif
+
+/* Catch the Ubuntu 12.04 value error. */
+#if PTRACE_EVENT_SECCOMP != 7
+#undef PTRACE_EVENT_SECCOMP
+#endif
+
+#ifndef PTRACE_EVENT_SECCOMP
+#define PTRACE_EVENT_SECCOMP 7
+#endif
+
+#define IS_SECCOMP_EVENT(status) ((status >> 16) == PTRACE_EVENT_SECCOMP)
+bool tracer_running;
+void tracer_stop(int sig)
+{
+ tracer_running = false;
+}
+
+typedef void tracer_func_t(struct __test_metadata *_metadata,
+ pid_t tracee, int status, void *args);
+
+void tracer(struct __test_metadata *_metadata, int fd, pid_t tracee,
+ tracer_func_t tracer_func, void *args) {
+ int ret = -1;
+ struct sigaction action = {
+ .sa_handler = tracer_stop,
+ };
+
+ /* Allow external shutdown. */
+ tracer_running = true;
+ ASSERT_EQ(0, sigaction(SIGUSR1, &action, NULL));
+
+ errno = 0;
+ while (ret == -1 && errno != EINVAL) {
+ ret = ptrace(PTRACE_ATTACH, tracee, NULL, 0);
+ }
+ ASSERT_EQ(0, ret) {
+ kill(tracee, SIGKILL);
+ }
+ /* Wait for attach stop */
+ wait(NULL);
+
+ ret = ptrace(PTRACE_SETOPTIONS, tracee, NULL, PTRACE_O_TRACESECCOMP);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Failed to set PTRACE_O_TRACESECCOMP");
+ kill(tracee, SIGKILL);
+ }
+ ptrace(PTRACE_CONT, tracee, NULL, 0);
+
+ /* Unblock the tracee */
+ ASSERT_EQ(1, write(fd, "A", 1));
+ ASSERT_EQ(0, close(fd));
+
+ /* Run until we're shut down. Must assert to stop execution. */
+ while (tracer_running) {
+ int status;
+ if (wait(&status) != tracee)
+ continue;
+ if (WIFSIGNALED(status) || WIFEXITED(status))
+ /* Child is dead. Time to go. */
+ return;
+
+ /* Make sure this is a seccomp event. */
+ ASSERT_EQ(true, IS_SECCOMP_EVENT(status));
+
+ tracer_func(_metadata, tracee, status, args);
+
+ ret = ptrace(PTRACE_CONT, tracee, NULL, NULL);
+ ASSERT_EQ(0, ret);
+ }
+ /* Directly report the status of our test harness results. */
+ syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+/* Common tracer setup/teardown functions. */
+void cont_handler(int num) {
+}
+pid_t setup_trace_fixture(struct __test_metadata *_metadata,
+ tracer_func_t func, void *args) {
+ char sync;
+ int pipefd[2];
+ pid_t tracer_pid;
+ pid_t tracee = getpid();
+
+ /* Setup a pipe for clean synchronization. */
+ ASSERT_EQ(0, pipe(pipefd));
+
+ /* Fork a child which we'll promote to tracer */
+ tracer_pid = fork();
+ ASSERT_LE(0, tracer_pid);
+ signal(SIGALRM, cont_handler);
+ if (tracer_pid == 0) {
+ close(pipefd[0]);
+ tracer(_metadata, pipefd[1], tracee, func, args);
+ syscall(__NR_exit, 0);
+ }
+ close(pipefd[1]);
+ prctl(PR_SET_PTRACER, tracer_pid, 0, 0, 0);
+ read(pipefd[0], &sync, 1);
+ close(pipefd[0]);
+
+ return tracer_pid;
+}
+void teardown_trace_fixture(struct __test_metadata *_metadata,
+ pid_t tracer) {
+ if (tracer) {
+ int status;
+ /*
+ * Extract the exit code from the other process and
+ * adopt it for ourselves in case its asserts failed.
+ */
+ ASSERT_EQ(0, kill(tracer, SIGUSR1));
+ ASSERT_EQ(tracer, waitpid(tracer, &status, 0));
+ if (WEXITSTATUS(status))
+ _metadata->passed = 0;
+ }
+}
+
+/* "poke" tracer arguments and function. */
+struct tracer_args_poke_t {
+ unsigned long poke_addr;
+};
+
+void tracer_poke(struct __test_metadata *_metadata, pid_t tracee, int status,
+ void *args) {
+ int ret;
+ unsigned long msg;
+ struct tracer_args_poke_t *info = (struct tracer_args_poke_t *)args;
+
+ ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
+ EXPECT_EQ(0, ret);
+ /* If this fails, don't try to recover. */
+ ASSERT_EQ(0x1001, msg) {
+ kill(tracee, SIGKILL);
+ }
+ /*
+ * Poke in the message.
+ * Registers are not touched to try to keep this relatively arch
+ * agnostic.
+ */
+ ret = ptrace(PTRACE_POKEDATA, tracee, info->poke_addr, 0x1001);
+ EXPECT_EQ(0, ret);
+}
+
+FIXTURE_DATA(TRACE_poke) {
+ struct sock_fprog prog;
+ pid_t tracer;
+ long poked;
+ struct tracer_args_poke_t tracer_args;
+};
+
+FIXTURE_SETUP(TRACE_poke) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1001),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ self->poked = 0;
+ memset(&self->prog, 0, sizeof(self->prog));
+ self->prog.filter = malloc(sizeof(filter));
+ ASSERT_NE(NULL, self->prog.filter);
+ memcpy(self->prog.filter, filter, sizeof(filter));
+ self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
+
+ /* Set up tracer args. */
+ self->tracer_args.poke_addr = (unsigned long)&self->poked;
+
+ /* Launch tracer. */
+ self->tracer = setup_trace_fixture(_metadata, tracer_poke,
+ &self->tracer_args);
+}
+
+FIXTURE_TEARDOWN(TRACE_poke) {
+ teardown_trace_fixture(_metadata, self->tracer);
+ if (self->prog.filter)
+ free(self->prog.filter);
+};
+
+TEST_F(TRACE_poke, read_has_side_effects) {
+ ssize_t ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(0, self->poked);
+ ret = read(-1, NULL, 0);
+ EXPECT_EQ(-1, ret);
+ EXPECT_EQ(0x1001, self->poked);
+}
+
+TEST_F(TRACE_poke, getpid_runs_normally) {
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ EXPECT_EQ(0, self->poked);
+ EXPECT_NE(0, syscall(__NR_getpid));
+ EXPECT_EQ(0, self->poked);
+}
+
+#if defined(__x86_64__)
+# define ARCH_REGS struct user_regs_struct
+# define SYSCALL_NUM orig_rax
+# define SYSCALL_RET rax
+#elif defined(__i386__)
+# define ARCH_REGS struct user_regs_struct
+# define SYSCALL_NUM orig_eax
+# define SYSCALL_RET eax
+#elif defined(__arm__)
+# define ARCH_REGS struct pt_regs
+# define SYSCALL_NUM ARM_r7
+# define SYSCALL_RET ARM_r0
+#elif defined(__aarch64__)
+# define ARCH_REGS struct user_pt_regs
+# define SYSCALL_NUM regs[8]
+# define SYSCALL_RET regs[0]
+#else
+# error "Do not know how to find your architecture's registers and syscalls"
+#endif
+
+/* Architecture-specific syscall fetching routine. */
+int get_syscall(struct __test_metadata *_metadata, pid_t tracee) {
+ struct iovec iov;
+ ARCH_REGS regs;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ EXPECT_EQ(0, ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov)) {
+ TH_LOG("PTRACE_GETREGSET failed");
+ return -1;
+ }
+
+ return regs.SYSCALL_NUM;
+}
+
+/* Architecture-specific syscall changing routine. */
+void change_syscall(struct __test_metadata *_metadata,
+ pid_t tracee, int syscall) {
+ struct iovec iov;
+ int ret;
+ ARCH_REGS regs;
+
+ iov.iov_base = ®s;
+ iov.iov_len = sizeof(regs);
+ ret = ptrace(PTRACE_GETREGSET, tracee, NT_PRSTATUS, &iov);
+ EXPECT_EQ(0, ret);
+
+#if defined(__x86_64__) || defined(__i386__) || defined(__aarch64__)
+ {
+ regs.SYSCALL_NUM = syscall;
+ }
+
+#elif defined(__arm__)
+# ifndef PTRACE_SET_SYSCALL
+# define PTRACE_SET_SYSCALL 23
+# endif
+ {
+ ret = ptrace(PTRACE_SET_SYSCALL, tracee, NULL, syscall);
+ EXPECT_EQ(0, ret);
+ }
+
+#else
+ ASSERT_EQ(1, 0) {
+ TH_LOG("How is the syscall changed on this architecture?");
+ }
+#endif
+
+ /* If syscall is skipped, change return value. */
+ if (syscall == -1)
+ regs.SYSCALL_RET = 1;
+
+ ret = ptrace(PTRACE_SETREGSET, tracee, NT_PRSTATUS, &iov);
+ EXPECT_EQ(0, ret);
+}
+
+void tracer_syscall(struct __test_metadata *_metadata, pid_t tracee,
+ int status, void *args) {
+ int ret;
+ unsigned long msg;
+
+ /* Make sure we got the right message. */
+ ret = ptrace(PTRACE_GETEVENTMSG, tracee, NULL, &msg);
+ EXPECT_EQ(0, ret);
+
+ switch (msg) {
+ case 0x1002:
+ /* change getpid to getppid. */
+ change_syscall(_metadata, tracee, __NR_getppid);
+ break;
+ case 0x1003:
+ /* skip gettid. */
+ change_syscall(_metadata, tracee, -1);
+ break;
+ case 0x1004:
+ /* do nothing (allow getppid) */
+ break;
+ default:
+ EXPECT_EQ(0, msg) {
+ TH_LOG("Unknown PTRACE_GETEVENTMSG: 0x%lx", msg);
+ kill(tracee, SIGKILL);
+ }
+ }
+
+}
+
+FIXTURE_DATA(TRACE_syscall) {
+ struct sock_fprog prog;
+ pid_t tracer, mytid, mypid, parent;
+};
+
+FIXTURE_SETUP(TRACE_syscall) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getpid, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1002),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_gettid, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1003),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_getppid, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE | 0x1004),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+
+ memset(&self->prog, 0, sizeof(self->prog));
+ self->prog.filter = malloc(sizeof(filter));
+ ASSERT_NE(NULL, self->prog.filter);
+ memcpy(self->prog.filter, filter, sizeof(filter));
+ self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
+
+ /* Prepare some testable syscall results. */
+ self->mytid = syscall(__NR_gettid);
+ ASSERT_GT(self->mytid, 0);
+ ASSERT_NE(self->mytid, 1) {
+ TH_LOG("Running this test as init is not supported. :)");
+ }
+
+ self->mypid = getpid();
+ ASSERT_GT(self->mypid, 0);
+ ASSERT_EQ(self->mytid, self->mypid);
+
+ self->parent = getppid();
+ ASSERT_GT(self->parent, 0);
+ ASSERT_NE(self->parent, self->mypid);
+
+ /* Launch tracer. */
+ self->tracer = setup_trace_fixture(_metadata, tracer_syscall, NULL);
+}
+
+FIXTURE_TEARDOWN(TRACE_syscall) {
+ teardown_trace_fixture(_metadata, self->tracer);
+ if (self->prog.filter)
+ free(self->prog.filter);
+};
+
+TEST_F(TRACE_syscall, syscall_allowed) {
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ /* getppid works as expected (no changes). */
+ EXPECT_EQ(self->parent, syscall(__NR_getppid));
+ EXPECT_NE(self->mypid, syscall(__NR_getppid));
+}
+
+TEST_F(TRACE_syscall, syscall_redirected) {
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ /* getpid has been redirected to getppid as expected. */
+ EXPECT_EQ(self->parent, syscall(__NR_getpid));
+ EXPECT_NE(self->mypid, syscall(__NR_getpid));
+}
+
+TEST_F(TRACE_syscall, syscall_dropped) {
+ long ret;
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog, 0, 0);
+ ASSERT_EQ(0, ret);
+
+ /* gettid has been skipped and an altered return value stored. */
+ EXPECT_EQ(1, syscall(__NR_gettid));
+ EXPECT_NE(self->mytid, syscall(__NR_gettid));
+}
+
+#ifndef __NR_seccomp
+# if defined(__i386__)
+# define __NR_seccomp 354
+# elif defined(__x86_64__)
+# define __NR_seccomp 317
+# elif defined(__arm__)
+# define __NR_seccomp 383
+# elif defined(__aarch64__)
+# define __NR_seccomp 277
+# else
+# warning "seccomp syscall number unknown for this architecture"
+# define __NR_seccomp 0xffff
+# endif
+#endif
+
+#ifndef SECCOMP_SET_MODE_STRICT
+#define SECCOMP_SET_MODE_STRICT 0
+#endif
+
+#ifndef SECCOMP_SET_MODE_FILTER
+#define SECCOMP_SET_MODE_FILTER 1
+#endif
+
+#ifndef SECCOMP_FLAG_FILTER_TSYNC
+#define SECCOMP_FLAG_FILTER_TSYNC 1
+#endif
+
+#ifndef seccomp
+int seccomp(unsigned int op, unsigned int flags, struct sock_fprog *filter)
+{
+ errno = 0;
+ return syscall(__NR_seccomp, op, flags, filter);
+}
+#endif
+
+TEST(seccomp_syscall) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* Reject insane operation. */
+ ret = seccomp(-1, 0, &prog);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Did not reject crazy op value!");
+ }
+
+ /* Reject strict with flags or pointer. */
+ ret = seccomp(SECCOMP_SET_MODE_STRICT, -1, NULL);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Did not reject mode strict with flags!");
+ }
+ ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, &prog);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Did not reject mode strict with uargs!");
+ }
+
+ /* Reject insane args for filter. */
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, -1, &prog);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Did not reject crazy filter flags!");
+ }
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, NULL);
+ EXPECT_EQ(EFAULT, errno) {
+ TH_LOG("Did not reject NULL filter!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
+ EXPECT_EQ(0, errno) {
+ TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER: %s",
+ strerror(errno));
+ }
+}
+
+TEST(seccomp_syscall_mode_lock) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("Could not install filter!");
+ }
+
+ /* Make sure neither entry point will switch to strict. */
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_STRICT, 0, 0, 0);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Switched to mode strict!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_STRICT, 0, NULL);
+ EXPECT_EQ(EINVAL, errno) {
+ TH_LOG("Switched to mode strict!");
+ }
+}
+
+TEST(TSYNC_first) {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+ long ret = prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &prog);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("Could not install initial filter with TSYNC!");
+ }
+}
+
+#define TSYNC_SIBLINGS 2
+struct tsync_sibling {
+ pthread_t tid;
+ pid_t system_tid;
+ sem_t *started;
+ pthread_cond_t *cond;
+ pthread_mutex_t *mutex;
+ int diverge;
+ int num_waits;
+ struct sock_fprog *prog;
+ struct __test_metadata *metadata;
+};
+
+FIXTURE_DATA(TSYNC) {
+ struct sock_fprog root_prog, apply_prog;
+ struct tsync_sibling sibling[TSYNC_SIBLINGS];
+ sem_t started;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+ int sibling_count;
+};
+
+FIXTURE_SETUP(TSYNC) {
+ struct sock_filter root_filter[] = {
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_filter apply_filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ memset(&self->root_prog, 0, sizeof(self->root_prog));
+ memset(&self->apply_prog, 0, sizeof(self->apply_prog));
+ memset(&self->sibling, 0, sizeof(self->sibling));
+ self->root_prog.filter = malloc(sizeof(root_filter));
+ ASSERT_NE(NULL, self->root_prog.filter);
+ memcpy(self->root_prog.filter, &root_filter, sizeof(root_filter));
+ self->root_prog.len = (unsigned short)(sizeof(root_filter)/sizeof(root_filter[0]));
+
+ self->apply_prog.filter = malloc(sizeof(apply_filter));
+ ASSERT_NE(NULL, self->apply_prog.filter);
+ memcpy(self->apply_prog.filter, &apply_filter, sizeof(apply_filter));
+ self->apply_prog.len = (unsigned short)(sizeof(apply_filter)/sizeof(apply_filter[0]));
+
+ self->sibling_count = 0;
+ pthread_mutex_init(&self->mutex, NULL);
+ pthread_cond_init(&self->cond, NULL);
+ sem_init(&self->started, 0, 0);
+ self->sibling[0].tid = 0;
+ self->sibling[0].cond = &self->cond;
+ self->sibling[0].started = &self->started;
+ self->sibling[0].mutex = &self->mutex;
+ self->sibling[0].diverge = 0;
+ self->sibling[0].num_waits = 1;
+ self->sibling[0].prog = &self->root_prog;
+ self->sibling[0].metadata = _metadata;
+ self->sibling[1].tid = 0;
+ self->sibling[1].cond = &self->cond;
+ self->sibling[1].started = &self->started;
+ self->sibling[1].mutex = &self->mutex;
+ self->sibling[1].diverge = 0;
+ self->sibling[1].prog = &self->root_prog;
+ self->sibling[1].num_waits = 1;
+ self->sibling[1].metadata = _metadata;
+}
+
+FIXTURE_TEARDOWN(TSYNC) {
+ int sib = 0;
+ if (self->root_prog.filter)
+ free(self->root_prog.filter);
+ if (self->apply_prog.filter)
+ free(self->apply_prog.filter);
+
+ for ( ; sib < self->sibling_count; ++sib) {
+ struct tsync_sibling *s = &self->sibling[sib];
+ void *status;
+ if (!s->tid)
+ continue;
+ if (pthread_kill(s->tid, 0)) {
+ //pthread_cancel(s->tid); // ANDROID
+ pthread_join(s->tid, &status);
+ }
+ }
+ pthread_mutex_destroy(&self->mutex);
+ pthread_cond_destroy(&self->cond);
+ sem_destroy(&self->started);
+};
+
+void *tsync_sibling(void *data)
+{
+ long ret = 0;
+ struct tsync_sibling *me = data;
+ me->system_tid = syscall(__NR_gettid);
+
+ pthread_mutex_lock(me->mutex);
+ if (me->diverge) {
+ /* Just re-apply the root prog to fork the tree */
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER,
+ me->prog, 0, 0);
+ }
+ sem_post(me->started);
+ /* Return outside of started so parent notices failures. */
+ if (ret) {
+ pthread_mutex_unlock(me->mutex);
+ return (void *)SIBLING_EXIT_FAILURE;
+ }
+ do {
+ pthread_cond_wait(me->cond, me->mutex);
+ me->num_waits = me->num_waits - 1;
+ }
+ while (me->num_waits);
+ pthread_mutex_unlock(me->mutex);
+ long nnp = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
+ if (!nnp)
+ return (void*)SIBLING_EXIT_NEWPRIVS;
+ read(0, NULL, 0);
+ return (void *)SIBLING_EXIT_UNKILLED;
+}
+
+void tsync_start_sibling(struct tsync_sibling *sibling)
+{
+ pthread_create(&sibling->tid, NULL, tsync_sibling, (void *)sibling);
+}
+
+TEST_F(TSYNC, siblings_fail_prctl) {
+ long ret;
+ void *status;
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_prctl, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ERRNO | EINVAL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /* Check prctl failure detection by requesting sib 0 diverge. */
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("setting filter failed");
+ }
+
+ self->sibling[0].diverge = 1;
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ /* Signal the threads to clean up*/
+ pthread_mutex_lock(&self->mutex);
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+
+ /* Ensure diverging sibling failed to call prctl. */
+ pthread_join(self->sibling[0].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_FAILURE, (long)status);
+ pthread_join(self->sibling[1].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
+}
+
+TEST_F(TSYNC, two_siblings_with_ancestor) {
+ long ret;
+ void *status;
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
+ }
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Could install filter on all threads!");
+ }
+ /* Tell the siblings to test the policy */
+ pthread_mutex_lock(&self->mutex);
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+ /* Ensure they are both killed and don't exit cleanly. */
+ pthread_join(self->sibling[0].tid, &status);
+ EXPECT_EQ(0x0, (long)status);
+ pthread_join(self->sibling[1].tid, &status);
+ EXPECT_EQ(0x0, (long)status);
+}
+
+TEST_F(TSYNC, two_sibling_want_nnp) {
+ void *status;
+
+ /* start siblings before any prctl() operations */
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ /* Tell the siblings to test no policy */
+ pthread_mutex_lock(&self->mutex);
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+
+ /* Ensure they are both upset about lacking nnp. */
+ pthread_join(self->sibling[0].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status);
+ pthread_join(self->sibling[1].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_NEWPRIVS, (long)status);
+}
+
+TEST_F(TSYNC, two_siblings_with_no_filter) {
+ long ret;
+ void *status;
+
+ /* start siblings before any prctl() operations */
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Could install filter on all threads!");
+ }
+
+ /* Tell the siblings to test the policy */
+ pthread_mutex_lock(&self->mutex);
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+
+ /* Ensure they are both killed and don't exit cleanly. */
+ pthread_join(self->sibling[0].tid, &status);
+ EXPECT_EQ(0x0, (long)status);
+ pthread_join(self->sibling[1].tid, &status);
+ EXPECT_EQ(0x0, (long)status);
+}
+
+TEST_F(TSYNC, two_siblings_with_one_divergence) {
+ long ret;
+ void *status;
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
+ }
+ self->sibling[0].diverge = 1;
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(self->sibling[0].system_tid, ret) {
+ TH_LOG("Did not fail on diverged sibling.");
+ }
+
+ /* Wake the threads */
+ pthread_mutex_lock(&self->mutex);
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+
+ /* Ensure they are both unkilled. */
+ pthread_join(self->sibling[0].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
+ pthread_join(self->sibling[1].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
+}
+
+TEST_F(TSYNC, two_siblings_not_under_filter) {
+ long ret, sib;
+ void *status;
+
+ ASSERT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ /*
+ * Sibling 0 will have its own seccomp policy
+ * and Sibling 1 will not be under seccomp at
+ * all. Sibling 1 will enter seccomp and 0
+ * will cause failure.
+ */
+ self->sibling[0].diverge = 1;
+ tsync_start_sibling(&self->sibling[0]);
+ tsync_start_sibling(&self->sibling[1]);
+
+ while (self->sibling_count < TSYNC_SIBLINGS) {
+ sem_wait(&self->started);
+ self->sibling_count++;
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, 0, &self->root_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Kernel does not support SECCOMP_SET_MODE_FILTER!");
+ }
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(ret, self->sibling[0].system_tid) {
+ TH_LOG("Did not fail on diverged sibling.");
+ }
+ sib = 1;
+ if (ret == self->sibling[0].system_tid)
+ sib = 0;
+
+ pthread_mutex_lock(&self->mutex);
+
+ /* Increment the other siblings num_waits so we can clean up
+ * the one we just saw.
+ */
+ self->sibling[!sib].num_waits += 1;
+
+ /* Signal the thread to clean up*/
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+ pthread_join(self->sibling[sib].tid, &status);
+ EXPECT_EQ(SIBLING_EXIT_UNKILLED, (long)status);
+ /* Poll for actual task death. pthread_join doesn't guarantee it. */
+ while (!kill(self->sibling[sib].system_tid, 0)) sleep(0.1);
+ /* Switch to the remaining sibling */
+ sib = !sib;
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("Expected the remaining sibling to sync");
+ };
+
+ pthread_mutex_lock(&self->mutex);
+
+ /* If remaining sibling didn't have a chance to wake up during
+ * the first broadcast, manually reduce the num_waits now.
+ */
+ if (self->sibling[sib].num_waits > 1)
+ self->sibling[sib].num_waits = 1;
+ ASSERT_EQ(0, pthread_cond_broadcast(&self->cond)) {
+ TH_LOG("cond broadcast non-zero");
+ }
+ pthread_mutex_unlock(&self->mutex);
+ pthread_join(self->sibling[sib].tid, &status);
+ EXPECT_EQ(0, (long)status);
+ /* Poll for actual task death. pthread_join doesn't guarantee it. */
+ while (!kill(self->sibling[sib].system_tid, 0)) sleep(0.1);
+
+ ret = seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FLAG_FILTER_TSYNC,
+ &self->apply_prog);
+ ASSERT_EQ(0, ret); /* just us chickens */
+}
+
+/* Make sure restarted syscalls are seen directly as "restart_syscall". */
+TEST(syscall_restart) {
+ long ret;
+ unsigned long msg;
+ pid_t child_pid;
+ int pipefd[2];
+ int status;
+ siginfo_t info = { };
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD|BPF_W|BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+
+#ifdef __NR_sigreturn
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_sigreturn, 6, 0),
+#endif
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_read, 5, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_exit, 4, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_rt_sigreturn, 3, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_poll, 4, 0),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_restart_syscall, 4, 0),
+
+ /* Allow __NR_write for easy logging. */
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, __NR_write, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_KILL),
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x100), /* poll */
+ BPF_STMT(BPF_RET|BPF_K, SECCOMP_RET_TRACE|0x200), /* restart */
+ };
+ struct sock_fprog prog = {
+ .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
+ .filter = filter,
+ };
+
+ ASSERT_EQ(0, pipe(pipefd));
+
+ child_pid = fork();
+ ASSERT_LE(0, child_pid);
+ if (child_pid == 0) {
+ /* Child uses EXPECT not ASSERT to deliver status correctly. */
+ char buf = ' ';
+ struct pollfd fds = {
+ .fd = pipefd[0],
+ .events = POLLIN,
+ };
+
+ /* Attach parent as tracer and stop. */
+ EXPECT_EQ(0, ptrace(PTRACE_TRACEME));
+ EXPECT_EQ(0, raise(SIGSTOP));
+
+ EXPECT_EQ(0, close(pipefd[1]));
+
+ EXPECT_EQ(0, prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+ TH_LOG("Kernel does not support PR_SET_NO_NEW_PRIVS!");
+ }
+
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog, 0, 0);
+ EXPECT_EQ(0, ret) {
+ TH_LOG("Failed to install filter!");
+ }
+
+ EXPECT_EQ(1, read(pipefd[0], &buf, 1)) {
+ TH_LOG("Failed to read() sync from parent");
+ }
+ EXPECT_EQ('.', buf) {
+ TH_LOG("Failed to get sync data from read()");
+ }
+
+ /* Start poll to be interrupted. */
+ errno = 0;
+ EXPECT_EQ(1, poll(&fds, 1, -1)) {
+ TH_LOG("Call to poll() failed (errno %d)", errno);
+ }
+
+ /* Read final sync from parent. */
+ EXPECT_EQ(1, read(pipefd[0], &buf, 1)) {
+ TH_LOG("Failed final read() from parent");
+ }
+ EXPECT_EQ('!', buf) {
+ TH_LOG("Failed to get final data from read()");
+ }
+
+ /* Directly report the status of our test harness results. */
+ syscall(__NR_exit, _metadata->passed ? EXIT_SUCCESS
+ : EXIT_FAILURE);
+ }
+ EXPECT_EQ(0, close(pipefd[0]));
+
+ /* Attach to child, setup options, and release. */
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(0, ptrace(PTRACE_SETOPTIONS, child_pid, NULL,
+ PTRACE_O_TRACESECCOMP));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
+ ASSERT_EQ(1, write(pipefd[1], ".", 1));
+
+ /* Wait for poll() to start. */
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
+ ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
+ ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
+ ASSERT_EQ(0x100, msg);
+ EXPECT_EQ(__NR_poll, get_syscall(_metadata, child_pid));
+
+ /* Might as well check siginfo for sanity while we're here. */
+ ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
+ ASSERT_EQ(SIGTRAP, info.si_signo);
+ ASSERT_EQ(SIGTRAP | (PTRACE_EVENT_SECCOMP << 8), info.si_code);
+ EXPECT_EQ(0, info.si_errno);
+ EXPECT_EQ(getuid(), info.si_uid);
+ /* Verify signal delivery came from child (seccomp-triggered). */
+ EXPECT_EQ(child_pid, info.si_pid);
+
+ /* Interrupt poll with SIGSTOP (which we'll need to handle). */
+ ASSERT_EQ(0, kill(child_pid, SIGSTOP));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(SIGSTOP, WSTOPSIG(status));
+ /* Verify signal delivery came from parent now. */
+ ASSERT_EQ(0, ptrace(PTRACE_GETSIGINFO, child_pid, NULL, &info));
+ EXPECT_EQ(getpid(), info.si_pid);
+
+ /* Restart poll with SIGCONT, which triggers restart_syscall. */
+ ASSERT_EQ(0, kill(child_pid, SIGCONT));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(SIGCONT, WSTOPSIG(status));
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
+
+ /* Wait for restart_syscall() to start. */
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ ASSERT_EQ(true, WIFSTOPPED(status));
+ ASSERT_EQ(SIGTRAP, WSTOPSIG(status));
+ ASSERT_EQ(PTRACE_EVENT_SECCOMP, (status >> 16));
+ ASSERT_EQ(0, ptrace(PTRACE_GETEVENTMSG, child_pid, NULL, &msg));
+ ASSERT_EQ(0x200, msg);
+ ret = get_syscall(_metadata, child_pid);
+#if defined(__arm__)
+ /* FIXME: ARM does not expose true syscall in registers. */
+ EXPECT_EQ(__NR_poll, ret);
+#else
+ EXPECT_EQ(__NR_restart_syscall, ret);
+#endif
+
+ /* Write again to end poll. */
+ ASSERT_EQ(0, ptrace(PTRACE_CONT, child_pid, NULL, 0));
+ ASSERT_EQ(1, write(pipefd[1], "!", 1));
+ EXPECT_EQ(0, close(pipefd[1]));
+
+ ASSERT_EQ(child_pid, waitpid(child_pid, &status, 0));
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ _metadata->passed = 0;
+}
+
+/*
+ * TODO:
+ * - add microbenchmarks
+ * - expand NNP testing
+ * - better arch-specific TRACE and TRAP handlers.
+ * - endianness checking when appropriate
+ * - 64-bit arg prodding
+ * - arch value testing (x86 modes especially)
+ * - ...
+ */
+
+// ANDROID:begin
+struct __test_metadata* get_seccomp_test_list() {
+ return __test_list;
+}
+// ANDROID:end
+
+TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c b/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c
new file mode 100644
index 0000000..a0d06e7
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/sigsegv.c
@@ -0,0 +1,179 @@
+/* sigsegv.c
+ * Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * Forces a denied system call to trigger a SIGSEGV at the instruction after
+ * the call using a SIGSYS handler.. This can be useful when debugging
+ * frameworks have trouble tracing through the SIGSYS handler.
+ * Proof of concept using amd64 registers and 'syscall'.
+ */
+
+#include <asm/siginfo.h>
+#define __have_siginfo_t 1
+#define __have_sigval_t 1
+#define __have_sigevent_t 1
+
+#include <linux/filter.h>
+#include <sys/prctl.h>
+#include <linux/prctl.h>
+#include <linux/seccomp.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <string.h>
+#include <syscall.h>
+#define __USE_GNU 1
+#include <sys/ucontext.h>
+#include <sys/mman.h>
+
+#include "test_harness.h"
+
+#ifndef PR_SET_NO_NEW_PRIVS
+#define PR_SET_NO_NEW_PRIVS 38
+#define PR_GET_NO_NEW_PRIVS 39
+#endif
+
+#if defined(__i386__)
+#define REG_IP REG_EIP
+#define REG_SP REG_ESP
+#define REG_RESULT REG_EAX
+#define REG_SYSCALL REG_EAX
+#define REG_ARG0 REG_EBX
+#define REG_ARG1 REG_ECX
+#define REG_ARG2 REG_EDX
+#define REG_ARG3 REG_ESI
+#define REG_ARG4 REG_EDI
+#define REG_ARG5 REG_EBP
+#elif defined(__x86_64__)
+#define REG_IP REG_RIP
+#define REG_SP REG_RSP
+#define REG_RESULT REG_RAX
+#define REG_SYSCALL REG_RAX
+#define REG_ARG0 REG_RDI
+#define REG_ARG1 REG_RSI
+#define REG_ARG2 REG_RDX
+#define REG_ARG3 REG_R10
+#define REG_ARG4 REG_R8
+#define REG_ARG5 REG_R9
+#endif
+
+FIXTURE_DATA(TRAP) {
+ struct sock_fprog prog;
+};
+
+FIXTURE_SETUP(TRAP) {
+ /* instruction after the syscall. Will be arch specific, of course. */
+ {
+ struct sock_filter filter[] = {
+ BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
+ offsetof(struct seccomp_data, nr)),
+ /* Whitelist anything you might need in the sigaction */
+#ifdef __NR_sigreturn
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_sigreturn, 4, 0),
+#endif
+ /* TODO: only allow PROT_NONE */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_mprotect, 3, 0),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_exit, 2, 0),
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_rt_sigreturn, 1, 0),
+ /* Allow __NR_write so easy logging. */
+ BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, __NR_write, 0, 1),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW),
+ BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_TRAP),
+ };
+ memset(&self->prog, 0, sizeof(self->prog));
+ self->prog.filter = malloc(sizeof(filter));
+ ASSERT_NE(NULL, self->prog.filter);
+ memcpy(self->prog.filter, filter, sizeof(filter));
+ self->prog.len = (unsigned short)(sizeof(filter)/sizeof(filter[0]));
+ }
+}
+
+FIXTURE_TEARDOWN(TRAP) {
+ if (self->prog.filter)
+ free(self->prog.filter);
+};
+
+struct arch_sigsys {
+ void *_call_addr; /* calling user insn */
+ int _syscall; /* triggering system call number */
+ unsigned int _arch; /* AUDIT_ARCH_* of syscall */
+};
+
+#define _ALIGN(x,sz) (((x + ((sz)-1)) & ~((sz)-1)) - (sz))
+#define ALIGN(x,sz) ((typeof(x))_ALIGN((unsigned long)(x),(unsigned long)(sz)))
+static long local_mprotect(void *target, unsigned long sz)
+{
+ register unsigned long res asm ("rax") = __NR_mprotect;
+ __attribute__((unused)) register void *addr asm ("rdi") = ALIGN(target, sz);
+ __attribute__((unused)) register long len asm ("rsi") = sz;
+ __attribute__((unused)) register long num asm ("rdx") = PROT_NONE;
+ __asm__("syscall\n");
+ return res;
+}
+
+static void TRAP_action(int nr, siginfo_t *info, void *void_context)
+{
+ ucontext_t *ctx = (ucontext_t *)void_context;
+ char buf[256];
+ int len;
+ struct arch_sigsys *sys = (struct arch_sigsys *)
+#ifdef si_syscall
+ &(info->si_call_addr);
+#else
+ &(info->si_pid);
+#endif
+
+ if (info->si_code != SYS_SECCOMP)
+ return;
+ if (!ctx)
+ return;
+ len = snprintf(buf, sizeof(buf),
+ "@0x%lX:%X:%d:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX:0x%lX [0x%lX]\n",
+ (unsigned long)sys->_call_addr,
+ sys->_arch,
+ sys->_syscall,
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG0],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG1],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG2],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG3],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG4],
+ (unsigned long)ctx->uc_mcontext.gregs[REG_ARG5],
+ (unsigned long)ALIGN(ctx->uc_mcontext.gregs[REG_IP],
+ 4096));
+ /* Emit some useful logs or whatever. */
+ syscall(__NR_write, STDOUT_FILENO, buf, len);
+ /* Make the calling page non-exec */
+ /* Careful on how it is called since it may make the syscall() instructions non-exec. */
+ local_mprotect((void *)ctx->uc_mcontext.gregs[REG_IP], sysconf(_SC_PAGE_SIZE));
+}
+
+TEST_F_SIGNAL(TRAP, sigsegv, SIGSEGV) {
+ int ret;
+ struct sigaction act;
+ sigset_t mask;
+ memset(&act, 0, sizeof(act));
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGSYS);
+
+ act.sa_sigaction = &TRAP_action;
+ act.sa_flags = SA_SIGINFO;
+ ret = sigaction(SIGSYS, &act, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigaction failed");
+ }
+ ret = sigprocmask(SIG_UNBLOCK, &mask, NULL);
+ ASSERT_EQ(0, ret) {
+ TH_LOG("sigprocmask failed");
+ }
+
+ ret = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
+ ASSERT_EQ(0, ret);
+ ret = prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &self->prog);
+ ASSERT_EQ(0, ret);
+
+ /* Call anything! */
+ ret = syscall(__NR_getpid);
+}
+
+TEST_HARNESS_MAIN
diff --git a/tests/tests/os/jni/seccomp-tests/tests/test_harness.h b/tests/tests/os/jni/seccomp-tests/tests/test_harness.h
new file mode 100644
index 0000000..597e40c
--- /dev/null
+++ b/tests/tests/os/jni/seccomp-tests/tests/test_harness.h
@@ -0,0 +1,521 @@
+/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ *
+ * test_harness.h: simple C unit test helper.
+ *
+ * Usage:
+ * #include "test_harness.h"
+ * TEST(standalone_test) {
+ * do_some_stuff;
+ * EXPECT_GT(10, stuff) {
+ * stuff_state_t state;
+ * enumerate_stuff_state(&state);
+ * TH_LOG("expectation failed with state: %s", state.msg);
+ * }
+ * more_stuff;
+ * ASSERT_NE(some_stuff, NULL) TH_LOG("how did it happen?!");
+ * last_stuff;
+ * EXPECT_EQ(0, last_stuff);
+ * }
+ *
+ * FIXTURE(my_fixture) {
+ * mytype_t *data;
+ * int awesomeness_level;
+ * };
+ * FIXTURE_SETUP(my_fixture) {
+ * self->data = mytype_new();
+ * ASSERT_NE(NULL, self->data);
+ * }
+ * FIXTURE_TEARDOWN(my_fixture) {
+ * mytype_free(self->data);
+ * }
+ * TEST_F(my_fixture, data_is_good) {
+ * EXPECT_EQ(1, is_my_data_good(self->data));
+ * }
+ *
+ * TEST_HARNESS_MAIN
+ *
+ * API inspired by code.google.com/p/googletest
+ */
+#ifndef TEST_HARNESS_H_
+#define TEST_HARNESS_H_
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <android/log.h> // ANDROID
+
+/* All exported functionality should be declared through this macro. */
+#define TEST_API(x) _##x
+
+/*
+ * Exported APIs
+ */
+
+/* TEST(name) { implementation }
+ * Defines a test by name.
+ * Names must be unique and tests must not be run in parallel. The
+ * implementation containing block is a function and scoping should be treated
+ * as such. Returning early may be performed with a bare "return;" statement.
+ *
+ * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
+ */
+#define TEST TEST_API(TEST)
+
+/* TEST_SIGNAL(name, signal) { implementation }
+ * Defines a test by name and the expected term signal.
+ * Names must be unique and tests must not be run in parallel. The
+ * implementation containing block is a function and scoping should be treated
+ * as such. Returning early may be performed with a bare "return;" statement.
+ *
+ * EXPECT_* and ASSERT_* are valid in a TEST() { } context.
+ */
+#define TEST_SIGNAL TEST_API(TEST_SIGNAL)
+
+/* FIXTURE(datatype name) {
+ * type property1;
+ * ...
+ * };
+ * Defines the data provided to TEST_F()-defined tests as |self|. It should be
+ * populated and cleaned up using FIXTURE_SETUP and FIXTURE_TEARDOWN.
+ */
+#define FIXTURE TEST_API(FIXTURE)
+
+/* FIXTURE_DATA(datatype name)
+ * This call may be used when the type of the fixture data
+ * is needed. In general, this should not be needed unless
+ * the |self| is being passed to a helper directly.
+ */
+#define FIXTURE_DATA TEST_API(FIXTURE_DATA)
+
+/* FIXTURE_SETUP(fixture name) { implementation }
+ * Populates the required "setup" function for a fixture. An instance of the
+ * datatype defined with _FIXTURE_DATA will be exposed as |self| for the
+ * implementation.
+ *
+ * ASSERT_* are valid for use in this context and will prempt the execution
+ * of any dependent fixture tests.
+ *
+ * A bare "return;" statement may be used to return early.
+ */
+#define FIXTURE_SETUP TEST_API(FIXTURE_SETUP)
+
+/* FIXTURE_TEARDOWN(fixture name) { implementation }
+ * Populates the required "teardown" function for a fixture. An instance of the
+ * datatype defined with _FIXTURE_DATA will be exposed as |self| for the
+ * implementation to clean up.
+ *
+ * A bare "return;" statement may be used to return early.
+ */
+#define FIXTURE_TEARDOWN TEST_API(FIXTURE_TEARDOWN)
+
+/* TEST_F(fixture, name) { implementation }
+ * Defines a test that depends on a fixture (e.g., is part of a test case).
+ * Very similar to TEST() except that |self| is the setup instance of fixture's
+ * datatype exposed for use by the implementation.
+ */
+#define TEST_F TEST_API(TEST_F)
+
+#define TEST_F_SIGNAL TEST_API(TEST_F_SIGNAL)
+
+/* Use once to append a main() to the test file. E.g.,
+ * TEST_HARNESS_MAIN
+ */
+#define TEST_HARNESS_MAIN TEST_API(TEST_HARNESS_MAIN)
+
+/*
+ * Operators for use in TEST and TEST_F.
+ * ASSERT_* calls will stop test execution immediately.
+ * EXPECT_* calls will emit a failure warning, note it, and continue.
+ */
+
+/* ASSERT_EQ(expected, measured): expected == measured */
+#define ASSERT_EQ TEST_API(ASSERT_EQ)
+/* ASSERT_NE(expected, measured): expected != measured */
+#define ASSERT_NE TEST_API(ASSERT_NE)
+/* ASSERT_LT(expected, measured): expected < measured */
+#define ASSERT_LT TEST_API(ASSERT_LT)
+/* ASSERT_LE(expected, measured): expected <= measured */
+#define ASSERT_LE TEST_API(ASSERT_LE)
+/* ASSERT_GT(expected, measured): expected > measured */
+#define ASSERT_GT TEST_API(ASSERT_GT)
+/* ASSERT_GE(expected, measured): expected >= measured */
+#define ASSERT_GE TEST_API(ASSERT_GE)
+/* ASSERT_NULL(measured): NULL == measured */
+#define ASSERT_NULL TEST_API(ASSERT_NULL)
+/* ASSERT_TRUE(measured): measured != 0 */
+#define ASSERT_TRUE TEST_API(ASSERT_TRUE)
+/* ASSERT_FALSE(measured): measured == 0 */
+#define ASSERT_FALSE TEST_API(ASSERT_FALSE)
+/* ASSERT_STREQ(expected, measured): !strcmp(expected, measured) */
+#define ASSERT_STREQ TEST_API(ASSERT_STREQ)
+/* ASSERT_STRNE(expected, measured): strcmp(expected, measured) */
+#define ASSERT_STRNE TEST_API(ASSERT_STRNE)
+/* EXPECT_EQ(expected, measured): expected == measured */
+#define EXPECT_EQ TEST_API(EXPECT_EQ)
+/* EXPECT_NE(expected, measured): expected != measured */
+#define EXPECT_NE TEST_API(EXPECT_NE)
+/* EXPECT_LT(expected, measured): expected < measured */
+#define EXPECT_LT TEST_API(EXPECT_LT)
+/* EXPECT_LE(expected, measured): expected <= measured */
+#define EXPECT_LE TEST_API(EXPECT_LE)
+/* EXPECT_GT(expected, measured): expected > measured */
+#define EXPECT_GT TEST_API(EXPECT_GT)
+/* EXPECT_GE(expected, measured): expected >= measured */
+#define EXPECT_GE TEST_API(EXPECT_GE)
+/* EXPECT_NULL(measured): NULL == measured */
+#define EXPECT_NULL TEST_API(EXPECT_NULL)
+/* EXPECT_TRUE(measured): 0 != measured */
+#define EXPECT_TRUE TEST_API(EXPECT_TRUE)
+/* EXPECT_FALSE(measured): 0 == measured */
+#define EXPECT_FALSE TEST_API(EXPECT_FALSE)
+/* EXPECT_STREQ(expected, measured): !strcmp(expected, measured) */
+#define EXPECT_STREQ TEST_API(EXPECT_STREQ)
+/* EXPECT_STRNE(expected, measured): strcmp(expected, measured) */
+#define EXPECT_STRNE TEST_API(EXPECT_STRNE)
+
+/* TH_LOG(format, ...)
+ * Optional debug logging function available for use in tests.
+ * Logging may be enabled or disabled by defining TH_LOG_ENABLED.
+ * E.g., #define TH_LOG_ENABLED 1
+ * If no definition is provided, logging is enabled by default.
+ */
+#define TH_LOG TEST_API(TH_LOG)
+
+/*
+ * Internal implementation.
+ *
+ */
+
+/* Utilities exposed to the test definitions */
+#ifndef TH_LOG_STREAM
+# define TH_LOG_STREAM stderr
+#endif
+
+#ifndef TH_LOG_ENABLED
+# define TH_LOG_ENABLED 1
+#endif
+
+#define _TH_LOG(fmt, ...) do { \
+ if (TH_LOG_ENABLED) \
+ __TH_LOG(fmt, ##__VA_ARGS__); \
+} while (0)
+
+/* Unconditional logger for internal use. */
+// ANDROID:begin
+#define __TH_LOG(fmt, ...) \
+ __android_log_print(ANDROID_LOG_ERROR, "SeccompBpfTest-KernelUnit", "%s:%d:%s:" fmt "\n", \
+ __FILE__, __LINE__, _metadata->name, ##__VA_ARGS__)
+// ANDROID:end
+
+/* Defines the test function and creates the registration stub. */
+#define _TEST(test_name) __TEST_IMPL(test_name, -1)
+
+#define _TEST_SIGNAL(test_name, signal) __TEST_IMPL(test_name, signal)
+
+#define __TEST_IMPL(test_name, _signal) \
+ static void test_name(struct __test_metadata *_metadata); \
+ static struct __test_metadata _##test_name##_object = \
+ { name: "global." #test_name, fn: &test_name, termsig: _signal }; \
+ static void __attribute__((constructor)) _register_##test_name(void) { \
+ __register_test(&_##test_name##_object); \
+ } \
+ static void test_name( \
+ struct __test_metadata __attribute__((unused)) *_metadata)
+
+/* Wraps the struct name so we have one less argument to pass around. */
+#define _FIXTURE_DATA(fixture_name) struct _test_data_##fixture_name
+
+/* Called once per fixture to setup the data and register. */
+#define _FIXTURE(fixture_name) \
+ static void __attribute__((constructor)) \
+ _register_##fixture_name##_data(void) { \
+ __fixture_count++; \
+ } \
+ _FIXTURE_DATA(fixture_name)
+
+/* Prepares the setup function for the fixture. |_metadata| is included
+ * so that ASSERT_* work as a convenience.
+ */
+#define _FIXTURE_SETUP(fixture_name) \
+ void fixture_name##_setup( \
+ struct __test_metadata __attribute__((unused)) *_metadata, \
+ _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
+#define _FIXTURE_TEARDOWN(fixture_name) \
+ void fixture_name##_teardown( \
+ struct __test_metadata __attribute__((unused)) *_metadata, \
+ _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
+
+/* Emits test registration and helpers for fixture-based test
+ * cases.
+ * TODO(wad) register fixtures on dedicated test lists.
+ */
+#define _TEST_F(fixture_name, test_name) \
+ __TEST_F_IMPL(fixture_name, test_name, -1)
+
+#define _TEST_F_SIGNAL(fixture_name, test_name, signal) \
+ __TEST_F_IMPL(fixture_name, test_name, signal)
+
+#define __TEST_F_IMPL(fixture_name, test_name, signal) \
+ static void fixture_name##_##test_name( \
+ struct __test_metadata *_metadata, \
+ _FIXTURE_DATA(fixture_name) *self); \
+ static inline void wrapper_##fixture_name##_##test_name( \
+ struct __test_metadata *_metadata) { \
+ /* fixture data is allocated, setup, and torn down per call. */ \
+ _FIXTURE_DATA(fixture_name) self; \
+ memset(&self, 0, sizeof(_FIXTURE_DATA(fixture_name))); \
+ fixture_name##_setup(_metadata, &self); \
+ /* Let setup failure terminate early. */ \
+ if (!_metadata->passed) return; \
+ fixture_name##_##test_name(_metadata, &self); \
+ fixture_name##_teardown(_metadata, &self); \
+ } \
+ static struct __test_metadata _##fixture_name##_##test_name##_object = { \
+ name: #fixture_name "." #test_name, \
+ fn: &wrapper_##fixture_name##_##test_name, \
+ termsig: signal, \
+ }; \
+ static void __attribute__((constructor)) \
+ _register_##fixture_name##_##test_name(void) { \
+ __register_test(&_##fixture_name##_##test_name##_object); \
+ } \
+ static void fixture_name##_##test_name( \
+ struct __test_metadata __attribute__((unused)) *_metadata, \
+ _FIXTURE_DATA(fixture_name) __attribute__((unused)) *self)
+
+/* Exports a simple wrapper to run the test harness. */
+#define _TEST_HARNESS_MAIN \
+ static void __attribute__((constructor)) __constructor_order_last(void) { \
+ if (!__constructor_order) \
+ __constructor_order = _CONSTRUCTOR_ORDER_BACKWARD; \
+ } \
+ int seccomp_test_main(int argc, char **argv) { return test_harness_run(argc, argv); } // ANDROID
+
+#define _ASSERT_EQ(_expected, _seen) \
+ __EXPECT(_expected, _seen, ==, 1)
+#define _ASSERT_NE(_expected, _seen) \
+ __EXPECT(_expected, _seen, !=, 1)
+#define _ASSERT_LT(_expected, _seen) \
+ __EXPECT(_expected, _seen, <, 1)
+#define _ASSERT_LE(_expected, _seen) \
+ __EXPECT(_expected, _seen, <=, 1)
+#define _ASSERT_GT(_expected, _seen) \
+ __EXPECT(_expected, _seen, >, 1)
+#define _ASSERT_GE(_expected, _seen) \
+ __EXPECT(_expected, _seen, >=, 1)
+#define _ASSERT_NULL(_seen) \
+ __EXPECT(NULL, _seen, ==, 1)
+
+#define _ASSERT_TRUE(_seen) \
+ _ASSERT_NE(0, _seen)
+#define _ASSERT_FALSE(_seen) \
+ _ASSERT_EQ(0, _seen)
+#define _ASSERT_STREQ(_expected, _seen) \
+ __EXPECT_STR(_expected, _seen, ==, 1)
+#define _ASSERT_STRNE(_expected, _seen) \
+ __EXPECT_STR(_expected, _seen, !=, 1)
+
+#define _EXPECT_EQ(_expected, _seen) \
+ __EXPECT(_expected, _seen, ==, 0)
+#define _EXPECT_NE(_expected, _seen) \
+ __EXPECT(_expected, _seen, !=, 0)
+#define _EXPECT_LT(_expected, _seen) \
+ __EXPECT(_expected, _seen, <, 0)
+#define _EXPECT_LE(_expected, _seen) \
+ __EXPECT(_expected, _seen, <=, 0)
+#define _EXPECT_GT(_expected, _seen) \
+ __EXPECT(_expected, _seen, >, 0)
+#define _EXPECT_GE(_expected, _seen) \
+ __EXPECT(_expected, _seen, >=, 0)
+
+#define _EXPECT_NULL(_seen) \
+ __EXPECT(NULL, _seen, ==, 0)
+#define _EXPECT_TRUE(_seen) \
+ _EXPECT_NE(0, _seen)
+#define _EXPECT_FALSE(_seen) \
+ _EXPECT_EQ(0, _seen)
+
+#define _EXPECT_STREQ(_expected, _seen) \
+ __EXPECT_STR(_expected, _seen, ==, 0)
+#define _EXPECT_STRNE(_expected, _seen) \
+ __EXPECT_STR(_expected, _seen, !=, 0)
+
+/* Support an optional handler after and ASSERT_* or EXPECT_*. The approach is
+ * not thread-safe, but it should be fine in most sane test scenarios.
+ *
+ * Using __bail(), which optionally abort()s, is the easiest way to early
+ * return while still providing an optional block to the API consumer.
+ */
+#define OPTIONAL_HANDLER(_assert) \
+ for (; _metadata->trigger; _metadata->trigger = __bail(_assert))
+
+#define __EXPECT(_expected, _seen, _t, _assert) do { \
+ /* Avoid multiple evaluation of the cases */ \
+ __typeof__(_expected) __exp = (_expected); \
+ __typeof__(_seen) __seen = (_seen); \
+ if (!(__exp _t __seen)) { \
+ unsigned long long __exp_print = 0; \
+ unsigned long long __seen_print = 0; \
+ /* Avoid casting complaints the scariest way we can. */ \
+ memcpy(&__exp_print, &__exp, sizeof(__exp)); \
+ memcpy(&__seen_print, &__seen, sizeof(__seen)); \
+ __TH_LOG("Expected %s (%llu) %s %s (%llu)", \
+ #_expected, __exp_print, #_t, \
+ #_seen, __seen_print); \
+ _metadata->passed = 0; \
+ /* Ensure the optional handler is triggered */ \
+ _metadata->trigger = 1; \
+ } \
+} while (0); OPTIONAL_HANDLER(_assert)
+
+#define __EXPECT_STR(_expected, _seen, _t, _assert) do { \
+ const char *__exp = (_expected); \
+ const char *__seen = (_seen); \
+ if (!(strcmp(__exp, __seen) _t 0)) { \
+ __TH_LOG("Expected '%s' %s '%s'.", __exp, #_t, __seen); \
+ _metadata->passed = 0; \
+ _metadata->trigger = 1; \
+ } \
+} while (0); OPTIONAL_HANDLER(_assert)
+
+/* Contains all the information for test execution and status checking. */
+struct __test_metadata {
+ const char *name;
+ void (*fn)(struct __test_metadata *);
+ int termsig;
+ int passed;
+ int trigger; /* extra handler after the evaluation */
+ struct __test_metadata *prev, *next;
+};
+
+/* Storage for the (global) tests to be run. */
+static struct __test_metadata *__test_list = NULL;
+static unsigned int __test_count = 0;
+static unsigned int __fixture_count = 0;
+static int __constructor_order = 0;
+
+#define _CONSTRUCTOR_ORDER_FORWARD 1
+#define _CONSTRUCTOR_ORDER_BACKWARD -1
+
+/*
+ * Since constructors are called in reverse order, reverse the test
+ * list so tests are run in source declaration order.
+ * https://gcc.gnu.org/onlinedocs/gccint/Initialization.html
+ * However, it seems not all toolchains do this correctly, so use
+ * __constructor_order to detect which direction is called first
+ * and adjust list building logic to get things running in the right
+ * direction.
+ */
+static inline void __register_test(struct __test_metadata *t) {
+ __test_count++;
+ /* Circular linked list where only prev is circular. */
+ if (__test_list == NULL) {
+ __test_list = t;
+ t->next = NULL;
+ t->prev = t;
+ return;
+ }
+ if (__constructor_order == _CONSTRUCTOR_ORDER_FORWARD) {
+ t->next = NULL;
+ t->prev = __test_list->prev;
+ t->prev->next = t;
+ __test_list->prev = t;
+ } else {
+ t->next = __test_list;
+ t->next->prev = t;
+ t->prev = t;
+ __test_list = t;
+ }
+}
+
+static inline int __bail(int for_realz) {
+ if (for_realz)
+ abort();
+ return 0;
+}
+
+void __run_test(struct __test_metadata *t) {
+ pid_t child_pid;
+ int status;
+ t->passed = 1;
+ t->trigger = 0;
+ printf("[ RUN ] %s\n", t->name);
+ child_pid = fork();
+ if (child_pid < 0) {
+ printf("ERROR SPAWNING TEST CHILD\n");
+ t->passed = 0;
+ } else if (child_pid == 0) {
+ t->fn(t);
+ _exit(t->passed);
+ } else {
+ /* TODO(wad) add timeout support. */
+ waitpid(child_pid, &status, 0);
+ if (WIFEXITED(status)) {
+ t->passed = t->termsig == -1 ? WEXITSTATUS(status) : 0;
+ if (t->termsig != -1) {
+ fprintf(TH_LOG_STREAM,
+ "%s: Test exited normally instead of by signal (code: %d)\n",
+ t->name,
+ WEXITSTATUS(status));
+ }
+ } else if (WIFSIGNALED(status)) {
+ t->passed = 0;
+ if (WTERMSIG(status) == SIGABRT) {
+ fprintf(TH_LOG_STREAM,
+ "%s: Test terminated by assertion\n",
+ t->name);
+ } else if (WTERMSIG(status) == t->termsig) {
+ t->passed = 1;
+ } else {
+ fprintf(TH_LOG_STREAM,
+ "%s: Test terminated unexpectedly by signal %d\n",
+ t->name,
+ WTERMSIG(status));
+ }
+ } else {
+ fprintf(TH_LOG_STREAM,
+ "%s: Test ended in some other way [%u]\n",
+ t->name,
+ status);
+ }
+ }
+ printf("[ %4s ] %s\n", (t->passed ? "OK" : "FAIL"), t->name);
+}
+
+static int test_harness_run(int __attribute__((unused)) argc,
+ char __attribute__((unused)) **argv) {
+ struct __test_metadata *t;
+ int ret = 0;
+ unsigned int count = 0;
+ unsigned int pass_count = 0;
+
+ /* TODO(wad) add optional arguments similar to gtest. */
+ printf("[==========] Running %u tests from %u test cases.\n",
+ __test_count, __fixture_count + 1);
+ for (t = __test_list; t; t = t->next) {
+ count++;
+ __run_test(t);
+ if (t->passed)
+ pass_count++;
+ else
+ ret = 1;
+ }
+ /* TODO(wad) organize by fixtures since ordering is not guaranteed now. */
+ printf("[==========] %u / %u tests passed.\n", pass_count, count);
+ printf("[ %s ]\n", (ret ? "FAILED" : "PASSED"));
+ return ret;
+}
+
+static void __attribute__((constructor)) __constructor_order_first(void) {
+ if (!__constructor_order)
+ __constructor_order = _CONSTRUCTOR_ORDER_FORWARD;
+}
+
+#endif /* TEST_HARNESS_H_ */
diff --git a/tests/tests/os/src/android/os/cts/MessageQueueTest.java b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
index f5d6415..8906c42 100644
--- a/tests/tests/os/src/android/os/cts/MessageQueueTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
@@ -21,7 +21,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
-import android.os.MessageQueue.FileDescriptorCallback;
+import android.os.MessageQueue.OnFileDescriptorEventListener;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
@@ -214,8 +214,13 @@
public void testRegisterFileDescriptorCallbackThrowsWhenFdIsNull() {
MessageQueue queue = Looper.getMainLooper().getQueue();
try {
- queue.registerFileDescriptorCallback(null, 0,
- new FileDescriptorCallback() { });
+ queue.addOnFileDescriptorEventListener(null, 0,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ return 0;
+ }
+ });
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
@@ -228,7 +233,7 @@
try (ParcelFileDescriptor reader = pipe[0];
ParcelFileDescriptor writer = pipe[1]) {
try {
- queue.registerFileDescriptorCallback(reader.getFileDescriptor(), 0, null);
+ queue.addOnFileDescriptorEventListener(reader.getFileDescriptor(), 0, null);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
@@ -239,7 +244,7 @@
public void testUnregisterFileDescriptorCallbackThrowsWhenFdIsNull() throws Exception {
MessageQueue queue = Looper.getMainLooper().getQueue();
try {
- queue.unregisterFileDescriptorCallback(null);
+ queue.removeOnFileDescriptorEventListener(null);
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException ex) {
// expected
@@ -252,7 +257,7 @@
ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
try (ParcelFileDescriptor reader = pipe[0];
ParcelFileDescriptor writer = pipe[1]) {
- queue.unregisterFileDescriptorCallback(reader.getFileDescriptor());
+ queue.removeOnFileDescriptorEventListener(reader.getFileDescriptor());
}
}
@@ -272,7 +277,7 @@
// Prepare to write a lot of data to the pipe asynchronously.
// We don't actually care about the content (assume pipes work correctly)
// so we just write lots of zeros.
- FileDescriptorCallback writerCallback = new FileDescriptorCallback() {
+ OnFileDescriptorEventListener writerCallback = new OnFileDescriptorEventListener() {
private byte[] mBuffer = new byte[4096];
private int mRemaining = size;
private boolean mDone;
@@ -283,14 +288,14 @@
if (!mDone) {
// When an error happens because the reader closed its end,
// signal the test, and remove the callback.
- if ((events & FileDescriptorCallback.EVENT_ERROR) != 0) {
+ if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) {
writerSawError.countDown();
mDone = true;
return 0;
}
// Write all output until an error is observed.
- if ((events & FileDescriptorCallback.EVENT_OUTPUT) != 0) {
+ if ((events & OnFileDescriptorEventListener.EVENT_OUTPUT) != 0) {
int count = Math.min(mBuffer.length, mRemaining);
try {
writer.write(mBuffer, 0, count);
@@ -309,7 +314,7 @@
};
// Prepare to read all of that data.
- FileDescriptorCallback readerCallback = new FileDescriptorCallback() {
+ OnFileDescriptorEventListener readerCallback = new OnFileDescriptorEventListener() {
private byte[] mBuffer = new byte[4096];
private int mRemaining = size;
private boolean mDone;
@@ -319,14 +324,14 @@
assertEquals(pipe[0].getFileDescriptor(), fd);
if (!mDone) {
// Errors should not happen.
- if ((events & FileDescriptorCallback.EVENT_ERROR) != 0) {
+ if ((events & OnFileDescriptorEventListener.EVENT_ERROR) != 0) {
fail("Saw unexpected error.");
return 0;
}
// Read until everything is read, signal the test,
// and remove the callback.
- if ((events & FileDescriptorCallback.EVENT_INPUT) != 0) {
+ if ((events & OnFileDescriptorEventListener.EVENT_INPUT) != 0) {
try {
int count = reader.read(mBuffer, 0, mBuffer.length);
mRemaining -= count;
@@ -349,10 +354,10 @@
};
// Register the callbacks.
- queue.registerFileDescriptorCallback(reader.getFD(),
- FileDescriptorCallback.EVENT_INPUT, readerCallback);
- queue.registerFileDescriptorCallback(writer.getFD(),
- FileDescriptorCallback.EVENT_OUTPUT, writerCallback);
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_INPUT, readerCallback);
+ queue.addOnFileDescriptorEventListener(writer.getFD(),
+ OnFileDescriptorEventListener.EVENT_OUTPUT, writerCallback);
// Wait for the reader to see all of the data that the writer
// is prepared to send.
@@ -368,8 +373,8 @@
// The reader and writer should already be unregistered.
// Try to unregistered them again to ensure nothing bad happens.
- queue.unregisterFileDescriptorCallback(reader.getFD());
- queue.unregisterFileDescriptorCallback(writer.getFD());
+ queue.removeOnFileDescriptorEventListener(reader.getFD());
+ queue.removeOnFileDescriptorEventListener(writer.getFD());
}
} finally {
thread.quitAndRethrow();
@@ -401,8 +406,8 @@
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader.getFD(),
- FileDescriptorCallback.EVENT_ERROR, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
@@ -438,8 +443,8 @@
final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader2.getFD(),
- FileDescriptorCallback.EVENT_INPUT, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader2.getFD(),
+ OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
@@ -487,8 +492,8 @@
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader.getFD(),
- FileDescriptorCallback.EVENT_ERROR, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
@@ -528,8 +533,8 @@
final FileOutputStream writer2 = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader2.getFD(),
- FileDescriptorCallback.EVENT_INPUT, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader2.getFD(),
+ OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
@@ -580,8 +585,8 @@
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader.getFD(),
- FileDescriptorCallback.EVENT_ERROR, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
@@ -605,9 +610,9 @@
}
// Now we have a new pipe, make sure we can register it successfully.
- queue.registerFileDescriptorCallback(pipe[0].getFileDescriptor(),
- FileDescriptorCallback.EVENT_INPUT,
- new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(pipe[0].getFileDescriptor(),
+ OnFileDescriptorEventListener.EVENT_INPUT,
+ new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke2[0] = true;
@@ -672,8 +677,8 @@
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
final boolean[] awoke = new boolean[1];
- queue.registerFileDescriptorCallback(reader.getFD(),
- FileDescriptorCallback.EVENT_ERROR, new FileDescriptorCallback() {
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
awoke[0] = true;
diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java
index 6c49337..e8de783 100644
--- a/tests/tests/os/src/android/os/cts/SeccompTest.java
+++ b/tests/tests/os/src/android/os/cts/SeccompTest.java
@@ -19,6 +19,9 @@
import junit.framework.TestCase;
public class SeccompTest extends TestCase {
+ static {
+ System.loadLibrary("ctsos_jni");
+ }
public void testSeccomp() {
if (CpuFeatures.isArm64Cpu() || CpuFeatures.isArm64CpuIn32BitMode()) {
@@ -30,4 +33,116 @@
OSFeatures.hasSeccompSupport());
}
}
+
+ public void testKernelBasicTests() {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final String[] tests = {
+ "global.mode_strict_support",
+ "global.mode_strict_cannot_call_prctl",
+ "global.no_new_privs_support",
+ "global.mode_filter_support",
+ /* "global.mode_filter_without_nnp", // all Android processes already have nnp */
+ "global.filter_size_limits",
+ "global.filter_chain_limits",
+ "global.mode_filter_cannot_move_to_strict",
+ "global.mode_filter_get_seccomp",
+ "global.ALLOW_all",
+ "global.empty_prog",
+ "global.unknown_ret_is_kill_inside",
+ "global.unknown_ret_is_kill_above_allow",
+ "global.KILL_all",
+ "global.KILL_one",
+ "global.KILL_one_arg_one",
+ "global.KILL_one_arg_six",
+ "global.arg_out_of_range",
+ "global.ERRNO_one",
+ "global.ERRNO_one_ok",
+ };
+ runKernelUnitTestSuite(tests);
+ }
+
+ public void testKernelTrapTests() {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final String[] tests = {
+ "TRAP.dfl",
+ "TRAP.ign",
+ "TRAP.handler",
+ };
+ runKernelUnitTestSuite(tests);
+ }
+
+ public void testKernelPrecedenceTests() {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final String[] tests = {
+ "precedence.allow_ok",
+ "precedence.kill_is_highest",
+ "precedence.kill_is_highest_in_any_order",
+ "precedence.trap_is_second",
+ "precedence.trap_is_second_in_any_order",
+ "precedence.errno_is_third",
+ "precedence.errno_is_third_in_any_order",
+ "precedence.trace_is_fourth",
+ "precedence.trace_is_fourth_in_any_order",
+ };
+ runKernelUnitTestSuite(tests);
+ }
+
+ /* // The SECCOMP_RET_TRACE does not work under Android Arm32.
+ public void testKernelTraceTests() {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final String[] tests = {
+ "TRACE_poke.read_has_side_effects",
+ "TRACE_poke.getpid_runs_normally",
+ "TRACE_syscall.syscall_allowed",
+ "TRACE_syscall.syscall_redirected",
+ "TRACE_syscall.syscall_dropped",
+ };
+ runKernelUnitTestSuite(tests);
+ }
+ */
+
+ public void testKernelTSYNCTests() {
+ if (!OSFeatures.needsSeccompSupport())
+ return;
+
+ final String[] tests = {
+ "global.seccomp_syscall",
+ "global.seccomp_syscall_mode_lock",
+ "global.TSYNC_first",
+ "TSYNC.siblings_fail_prctl",
+ "TSYNC.two_siblings_with_ancestor",
+ /* "TSYNC.two_sibling_want_nnp", // all Android processes already have nnp */
+ "TSYNC.two_siblings_with_no_filter",
+ "TSYNC.two_siblings_with_one_divergence",
+ "TSYNC.two_siblings_not_under_filter",
+ /* "global.syscall_restart", // ptrace attach fails */
+ };
+ runKernelUnitTestSuite(tests);
+ }
+
+ /**
+ * Runs a kernel unit test suite (an array of kernel test names).
+ */
+ private void runKernelUnitTestSuite(final String[] tests) {
+ for (final String test : tests) {
+ // TODO: Replace the URL with the documentation when it's finished.
+ assertTrue(test + " failed. This test requires kernel functionality to pass. "
+ + "Please go to http://XXXXX for instructions on how to enable or "
+ + "backport the required functionality.",
+ runKernelUnitTest(test));
+ }
+ }
+
+ /**
+ * Runs the seccomp_bpf_unittest of the given name.
+ */
+ private native boolean runKernelUnitTest(final String name);
}
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_AuthorizationTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_AuthorizationTest.java
deleted file mode 100644
index 3fea65d..0000000
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_AuthorizationTest.java
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider.cts;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Contacts.AggregationSuggestions;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
-import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
-import android.provider.cts.contacts.DatabaseAsserts;
-import android.test.AndroidTestCase;
-
-/**
- * CTS tests for {@link android.provider.ContactsContract.Authorization} APIs.
- *
- * It isn't possible to fully test the Authorization API. Suppose this apk doesn't have
- * the necessary permissions. In this case, we can't call the authorization API in the first place.
- * On the other hand, suppose this apk does have the necessary permissions. In this case, we can't
- * check that the Authorization API added any permissions to the URI since we could already use the
- * URI anyway.
- */
-public class ContactsContract_AuthorizationTest extends AndroidTestCase {
- private static final String[] TEST_PROJECTION = new String[] {Contacts.DISPLAY_NAME};
-
- private ContentResolver mResolver;
- private ContactsContract_TestDataBuilder mBuilder;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = getContext().getContentResolver();
- ContentProviderClient provider =
- mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
- mBuilder = new ContactsContract_TestDataBuilder(provider);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mBuilder.cleanup();
- }
-
- public void testAuthorization_contact1() throws Exception {
- // Setup
- Uri [] contactUris = setupTwoContacts();
-
- // Execute
- Uri preAuthorizedUri = getPreAuthorizedUri(contactUris[0]);
-
- // Verify: the pre-authorized URI is different than the original URI, but still works.
- assertNotSame(preAuthorizedUri, contactUris[0]);
- Cursor cursor = mResolver.query(preAuthorizedUri, TEST_PROJECTION, null, null, null);
- assertEquals(1, cursor.getCount());
- ContentValues values = new ContentValues();
- values.put(Contacts.DISPLAY_NAME, "first1 last1");
- DatabaseAsserts.assertCursorValuesMatchExactly(cursor, values);
- cursor.close();
- }
-
- public void testAuthorization_contact2() throws Exception {
- // Setup
- Uri [] contactUris = setupTwoContacts();
-
- // Execute
- Uri preAuthorizedUri = getPreAuthorizedUri(contactUris[1]);
-
- // Verify: the pre-authorized URI is different than the original URI, but still works.
- assertNotSame(preAuthorizedUri, contactUris[1]);
- Cursor cursor = mResolver.query(preAuthorizedUri, TEST_PROJECTION, null, null, null);
- assertEquals(1, cursor.getCount());
- ContentValues values = new ContentValues();
- values.put(Contacts.DISPLAY_NAME, "first2 last2");
- DatabaseAsserts.assertCursorValuesMatchExactly(cursor, values);
- cursor.close();
- }
-
- public void testAuthorization_profile() throws Exception {
- try {
- getPreAuthorizedUri(ContactsContract.Profile.CONTENT_URI);
- fail("getPreAuthorizedUri(ContactsContract.Profile.CONTENT_URI) did not throw"
- + "SecurityException as expected");
- } catch (SecurityException se) {
- // Verify: can't authorize a profile URI without the READ_PROFILE permission.
- }
- }
-
- private Uri getPreAuthorizedUri(Uri uri) {
- final Bundle uriBundle = new Bundle();
- uriBundle.putParcelable(ContactsContract.Authorization.KEY_URI_TO_AUTHORIZE, uri);
- final Bundle authResponse = mResolver.call(
- ContactsContract.AUTHORITY_URI,
- ContactsContract.Authorization.AUTHORIZATION_METHOD,
- null,
- uriBundle);
- return authResponse.getParcelable(ContactsContract.Authorization.KEY_AUTHORIZED_URI);
- }
-
- private Uri[] setupTwoContacts() throws Exception {
- TestRawContact rawContact1 = mBuilder.newRawContact()
- .with(RawContacts.ACCOUNT_TYPE, "test_account")
- .with(RawContacts.ACCOUNT_NAME, "test_name")
- .insert();
- rawContact1.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
- .with(StructuredName.GIVEN_NAME, "first1")
- .with(StructuredName.FAMILY_NAME, "last1")
- .insert();
- rawContact1.load();
- TestContact testContact1 = rawContact1.getContact().load();
- Uri contactUri1 = testContact1.getUri();
-
- TestRawContact rawContact2 = mBuilder.newRawContact()
- .with(RawContacts.ACCOUNT_TYPE, "test_account")
- .with(RawContacts.ACCOUNT_NAME, "test_name")
- .insert();
- rawContact2.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
- .with(StructuredName.GIVEN_NAME, "first2")
- .with(StructuredName.FAMILY_NAME, "last2")
- .insert();
- rawContact2.load();
- TestContact testContact2 = rawContact2.getContact().load();
- Uri contactUri2 = testContact2.getUri();
-
- return new Uri[] {contactUri1, contactUri2};
- }
-
-}
-
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_IsSuperPrimaryName.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_IsSuperPrimaryName.java
index 4ff6a88..603cb619 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_IsSuperPrimaryName.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_IsSuperPrimaryName.java
@@ -112,10 +112,10 @@
//
// Execute: make the non primary name IS_SUPER_PRIMARY
- TestData nonPrimaryName = isFirstNamePrimary ? name1 : name2;
+ TestData nonPrimaryName = !isFirstNamePrimary ? name1 : name2;
ContentValues values = new ContentValues();
values.put(StructuredName.IS_SUPER_PRIMARY, 1);
- mResolver.update(nonPrimaryName.getContentUri(), values, null, null);
+ mResolver.update(nonPrimaryName.getUri(), values, null, null);
// Verify: the IS_SUPER_PRIMARY values swap
name1.load();
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
deleted file mode 100644
index 54bea8e..0000000
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.provider.cts;
-
-import android.content.ContentProviderClient;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.provider.ContactsContract;
-import android.provider.ContactsContract.CommonDataKinds.StructuredName;
-import android.provider.ContactsContract.ProviderStatus;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
-import android.provider.cts.contacts.DatabaseAsserts;
-import android.test.AndroidTestCase;
-
-/**
- * CTS tests for {@link android.provider.ContactsContract.ProviderStatus}.
- *
- * Unfortunately, we can't check that the value of ProviderStatus equals
- * {@link ProviderStatus#STATUS_NO_ACCOUNTS_NO_CONTACTS} initially. Some carriers pre-install
- * accounts. As a result, the value STATUS_NO_ACCOUNTS_NO_CONTACTS will never be achieved.
- */
-public class ContactsContract_ProviderStatus extends AndroidTestCase {
- private ContentResolver mResolver;
- private ContactsContract_TestDataBuilder mBuilder;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mResolver = getContext().getContentResolver();
- ContentProviderClient provider =
- mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
- mBuilder = new ContactsContract_TestDataBuilder(provider);
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- mBuilder.cleanup();
- }
-
- public void testProviderStatus_addedContacts() throws Exception {
- // Setup: add a contact to CP2.
- TestRawContact rawContact1 = mBuilder.newRawContact()
- .with(RawContacts.ACCOUNT_TYPE, "test_account")
- .with(RawContacts.ACCOUNT_NAME, "test_name")
- .insert();
- rawContact1.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
- .with(StructuredName.GIVEN_NAME, "first1")
- .with(StructuredName.FAMILY_NAME, "last1")
- .insert();
-
- // Execute: fetch CP2 status
- Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI, null, null, null, null);
-
- // Verify: CP2 status is normal instead of STATUS_NO_ACCOUNTS_NO_CONTACTS.
- try {
- assertEquals(1, cursor.getCount());
- cursor.moveToFirst();
- ContentValues values = new ContentValues();
- values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_NORMAL);
- DatabaseAsserts.assertCursorValuesMatchExactly(cursor, values);
- } finally {
- if (cursor != null) {
- cursor.close();
- }
- }
- }
-}
-
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
index 131b3fd4..9fa9c15 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ElementTest.java
@@ -183,6 +183,13 @@
// A_8 is in U8
Element[] BOOLEAN = { Element.BOOLEAN(mRS) };
Element[] ELEMENT = { Element.ELEMENT(mRS) };
+ Element[] F16 = { Element.F16(mRS) };
+ Element[] F16_2 = { Element.F16_2(mRS),
+ Element.createVector(mRS, Element.DataType.FLOAT_16, 2) };
+ Element[] F16_3 = { Element.F16_3(mRS),
+ Element.createVector(mRS, Element.DataType.FLOAT_16, 3) };
+ Element[] F16_4 = { Element.F16_4(mRS),
+ Element.createVector(mRS, Element.DataType.FLOAT_16, 4) };
Element[] F32 = { Element.F32(mRS) };
Element[] F32_2 = { Element.F32_2(mRS),
Element.createVector(mRS, Element.DataType.FLOAT_32, 2) };
@@ -236,8 +243,10 @@
Element.createPixel(mRS, Element.DataType.UNSIGNED_8,
Element.DataKind.PIXEL_RGBA) };
- Element[][] ElementArrs = { ALLOCATION, BOOLEAN, ELEMENT, F32, F32_2,
- F32_3, F32_4, F64, I16, I32, I64, I8,
+ Element[][] ElementArrs = { ALLOCATION, BOOLEAN, ELEMENT,
+ F16, F16_2, F16_3, F16_4,
+ F32, F32_2, F32_3, F32_4,
+ F64, I16, I32, I64, I8,
MATRIX_2X2, MATRIX_3X3, MATRIX_4X4, MESH,
PROGRAM_FRAGMENT, PROGRAM_RASTER,
PROGRAM_STORE, PROGRAM_VERTEX, RGBA_4444,
@@ -272,6 +281,10 @@
eb.add(Element.RGB_565(mRS), "RGB_565", arraySize);
eb.add(Element.RGB_888(mRS), "RGB_888", arraySize);
eb.add(Element.RGBA_8888(mRS), "RGBA_8888", arraySize);
+ eb.add(Element.F16(mRS), "F16", arraySize);
+ eb.add(Element.F16_2(mRS), "F16_2", arraySize);
+ eb.add(Element.F16_3(mRS), "F16_3", arraySize);
+ eb.add(Element.F16_4(mRS), "F16_4", arraySize);
eb.add(Element.F32(mRS), "F32", arraySize);
eb.add(Element.F32_2(mRS), "F32_2", arraySize);
eb.add(Element.F32_3(mRS), "F32_3", arraySize);
@@ -338,6 +351,10 @@
assertFalse(Element.RGB_565(mRS).isComplex());
assertFalse(Element.RGB_888(mRS).isComplex());
assertFalse(Element.RGBA_8888(mRS).isComplex());
+ assertFalse(Element.F16(mRS).isComplex());
+ assertFalse(Element.F16_2(mRS).isComplex());
+ assertFalse(Element.F16_3(mRS).isComplex());
+ assertFalse(Element.F16_4(mRS).isComplex());
assertFalse(Element.F32(mRS).isComplex());
assertFalse(Element.F32_2(mRS).isComplex());
assertFalse(Element.F32_3(mRS).isComplex());
@@ -416,6 +433,7 @@
// Uncomment when NONE is no longer hidden.
//assertEquals(DataType.NONE, DataType.valueOf("NONE"));
+ assertEquals(DataType.FLOAT_16, DataType.valueOf("FLOAT_16"));
assertEquals(DataType.FLOAT_32, DataType.valueOf("FLOAT_32"));
assertEquals(DataType.FLOAT_64, DataType.valueOf("FLOAT_64"));
assertEquals(DataType.SIGNED_8, DataType.valueOf("SIGNED_8"));
@@ -452,6 +470,7 @@
for (DataType dt : DataType.values()) {
switch (dt) {
+ case FLOAT_16:
case FLOAT_32:
case FLOAT_64:
case SIGNED_8:
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java b/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java
index 87a03ad..4ec84ad 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/Intrinsic3DLut.java
@@ -136,5 +136,12 @@
checkError();
}
+ public void test_ID() {
+ ScriptIntrinsic3DLUT s = ScriptIntrinsic3DLUT.create(mRS, Element.U8_4(mRS));
+ Script.KernelID kid = s.getKernelID();
+ if (kid == null) {
+ throw new IllegalStateException("kid must be valid");
+ }
+ }
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java
index 076dcd4..4e99391 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicBlur.java
@@ -141,5 +141,17 @@
checkError();
}
+ public void test_ID() {
+ ScriptIntrinsicBlur s = ScriptIntrinsicBlur.create(mRS, Element.U8_4(mRS));
+ Script.KernelID kid = s.getKernelID();
+ if (kid == null) {
+ throw new IllegalStateException("kid must be valid");
+ }
+
+ Script.FieldID fid = s.getFieldID_Input();
+ if (fid == null) {
+ throw new IllegalStateException("fid must be valid");
+ }
+ }
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
index 8faeb22..8a2bc27 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
@@ -247,4 +247,17 @@
checkError();
}
+ public void test_ID() {
+ ScriptIntrinsicConvolve3x3 s = ScriptIntrinsicConvolve3x3.create(mRS, Element.U8_4(mRS));
+ Script.KernelID kid = s.getKernelID();
+ if (kid == null) {
+ throw new IllegalStateException("kid must be valid");
+ }
+
+ Script.FieldID fid = s.getFieldID_Input();
+ if (fid == null) {
+ throw new IllegalStateException("fid must be valid");
+ }
+ }
+
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
index 0753c62..410aebd 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
@@ -184,4 +184,17 @@
checkError();
}
+ public void test_ID() {
+ ScriptIntrinsicConvolve5x5 s = ScriptIntrinsicConvolve5x5.create(mRS, Element.U8_4(mRS));
+ Script.KernelID kid = s.getKernelID();
+ if (kid == null) {
+ throw new IllegalStateException("kid must be valid");
+ }
+
+ Script.FieldID fid = s.getFieldID_Input();
+ if (fid == null) {
+ throw new IllegalStateException("fid must be valid");
+ }
+ }
+
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicLut.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicLut.java
index 1567639..3309bb0 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicLut.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicLut.java
@@ -83,5 +83,12 @@
}
+ public void test_ID() {
+ ScriptIntrinsicLUT s = ScriptIntrinsicLUT.create(mRS, Element.U8_4(mRS));
+ Script.KernelID kid = s.getKernelID();
+ if (kid == null) {
+ throw new IllegalStateException("kid must be valid");
+ }
+ }
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TypeTest.java b/tests/tests/renderscript/src/android/renderscript/cts/TypeTest.java
index 13d4977..abb532d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/TypeTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TypeTest.java
@@ -143,6 +143,14 @@
assertTrue(t.getY() == 4);
}
+ public void testGetYuv() {
+ Type.Builder b = new Type.Builder(mRS, Element.F32(mRS));
+ b.setX(64).setY(64);
+ b.setYuvFormat(android.graphics.ImageFormat.YV12);
+ Type t = b.create();
+ assertTrue(t.getYuv() == android.graphics.ImageFormat.YV12);
+ }
+
public void testGetZ() {
Type.Builder b = new Type.Builder(mRS, Element.F32(mRS));
b.setX(3).setY(4);
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_blur.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_blur.rs
index 88f9ca5..aea9745 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_blur.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_blur.rs
@@ -74,7 +74,7 @@
float4 blurredPixel = 0;
int gi = 0;
for (int r = -radius; r <= radius; r ++) {
- int validH = rsClamp((int)y + r, (int)0, (int)(height - 1));
+ int validH = clamp((int)y + r, (int)0, (int)(height - 1));
float4 i = rsGetElementAt_float4(ScratchPixel2, x, validH);
blurredPixel += i * gaussian[gi++];
}
@@ -86,11 +86,9 @@
int gi = 0;
for (int r = -radius; r <= radius; r ++) {
// Stepping left and right away from the pixel
- int validX = rsClamp((int)x + r, (int)0, (int)(width - 1));
+ int validX = clamp((int)x + r, (int)0, (int)(width - 1));
float4 i = rsGetElementAt_float4(ScratchPixel1, validX, y);
blurredPixel += i * gaussian[gi++];
}
return blurredPixel;
}
-
-
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/shared.rsh b/tests/tests/renderscript/src/android/renderscript/cts/shared.rsh
index b91611d..2ad81fc 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/shared.rsh
+++ b/tests/tests/renderscript/src/android/renderscript/cts/shared.rsh
@@ -3,11 +3,11 @@
static int64_t g_time;
-static void start(void) {
+static inline void start(void) {
g_time = rsUptimeMillis();
}
-static float end(void) {
+static inline float end(void) {
int64_t t = rsUptimeMillis() - g_time;
return ((float)t) / 1000.f;
}
@@ -24,4 +24,3 @@
/* These constants must match those in UnitTest.java */
static const int RS_MSG_TEST_PASSED = 100;
static const int RS_MSG_TEST_FAILED = 101;
-
diff --git a/tests/tests/text/src/android/text/cts/BidiFormatterTest.java b/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
index 645ab5b..5ace8b2 100644
--- a/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
+++ b/tests/tests/text/src/android/text/cts/BidiFormatterTest.java
@@ -107,6 +107,9 @@
}
public void testUnicodeWrap() {
+ // Make sure an input of null doesn't crash anything.
+ assertNull(LTR_FMT.unicodeWrap(null));
+
// Uniform directionality in opposite context.
assertEquals("uniform dir opposite to LTR context",
RLE + "." + HE + "." + PDF + LRM,
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index c11694e..79406e0 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -67,6 +67,15 @@
android:resource="@xml/stub_tv_input_service" />
</service>
+ <service android:name="android.media.tv.cts.TvInputManagerTest$StubTvInputService2"
+ 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>
+
<service android:name="android.media.tv.cts.TvInputServiceTest$CountingTvInputService"
android:permission="android.permission.BIND_TV_INPUT">
<intent-filter>
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 2082d3f..b4bc6eb 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -26,6 +26,8 @@
import android.graphics.BitmapFactory;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
+import android.media.tv.TvContract.Channels;
+import android.media.tv.TvContract.Programs.Genres;
import android.net.Uri;
import android.test.AndroidTestCase;
@@ -33,6 +35,8 @@
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.Arrays;
+import java.util.List;
/**
* Test for {@link android.media.tv.TvContract}.
@@ -80,6 +84,11 @@
private static long OPERATION_TIME = 1000l;
+ private static final String ENCODED_GENRE_STRING = Genres.ANIMAL_WILDLIFE + "," + Genres.COMEDY
+ + "," + Genres.DRAMA + "," + Genres.EDUCATION + "," + Genres.FAMILY_KIDS + ","
+ + Genres.GAMING + "," + Genres.MOVIES + "," + Genres.NEWS + "," + Genres.SHOPPING + ","
+ + Genres.SPORTS + "," + Genres.TRAVEL;
+
private String mInputId;
private ContentResolver mContentResolver;
private Uri mChannelsUri;
@@ -510,7 +519,6 @@
values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
Uri channelUri = mContentResolver.insert(mChannelsUri, values);
assertNotNull(channelUri);
- long channelId = ContentUris.parseId(channelUri);
try (Cursor cursor = mContentResolver.query(
channelUri, CHANNELS_PROJECTION, null, null, null)) {
cursor.moveToNext();
@@ -522,4 +530,58 @@
}
values.clear();
}
+
+ public void testChannelsGetVideoResolution() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_480I));
+ assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_480P));
+ assertEquals(Channels.VIDEO_RESOLUTION_SD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_576I));
+ assertEquals(Channels.VIDEO_RESOLUTION_ED, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_576P));
+ assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_720P));
+ assertEquals(Channels.VIDEO_RESOLUTION_HD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_1080I));
+ assertEquals(Channels.VIDEO_RESOLUTION_FHD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_1080P));
+ assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_2160P));
+ assertEquals(Channels.VIDEO_RESOLUTION_UHD, Channels.getVideoResolution(
+ Channels.VIDEO_FORMAT_4320P));
+ assertEquals(null, Channels.getVideoResolution("Unknown format"));
+ }
+
+ public void testProgramsGenresDecode() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ List genres = Arrays.asList(Genres.decode(ENCODED_GENRE_STRING));
+ assertEquals(11, genres.size());
+ assertTrue(genres.contains(Genres.ANIMAL_WILDLIFE));
+ assertTrue(genres.contains(Genres.COMEDY));
+ assertTrue(genres.contains(Genres.DRAMA));
+ assertTrue(genres.contains(Genres.EDUCATION));
+ assertTrue(genres.contains(Genres.FAMILY_KIDS));
+ assertTrue(genres.contains(Genres.GAMING));
+ assertTrue(genres.contains(Genres.MOVIES));
+ assertTrue(genres.contains(Genres.NEWS));
+ assertTrue(genres.contains(Genres.SHOPPING));
+ assertTrue(genres.contains(Genres.SPORTS));
+ assertTrue(genres.contains(Genres.TRAVEL));
+ assertFalse(genres.contains(","));
+ }
+
+ public void testProgramsGenresEncode() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ assertEquals(ENCODED_GENRE_STRING, Genres.encode(Genres.ANIMAL_WILDLIFE,
+ Genres.COMEDY, Genres.DRAMA, Genres.EDUCATION, Genres.FAMILY_KIDS, Genres.GAMING,
+ Genres.MOVIES, Genres.NEWS, Genres.SHOPPING, Genres.SPORTS, Genres.TRAVEL));
+ }
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
index 440ecb2..de91916 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputInfoTest.java
@@ -20,8 +20,10 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
+import android.os.Parcel;
import android.test.AndroidTestCase;
/**
@@ -48,6 +50,53 @@
mPackageManager = getContext().getPackageManager();
}
+ public void testTvInputInfoOp() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ // Test describeContents
+ assertEquals(0, mStubInfo.describeContents());
+
+ // Test equals
+ assertTrue(mStubInfo.equals(mStubInfo));
+
+ // Test getId
+ final ComponentName componentName =
+ new ComponentName(getContext(), StubTunerTvInputService.class);
+ final String id = TvContract.buildInputId(componentName);
+ assertEquals(id, mStubInfo.getId());
+
+ // Test getServiceInfo
+ assertEquals(getContext().getPackageManager().getServiceInfo(componentName, 0).name,
+ mStubInfo.getServiceInfo().name);
+
+ // Test hashCode
+ assertEquals(id.hashCode(), mStubInfo.hashCode());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ mStubInfo.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ TvInputInfo infoFromParcel = TvInputInfo.CREATOR.createFromParcel(p);
+ assertEquals(mStubInfo.createSettingsIntent().getComponent(),
+ infoFromParcel.createSettingsIntent().getComponent());
+ assertEquals(mStubInfo.createSetupIntent().getComponent(),
+ infoFromParcel.createSetupIntent().getComponent());
+ assertEquals(mStubInfo.describeContents(), infoFromParcel.describeContents());
+ assertTrue(mStubInfo.equals(infoFromParcel));
+ assertEquals(mStubInfo.getId(), infoFromParcel.getId());
+ assertEquals(mStubInfo.getParentId(), infoFromParcel.getParentId());
+ assertEquals(mStubInfo.getServiceInfo().name, infoFromParcel.getServiceInfo().name);
+ assertEquals(mStubInfo.getType(), infoFromParcel.getType());
+ assertEquals(mStubInfo.hashCode(), infoFromParcel.hashCode());
+ assertEquals(mStubInfo.isPassthroughInput(), infoFromParcel.isPassthroughInput());
+ assertEquals(mStubInfo.loadIcon(getContext()).getConstantState(),
+ infoFromParcel.loadIcon(getContext()).getConstantState());
+ assertEquals(mStubInfo.loadLabel(getContext()), infoFromParcel.loadLabel(getContext()));
+ assertEquals(mStubInfo.toString(), infoFromParcel.toString());
+ p.recycle();
+ }
+
public void testGetIntentForSettingsActivity() throws Exception {
if (!Utils.hasTvInputFramework(getContext())) {
return;
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
index 790adf9..ff66dc6 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -16,26 +16,38 @@
package android.media.tv.cts;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.cts.util.PollingCheck;
+import android.media.tv.TvContentRating;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager;
-import android.test.AndroidTestCase;
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase2;
+import java.util.ArrayList;
import java.util.List;
/**
* Test for {@link android.media.tv.TvInputManager}.
*/
-public class TvInputManagerTest extends AndroidTestCase {
+public class TvInputManagerTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+ /** The maximum time to wait for an operation. */
+ private static final long TIME_OUT_MS = 15000L;
+
private static final String[] VALID_TV_INPUT_SERVICES = {
StubTunerTvInputService.class.getName()
};
private static final String[] INVALID_TV_INPUT_SERVICES = {
NoMetadataTvInputService.class.getName(), NoPermissionTvInputService.class.getName()
};
+ private static final TvContentRating DUMMY_RATING = TvContentRating.createRating(
+ "com.android.tv", "US_TV", "US_TV_PG", "US_TV_D", "US_TV_L");
private String mStubId;
private TvInputManager mManager;
+ private LoggingCallback mCallabck = new LoggingCallback();
private static TvInputInfo getInfoForClassName(List<TvInputInfo> list, String name) {
for (TvInputInfo info : list) {
@@ -46,33 +58,37 @@
return null;
}
+ public TvInputManagerTest() {
+ super(TvViewStubActivity.class);
+ }
+
@Override
public void setUp() throws Exception {
- if (!Utils.hasTvInputFramework(getContext())) {
+ if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
- mManager = (TvInputManager) mContext.getSystemService(Context.TV_INPUT_SERVICE);
+ mManager = (TvInputManager) getActivity().getSystemService(Context.TV_INPUT_SERVICE);
mStubId = getInfoForClassName(
- mManager.getTvInputList(), StubTunerTvInputService.class.getName()).getId();
+ mManager.getTvInputList(), StubTvInputService2.class.getName()).getId();
}
public void testGetInputState() throws Exception {
- if (!Utils.hasTvInputFramework(getContext())) {
+ if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
assertEquals(mManager.getInputState(mStubId), TvInputManager.INPUT_STATE_CONNECTED);
}
public void testGetTvInputInfo() throws Exception {
- if (!Utils.hasTvInputFramework(getContext())) {
+ if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
assertEquals(mManager.getTvInputInfo(mStubId), getInfoForClassName(
- mManager.getTvInputList(), StubTunerTvInputService.class.getName()));
+ mManager.getTvInputList(), StubTvInputService2.class.getName()));
}
public void testGetTvInputList() throws Exception {
- if (!Utils.hasTvInputFramework(getContext())) {
+ if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
List<TvInputInfo> list = mManager.getTvInputList();
@@ -85,4 +101,130 @@
getInfoForClassName(list, name));
}
}
+
+ public void testIsParentalControlsEnabled() {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ try {
+ mManager.isParentalControlsEnabled();
+ } catch (Exception e) {
+ fail();
+ }
+ }
+
+ public void testIsRatingBlocked() {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ try {
+ mManager.isRatingBlocked(DUMMY_RATING);
+ } catch (Exception e) {
+ fail();
+ }
+ }
+
+ public void testRegisterUnregisterCallback() {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mManager.registerCallback(mCallabck, new Handler());
+ mManager.unregisterCallback(mCallabck);
+ } catch (Exception e) {
+ fail();
+ }
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+ public void testInputAddedAndRemoved() {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mManager.registerCallback(mCallabck, new Handler());
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+
+ // Test if onInputRemoved() is called.
+ mCallabck.resetLogs();
+ PackageManager pm = getActivity().getPackageManager();
+ ComponentName component = new ComponentName(getActivity(), StubTvInputService2.class);
+ assertTrue(PackageManager.COMPONENT_ENABLED_STATE_DISABLED != pm.getComponentEnabledSetting(
+ component));
+ pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mCallabck.isInputRemoved(mStubId);
+ }
+ }.run();
+
+ // Test if onInputAdded() is called.
+ mCallabck.resetLogs();
+ assertEquals(PackageManager.COMPONENT_ENABLED_STATE_DISABLED, pm.getComponentEnabledSetting(
+ component));
+ pm.setComponentEnabledSetting(component, PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mCallabck.isInputAdded(mStubId);
+ }
+ }.run();
+
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mManager.unregisterCallback(mCallabck);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ }
+
+ private static class LoggingCallback extends TvInputManager.TvInputCallback {
+ private final List<String> mAddedInputs = new ArrayList<>();
+ private final List<String> mRemovedInputs = new ArrayList<>();
+
+ @Override
+ public synchronized void onInputAdded(String inputId) {
+ mAddedInputs.add(inputId);
+ }
+
+ @Override
+ public synchronized void onInputRemoved(String inputId) {
+ mRemovedInputs.add(inputId);
+ }
+
+ public synchronized void resetLogs() {
+ mAddedInputs.clear();
+ mRemovedInputs.clear();
+ }
+
+ public synchronized boolean isInputAdded(String inputId) {
+ return mRemovedInputs.isEmpty() && mAddedInputs.size() == 1 && mAddedInputs.contains(
+ inputId);
+ }
+
+ public synchronized boolean isInputRemoved(String inputId) {
+ return mAddedInputs.isEmpty() && mRemovedInputs.size() == 1 && mRemovedInputs.contains(
+ inputId);
+ }
+ }
+
+ public static class StubTvInputService2 extends StubTvInputService {
+ @Override
+ public Session onCreateSession(String inputId) {
+ return null;
+ }
+ }
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
index f0ee2772..b4c863a 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
@@ -20,6 +20,7 @@
import android.app.Instrumentation;
import android.content.Context;
import android.cts.util.PollingCheck;
+import android.media.PlaybackParams;
import android.media.tv.TvContentRating;
import android.media.tv.TvContract;
import android.media.tv.TvInputInfo;
@@ -28,9 +29,15 @@
import android.media.tv.TvView;
import android.media.tv.cts.TvInputServiceTest.CountingTvInputService.CountingSession;
import android.net.Uri;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
+import android.view.InputDevice;
import android.view.KeyEvent;
+import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceView;
+import android.view.View;
+import android.widget.LinearLayout;
import com.android.cts.tv.R;
@@ -46,8 +53,8 @@
private static final long TIME_OUT = 15000L;
private static final String mDummyTrackId = "dummyTrackId";
private static final TvTrackInfo mDummyTrack =
- new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, mDummyTrackId)
- .setLanguage("und").build();
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, mDummyTrackId)
+ .setVideoWidth(1920).setVideoHeight(1080).setLanguage("und").build();
private TvView mTvView;
private Activity mActivity;
@@ -55,6 +62,8 @@
private TvInputManager mManager;
private TvInputInfo mStubInfo;
private final StubCallback mCallback = new StubCallback();
+ private final StubTimeShiftPositionCallback mTimeShiftPositionCallback =
+ new StubTimeShiftPositionCallback();
private static class StubCallback extends TvView.TvInputCallback {
private int mChannelRetunedCount;
@@ -62,8 +71,10 @@
private int mVideoUnavailableCount;
private int mTrackSelectedCount;
private int mTrackChangedCount;
+ private int mVideoSizeChanged;
private int mContentAllowedCount;
private int mContentBlockedCount;
+ private int mTimeShiftStatusChangedCount;
@Override
public void onChannelRetuned(String inputId, Uri channelUri) {
@@ -91,6 +102,11 @@
}
@Override
+ public void onVideoSizeChanged(String inputId, int width, int height) {
+ mVideoSizeChanged++;
+ }
+
+ @Override
public void onContentAllowed(String inputId) {
mContentAllowedCount++;
}
@@ -99,6 +115,42 @@
public void onContentBlocked(String inputId, TvContentRating rating) {
mContentBlockedCount++;
}
+
+ @Override
+ public void onTimeShiftStatusChanged(String inputId, int status) {
+ mTimeShiftStatusChangedCount++;
+ }
+
+ public void resetCounts() {
+ mChannelRetunedCount = 0;
+ mVideoAvailableCount = 0;
+ mVideoUnavailableCount = 0;
+ mTrackSelectedCount = 0;
+ mTrackChangedCount = 0;
+ mContentAllowedCount = 0;
+ mContentBlockedCount = 0;
+ mTimeShiftStatusChangedCount = 0;
+ }
+ }
+
+ private static class StubTimeShiftPositionCallback extends TvView.TimeShiftPositionCallback {
+ private int mTimeShiftStartPositionChanged;
+ private int mTimeShiftCurrentPositionChanged;
+
+ @Override
+ public void onTimeShiftStartPositionChanged(String inputId, long timeMs) {
+ mTimeShiftStartPositionChanged++;
+ }
+
+ @Override
+ public void onTimeShiftCurrentPositionChanged(String inputId, long timeMs) {
+ mTimeShiftCurrentPositionChanged++;
+ }
+
+ public void resetCounts() {
+ mTimeShiftStartPositionChanged = 0;
+ mTimeShiftCurrentPositionChanged = 0;
+ }
}
public TvInputServiceTest() {
@@ -135,7 +187,18 @@
verifyCommandSetStreamVolume();
verifyCommandSetCaptionEnabled();
verifyCommandSelectTrack();
- verifyCommandDispatchKeyEvent();
+ verifyCommandDispatchKeyDown();
+ verifyCommandDispatchKeyMultiple();
+ verifyCommandDispatchKeyUp();
+ verifyCommandDispatchTouchEvent();
+ verifyCommandDispatchTrackballEvent();
+ verifyCommandDispatchGenericMotionEvent();
+ verifyCommandTimeShiftPause();
+ verifyCommandTimeShiftResume();
+ verifyCommandTimeShiftSeekTo();
+ verifyCommandTimeShiftSetPlaybackParams();
+ verifyCommandSetTimeShiftPositionCallback();
+ verifyCommandOverlayViewSizeChanged();
verifyCallbackChannelRetuned();
verifyCallbackVideoAvailable();
verifyCallbackVideoUnavailable();
@@ -143,6 +206,8 @@
verifyCallbackTrackSelected();
verifyCallbackContentAllowed();
verifyCallbackContentBlocked();
+ verifyCallbackTimeShiftStatusChanged();
+ verifyCallbackLayoutSurface();
runTestOnUiThread(new Runnable() {
@Override
@@ -161,12 +226,13 @@
@Override
protected boolean check() {
CountingSession session = CountingTvInputService.sSession;
- return session != null && session.mTuneCount > 0;
+ return session != null && session.mTuneCount > 0 && session.mCreateOverlayView > 0;
}
}.run();
}
public void verifyCommandSetStreamVolume() {
+ resetCounts();
mTvView.setStreamVolume(1.0f);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@@ -179,6 +245,7 @@
}
public void verifyCommandSetCaptionEnabled() {
+ resetCounts();
mTvView.setCaptionEnabled(true);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@@ -191,18 +258,21 @@
}
public void verifyCommandSelectTrack() {
- mTvView.selectTrack(TvTrackInfo.TYPE_AUDIO, "dummyTrackId");
+ resetCounts();
+ verifyCallbackTracksChanged();
+ mTvView.selectTrack(mDummyTrack.getType(), mDummyTrack.getId());
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@Override
protected boolean check() {
CountingSession session = CountingTvInputService.sSession;
- return session != null && session.mSetStreamVolumeCount > 0;
+ return session != null && session.mSelectTrackCount > 0;
}
}.run();
}
- public void verifyCommandDispatchKeyEvent() {
+ public void verifyCommandDispatchKeyDown() {
+ resetCounts();
mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_K));
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@@ -214,7 +284,167 @@
}.run();
}
+ public void verifyCommandDispatchKeyMultiple() {
+ resetCounts();
+ mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_MULTIPLE, KeyEvent.KEYCODE_K));
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mKeyMultipleCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandDispatchKeyUp() {
+ resetCounts();
+ mTvView.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_K));
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mKeyUpCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandDispatchTouchEvent() {
+ resetCounts();
+ long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+ 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+ event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mTvView.dispatchTouchEvent(event);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTouchEventCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandDispatchTrackballEvent() {
+ resetCounts();
+ long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+ 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+ event.setSource(InputDevice.SOURCE_TRACKBALL);
+ mTvView.dispatchTouchEvent(event);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTrackballEventCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandDispatchGenericMotionEvent() {
+ resetCounts();
+ long now = SystemClock.uptimeMillis();
+ MotionEvent event = MotionEvent.obtain(now, now, MotionEvent.ACTION_DOWN, 1.0f, 1.0f,
+ 1.0f, 1.0f, 0, 1.0f, 1.0f, 0, 0);
+ mTvView.dispatchGenericMotionEvent(event);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mGenricMotionEventCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandTimeShiftPause() {
+ resetCounts();
+ mTvView.timeShiftPause();
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTimeShiftPauseCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandTimeShiftResume() {
+ resetCounts();
+ mTvView.timeShiftResume();
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTimeShiftResumeCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandTimeShiftSeekTo() {
+ resetCounts();
+ mTvView.timeShiftSeekTo(0);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTimeShiftSeekToCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandTimeShiftSetPlaybackParams() {
+ resetCounts();
+ mTvView.timeShiftSetPlaybackParams(new PlaybackParams().setSpeed(2.0f)
+ .setAudioFallbackMode(PlaybackParams.AUDIO_FALLBACK_MODE_DEFAULT));
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mTimeShiftSetPlaybackParamsCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandSetTimeShiftPositionCallback() {
+ resetCounts();
+ mTvView.setTimeShiftPositionCallback(mTimeShiftPositionCallback);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mTimeShiftPositionCallback.mTimeShiftCurrentPositionChanged > 0
+ && mTimeShiftPositionCallback.mTimeShiftStartPositionChanged > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCommandOverlayViewSizeChanged() {
+ resetCounts();
+ mActivity.runOnUiThread(new Runnable() {
+ public void run() {
+ mTvView.setLayoutParams(new LinearLayout.LayoutParams(10, 20));
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = CountingTvInputService.sSession;
+ return session != null && session.mOverlayViewSizeChangedCount > 0;
+ }
+ }.run();
+ }
+
public void verifyCallbackChannelRetuned() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
Uri fakeChannelUri = TvContract.buildChannelUri(0);
@@ -228,6 +458,7 @@
}
public void verifyCallbackVideoAvailable() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyVideoAvailable();
@@ -240,6 +471,7 @@
}
public void verifyCallbackVideoUnavailable() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
@@ -252,6 +484,7 @@
}
public void verifyCallbackTracksChanged() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
@@ -265,7 +498,23 @@
}.run();
}
+ public void verifyCallbackVideoSizeChanged() {
+ resetCounts();
+ CountingSession session = CountingTvInputService.sSession;
+ assertNotNull(session);
+ ArrayList<TvTrackInfo> tracks = new ArrayList<>();
+ tracks.add(mDummyTrack);
+ session.notifyTracksChanged(tracks);
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mCallback.mVideoSizeChanged > 0;
+ }
+ }.run();
+ }
+
public void verifyCallbackTrackSelected() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyTrackSelected(mDummyTrack.getType(), mDummyTrack.getId());
@@ -278,6 +527,7 @@
}
public void verifyCallbackContentAllowed() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyContentAllowed();
@@ -290,6 +540,7 @@
}
public void verifyCallbackContentBlocked() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
@@ -303,13 +554,59 @@
}.run();
}
+ public void verifyCallbackTimeShiftStatusChanged() {
+ resetCounts();
+ CountingSession session = CountingTvInputService.sSession;
+ assertNotNull(session);
+ session.notifyTimeShiftStatusChanged(TvInputManager.TIME_SHIFT_STATUS_AVAILABLE);
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ return mCallback.mTimeShiftStatusChangedCount > 0;
+ }
+ }.run();
+ }
+
+ public void verifyCallbackLayoutSurface() {
+ resetCounts();
+ final int left = 10;
+ final int top = 20;
+ final int right = 30;
+ final int bottom = 40;
+ CountingSession session = CountingTvInputService.sSession;
+ assertNotNull(session);
+ session.layoutSurface(left, top, right, bottom);
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ int childCount = mTvView.getChildCount();
+ for (int i = 0; i < childCount; ++i) {
+ View v = mTvView.getChildAt(i);
+ if (v instanceof SurfaceView) {
+ return v.getLeft() == left && v.getTop() == top && v.getRight() == right
+ && v.getBottom() == bottom;
+ }
+ }
+ return false;
+ }
+ }.run();
+ }
+
+ private void resetCounts() {
+ if (CountingTvInputService.sSession != null) {
+ CountingTvInputService.sSession.resetCounts();
+ }
+ mCallback.resetCounts();
+ mTimeShiftPositionCallback.resetCounts();
+ }
+
public static class CountingTvInputService extends StubTvInputService {
- static CountingTvInputService sInstance;
static CountingSession sSession;
@Override
public Session onCreateSession(String inputId) {
sSession = new CountingSession(this);
+ sSession.setOverlayViewEnabled(true);
return sSession;
}
@@ -318,12 +615,48 @@
public volatile int mSetStreamVolumeCount;
public volatile int mSetCaptionEnabledCount;
public volatile int mSelectTrackCount;
+ public volatile int mCreateOverlayView;
public volatile int mKeyDownCount;
+ public volatile int mKeyLongPressCount;
+ public volatile int mKeyMultipleCount;
+ public volatile int mKeyUpCount;
+ public volatile int mTouchEventCount;
+ public volatile int mTrackballEventCount;
+ public volatile int mGenricMotionEventCount;
+ public volatile int mOverlayViewSizeChangedCount;
+ public volatile int mTimeShiftPauseCount;
+ public volatile int mTimeShiftResumeCount;
+ public volatile int mTimeShiftSeekToCount;
+ public volatile int mTimeShiftSetPlaybackParamsCount;
+ public volatile long mTimeShiftGetCurrentPositionCount;
+ public volatile long mTimeShiftGetStartPositionCount;
CountingSession(Context context) {
super(context);
}
+ public void resetCounts() {
+ mTuneCount = 0;
+ mSetStreamVolumeCount = 0;
+ mSetCaptionEnabledCount = 0;
+ mSelectTrackCount = 0;
+ mCreateOverlayView = 0;
+ mKeyDownCount = 0;
+ mKeyLongPressCount = 0;
+ mKeyMultipleCount = 0;
+ mKeyUpCount = 0;
+ mTouchEventCount = 0;
+ mTrackballEventCount = 0;
+ mGenricMotionEventCount = 0;
+ mOverlayViewSizeChangedCount = 0;
+ mTimeShiftPauseCount = 0;
+ mTimeShiftResumeCount = 0;
+ mTimeShiftSeekToCount = 0;
+ mTimeShiftSetPlaybackParamsCount = 0;
+ mTimeShiftGetCurrentPositionCount = 0;
+ mTimeShiftGetStartPositionCount = 0;
+ }
+
@Override
public void onRelease() {
}
@@ -356,10 +689,87 @@
}
@Override
+ public View onCreateOverlayView() {
+ mCreateOverlayView++;
+ return null;
+ }
+
+ @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
mKeyDownCount++;
return false;
}
+
+ @Override
+ public boolean onKeyLongPress(int keyCode, KeyEvent event) {
+ mKeyLongPressCount++;
+ return false;
+ }
+
+ @Override
+ public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
+ mKeyMultipleCount++;
+ return false;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ mKeyUpCount++;
+ return false;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ mTouchEventCount++;
+ return false;
+ }
+
+ @Override
+ public boolean onTrackballEvent(MotionEvent event) {
+ mTrackballEventCount++;
+ return false;
+ }
+
+ @Override
+ public boolean onGenericMotionEvent(MotionEvent event) {
+ mGenricMotionEventCount++;
+ return false;
+ }
+
+ @Override
+ public void onTimeShiftPause() {
+ mTimeShiftPauseCount++;
+ }
+
+ @Override
+ public void onTimeShiftResume() {
+ mTimeShiftResumeCount++;
+ }
+
+ @Override
+ public void onTimeShiftSeekTo(long timeMs) {
+ mTimeShiftSeekToCount++;
+ }
+
+ @Override
+ public void onTimeShiftSetPlaybackParams(PlaybackParams param) {
+ mTimeShiftSetPlaybackParamsCount++;
+ }
+
+ @Override
+ public long onTimeShiftGetCurrentPosition() {
+ return ++mTimeShiftGetCurrentPositionCount;
+ }
+
+ @Override
+ public long onTimeShiftGetStartPosition() {
+ return ++mTimeShiftGetStartPositionCount;
+ }
+
+ @Override
+ public void onOverlayViewSizeChanged(int width, int height) {
+ mOverlayViewSizeChangedCount++;
+ }
}
}
}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
new file mode 100644
index 0000000..a99bd77
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/TvTrackInfoTest.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.cts;
+
+import android.media.tv.TvTrackInfo;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.tv.TvTrackInfo}.
+ */
+public class TvTrackInfoTest extends AndroidTestCase {
+
+ public void testAudioTrackInfoOp() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final Bundle bundle = new Bundle();
+ final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "id_audio")
+ .setAudioChannelCount(2)
+ .setAudioSampleRate(48000)
+ .setLanguage("eng")
+ .setExtra(bundle)
+ .build();
+ assertEquals(TvTrackInfo.TYPE_AUDIO, info.getType());
+ assertEquals("id_audio", info.getId());
+ assertEquals(2, info.getAudioChannelCount());
+ assertEquals(48000, info.getAudioSampleRate());
+ assertEquals("eng", info.getLanguage());
+ assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+ assertEquals(0, info.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+ assertEquals(TvTrackInfo.TYPE_AUDIO, infoFromParcel.getType());
+ assertEquals("id_audio", infoFromParcel.getId());
+ assertEquals(2, infoFromParcel.getAudioChannelCount());
+ assertEquals(48000, infoFromParcel.getAudioSampleRate());
+ assertEquals("eng", infoFromParcel.getLanguage());
+ assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+ assertEquals(0, infoFromParcel.describeContents());
+ p.recycle();
+ }
+
+ public void testVideoTrackInfoOp() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean("testTrue", true);
+ final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "id_video")
+ .setVideoWidth(1920)
+ .setVideoHeight(1080)
+ .setVideoFrameRate(29.97f)
+ .setLanguage("eng")
+ .setExtra(bundle)
+ .build();
+ assertEquals(TvTrackInfo.TYPE_VIDEO, info.getType());
+ assertEquals("id_video", info.getId());
+ assertEquals(1920, info.getVideoWidth());
+ assertEquals(1080, info.getVideoHeight());
+ assertEquals(29.97f, info.getVideoFrameRate());
+ assertEquals("eng", info.getLanguage());
+ assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+ assertEquals(0, info.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+ assertEquals(TvTrackInfo.TYPE_VIDEO, infoFromParcel.getType());
+ assertEquals("id_video", infoFromParcel.getId());
+ assertEquals(1920, infoFromParcel.getVideoWidth());
+ assertEquals(1080, infoFromParcel.getVideoHeight());
+ assertEquals(29.97f, infoFromParcel.getVideoFrameRate());
+ assertEquals("eng", infoFromParcel.getLanguage());
+ assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+ assertEquals(0, infoFromParcel.describeContents());
+ p.recycle();
+ }
+
+ public void testSubtitleTrackInfoOp() {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final Bundle bundle = new Bundle();
+ bundle.putBoolean("testTrue", true);
+ final TvTrackInfo info = new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "id_subtitle")
+ .setLanguage("eng")
+ .setExtra(bundle)
+ .build();
+ assertEquals(TvTrackInfo.TYPE_SUBTITLE, info.getType());
+ assertEquals("id_subtitle", info.getId());
+ assertEquals("eng", info.getLanguage());
+ assertEquals(bundle.get("testTrue"), info.getExtra().get("testTrue"));
+ assertEquals(0, info.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ info.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ TvTrackInfo infoFromParcel = TvTrackInfo.CREATOR.createFromParcel(p);
+ assertEquals(TvTrackInfo.TYPE_SUBTITLE, infoFromParcel.getType());
+ assertEquals("id_subtitle", infoFromParcel.getId());
+ assertEquals("eng", infoFromParcel.getLanguage());
+ assertEquals(bundle.get("testTrue"), infoFromParcel.getExtra().get("testTrue"));
+ assertEquals(0, infoFromParcel.describeContents());
+ p.recycle();
+ }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
index 930dd6a..1c59462 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvViewTest.java
@@ -47,7 +47,7 @@
*/
public class TvViewTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
/** The maximum time to wait for an operation. */
- private static final long TIME_OUT = 15000L;
+ private static final long TIME_OUT_MS = 15000L;
private TvView mTvView;
private Activity mActivity;
@@ -61,6 +61,7 @@
private final Map<String, SparseIntArray> mSelectedTrackGenerationMap = new ArrayMap<>();
private final Map<String, Integer> mTracksGenerationMap = new ArrayMap<>();
private final Object mLock = new Object();
+ private volatile int mConnectionFailedCount;
public boolean isVideoAvailable(String inputId) {
synchronized (mLock) {
@@ -80,6 +81,19 @@
}
}
+ public void resetConnectionFailedCount() {
+ mConnectionFailedCount = 0;
+ }
+
+ public int getConnectionFailedCount() {
+ return mConnectionFailedCount;
+ }
+
+ @Override
+ public void onConnectionFailed(String inputId) {
+ mConnectionFailedCount++;
+ }
+
@Override
public void onVideoAvailable(String inputId) {
synchronized (mLock) {
@@ -201,7 +215,7 @@
Uri channelUri = TvContract.buildChannelUri(channelId);
mTvView.tune(mStubInfo.getId(), channelUri);
mInstrumentation.waitForIdleSync();
- new PollingCheck(TIME_OUT) {
+ new PollingCheck(TIME_OUT_MS) {
@Override
protected boolean check() {
return mCallback.isVideoAvailable(mStubInfo.getId());
@@ -232,7 +246,7 @@
if ((track == null && selectedTrackId != null)
|| (track != null && !track.getId().equals(selectedTrackId))) {
// Check generation change only if we're actually changing track.
- new PollingCheck(TIME_OUT) {
+ new PollingCheck(TIME_OUT_MS) {
@Override
protected boolean check() {
return mCallback.getSelectedTrackGeneration(
@@ -258,6 +272,8 @@
case TvTrackInfo.TYPE_VIDEO:
assertEquals(track.getVideoHeight(), selectedTrack.getVideoHeight());
assertEquals(track.getVideoWidth(), selectedTrack.getVideoWidth());
+ assertEquals(track.getVideoPixelAspectRatio(),
+ selectedTrack.getVideoPixelAspectRatio(), 0.001f);
break;
case TvTrackInfo.TYPE_AUDIO:
assertEquals(track.getAudioChannelCount(),
@@ -267,6 +283,7 @@
break;
case TvTrackInfo.TYPE_SUBTITLE:
assertEquals(track.getLanguage(), selectedTrack.getLanguage());
+ assertEquals(track.getDescription(), selectedTrack.getDescription());
break;
default:
fail("Unrecognized type: " + track.getType());
@@ -281,7 +298,7 @@
TvTrackInfo videoTrack1 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-HD")
.setVideoHeight(1920).setVideoWidth(1080).build();
TvTrackInfo videoTrack2 = new TvTrackInfo.Builder(TvTrackInfo.TYPE_VIDEO, "video-SD")
- .setVideoHeight(640).setVideoWidth(360).build();
+ .setVideoHeight(640).setVideoWidth(360).setVideoPixelAspectRatio(1.09f).build();
TvTrackInfo audioTrack1 =
new TvTrackInfo.Builder(TvTrackInfo.TYPE_AUDIO, "audio-stereo-eng")
.setLanguage("eng").setAudioChannelCount(2).setAudioSampleRate(48000).build();
@@ -293,17 +310,20 @@
TvTrackInfo subtitleTrack2 =
new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-esp")
.setLanguage("esp").build();
+ TvTrackInfo subtitleTrack3 =
+ new TvTrackInfo.Builder(TvTrackInfo.TYPE_SUBTITLE, "subtitle-eng2")
+ .setLanguage("eng").setDescription("audio commentary").build();
StubTunerTvInputService.injectTrack(videoTrack1, videoTrack2, audioTrack1, audioTrack2,
subtitleTrack1, subtitleTrack2);
final List<TvTrackInfo> tracks = new ArrayList<TvTrackInfo>();
Collections.addAll(tracks, videoTrack1, videoTrack2, audioTrack1, audioTrack2,
- subtitleTrack1, subtitleTrack2);
+ subtitleTrack1, subtitleTrack2, subtitleTrack3);
tryTuneAllChannels(new Runnable() {
@Override
public void run() {
- new PollingCheck(TIME_OUT) {
+ new PollingCheck(TIME_OUT_MS) {
@Override
protected boolean check() {
return mTvView.getTracks(TvTrackInfo.TYPE_AUDIO) != null;
@@ -325,7 +345,7 @@
unhandledEvent[0] = null;
mInstrumentation.sendKeySync(keyEvent);
mInstrumentation.waitForIdleSync();
- new PollingCheck(TIME_OUT) {
+ new PollingCheck(TIME_OUT_MS) {
@Override
protected boolean check() {
return unhandledEvent[0] != null;
@@ -362,7 +382,7 @@
Uri channelUri = TvContract.buildChannelUri(channelId);
mTvView.tune(mStubInfo.getId(), channelUri);
mInstrumentation.waitForIdleSync();
- new PollingCheck(TIME_OUT) {
+ new PollingCheck(TIME_OUT_MS) {
@Override
protected boolean check() {
return mCallback.isVideoAvailable(mStubInfo.getId());
@@ -382,4 +402,19 @@
verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
verifyKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_GUIDE), unhandledEvent);
}
+
+ public void testConnectionFailed() throws Throwable {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ mCallback.resetConnectionFailedCount();
+ mTvView.tune("invalid_input_id", TvContract.Channels.CONTENT_URI);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mCallback.getConnectionFailedCount() > 0;
+ }
+ }.run();
+ }
}
diff --git a/tests/tests/view/src/android/view/cts/ActionModeCallback2Test.java b/tests/tests/view/src/android/view/cts/ActionModeCallback2Test.java
new file mode 100644
index 0000000..e75b7ae
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ActionModeCallback2Test.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view.cts;
+
+import android.graphics.Rect;
+import android.test.AndroidTestCase;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+
+public class ActionModeCallback2Test extends AndroidTestCase {
+ private static final int VIEW_WIDTH = 123;
+ private static final int VIEW_HEIGHT = 456;
+
+ public void testCallbackOnGetContentRectDefaultWithView() {
+ View view = new View(mContext);
+ view.setLeft(0);
+ view.setRight(VIEW_WIDTH);
+ view.setTop(0);
+ view.setBottom(VIEW_HEIGHT);
+
+ Rect outRect = new Rect();
+ MockActionModeCallback2 callback = new MockActionModeCallback2();
+ callback.onGetContentRect(null, view, outRect);
+
+ assertEquals(0, outRect.top);
+ assertEquals(0, outRect.left);
+ assertEquals(VIEW_HEIGHT, outRect.bottom);
+ assertEquals(VIEW_WIDTH, outRect.right);
+ }
+
+ public void testCallbackOnGetContentRectDefaultWithoutView() {
+ Rect outRect = new Rect();
+ MockActionModeCallback2 callback = new MockActionModeCallback2();
+ callback.onGetContentRect(null, null, outRect);
+
+ assertEquals(0, outRect.top);
+ assertEquals(0, outRect.left);
+ assertEquals(0, outRect.bottom);
+ assertEquals(0, outRect.right);
+ }
+
+ private static class MockActionModeCallback2 extends ActionMode.Callback2 {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {}
+ }
+
+}
diff --git a/tests/tests/view/src/android/view/cts/ActionModeTest.java b/tests/tests/view/src/android/view/cts/ActionModeTest.java
new file mode 100644
index 0000000..61df9fe
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/ActionModeTest.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.view.cts;
+
+import android.test.AndroidTestCase;
+import android.view.ActionMode;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.View;
+
+public class ActionModeTest extends AndroidTestCase {
+
+ public void testSetType() {
+ ActionMode actionMode = new MockActionMode();
+ assertEquals(ActionMode.TYPE_PRIMARY, actionMode.getType());
+
+ actionMode.setType(ActionMode.TYPE_FLOATING);
+ assertEquals(ActionMode.TYPE_FLOATING, actionMode.getType());
+
+ actionMode.setType(ActionMode.TYPE_PRIMARY);
+ assertEquals(ActionMode.TYPE_PRIMARY, actionMode.getType());
+ }
+
+ public void testInvalidateContentRectDoesNotInvalidateFull() {
+ MockActionMode actionMode = new MockActionMode();
+
+ actionMode.invalidateContentRect();
+
+ assertFalse(actionMode.mInvalidateWasCalled);
+ }
+
+ private static class MockActionMode extends ActionMode {
+ boolean mInvalidateWasCalled = false;
+
+ @Override
+ public void setTitle(CharSequence title) {}
+
+ @Override
+ public void setTitle(int resId) {}
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {}
+
+ @Override
+ public void setSubtitle(int resId) {}
+
+ @Override
+ public void setCustomView(View view) {}
+
+ @Override
+ public void invalidate() {
+ mInvalidateWasCalled = true;
+ }
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public Menu getMenu() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return null;
+ }
+
+ @Override
+ public View getCustomView() {
+ return null;
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return null;
+ }
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index 3024e40..34e4d09 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -56,6 +56,8 @@
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuInflater;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.TouchDelegate;
@@ -3383,6 +3385,156 @@
bg.hasCalledSetTint());
}
+ public void testStartActionModeWithParent() {
+ View view = new View(mActivity);
+ MockViewGroup parent = new MockViewGroup(mActivity);
+ parent.addView(view);
+
+ ActionMode mode = view.startActionMode(null);
+
+ assertNotNull(mode);
+ assertEquals(NO_OP_ACTION_MODE, mode);
+ assertTrue(parent.isStartActionModeForChildCalled);
+ assertEquals(ActionMode.TYPE_PRIMARY, parent.startActionModeForChildType);
+ }
+
+ public void testStartActionModeWithoutParent() {
+ View view = new View(mActivity);
+
+ ActionMode mode = view.startActionMode(null);
+
+ assertNull(mode);
+ }
+
+ public void testStartActionModeTypedWithParent() {
+ View view = new View(mActivity);
+ MockViewGroup parent = new MockViewGroup(mActivity);
+ parent.addView(view);
+
+ ActionMode mode = view.startActionMode(null, ActionMode.TYPE_FLOATING);
+
+ assertNotNull(mode);
+ assertEquals(NO_OP_ACTION_MODE, mode);
+ assertTrue(parent.isStartActionModeForChildCalled);
+ assertEquals(ActionMode.TYPE_FLOATING, parent.startActionModeForChildType);
+ }
+
+ public void testStartActionModeTypedWithoutParent() {
+ View view = new View(mActivity);
+
+ ActionMode mode = view.startActionMode(null, ActionMode.TYPE_FLOATING);
+
+ assertNull(mode);
+ }
+
+ private static class MockViewGroup extends ViewGroup {
+ boolean isStartActionModeForChildCalled = false;
+ int startActionModeForChildType = ActionMode.TYPE_PRIMARY;
+
+ public MockViewGroup(Context context) {
+ super(context);
+ }
+
+ @Override
+ public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
+ isStartActionModeForChildCalled = true;
+ startActionModeForChildType = ActionMode.TYPE_PRIMARY;
+ return NO_OP_ACTION_MODE;
+ }
+
+ @Override
+ public ActionMode startActionModeForChild(
+ View originalView, ActionMode.Callback callback, int type) {
+ isStartActionModeForChildCalled = true;
+ startActionModeForChildType = type;
+ return NO_OP_ACTION_MODE;
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+ // no-op
+ }
+ }
+
+ private static final ActionMode NO_OP_ACTION_MODE =
+ new ActionMode() {
+ @Override
+ public void setTitle(CharSequence title) {}
+
+ @Override
+ public void setTitle(int resId) {}
+
+ @Override
+ public void setSubtitle(CharSequence subtitle) {}
+
+ @Override
+ public void setSubtitle(int resId) {}
+
+ @Override
+ public void setCustomView(View view) {}
+
+ @Override
+ public void invalidate() {}
+
+ @Override
+ public void finish() {}
+
+ @Override
+ public Menu getMenu() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getTitle() {
+ return null;
+ }
+
+ @Override
+ public CharSequence getSubtitle() {
+ return null;
+ }
+
+ @Override
+ public View getCustomView() {
+ return null;
+ }
+
+ @Override
+ public MenuInflater getMenuInflater() {
+ return null;
+ }
+ };
+
+ public void testTranslationSetter() {
+ View view = new View(mActivity);
+ float offset = 10.0f;
+ view.setTranslationX(offset);
+ view.setTranslationY(offset);
+ view.setTranslationZ(offset);
+ view.setElevation(offset);
+
+ assertEquals("Incorrect translationX", offset, view.getTranslationX());
+ assertEquals("Incorrect translationY", offset, view.getTranslationY());
+ assertEquals("Incorrect translationZ", offset, view.getTranslationZ());
+ assertEquals("Incorrect elevation", offset, view.getElevation());
+ }
+
+ public void testXYZ() {
+ View view = new View(mActivity);
+ float offset = 10.0f;
+ float start = 15.0f;
+ view.setTranslationX(offset);
+ view.setLeft((int) start);
+ view.setTranslationY(offset);
+ view.setTop((int) start);
+ view.setTranslationZ(offset);
+ view.setElevation(start);
+
+ assertEquals("Incorrect X value", offset + start, view.getX());
+ assertEquals("Incorrect Y value", offset + start, view.getY());
+ assertEquals("Incorrect Z value", offset + start, view.getZ());
+ }
+
private static class MockDrawable extends Drawable {
private boolean mCalledSetTint = false;
diff --git a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
index e393bb6..2a6af6e 100644
--- a/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/PostMessageTest.java
@@ -105,7 +105,7 @@
}
loadPage(TITLE_FROM_POST_MESSAGE);
WebMessage message = new WebMessage(WEBVIEW_MESSAGE);
- mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
waitForTitle(WEBVIEW_MESSAGE);
}
@@ -117,7 +117,7 @@
}
loadPage(TITLE_FROM_POST_MESSAGE);
for (int i = 0; i < 10; i++) {
- mOnUiThread.postMessageToMainFrame(new WebMessage(Integer.toString(i)),
+ mOnUiThread.postWebMessage(new WebMessage(Integer.toString(i)),
Uri.parse(BASE_URI));
}
waitForTitle("0123456789");
@@ -131,7 +131,7 @@
loadPage(CHANNEL_MESSAGE);
final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
- mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
final int messageCount = 3;
final CountDownLatch latch = new CountDownLatch(messageCount);
runTestOnUiThread(new Runnable() {
@@ -162,7 +162,7 @@
loadPage(CHANNEL_MESSAGE);
final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
- mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
runTestOnUiThread(new Runnable() {
@Override
public void run() {
@@ -205,7 +205,7 @@
loadPage(CHANNEL_FROM_JS);
final WebMessagePort[] channel = mOnUiThread.createWebMessageChannel();
WebMessage message = new WebMessage(WEBVIEW_MESSAGE, new WebMessagePort[]{channel[1]});
- mOnUiThread.postMessageToMainFrame(message, Uri.parse(BASE_URI));
+ mOnUiThread.postWebMessage(message, Uri.parse(BASE_URI));
runTestOnUiThread(new Runnable() {
@Override
public void run() {
diff --git a/tests/webgl/Android.mk b/tests/webgl/Android.mk
deleted file mode 100755
index ce22dd8..0000000
--- a/tests/webgl/Android.mk
+++ /dev/null
@@ -1,34 +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.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-
-# Don't include this package in any target.
-LOCAL_MODULE_TAGS := optional
-
-# When built, explicitly put it in the data partition.
-LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
-
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
-# Must match the package name in CtsTestCaseList.mk
-LOCAL_PACKAGE_NAME := CtsWebGLTestCases
-
-LOCAL_SDK_VERSION := current
-
-include $(BUILD_CTS_PACKAGE)
diff --git a/tests/webgl/AndroidManifest.xml b/tests/webgl/AndroidManifest.xml
deleted file mode 100755
index d648032..0000000
--- a/tests/webgl/AndroidManifest.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.webgl.cts">
-
- <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application android:maxRecents="1">
- <uses-library android:name="android.test.runner" />
- <activity android:name="android.webgl.WebGLActivity" >
- <intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
- </intent-filter>
- </activity>
- </application>
-
-
- <!-- self-instrumenting test package. -->
- <instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
- android:label="CTS WebGL tests"
- android:targetPackage="android.webgl.cts" >
- <meta-data
- android:name="listener"
- android:value="com.android.cts.runner.CtsTestRunListener" />
- </instrumentation>
-</manifest>
-
diff --git a/tests/webgl/res/raw/extract_webgl_tests.py b/tests/webgl/res/raw/extract_webgl_tests.py
deleted file mode 100755
index 1511632..0000000
--- a/tests/webgl/res/raw/extract_webgl_tests.py
+++ /dev/null
@@ -1,69 +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.
-
-import sys
-import os
-
-if len(sys.argv) != 3:
- raise Exception("Usage: extract_webgl_tests.py <webgl_sdk_tests_path> <version>")
-
-top_list = sys.argv[1] + "/00_test_list.txt"
-version = sys.argv[2]
-tests = []
-lists = []
-lists.append(top_list)
-
-def filter_by_version(lines):
- version_lines = [ line for line in lines if "--min-version" in line ]
- version_lines.extend([ line for line in lines if "--max-version" in line ])
- lines = [ line for line in lines if not line in version_lines ]
- for line in version_lines:
- assert len(line.split()) == 3
- min_version = line.split()[1] if line.split()[0] == "--min-version" else "0.0.0"
- max_version = line.split()[1] if line.split()[0] == "--max-version" else "9.9.9"
- test = line.split()[2]
- if (version >= min_version and version <= max_version):
- lines.append(test)
- return lines
-
-while not len(lists) == 0:
- lists2 = lists
- lists = []
- for list in lists2:
- directory = os.path.dirname(os.path.realpath(list))
- with open(list) as file:
- # Filter out comments and --min-version
- lines = [ line.strip() for line in file.readlines()]
- lines = [ line for line in lines if not "//" in line ]
- lines = [ line for line in lines if not "#" in line ]
- lines = [ line.replace("--slow","") for line in lines ]
- lines = filter_by_version(lines)
- # Append lists and tests found in this list.
- lines = [ directory + "/" + line for line in lines ]
- lists.extend([ line for line in lines if "00_test_list.txt" in line ])
- tests.extend([ line for line in lines if ".html" in line ])
-
-# Directories for formating test-names/relative-paths.
-name_directory = os.path.dirname(os.path.realpath(top_list))
-path_directory = os.path.realpath(os.path.join(name_directory, os.pardir))
-
-tests = sorted(tests)
-for test in tests:
- test_path = test.replace(path_directory + "/", "")
- test_name = test.replace(name_directory + "/", "")
- test_name = test_name.replace("/","_")
- test_name = test_name.replace(".","_")
- test_name = test_name.replace("-","_")
- print " public void test_" + test_name + "() throws Exception { doTest(\"" + test_path + "\"); }"
-
diff --git a/tests/webgl/res/raw/harness.html b/tests/webgl/res/raw/harness.html
deleted file mode 100644
index 5ae56ef..0000000
--- a/tests/webgl/res/raw/harness.html
+++ /dev/null
@@ -1,44 +0,0 @@
-<!DOCTYPE html>
-<!-- saved from url=(0057)http://www.corp.google.com/~vollick/timing-functions.html -->
-<html>
-<head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
-
-<script type="text/javascript">
- // Check for WebGL Support.
- function supportsWebGL() {
- var canvas = document.createElement('canvas');
- gl = canvas.getContext("webgl");
- return !!gl;
- }
-
- // Pass the WebGL harness calls through to the native app.
- webglTestHarness = {
- notifyFinished: function() {
- WebGLCallback.notifyFinished();
- },
- reportResults: function(type, success, msg) {
- WebGLCallback.reportResults(type, success, msg);
- }
- }
- function navigateToTest() {
- if (supportsWebGL())
- window.open(WebGLCallback.getUrlToTest(), "TestFrame");
- else
- WebGLCallback.notifyFinished();
- }
- window.addEventListener('load', navigateToTest, false);
-</script>
-
-<style type="text/css">
-body, html { margin: 0; padding: 0; height: 100%; overflow: hidden; }
-#content { position:absolute; left: 0; right: 0; bottom: 0; top: 0px; }
-</style>
-
-</head>
-
-<body>
- <div id="content">
- <iframe name="TestFrame" width="100%" height="100%" frameborder="0"/>
- </div>
-</body>
-</html>
diff --git a/tests/webgl/res/raw/webgl_sdk_tests.zip b/tests/webgl/res/raw/webgl_sdk_tests.zip
deleted file mode 100644
index a2086b0..0000000
--- a/tests/webgl/res/raw/webgl_sdk_tests.zip
+++ /dev/null
Binary files differ
diff --git a/tests/webgl/src/android/webgl/WebGLActivity.java b/tests/webgl/src/android/webgl/WebGLActivity.java
deleted file mode 100644
index 3f911c4..0000000
--- a/tests/webgl/src/android/webgl/WebGLActivity.java
+++ /dev/null
@@ -1,142 +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.webgl;
-
-import android.app.Activity;
-import android.content.Context;
-import android.content.res.Resources;
-import android.cts.util.NullWebViewUtils;
-import android.os.Bundle;
-import android.util.Log;
-import android.webgl.cts.R;
-import android.webkit.WebView;
-import android.webkit.JavascriptInterface;
-import android.webkit.WebViewClient;
-import android.widget.Toast;
-import java.lang.Override;
-import java.io.InputStream;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A simple activity for testing WebGL Conformance with WebView.
- */
-public class WebGLActivity extends Activity {
-
- Semaphore mFinished = new Semaphore(0, false);
- Semaphore mDestroyed = new Semaphore(0, false);
- String mWebGlHarnessUrl;
- WebView mWebView;
-
- // The following members are synchronized.
- String mWebGLTestUrl;
- boolean mPassed = true;
- StringBuilder mMessage = new StringBuilder("\n");
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
-
- mWebGlHarnessUrl = "file://" + getCacheDir() + "/harness.html";
- try {
- mWebView = new WebView(this);
- } catch (Exception e) {
- NullWebViewUtils.determineIfWebViewAvailable(this, e);
- }
-
- if (mWebView == null) {
- return;
- }
-
- mWebView.getSettings().setJavaScriptEnabled(true);
- mWebView.getSettings().setAllowFileAccessFromFileURLs(true);
- mWebView.getSettings().setMediaPlaybackRequiresUserGesture(false);
- mWebView.setWebViewClient(new WebViewClient() {
- @Override
- public boolean shouldOverrideUrlLoading(WebView webView, String url) {
- return false;
- }
- });
-
- mWebView.addJavascriptInterface(new Object() {
- @JavascriptInterface
- public String getUrlToTest() {
- synchronized(WebGLActivity.this) {
- return mWebGLTestUrl;
- }
- }
-
- @JavascriptInterface
- public void reportResults(String type, boolean success, String message) {
- synchronized(WebGLActivity.this) {
- mMessage.append((success ? "PASS " : "FAIL ") + message + "\n");
- mPassed &= success;
- }
- }
-
- @JavascriptInterface
- public void notifyFinished() {
- mFinished.release();
- }
-
- @JavascriptInterface
- public void alert(String string) {
- Log.i(mWebGLTestUrl, string);
- }
- }, "WebGLCallback");
- setContentView(mWebView);
- }
-
- public void navigateToTest(String url) throws Exception {
- if (!NullWebViewUtils.isWebViewAvailable()) {
- return;
- }
-
- synchronized(WebGLActivity.this) {
- mWebGLTestUrl = url;
- }
-
- // Load harness.html, which will load mWebGLTestUrl in an <iframe>.
- runOnUiThread(new Runnable() {
- public void run() {
- mWebView.loadUrl(mWebGlHarnessUrl);
- }
- });
-
- // Wait on test completion.
- boolean finished = mFinished.tryAcquire(60, TimeUnit.SECONDS);
- String message;
- synchronized(WebGLActivity.this) {
- message = mMessage.toString();
- }
-
- // Destroy the webview and wait for it.
- runOnUiThread(new Runnable() {
- public void run() {
- mWebView.destroy();
- finish();
- mDestroyed.release();
- }
- });
- mDestroyed.acquire();
-
- if (!finished)
- throw new Exception("\n" + url + "\n Test timed-out after 60 seconds: " + message);
- if(!mPassed)
- throw new Exception("\n" + url + "\n Test failed: " + message);
- }
-}
diff --git a/tests/webgl/src/android/webgl/cts/WebGLConformanceSuite.java b/tests/webgl/src/android/webgl/cts/WebGLConformanceSuite.java
deleted file mode 100644
index 60f663a..0000000
--- a/tests/webgl/src/android/webgl/cts/WebGLConformanceSuite.java
+++ /dev/null
@@ -1,51 +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.webgl.cts;
-
-import android.util.Log;
-import android.webgl.cts.R;
-import android.webgl.WebGLActivity;
-import java.lang.Override;
-import java.io.File;
-import java.io.InputStream;
-
-/**
- * A Singleton class to wrap the WebGL Conformance Test Suite.
- */
-public class WebGLConformanceSuite {
- private final String TAG = "WebGLConformanceSuite";
- private static volatile WebGLConformanceSuite mInstance = null;
-
- private WebGLConformanceSuite(WebGLActivity activity) throws Exception {
- Log.i(TAG, "Unzipping WebGL Conformance Suite: "
- + activity.getCacheDir().getPath());
- InputStream suite = activity.getResources().openRawResource(R.raw.webgl_sdk_tests);
- ZipUtil.unzipToPath(suite, activity.getCacheDir());
- InputStream harness = activity.getResources().openRawResource(R.raw.harness);
- ZipUtil.streamToPath(harness, activity.getCacheDir(), "harness.html");
- }
-
- public static WebGLConformanceSuite init(WebGLActivity activity)
- throws Exception {
- if (mInstance == null) {
- synchronized (WebGLConformanceSuite.class) {
- mInstance = new WebGLConformanceSuite(activity);
- }
- }
- return mInstance;
- }
-}
diff --git a/tests/webgl/src/android/webgl/cts/WebGLTest.java b/tests/webgl/src/android/webgl/cts/WebGLTest.java
deleted file mode 100644
index d45c190..0000000
--- a/tests/webgl/src/android/webgl/cts/WebGLTest.java
+++ /dev/null
@@ -1,419 +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.webgl.cts;
-
-import android.webgl.WebGLActivity;
-import android.webgl.cts.R;
-import android.test.ActivityInstrumentationTestCase2;
-import java.io.InputStream;
-
-/**
- * A simple wrapper to load each WebGL conformance test in WebView.
- *
- * This test uses {@link android.test.ActivityInstrumentationTestCase2} to instrument the
- * {@link android.webgl.WebGLActivity}.
- */
-public class WebGLTest extends ActivityInstrumentationTestCase2<WebGLActivity> {
-
- /**
- * A reference to the activity whose shared preferences are being tested.
- */
- private WebGLActivity mActivity;
- private WebGLConformanceSuite mWebGL_1_0_1;
-
- public WebGLTest() {
- super(WebGLActivity.class);
- }
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- // Start the activity and get a reference to it.
- mActivity = getActivity();
- // Wait for the UI Thread to become idle.
- getInstrumentation().waitForIdleSync();
- mWebGL_1_0_1 = WebGLConformanceSuite.init(mActivity);
- }
-
- @Override
- protected void tearDown() throws Exception {
- // Scrub the activity so it can be freed. The next time the setUp will create a new activity
- // rather than reusing the old one.
- mActivity = null;
- super.tearDown();
- }
-
- protected void doTest(String testPage) throws Exception {
- mActivity.navigateToTest(testPage);
- }
-
- /**
- * The remainder of this file is generated using this command:
- * extract_webgl_tests.py tests 1.0.1
- */
- public void test_conformance_attribs_gl_enable_vertex_attrib_html() throws Exception { doTest("tests/conformance/attribs/gl-enable-vertex-attrib.html"); }
- public void test_conformance_attribs_gl_vertex_attrib_zero_issues_html() throws Exception { doTest("tests/conformance/attribs/gl-vertex-attrib-zero-issues.html"); }
- public void test_conformance_attribs_gl_vertex_attrib_html() throws Exception { doTest("tests/conformance/attribs/gl-vertex-attrib.html"); }
- public void test_conformance_attribs_gl_vertexattribpointer_offsets_html() throws Exception { doTest("tests/conformance/attribs/gl-vertexattribpointer-offsets.html"); }
- public void test_conformance_attribs_gl_vertexattribpointer_html() throws Exception { doTest("tests/conformance/attribs/gl-vertexattribpointer.html"); }
- public void test_conformance_buffers_buffer_bind_test_html() throws Exception { doTest("tests/conformance/buffers/buffer-bind-test.html"); }
- public void test_conformance_buffers_buffer_data_array_buffer_html() throws Exception { doTest("tests/conformance/buffers/buffer-data-array-buffer.html"); }
- public void test_conformance_buffers_index_validation_copies_indices_html() throws Exception { doTest("tests/conformance/buffers/index-validation-copies-indices.html"); }
- public void test_conformance_buffers_index_validation_crash_with_buffer_sub_data_html() throws Exception { doTest("tests/conformance/buffers/index-validation-crash-with-buffer-sub-data.html"); }
- public void test_conformance_buffers_index_validation_verifies_too_many_indices_html() throws Exception { doTest("tests/conformance/buffers/index-validation-verifies-too-many-indices.html"); }
- public void test_conformance_buffers_index_validation_with_resized_buffer_html() throws Exception { doTest("tests/conformance/buffers/index-validation-with-resized-buffer.html"); }
- public void test_conformance_buffers_index_validation_html() throws Exception { doTest("tests/conformance/buffers/index-validation.html"); }
- public void test_conformance_canvas_buffer_offscreen_test_html() throws Exception { doTest("tests/conformance/canvas/buffer-offscreen-test.html"); }
- public void test_conformance_canvas_buffer_preserve_test_html() throws Exception { doTest("tests/conformance/canvas/buffer-preserve-test.html"); }
- public void test_conformance_canvas_canvas_test_html() throws Exception { doTest("tests/conformance/canvas/canvas-test.html"); }
- public void test_conformance_canvas_canvas_zero_size_html() throws Exception { doTest("tests/conformance/canvas/canvas-zero-size.html"); }
- public void test_conformance_canvas_drawingbuffer_static_canvas_test_html() throws Exception { doTest("tests/conformance/canvas/drawingbuffer-static-canvas-test.html"); }
- public void test_conformance_canvas_drawingbuffer_test_html() throws Exception { doTest("tests/conformance/canvas/drawingbuffer-test.html"); }
- public void test_conformance_canvas_viewport_unchanged_upon_resize_html() throws Exception { doTest("tests/conformance/canvas/viewport-unchanged-upon-resize.html"); }
- public void test_conformance_context_constants_and_properties_html() throws Exception { doTest("tests/conformance/context/constants-and-properties.html"); }
- public void test_conformance_context_context_attributes_alpha_depth_stencil_antialias_html() throws Exception { doTest("tests/conformance/context/context-attributes-alpha-depth-stencil-antialias.html"); }
- public void test_conformance_context_context_lost_restored_html() throws Exception { doTest("tests/conformance/context/context-lost-restored.html"); }
- public void test_conformance_context_context_lost_html() throws Exception { doTest("tests/conformance/context/context-lost.html"); }
- public void test_conformance_context_context_type_test_html() throws Exception { doTest("tests/conformance/context/context-type-test.html"); }
- public void test_conformance_context_incorrect_context_object_behaviour_html() throws Exception { doTest("tests/conformance/context/incorrect-context-object-behaviour.html"); }
- public void test_conformance_context_methods_html() throws Exception { doTest("tests/conformance/context/methods.html"); }
- public void test_conformance_context_premultiplyalpha_test_html() throws Exception { doTest("tests/conformance/context/premultiplyalpha-test.html"); }
- public void test_conformance_context_resource_sharing_test_html() throws Exception { doTest("tests/conformance/context/resource-sharing-test.html"); }
- public void test_conformance_extensions_oes_standard_derivatives_html() throws Exception { doTest("tests/conformance/extensions/oes-standard-derivatives.html"); }
- public void test_conformance_extensions_oes_texture_float_with_canvas_html() throws Exception { doTest("tests/conformance/extensions/oes-texture-float-with-canvas.html"); }
- public void test_conformance_extensions_oes_texture_float_with_image_data_html() throws Exception { doTest("tests/conformance/extensions/oes-texture-float-with-image-data.html"); }
- public void test_conformance_extensions_oes_texture_float_with_image_html() throws Exception { doTest("tests/conformance/extensions/oes-texture-float-with-image.html"); }
- public void test_conformance_extensions_oes_texture_float_with_video_html() throws Exception { doTest("tests/conformance/extensions/oes-texture-float-with-video.html"); }
- public void test_conformance_extensions_oes_texture_float_html() throws Exception { doTest("tests/conformance/extensions/oes-texture-float.html"); }
- public void test_conformance_extensions_oes_vertex_array_object_html() throws Exception { doTest("tests/conformance/extensions/oes-vertex-array-object.html"); }
- public void test_conformance_extensions_webgl_debug_renderer_info_html() throws Exception { doTest("tests/conformance/extensions/webgl-debug-renderer-info.html"); }
- public void test_conformance_extensions_webgl_debug_shaders_html() throws Exception { doTest("tests/conformance/extensions/webgl-debug-shaders.html"); }
- public void test_conformance_glsl_functions_glsl_function_abs_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-abs.html"); }
- public void test_conformance_glsl_functions_glsl_function_acos_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-acos.html"); }
- public void test_conformance_glsl_functions_glsl_function_asin_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-asin.html"); }
- public void test_conformance_glsl_functions_glsl_function_atan_xy_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-atan-xy.html"); }
- public void test_conformance_glsl_functions_glsl_function_atan_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-atan.html"); }
- public void test_conformance_glsl_functions_glsl_function_ceil_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-ceil.html"); }
- public void test_conformance_glsl_functions_glsl_function_clamp_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-clamp-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_clamp_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-clamp-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_cos_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-cos.html"); }
- public void test_conformance_glsl_functions_glsl_function_cross_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-cross.html"); }
- public void test_conformance_glsl_functions_glsl_function_distance_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-distance.html"); }
- public void test_conformance_glsl_functions_glsl_function_dot_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-dot.html"); }
- public void test_conformance_glsl_functions_glsl_function_faceforward_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-faceforward.html"); }
- public void test_conformance_glsl_functions_glsl_function_floor_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-floor.html"); }
- public void test_conformance_glsl_functions_glsl_function_fract_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-fract.html"); }
- public void test_conformance_glsl_functions_glsl_function_length_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-length.html"); }
- public void test_conformance_glsl_functions_glsl_function_max_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-max-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_max_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-max-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_min_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-min-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_min_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-min-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_mix_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-mix-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_mix_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-mix-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_mod_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-mod-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_mod_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-mod-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_normalize_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-normalize.html"); }
- public void test_conformance_glsl_functions_glsl_function_reflect_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-reflect.html"); }
- public void test_conformance_glsl_functions_glsl_function_sign_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-sign.html"); }
- public void test_conformance_glsl_functions_glsl_function_sin_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-sin.html"); }
- public void test_conformance_glsl_functions_glsl_function_smoothstep_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-smoothstep-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_smoothstep_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-smoothstep-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_step_float_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-step-float.html"); }
- public void test_conformance_glsl_functions_glsl_function_step_gentype_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function-step-gentype.html"); }
- public void test_conformance_glsl_functions_glsl_function_html() throws Exception { doTest("tests/conformance/glsl/functions/glsl-function.html"); }
- public void test_conformance_glsl_implicit_add_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_mat2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_mat2.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_mat3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_mat3.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_mat4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_mat4.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_add_int_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_int_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_add_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_add_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_add_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/add_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_assign_int_to_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/assign_int_to_float.vert.html"); }
- public void test_conformance_glsl_implicit_assign_ivec2_to_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/assign_ivec2_to_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_assign_ivec3_to_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/assign_ivec3_to_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_assign_ivec4_to_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/assign_ivec4_to_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_construct_struct_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/construct_struct.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_mat2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_mat2.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_mat3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_mat3.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_mat4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_mat4.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_divide_int_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_int_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_divide_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_divide_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_divide_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/divide_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_equal_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/equal_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_equal_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/equal_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_equal_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/equal_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_equal_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/equal_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_function_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/function_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_function_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/function_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_function_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/function_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_function_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/function_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_greater_than_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/greater_than.vert.html"); }
- public void test_conformance_glsl_implicit_greater_than_equal_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/greater_than_equal.vert.html"); }
- public void test_conformance_glsl_implicit_less_than_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/less_than.vert.html"); }
- public void test_conformance_glsl_implicit_less_than_equal_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/less_than_equal.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_mat2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_mat2.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_mat3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_mat3.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_mat4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_mat4.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_int_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_int_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_multiply_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/multiply_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_not_equal_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/not_equal_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_not_equal_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/not_equal_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_not_equal_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/not_equal_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_not_equal_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/not_equal_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_mat2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_mat2.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_mat3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_mat3.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_mat4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_mat4.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_int_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_int_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_subtract_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/subtract_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_implicit_ternary_int_float_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/ternary_int_float.vert.html"); }
- public void test_conformance_glsl_implicit_ternary_ivec2_vec2_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/ternary_ivec2_vec2.vert.html"); }
- public void test_conformance_glsl_implicit_ternary_ivec3_vec3_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/ternary_ivec3_vec3.vert.html"); }
- public void test_conformance_glsl_implicit_ternary_ivec4_vec4_vert_html() throws Exception { doTest("tests/conformance/glsl/implicit/ternary_ivec4_vec4.vert.html"); }
- public void test_conformance_glsl_misc_attrib_location_length_limits_html() throws Exception { doTest("tests/conformance/glsl/misc/attrib-location-length-limits.html"); }
- public void test_conformance_glsl_misc_embedded_struct_definitions_forbidden_html() throws Exception { doTest("tests/conformance/glsl/misc/embedded-struct-definitions-forbidden.html"); }
- public void test_conformance_glsl_misc_empty_main_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/empty_main.vert.html"); }
- public void test_conformance_glsl_misc_gl_position_unset_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/gl_position_unset.vert.html"); }
- public void test_conformance_glsl_misc_glsl_function_nodes_html() throws Exception { doTest("tests/conformance/glsl/misc/glsl-function-nodes.html"); }
- public void test_conformance_glsl_misc_glsl_long_variable_names_html() throws Exception { doTest("tests/conformance/glsl/misc/glsl-long-variable-names.html"); }
- public void test_conformance_glsl_misc_non_ascii_comments_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/non-ascii-comments.vert.html"); }
- public void test_conformance_glsl_misc_non_ascii_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/non-ascii.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_256_character_identifier_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-256-character-identifier.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_257_character_identifier_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-257-character-identifier.frag.html"); }
- public void test_conformance_glsl_misc_shader_with__webgl_identifier_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-_webgl-identifier.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_arbitrary_indexing_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-arbitrary-indexing.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_arbitrary_indexing_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-arbitrary-indexing.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_attrib_array_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-attrib-array.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_attrib_struct_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-attrib-struct.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_clipvertex_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-clipvertex.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_default_precision_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-default-precision.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_default_precision_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-default-precision.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_define_line_continuation_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-define-line-continuation.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_dfdx_no_ext_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-dfdx-no-ext.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_dfdx_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-dfdx.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_error_directive_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-error-directive.html"); }
- public void test_conformance_glsl_misc_shader_with_explicit_int_cast_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-explicit-int-cast.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_float_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-float-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_frag_depth_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-frag-depth.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_function_recursion_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-function-recursion.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_glcolor_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-glcolor.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_gles_1_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-gles-1.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_gles_symbol_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-gles-symbol.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_glprojectionmatrix_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-glprojectionmatrix.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_implicit_vec3_to_vec4_cast_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-implicit-vec3-to-vec4-cast.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_include_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-include.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_int_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-int-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_invalid_identifier_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-invalid-identifier.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_ivec2_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-ivec2-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_ivec3_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-ivec3-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_ivec4_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-ivec4-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_limited_indexing_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-limited-indexing.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_long_line_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-long-line.html"); }
- public void test_conformance_glsl_misc_shader_with_non_ascii_error_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-non-ascii-error.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_precision_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-precision.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_quoted_error_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-quoted-error.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_undefined_preprocessor_symbol_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-undefined-preprocessor-symbol.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_uniform_in_loop_condition_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-uniform-in-loop-condition.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_vec2_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-vec2-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_vec3_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-vec3-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_vec4_return_value_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-vec4-return-value.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_version_100_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-version-100.frag.html"); }
- public void test_conformance_glsl_misc_shader_with_version_100_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-version-100.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_version_120_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-version-120.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_version_130_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-version-130.vert.html"); }
- public void test_conformance_glsl_misc_shader_with_webgl_identifier_vert_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-with-webgl-identifier.vert.html"); }
- public void test_conformance_glsl_misc_shader_without_precision_frag_html() throws Exception { doTest("tests/conformance/glsl/misc/shader-without-precision.frag.html"); }
- public void test_conformance_glsl_misc_shared_html() throws Exception { doTest("tests/conformance/glsl/misc/shared.html"); }
- public void test_conformance_glsl_misc_struct_nesting_exceeds_maximum_html() throws Exception { doTest("tests/conformance/glsl/misc/struct-nesting-exceeds-maximum.html"); }
- public void test_conformance_glsl_misc_struct_nesting_under_maximum_html() throws Exception { doTest("tests/conformance/glsl/misc/struct-nesting-under-maximum.html"); }
- public void test_conformance_glsl_misc_uniform_location_length_limits_html() throws Exception { doTest("tests/conformance/glsl/misc/uniform-location-length-limits.html"); }
- public void test_conformance_glsl_reserved__webgl_field_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/_webgl_field.vert.html"); }
- public void test_conformance_glsl_reserved__webgl_function_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/_webgl_function.vert.html"); }
- public void test_conformance_glsl_reserved__webgl_struct_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/_webgl_struct.vert.html"); }
- public void test_conformance_glsl_reserved__webgl_variable_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/_webgl_variable.vert.html"); }
- public void test_conformance_glsl_reserved_webgl_field_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/webgl_field.vert.html"); }
- public void test_conformance_glsl_reserved_webgl_function_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/webgl_function.vert.html"); }
- public void test_conformance_glsl_reserved_webgl_struct_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/webgl_struct.vert.html"); }
- public void test_conformance_glsl_reserved_webgl_variable_vert_html() throws Exception { doTest("tests/conformance/glsl/reserved/webgl_variable.vert.html"); }
- public void test_conformance_glsl_variables_gl_fragcoord_html() throws Exception { doTest("tests/conformance/glsl/variables/gl-fragcoord.html"); }
- public void test_conformance_glsl_variables_gl_frontfacing_html() throws Exception { doTest("tests/conformance/glsl/variables/gl-frontfacing.html"); }
- public void test_conformance_glsl_variables_gl_pointcoord_html() throws Exception { doTest("tests/conformance/glsl/variables/gl-pointcoord.html"); }
- public void test_conformance_limits_gl_max_texture_dimensions_html() throws Exception { doTest("tests/conformance/limits/gl-max-texture-dimensions.html"); }
- public void test_conformance_limits_gl_min_attribs_html() throws Exception { doTest("tests/conformance/limits/gl-min-attribs.html"); }
- public void test_conformance_limits_gl_min_textures_html() throws Exception { doTest("tests/conformance/limits/gl-min-textures.html"); }
- public void test_conformance_limits_gl_min_uniforms_html() throws Exception { doTest("tests/conformance/limits/gl-min-uniforms.html"); }
- public void test_conformance_misc_bad_arguments_test_html() throws Exception { doTest("tests/conformance/misc/bad-arguments-test.html"); }
- public void test_conformance_misc_error_reporting_html() throws Exception { doTest("tests/conformance/misc/error-reporting.html"); }
- public void test_conformance_misc_functions_returning_strings_html() throws Exception { doTest("tests/conformance/misc/functions-returning-strings.html"); }
- public void test_conformance_misc_instanceof_test_html() throws Exception { doTest("tests/conformance/misc/instanceof-test.html"); }
- public void test_conformance_misc_invalid_passed_params_html() throws Exception { doTest("tests/conformance/misc/invalid-passed-params.html"); }
- public void test_conformance_misc_is_object_html() throws Exception { doTest("tests/conformance/misc/is-object.html"); }
- public void test_conformance_misc_null_object_behaviour_html() throws Exception { doTest("tests/conformance/misc/null-object-behaviour.html"); }
- public void test_conformance_misc_object_deletion_behaviour_html() throws Exception { doTest("tests/conformance/misc/object-deletion-behaviour.html"); }
- public void test_conformance_misc_shader_precision_format_html() throws Exception { doTest("tests/conformance/misc/shader-precision-format.html"); }
- public void test_conformance_misc_type_conversion_test_html() throws Exception { doTest("tests/conformance/misc/type-conversion-test.html"); }
- public void test_conformance_misc_uninitialized_test_html() throws Exception { doTest("tests/conformance/misc/uninitialized-test.html"); }
- public void test_conformance_misc_webgl_specific_html() throws Exception { doTest("tests/conformance/misc/webgl-specific.html"); }
- public void test_conformance_more_conformance_constants_html() throws Exception { doTest("tests/conformance/more/conformance/constants.html"); }
- public void test_conformance_more_conformance_getContext_html() throws Exception { doTest("tests/conformance/more/conformance/getContext.html"); }
- public void test_conformance_more_conformance_methods_html() throws Exception { doTest("tests/conformance/more/conformance/methods.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_A_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-A.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_B1_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-B1.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_B2_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-B2.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_B3_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-B3.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_B4_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-B4.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_C_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-C.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_D_G_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-D_G.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_G_I_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-G_I.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_L_S_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-L_S.html"); }
- public void test_conformance_more_conformance_quickCheckAPI_S_V_html() throws Exception { doTest("tests/conformance/more/conformance/quickCheckAPI-S_V.html"); }
- public void test_conformance_more_conformance_webGLArrays_html() throws Exception { doTest("tests/conformance/more/conformance/webGLArrays.html"); }
- public void test_conformance_more_functions_bindBuffer_html() throws Exception { doTest("tests/conformance/more/functions/bindBuffer.html"); }
- public void test_conformance_more_functions_bindBufferBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/bindBufferBadArgs.html"); }
- public void test_conformance_more_functions_bindFramebufferLeaveNonZero_html() throws Exception { doTest("tests/conformance/more/functions/bindFramebufferLeaveNonZero.html"); }
- public void test_conformance_more_functions_bufferData_html() throws Exception { doTest("tests/conformance/more/functions/bufferData.html"); }
- public void test_conformance_more_functions_bufferDataBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/bufferDataBadArgs.html"); }
- public void test_conformance_more_functions_bufferSubData_html() throws Exception { doTest("tests/conformance/more/functions/bufferSubData.html"); }
- public void test_conformance_more_functions_bufferSubDataBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/bufferSubDataBadArgs.html"); }
- public void test_conformance_more_functions_copyTexImage2D_html() throws Exception { doTest("tests/conformance/more/functions/copyTexImage2D.html"); }
- public void test_conformance_more_functions_copyTexImage2DBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/copyTexImage2DBadArgs.html"); }
- public void test_conformance_more_functions_copyTexSubImage2D_html() throws Exception { doTest("tests/conformance/more/functions/copyTexSubImage2D.html"); }
- public void test_conformance_more_functions_copyTexSubImage2DBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/copyTexSubImage2DBadArgs.html"); }
- public void test_conformance_more_functions_deleteBufferBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/deleteBufferBadArgs.html"); }
- public void test_conformance_more_functions_drawArrays_html() throws Exception { doTest("tests/conformance/more/functions/drawArrays.html"); }
- public void test_conformance_more_functions_drawArraysOutOfBounds_html() throws Exception { doTest("tests/conformance/more/functions/drawArraysOutOfBounds.html"); }
- public void test_conformance_more_functions_drawElements_html() throws Exception { doTest("tests/conformance/more/functions/drawElements.html"); }
- public void test_conformance_more_functions_drawElementsBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/drawElementsBadArgs.html"); }
- public void test_conformance_more_functions_isTests_html() throws Exception { doTest("tests/conformance/more/functions/isTests.html"); }
- public void test_conformance_more_functions_readPixels_html() throws Exception { doTest("tests/conformance/more/functions/readPixels.html"); }
- public void test_conformance_more_functions_readPixelsBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/readPixelsBadArgs.html"); }
- public void test_conformance_more_functions_texImage2D_html() throws Exception { doTest("tests/conformance/more/functions/texImage2D.html"); }
- public void test_conformance_more_functions_texImage2DBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/texImage2DBadArgs.html"); }
- public void test_conformance_more_functions_texImage2DHTML_html() throws Exception { doTest("tests/conformance/more/functions/texImage2DHTML.html"); }
- public void test_conformance_more_functions_texImage2DHTMLBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/texImage2DHTMLBadArgs.html"); }
- public void test_conformance_more_functions_texSubImage2D_html() throws Exception { doTest("tests/conformance/more/functions/texSubImage2D.html"); }
- public void test_conformance_more_functions_texSubImage2DBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/texSubImage2DBadArgs.html"); }
- public void test_conformance_more_functions_texSubImage2DHTML_html() throws Exception { doTest("tests/conformance/more/functions/texSubImage2DHTML.html"); }
- public void test_conformance_more_functions_texSubImage2DHTMLBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/texSubImage2DHTMLBadArgs.html"); }
- public void test_conformance_more_functions_uniformMatrix_html() throws Exception { doTest("tests/conformance/more/functions/uniformMatrix.html"); }
- public void test_conformance_more_functions_uniformMatrixBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/uniformMatrixBadArgs.html"); }
- public void test_conformance_more_functions_uniformf_html() throws Exception { doTest("tests/conformance/more/functions/uniformf.html"); }
- public void test_conformance_more_functions_uniformfArrayLen1_html() throws Exception { doTest("tests/conformance/more/functions/uniformfArrayLen1.html"); }
- public void test_conformance_more_functions_uniformfBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/uniformfBadArgs.html"); }
- public void test_conformance_more_functions_uniformi_html() throws Exception { doTest("tests/conformance/more/functions/uniformi.html"); }
- public void test_conformance_more_functions_uniformiBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/uniformiBadArgs.html"); }
- public void test_conformance_more_functions_vertexAttrib_html() throws Exception { doTest("tests/conformance/more/functions/vertexAttrib.html"); }
- public void test_conformance_more_functions_vertexAttribBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/vertexAttribBadArgs.html"); }
- public void test_conformance_more_functions_vertexAttribPointer_html() throws Exception { doTest("tests/conformance/more/functions/vertexAttribPointer.html"); }
- public void test_conformance_more_functions_vertexAttribPointerBadArgs_html() throws Exception { doTest("tests/conformance/more/functions/vertexAttribPointerBadArgs.html"); }
- public void test_conformance_more_glsl_arrayOutOfBounds_html() throws Exception { doTest("tests/conformance/more/glsl/arrayOutOfBounds.html"); }
- public void test_conformance_more_glsl_uniformOutOfBounds_html() throws Exception { doTest("tests/conformance/more/glsl/uniformOutOfBounds.html"); }
- public void test_conformance_programs_get_active_test_html() throws Exception { doTest("tests/conformance/programs/get-active-test.html"); }
- public void test_conformance_programs_gl_bind_attrib_location_test_html() throws Exception { doTest("tests/conformance/programs/gl-bind-attrib-location-test.html"); }
- public void test_conformance_programs_gl_get_active_attribute_html() throws Exception { doTest("tests/conformance/programs/gl-get-active-attribute.html"); }
- public void test_conformance_programs_gl_get_active_uniform_html() throws Exception { doTest("tests/conformance/programs/gl-get-active-uniform.html"); }
- public void test_conformance_programs_gl_getshadersource_html() throws Exception { doTest("tests/conformance/programs/gl-getshadersource.html"); }
- public void test_conformance_programs_gl_shader_test_html() throws Exception { doTest("tests/conformance/programs/gl-shader-test.html"); }
- public void test_conformance_programs_invalid_UTF_16_html() throws Exception { doTest("tests/conformance/programs/invalid-UTF-16.html"); }
- public void test_conformance_programs_program_test_html() throws Exception { doTest("tests/conformance/programs/program-test.html"); }
- public void test_conformance_reading_read_pixels_pack_alignment_html() throws Exception { doTest("tests/conformance/reading/read-pixels-pack-alignment.html"); }
- public void test_conformance_reading_read_pixels_test_html() throws Exception { doTest("tests/conformance/reading/read-pixels-test.html"); }
- public void test_conformance_renderbuffers_framebuffer_object_attachment_html() throws Exception { doTest("tests/conformance/renderbuffers/framebuffer-object-attachment.html"); }
- public void test_conformance_renderbuffers_framebuffer_test_html() throws Exception { doTest("tests/conformance/renderbuffers/framebuffer-test.html"); }
- public void test_conformance_renderbuffers_renderbuffer_initialization_html() throws Exception { doTest("tests/conformance/renderbuffers/renderbuffer-initialization.html"); }
- public void test_conformance_rendering_draw_arrays_out_of_bounds_html() throws Exception { doTest("tests/conformance/rendering/draw-arrays-out-of-bounds.html"); }
- public void test_conformance_rendering_draw_elements_out_of_bounds_html() throws Exception { doTest("tests/conformance/rendering/draw-elements-out-of-bounds.html"); }
- public void test_conformance_rendering_gl_clear_html() throws Exception { doTest("tests/conformance/rendering/gl-clear.html"); }
- public void test_conformance_rendering_gl_drawelements_html() throws Exception { doTest("tests/conformance/rendering/gl-drawelements.html"); }
- public void test_conformance_rendering_gl_scissor_test_html() throws Exception { doTest("tests/conformance/rendering/gl-scissor-test.html"); }
- public void test_conformance_rendering_line_loop_tri_fan_html() throws Exception { doTest("tests/conformance/rendering/line-loop-tri-fan.html"); }
- public void test_conformance_rendering_more_than_65536_indices_html() throws Exception { doTest("tests/conformance/rendering/more-than-65536-indices.html"); }
- public void test_conformance_rendering_multisample_corruption_html() throws Exception { doTest("tests/conformance/rendering/multisample-corruption.html"); }
- public void test_conformance_rendering_point_size_html() throws Exception { doTest("tests/conformance/rendering/point-size.html"); }
- public void test_conformance_rendering_triangle_html() throws Exception { doTest("tests/conformance/rendering/triangle.html"); }
- public void test_conformance_state_gl_enable_enum_test_html() throws Exception { doTest("tests/conformance/state/gl-enable-enum-test.html"); }
- public void test_conformance_state_gl_enum_tests_html() throws Exception { doTest("tests/conformance/state/gl-enum-tests.html"); }
- public void test_conformance_state_gl_get_calls_html() throws Exception { doTest("tests/conformance/state/gl-get-calls.html"); }
- public void test_conformance_state_gl_geterror_html() throws Exception { doTest("tests/conformance/state/gl-geterror.html"); }
- public void test_conformance_state_gl_getstring_html() throws Exception { doTest("tests/conformance/state/gl-getstring.html"); }
- public void test_conformance_state_gl_object_get_calls_html() throws Exception { doTest("tests/conformance/state/gl-object-get-calls.html"); }
- public void test_conformance_textures_compressed_tex_image_html() throws Exception { doTest("tests/conformance/textures/compressed-tex-image.html"); }
- public void test_conformance_textures_copy_tex_image_and_sub_image_2d_html() throws Exception { doTest("tests/conformance/textures/copy-tex-image-and-sub-image-2d.html"); }
- public void test_conformance_textures_gl_pixelstorei_html() throws Exception { doTest("tests/conformance/textures/gl-pixelstorei.html"); }
- public void test_conformance_textures_gl_teximage_html() throws Exception { doTest("tests/conformance/textures/gl-teximage.html"); }
- public void test_conformance_textures_origin_clean_conformance_html() throws Exception { doTest("tests/conformance/textures/origin-clean-conformance.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_array_buffer_view_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-array-buffer-view.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_canvas_rgb565_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgb565.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_canvas_rgba4444_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba4444.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_canvas_rgba5551_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-canvas-rgba5551.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_canvas_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-canvas.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_data_rgb565_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgb565.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_data_rgba4444_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba4444.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_data_rgba5551_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-data-rgba5551.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_data_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-data.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_rgb565_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-rgb565.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_rgba4444_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-rgba4444.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_rgba5551_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image-rgba5551.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_image_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-image.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgb565_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-video-rgb565.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba4444_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-video-rgba4444.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_video_rgba5551_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-video-rgba5551.html"); }
- public void test_conformance_textures_tex_image_and_sub_image_2d_with_video_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-sub-image-2d-with-video.html"); }
- public void test_conformance_textures_tex_image_and_uniform_binding_bugs_html() throws Exception { doTest("tests/conformance/textures/tex-image-and-uniform-binding-bugs.html"); }
- public void test_conformance_textures_tex_image_with_format_and_type_html() throws Exception { doTest("tests/conformance/textures/tex-image-with-format-and-type.html"); }
- public void test_conformance_textures_tex_image_with_invalid_data_html() throws Exception { doTest("tests/conformance/textures/tex-image-with-invalid-data.html"); }
- public void test_conformance_textures_tex_input_validation_html() throws Exception { doTest("tests/conformance/textures/tex-input-validation.html"); }
- public void test_conformance_textures_tex_sub_image_2d_bad_args_html() throws Exception { doTest("tests/conformance/textures/tex-sub-image-2d-bad-args.html"); }
- public void test_conformance_textures_tex_sub_image_2d_html() throws Exception { doTest("tests/conformance/textures/tex-sub-image-2d.html"); }
- public void test_conformance_textures_texparameter_test_html() throws Exception { doTest("tests/conformance/textures/texparameter-test.html"); }
- public void test_conformance_textures_texture_active_bind_2_html() throws Exception { doTest("tests/conformance/textures/texture-active-bind-2.html"); }
- public void test_conformance_textures_texture_active_bind_html() throws Exception { doTest("tests/conformance/textures/texture-active-bind.html"); }
- public void test_conformance_textures_texture_complete_html() throws Exception { doTest("tests/conformance/textures/texture-complete.html"); }
- public void test_conformance_textures_texture_mips_html() throws Exception { doTest("tests/conformance/textures/texture-mips.html"); }
- public void test_conformance_textures_texture_npot_video_html() throws Exception { doTest("tests/conformance/textures/texture-npot-video.html"); }
- public void test_conformance_textures_texture_npot_html() throws Exception { doTest("tests/conformance/textures/texture-npot.html"); }
- public void test_conformance_textures_texture_size_cube_maps_html() throws Exception { doTest("tests/conformance/textures/texture-size-cube-maps.html"); }
- public void test_conformance_textures_texture_size_html() throws Exception { doTest("tests/conformance/textures/texture-size.html"); }
- public void test_conformance_textures_texture_transparent_pixels_initialized_html() throws Exception { doTest("tests/conformance/textures/texture-transparent-pixels-initialized.html"); }
- public void test_conformance_typedarrays_array_buffer_crash_html() throws Exception { doTest("tests/conformance/typedarrays/array-buffer-crash.html"); }
- public void test_conformance_typedarrays_array_buffer_view_crash_html() throws Exception { doTest("tests/conformance/typedarrays/array-buffer-view-crash.html"); }
- public void test_conformance_typedarrays_array_unit_tests_html() throws Exception { doTest("tests/conformance/typedarrays/array-unit-tests.html"); }
- public void test_conformance_typedarrays_data_view_crash_html() throws Exception { doTest("tests/conformance/typedarrays/data-view-crash.html"); }
- public void test_conformance_typedarrays_data_view_test_html() throws Exception { doTest("tests/conformance/typedarrays/data-view-test.html"); }
- public void test_conformance_uniforms_gl_uniform_arrays_html() throws Exception { doTest("tests/conformance/uniforms/gl-uniform-arrays.html"); }
- public void test_conformance_uniforms_gl_uniform_bool_html() throws Exception { doTest("tests/conformance/uniforms/gl-uniform-bool.html"); }
- public void test_conformance_uniforms_gl_uniformmatrix4fv_html() throws Exception { doTest("tests/conformance/uniforms/gl-uniformmatrix4fv.html"); }
- public void test_conformance_uniforms_gl_unknown_uniform_html() throws Exception { doTest("tests/conformance/uniforms/gl-unknown-uniform.html"); }
- public void test_conformance_uniforms_null_uniform_location_html() throws Exception { doTest("tests/conformance/uniforms/null-uniform-location.html"); }
- public void test_conformance_uniforms_uniform_location_html() throws Exception { doTest("tests/conformance/uniforms/uniform-location.html"); }
- public void test_conformance_uniforms_uniform_samplers_test_html() throws Exception { doTest("tests/conformance/uniforms/uniform-samplers-test.html"); }
-}
diff --git a/tests/webgl/src/android/webgl/cts/ZipUtil.java b/tests/webgl/src/android/webgl/cts/ZipUtil.java
deleted file mode 100644
index 4b28e63..0000000
--- a/tests/webgl/src/android/webgl/cts/ZipUtil.java
+++ /dev/null
@@ -1,117 +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.webgl.cts;
-
-import android.util.Log;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.lang.String;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipInputStream;
-
-
-/**
- * Some boilerplate code to unzip files.
- */
-public class ZipUtil {
- private final static String TAG = "ZipUtil";
-
- /**
- * Stream to a file.
- */
- public static void streamToPath(InputStream is,
- File directory,
- String name) throws Exception {
- File file = new File(directory, name);
- streamToPath(is, file);
- }
-
- public static void streamToPath(InputStream is,
- File file) throws Exception {
- Log.i(TAG, "Streaming to path " + file.getPath());
- OutputStream os = null;
- os = new FileOutputStream(file);
- int count = -1;
- byte[] buffer = new byte[10 * 1024];
- while ((count = is.read(buffer)) != -1) {
- os.write(buffer, 0, count);
- }
- os.close();
- }
-
- /**
- * Unzip to a directory.
- */
- public static void unzipToPath(InputStream is,
- File filePath) throws Exception {
- ZipInputStream zis = new ZipInputStream(is);
- unzipToPath(zis, filePath.getPath());
- }
-
- public static void unzipToPath(ZipInputStream zis,
- String path) throws Exception {
- Log.i(TAG, "Unzipping to path " + path);
- byte[] buffer = new byte[10 * 1024];
- ZipEntry entry;
- while ((entry = zis.getNextEntry()) != null) {
- File entryFile = new File(path, entry.getName());
- if (entry.isDirectory()) {
- if (!entryFile.exists()) {
- entryFile.mkdirs();
- }
- continue;
- }
- if (entryFile.getParentFile() != null &&
- !entryFile.getParentFile().exists()) {
- entryFile.getParentFile().mkdirs();
- }
- if (!entryFile.exists()) {
- entryFile.createNewFile();
- entryFile.setReadable(true);
- entryFile.setExecutable(true);
- }
- streamToPath(zis, entryFile);
- }
- zis.close();
- }
-
- /**
- * Cleanup a directory.
- */
- static public boolean deleteDirectory(String directoryPath) {
- File path = new File(directoryPath);
- return deleteDirectory(path);
- }
-
- static public boolean deleteDirectory(File path) {
- if (path.exists()) {
- File[] files = path.listFiles();
- for(int i = 0; i < files.length; i++) {
- if(files[i].isDirectory()) {
- deleteDirectory(files[i]);
- } else {
- files[i].delete();
- }
- }
- return path.delete();
- }
- return false;
- }
-}
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 5eba5bb..45900de 100644
--- a/tools/selinux/SELinuxNeverallowTestFrame.py
+++ b/tools/selinux/SELinuxNeverallowTestFrame.py
@@ -72,8 +72,7 @@
/* obtain sepolicy file from running device */
devicePolicyFile = File.createTempFile("sepolicy", ".tmp");
devicePolicyFile.deleteOnExit();
- mDevice.executeAdbCommand("pull", "/sys/fs/selinux/policy",
- devicePolicyFile.getAbsolutePath());
+ mDevice.pullFile("/sys/fs/selinux/policy", devicePolicyFile);
}
"""
src_body = ""
diff --git a/tools/tradefed-host/Android.mk b/tools/tradefed-host/Android.mk
index bd28575..1f73e95 100644
--- a/tools/tradefed-host/Android.mk
+++ b/tools/tradefed-host/Android.mk
@@ -27,6 +27,8 @@
LOCAL_JAVA_LIBRARIES := tradefed-prebuilt hosttestlib
LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceinfolib
+LOCAL_JAR_MANIFEST := MANIFEST.mf
+
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_JAVA_LIBRARY)
diff --git a/tools/tradefed-host/MANIFEST.mf b/tools/tradefed-host/MANIFEST.mf
new file mode 100644
index 0000000..5528c06
--- /dev/null
+++ b/tools/tradefed-host/MANIFEST.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: com.android.cts.tradefed.testtype
+Implementation-Version: %BUILD_NUMBER%
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
index ca4e050..250a121 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/command/CtsConsole.java
@@ -56,7 +56,9 @@
@Override
public void run() {
- printLine(String.format("Android CTS %s", CtsBuildProvider.CTS_BUILD_VERSION));
+ printLine(String.format("Android CTS %s build:%s",
+ CtsBuildProvider.CTS_BUILD_VERSION,
+ Package.getPackage("com.android.cts.tradefed.command").getImplementationVersion()));
super.run();
}
diff --git a/tools/utils/buildCts.py b/tools/utils/buildCts.py
index 79a3354..6b74608 100755
--- a/tools/utils/buildCts.py
+++ b/tools/utils/buildCts.py
@@ -295,7 +295,6 @@
plan = tools.TestPlan(packages)
plan.Exclude('.*')
- plan.Include(r'android\.webgl')
self.__WritePlan(plan, 'CTS-webview')
@@ -409,8 +408,7 @@
'android.signature' : [],
'android.tv' : [],
'android.uiautomation' : [],
- 'android.uirendering' : [],
- 'android.webgl' : []}
+ 'android.uirendering' : []}
def BuildListForReleaseBuildTest():
""" Construct a defaultdict that maps package name to a list of tests