am 8e908f24: am 32fa2bc0: am f3517f18: Merge "Advertise 31 bytes in BLE advertise tests." into lmp-sprout-dev
* commit '8e908f24a27ae06ddd284393f4f027802c24c5e0':
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 5b434cc..6f475cc 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -34,6 +34,7 @@
CtsSplitApp_arm64-v8a \
CtsSplitApp_mips64 \
CtsSplitApp_mips \
+ CtsSplitAppDiffRevision \
CtsSplitAppDiffVersion \
CtsSplitAppDiffCert \
CtsSplitAppFeature \
@@ -68,6 +69,7 @@
CtsDeviceTaskswitchingAppB \
CtsDeviceTaskswitchingControl \
CtsDeviceUi \
+ CtsHostsideNetworkTestsApp \
CtsIntentReceiverApp \
CtsIntentSenderApp \
CtsLauncherAppsTests \
@@ -103,6 +105,7 @@
CtsDeviceBrowserBench \
CtsDeviceVideoPerf \
CtsDeviceOpenGl \
+ CtsDeviceTvProviderPerf \
CtsAccelerationTestCases \
CtsAccountManagerTestCases \
CtsAccessibilityServiceTestCases \
@@ -110,6 +113,7 @@
CtsAdminTestCases \
CtsAnimationTestCases \
CtsAppTestCases \
+ CtsAppWidgetTestCases \
CtsBluetoothTestCases \
CtsCalendarcommon2TestCases \
CtsContentTestCases \
@@ -176,6 +180,7 @@
CtsDevicePolicyManagerTestCases \
CtsDumpsysHostTestCases \
CtsHostJank \
+ CtsHostsideNetworkTests \
CtsHostUi \
CtsJdwpSecurityHostTestCases \
CtsMonkeyTestCases \
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index b713db9..24f4e75 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -198,7 +198,6 @@
return props.has_key("android.flash.info.available") and \
props["android.flash.info.available"] == 1
-
def per_frame_control(props):
"""Returns whether a device supports per frame control
@@ -211,6 +210,18 @@
return props.has_key("android.sync.maxLatency") and \
props["android.sync.maxLatency"] == 0
+def ev_compensation(props):
+ """Returns whether a device supports ev compensation
+
+ Args:
+ props: Camera properties object.
+
+ Return:
+ Boolean.
+ """
+ return props.has_key("android.control.aeCompensationRange") and \
+ props["android.control.aeCompensationRange"] != [0, 0]
+
class __UnitTest(unittest.TestCase):
"""Run a suite of unit tests on this module.
"""
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 035e70b..ad9786e 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -49,6 +49,7 @@
# Seconds timeout on each socket operation.
SOCK_TIMEOUT = 10.0
+ SEC_TO_NSEC = 1000*1000*1000.0
PACKAGE = 'com.android.cts.verifier.camera.its'
INTENT_START = 'com.android.cts.verifier.camera.its.START'
@@ -483,6 +484,18 @@
"dng" in formats and "raw10" in formats or \
"raw" in formats and "raw10" in formats:
raise its.error.Error('Different raw formats not supported')
+
+ # Detect long exposure time and set timeout accordingly
+ longest_exp_time = 0
+ for req in cmd["captureRequests"]:
+ if "android.sensor.exposureTime" in req and \
+ req["android.sensor.exposureTime"] > longest_exp_time:
+ longest_exp_time = req["android.sensor.exposureTime"]
+
+ extended_timeout = longest_exp_time / self.SEC_TO_NSEC + \
+ self.SOCK_TIMEOUT
+ self.sock.settimeout(extended_timeout)
+
print "Capturing %d frame%s with %d format%s [%s]" % (
ncap, "s" if ncap>1 else "", nsurf, "s" if nsurf>1 else "",
",".join(formats))
@@ -524,6 +537,7 @@
obj["metadata"] = mds[i]
objs.append(obj)
rets.append(objs if ncap>1 else objs[0])
+ self.sock.settimeout(self.SOCK_TIMEOUT)
return rets if len(rets)>1 else rets[0]
def report_result(camera_id, success, summary_path=None):
diff --git a/apps/CameraITS/pymodules/its/objects.py b/apps/CameraITS/pymodules/its/objects.py
index a531f3b..5967599 100644
--- a/apps/CameraITS/pymodules/its/objects.py
+++ b/apps/CameraITS/pymodules/its/objects.py
@@ -123,36 +123,64 @@
"android.tonemap.mode": 1,
}
-def get_available_output_sizes(fmt, props):
+def fastest_auto_capture_request(props):
+ """Return an auto capture request for the fastest capture.
+
+ Args:
+ props: the object returned from its.device.get_camera_properties().
+
+ Returns:
+ A capture request with everything set to auto and all filters that
+ may slow down capture set to OFF or FAST if possible
+ """
+ req = auto_capture_request()
+ turn_slow_filters_off(props, req)
+
+ return req
+
+def get_available_output_sizes(fmt, props, max_size=None, match_ar_size=None):
"""Return a sorted list of available output sizes for a given format.
Args:
fmt: the output format, as a string in ["jpg", "yuv", "raw"].
props: the object returned from its.device.get_camera_properties().
+ max_size: (Optional) A (w,h) tuple.
+ Sizes larger than max_size (either w or h) will be discarded.
+ match_ar_size: (Optional) A (w,h) tuple.
+ Sizes not matching the aspect ratio of match_ar_size will be
+ discarded.
Returns:
A sorted list of (w,h) tuples (sorted large-to-small).
"""
+ AR_TOLERANCE = 0.03
fmt_codes = {"raw":0x20, "raw10":0x25, "yuv":0x23, "jpg":0x100, "jpeg":0x100}
configs = props['android.scaler.streamConfigurationMap']\
['availableStreamConfigurations']
fmt_configs = [cfg for cfg in configs if cfg['format'] == fmt_codes[fmt]]
out_configs = [cfg for cfg in fmt_configs if cfg['input'] == False]
out_sizes = [(cfg['width'],cfg['height']) for cfg in out_configs]
+ if max_size:
+ out_sizes = [s for s in out_sizes if
+ s[0] <= max_size[0] and s[1] <= max_size[1]]
+ if match_ar_size:
+ ar = match_ar_size[0] / float(match_ar_size[1])
+ out_sizes = [s for s in out_sizes if
+ abs(ar - s[0] / float(s[1])) <= AR_TOLERANCE]
out_sizes.sort(reverse=True)
return out_sizes
def set_filter_off_or_fast_if_possible(props, req, available_modes, filter):
- """ Check and set controlKey to off or fast in req
+ """Check and set controlKey to off or fast in req.
Args:
props: the object returned from its.device.get_camera_properties().
- req: the input request.
+ req: the input request. filter will be set to OFF or FAST if possible.
available_modes: the key to check available modes.
filter: the filter key
Returns:
- None. control_key will be set to OFF or FAST if possible.
+ Nothing.
"""
if props.has_key(available_modes):
if 0 in props[available_modes]:
@@ -160,6 +188,41 @@
elif 1 in props[available_modes]:
req[filter] = 1
+def turn_slow_filters_off(props, req):
+ """Turn filters that may slow FPS down to OFF or FAST in input request.
+
+ This function modifies the request argument, such that filters that may
+ reduce the frames-per-second throughput of the camera device will be set to
+ OFF or FAST if possible.
+
+ Args:
+ props: the object returned from its.device.get_camera_properties().
+ req: the input request.
+
+ Returns:
+ Nothing.
+ """
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.noiseReduction.availableNoiseReductionModes",
+ "android.noiseReduction.mode")
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.colorCorrection.availableAberrationModes",
+ "android.colorCorrection.aberrationMode")
+ if props.has_key("android.request.availableCharacteristicsKeys"):
+ hot_pixel_modes = 393217 in props["android.request.availableCharacteristicsKeys"]
+ edge_modes = 196610 in props["android.request.availableCharacteristicsKeys"]
+ if props.has_key("android.request.availableRequestKeys"):
+ hot_pixel_mode = 393216 in props["android.request.availableRequestKeys"]
+ edge_mode = 196608 in props["android.request.availableRequestKeys"]
+ if hot_pixel_modes and hot_pixel_mode:
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.hotPixel.availableHotPixelModes",
+ "android.hotPixel.mode")
+ if edge_modes and edge_mode:
+ set_filter_off_or_fast_if_possible(props, req,
+ "android.edge.availableEdgeModes",
+ "android.edge.mode")
+
def get_fastest_manual_capture_settings(props):
"""Return a capture request and format spec for the fastest capture.
@@ -178,18 +241,7 @@
e = min(props['android.sensor.info.exposureTimeRange'])
req = manual_capture_request(s,e)
- set_filter_off_or_fast_if_possible(props, req,
- "android.noiseReduction.availableNoiseReductionModes",
- "android.noiseReduction.mode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.colorCorrection.availableAberrationModes",
- "android.colorCorrection.aberrationMode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.hotPixel.availableHotPixelModes",
- "android.hotPixel.mode")
- set_filter_off_or_fast_if_possible(props, req,
- "android.edge.availableEdgeModes",
- "android.edge.mode")
+ turn_slow_filters_off(props, req)
return req, out_spec
diff --git a/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py b/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
index fdf72be..87500c7 100644
--- a/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
+++ b/apps/CameraITS/tests/inprog/test_burst_sameness_auto.py
@@ -48,7 +48,7 @@
cam.do_3a(lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.blackLevel.lock"] = True
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
diff --git a/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py b/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
index a8d1d45..932c051 100644
--- a/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
+++ b/apps/CameraITS/tests/inprog/test_burst_sameness_fullres_auto.py
@@ -47,7 +47,7 @@
cam.do_3a(lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.blackLevel.lock"] = True
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
diff --git a/apps/CameraITS/tests/scene0/test_jitter.py b/apps/CameraITS/tests/scene0/test_jitter.py
index 82e8e38..c519792 100644
--- a/apps/CameraITS/tests/scene0/test_jitter.py
+++ b/apps/CameraITS/tests/scene0/test_jitter.py
@@ -33,7 +33,8 @@
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
- its.caps.skip_unless(its.caps.manual_sensor(props))
+ its.caps.skip_unless(its.caps.manual_sensor(props) and
+ its.caps.sensor_fusion(props))
req, fmt = its.objects.get_fastest_manual_capture_settings(props)
caps = cam.do_capture([req]*50, [fmt])
diff --git a/apps/CameraITS/tests/scene0/test_metadata.py b/apps/CameraITS/tests/scene0/test_metadata.py
index b4ca4cb..375a6af 100644
--- a/apps/CameraITS/tests/scene0/test_metadata.py
+++ b/apps/CameraITS/tests/scene0/test_metadata.py
@@ -48,16 +48,19 @@
check('props["android.info.supportedHardwareLevel"] is not None')
check('props["android.info.supportedHardwareLevel"] in [0,1,2]')
full = getval('props["android.info.supportedHardwareLevel"]') == 1
+ manual_sensor = its.caps.manual_sensor(props)
# Test: rollingShutterSkew, and frameDuration tags must all be present,
# and rollingShutterSkew must be greater than zero and smaller than all
# of the possible frame durations.
- check('md.has_key("android.sensor.frameDuration")')
- check('md["android.sensor.frameDuration"] is not None')
+ if manual_sensor:
+ check('md.has_key("android.sensor.frameDuration")')
+ check('md["android.sensor.frameDuration"] is not None')
check('md.has_key("android.sensor.rollingShutterSkew")')
check('md["android.sensor.rollingShutterSkew"] is not None')
- check('md["android.sensor.frameDuration"] > '
- 'md["android.sensor.rollingShutterSkew"] > 0')
+ if manual_sensor:
+ check('md["android.sensor.frameDuration"] > '
+ 'md["android.sensor.rollingShutterSkew"] > 0')
# Test: timestampSource must be a valid value.
check('props.has_key("android.sensor.info.timestampSource")')
diff --git a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
index a6a5214..c3c2147 100644
--- a/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
+++ b/apps/CameraITS/tests/scene0/test_param_sensitivity_burst.py
@@ -24,6 +24,7 @@
"""
NUM_STEPS = 3
+ ERROR_TOLERANCE = 0.97 # Allow ISO to be rounded down by 3%
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
@@ -41,7 +42,8 @@
for i,cap in enumerate(caps):
s_req = sens_list[i]
s_res = cap["metadata"]["android.sensor.sensitivity"]
- assert(s_req == s_res)
+ assert(s_req >= s_res)
+ assert(s_res/float(s_req) > ERROR_TOLERANCE)
if __name__ == '__main__':
main()
diff --git a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
index 563cebd..bb91c9a 100644
--- a/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
+++ b/apps/CameraITS/tests/scene1/test_ae_precapture_trigger.py
@@ -17,8 +17,10 @@
import its.objects
import its.target
+AE_FRAMES_PER_ITERATION = 8
+AE_CONVERGE_ITERATIONS = 3
# AE must converge within this number of auto requests under scene1
-THRESH_AE_CONVERGE = 8
+THRESH_AE_CONVERGE = AE_FRAMES_PER_ITERATION * AE_CONVERGE_ITERATIONS
def main():
"""Test the AE state machine when using the precapture trigger.
@@ -71,9 +73,12 @@
# Capture some more auto requests, and AE should converge.
auto_req['android.control.aePrecaptureTrigger'] = 0
- caps = cam.do_capture([auto_req] * THRESH_AE_CONVERGE, fmt)
- state = caps[-1]['metadata']['android.control.aeState']
- print "AE state after auto request:", state
+ for i in range(AE_CONVERGE_ITERATIONS):
+ caps = cam.do_capture([auto_req] * AE_FRAMES_PER_ITERATION, fmt)
+ state = caps[-1]['metadata']['android.control.aeState']
+ print "AE state after auto request:", state
+ if state == CONVERGED:
+ return
assert(state == CONVERGED)
if __name__ == '__main__':
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
index 6341c67..9b43a74 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_advanced.py
@@ -33,9 +33,16 @@
props = cam.get_camera_properties()
its.caps.skip_unless(its.caps.manual_sensor(props) and
its.caps.manual_post_proc(props) and
- its.caps.per_frame_control(props))
+ its.caps.per_frame_control(props) and
+ its.caps.ev_compensation(props))
- evs = range(-4,5)
+ ev_compensation_range = props['android.control.aeCompensationRange']
+ range_min = ev_compensation_range[0]
+ range_max = ev_compensation_range[1]
+ ev_per_step = its.objects.rational_to_float(
+ props['android.control.aeCompensationStep'])
+ steps_per_ev = int(1.0 / ev_per_step)
+ evs = range(range_min, range_max + 1, steps_per_ev)
lumas = []
for ev in evs:
# Re-converge 3A, and lock AE once converged. skip AF trigger as
@@ -58,10 +65,8 @@
tile = its.image.get_image_patch(y, 0.45,0.45,0.1,0.1)
lumas.append(its.image.compute_image_means(tile)[0])
- ev_step_size_in_stops = its.objects.rational_to_float(
- props['android.control.aeCompensationStep'])
- luma_increase_per_step = pow(2, ev_step_size_in_stops)
- print "ev_step_size_in_stops", ev_step_size_in_stops
+ luma_increase_per_step = pow(2, ev_per_step)
+ print "ev_step_size_in_stops", ev_per_step
imid = len(lumas) / 2
expected_lumas = [lumas[imid] / pow(luma_increase_per_step, i)
for i in range(imid , 0, -1)] + \
diff --git a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
index 13f318f..d09f2fd 100644
--- a/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1/test_ev_compensation_basic.py
@@ -13,6 +13,7 @@
# limitations under the License.
import its.image
+import its.caps
import its.device
import its.objects
import os.path
@@ -28,8 +29,12 @@
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
+ its.caps.skip_unless(its.caps.ev_compensation(props))
- evs = range(-4,5)
+ ev_per_step = its.objects.rational_to_float(
+ props['android.control.aeCompensationStep'])
+ steps_per_ev = int(1.0 / ev_per_step)
+ evs = range(-2 * steps_per_ev, 2 * steps_per_ev + 1, steps_per_ev)
lumas = []
for ev in evs:
# Re-converge 3A, and lock AE once converged. skip AF trigger as
@@ -49,6 +54,11 @@
pylab.plot(evs, lumas, 'r')
matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
+ # trim trailing 1.0s (for saturated image)
+ while lumas and lumas[-1] == 1.0:
+ lumas.pop(-1)
+ # Only allow positive EVs to give saturated image
+ assert(len(lumas) > 2)
luma_diffs = numpy.diff(lumas)
min_luma_diffs = min(luma_diffs)
print "Min of the luma value difference between adjacent ev comp: ", \
diff --git a/apps/CameraITS/tests/scene1/test_exposure.py b/apps/CameraITS/tests/scene1/test_exposure.py
index c55e7ad..d217bdb 100644
--- a/apps/CameraITS/tests/scene1/test_exposure.py
+++ b/apps/CameraITS/tests/scene1/test_exposure.py
@@ -35,7 +35,7 @@
THRESHOLD_MAX_OUTLIER_DIFF = 0.1
THRESHOLD_MIN_LEVEL = 0.1
THRESHOLD_MAX_LEVEL = 0.9
- THRESHOLD_MAX_ABS_GRAD = 0.001
+ THRESHOLD_MAX_LEVEL_DIFF = 0.025
mults = []
r_means = []
@@ -72,15 +72,18 @@
pylab.ylim([0,1])
matplotlib.pyplot.savefig("%s_plot_means.png" % (NAME))
- # Check for linearity. For each R,G,B channel, fit a line y=mx+b, and
- # assert that the gradient is close to 0 (flat) and that there are no
- # crazy outliers. Also ensure that the images aren't clamped to 0 or 1
+ # Check for linearity. Verify sample pixel mean values are close to each
+ # other. Also ensure that the images aren't clamped to 0 or 1
# (which would make them look like flat lines).
for chan in xrange(3):
values = [r_means, g_means, b_means][chan]
m, b = numpy.polyfit(mults, values, 1).tolist()
+ max_val = max(values)
+ min_val = min(values)
+ max_diff = max_val - min_val
print "Channel %d line fit (y = mx+b): m = %f, b = %f" % (chan, m, b)
- assert(abs(m) < THRESHOLD_MAX_ABS_GRAD)
+ print "Channel max %f min %f diff %f" % (max_val, min_val, max_diff)
+ assert(max_diff < THRESHOLD_MAX_LEVEL_DIFF)
assert(b > THRESHOLD_MIN_LEVEL and b < THRESHOLD_MAX_LEVEL)
for v in values:
assert(v > THRESHOLD_MIN_LEVEL and v < THRESHOLD_MAX_LEVEL)
diff --git a/apps/CameraITS/tests/scene1/test_linearity.py b/apps/CameraITS/tests/scene1/test_linearity.py
index a9063a9..2176f5e 100644
--- a/apps/CameraITS/tests/scene1/test_linearity.py
+++ b/apps/CameraITS/tests/scene1/test_linearity.py
@@ -33,7 +33,7 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
- RESIDUAL_THRESHOLD = 0.00005
+ RESIDUAL_THRESHOLD = 0.0003 # approximately each sample is off by 2/255
# The HAL3.2 spec requires that curves up to 64 control points in length
# must be supported.
diff --git a/apps/CameraITS/tests/scene1/test_locked_burst.py b/apps/CameraITS/tests/scene1/test_locked_burst.py
index 5cea30c..a33a9f8 100644
--- a/apps/CameraITS/tests/scene1/test_locked_burst.py
+++ b/apps/CameraITS/tests/scene1/test_locked_burst.py
@@ -15,6 +15,7 @@
import its.image
import its.device
import its.objects
+import its.caps
import os.path
import numpy
import pylab
@@ -25,13 +26,14 @@
"""Test 3A lock + YUV burst (using auto settings).
This is a test that is designed to pass even on limited devices that
- don't have MANUAL_SENSOR or PER_FRAME_CONTROLS. (They must be able to
- capture bursts with full res @ full frame rate to pass, however).
+ don't have MANUAL_SENSOR or PER_FRAME_CONTROLS. The test checks
+ YUV image consistency while the frame rate check is in CTS.
"""
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:
@@ -41,7 +43,7 @@
cam.do_3a(do_af=True, lock_ae=True, lock_awb=True)
# After 3A has converged, lock AE+AWB for the duration of the test.
- req = its.objects.auto_capture_request()
+ req = its.objects.fastest_auto_capture_request(props)
req["android.control.awbLock"] = True
req["android.control.aeLock"] = True
@@ -64,28 +66,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)
-
- # Also ensure that the burst was at full frame rate.
- fmt_code = 0x23
- configs = props['android.scaler.streamConfigurationMap']\
- ['availableStreamConfigurations']
- min_duration = None
- for cfg in configs:
- if cfg['format'] == fmt_code and cfg['input'] == False and \
- cfg['width'] == caps[0]["width"] and \
- cfg['height'] == caps[0]["height"]:
- min_duration = cfg["minFrameDuration"]
- assert(min_duration is not None)
- tstamps = [c['metadata']['android.sensor.timestamp'] for c in caps]
- deltas = [tstamps[i]-tstamps[i-1] for i in range(1,len(tstamps))]
- actual_fps = 1.0 / (max(deltas) / 1000000000.0)
- actual_fps_max = 1.0 / (min(deltas) / 1000000000.0)
- max_fps = 1.0 / (min_duration / 1000000000.0)
- print "Measure FPS min/max", actual_fps, "/", actual_fps_max
- print "FPS measured %.1f, max advertized %.1f" %(actual_fps, max_fps)
- assert(max_fps - FPS_MAX_DIFF <= actual_fps <= max_fps + FPS_MAX_DIFF)
+ " (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_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index f5176a7..219927d 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -34,10 +34,10 @@
"""
NAME = os.path.basename(__file__).split(".")[0]
- # List of variances for Y,U,V.
+ # List of variances for R,G,B.
variances = [[],[],[]]
- # Reference (baseline) variance for each of Y,U,V.
+ # Reference (baseline) variance for each of R,G,B.
ref_variance = []
nr_modes_reported = []
@@ -52,33 +52,32 @@
req = its.objects.manual_capture_request(s, e)
req["android.noiseReduction.mode"] = 0
cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
+ rgb_image,
"%s_low_gain.jpg" % (NAME))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- ref_variance.append(its.image.compute_image_variances(tile)[0])
+ rgb_tile = its.image.get_image_patch(rgb_image, 0.45, 0.45, 0.1, 0.1)
+ ref_variance = its.image.compute_image_variances(rgb_tile)
print "Ref variances:", ref_variance
+ e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
for i in range(3):
# NR modes 0, 1, 2 with high gain
- e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
req = its.objects.manual_capture_request(s, e)
req["android.noiseReduction.mode"] = i
cap = cam.do_capture(req)
+ rgb_image = its.image.convert_capture_to_rgb_image(cap)
nr_modes_reported.append(
cap["metadata"]["android.noiseReduction.mode"])
its.image.write_image(
- its.image.convert_capture_to_rgb_image(cap),
+ rgb_image,
"%s_high_gain_nr=%d.jpg" % (NAME, i))
- planes = its.image.convert_capture_to_planes(cap)
- for j in range(3):
- img = planes[j]
- tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
- variance = its.image.compute_image_variances(tile)[0]
- variances[j].append(variance / ref_variance[j])
+ rgb_tile = its.image.get_image_patch(
+ rgb_image, 0.45, 0.45, 0.1, 0.1)
+ rgb_vars = its.image.compute_image_variances(rgb_tile)
+ for chan in range(3):
+ variance = rgb_vars[chan]
+ variances[chan].append(variance / ref_variance[chan])
print "Variances with NR mode [0,1,2]:", variances
# Draw a plot.
diff --git a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
index 6c2b5c1..e176312 100644
--- a/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_burst_sensitivity.py
@@ -44,6 +44,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
index 14c5eb0..cc0ce14 100644
--- a/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
+++ b/apps/CameraITS/tests/scene1/test_raw_sensitivity.py
@@ -42,6 +42,8 @@
# Expose for the scene with min sensitivity
sens_min, sens_max = props['android.sensor.info.sensitivityRange']
+ # Digital gains might not be visible on RAW data
+ sens_max = props['android.sensor.maxAnalogSensitivity']
sens_step = (sens_max - sens_min) / NUM_STEPS
s_ae,e_ae,_,_,_ = cam.do_3a(get_results=True)
s_e_prod = s_ae * e_ae
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
index 33e7763..268b64a 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_dng.py
@@ -31,6 +31,12 @@
cam.do_3a()
req = its.objects.auto_capture_request()
+ max_dng_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_dng_size)[0]
+ out_surfaces = [{"format":"dng"},
+ {"format":"yuv", "width":w, "height":h}]
cap_dng, cap_yuv = cam.do_capture(req, cam.CAP_DNG_YUV)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
index 6daa243..e58cb1d 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_jpeg.py
@@ -27,13 +27,17 @@
THRESHOLD_MAX_RMS_DIFF = 0.01
- fmt_yuv = {"format":"yuv"}
- fmt_jpeg = {"format":"jpeg"}
-
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(its.caps.compute_target_exposure(props))
+ max_jpeg_size = \
+ its.objects.get_available_output_sizes("jpeg", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_jpeg_size)[0]
+ fmt_yuv = {"format":"yuv", "width":w, "height":h}
+ fmt_jpeg = {"format":"jpeg"}
+
# 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"]
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
index eb01c1a..2e7804b 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw.py
@@ -38,7 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True)
- cap_raw, cap_yuv = cam.do_capture(req, cam.CAP_RAW_YUV)
+ max_raw_size = \
+ its.objects.get_available_output_sizes("raw", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw_size)[0]
+ out_surfaces = [{"format":"raw"},
+ {"format":"yuv", "width":w, "height":h}]
+ cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
index 910a8ea..d02f84e 100644
--- a/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
+++ b/apps/CameraITS/tests/scene1/test_yuv_plus_raw10.py
@@ -38,8 +38,13 @@
e, s = its.target.get_target_exposure_combos(cam)["midExposureTime"]
req = its.objects.manual_capture_request(s, e, True)
+ max_raw10_size = \
+ its.objects.get_available_output_sizes("raw10", props)[0]
+ w,h = its.objects.get_available_output_sizes(
+ "yuv", props, (1920, 1080), max_raw10_size)[0]
cap_raw, cap_yuv = cam.do_capture(req,
- [{"format":"raw10"}, {"format":"yuv"}])
+ [{"format":"raw10"},
+ {"format":"yuv", "width":w, "height":h}])
img = its.image.convert_capture_to_rgb_image(cap_yuv)
its.image.write_image(img, "%s_yuv.jpg" % (NAME), True)
diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
index 49f47a9..efeb665 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py
@@ -81,9 +81,23 @@
else:
events, frames = load_data()
+ # Sanity check camera timestamps are enclosed by sensor timestamps
+ # This will catch bugs where camera and gyro timestamps go completely out
+ # of sync
+ cam_times = get_cam_times(events["cam"])
+ min_cam_time = min(cam_times) * NSEC_TO_SEC
+ max_cam_time = max(cam_times) * NSEC_TO_SEC
+ gyro_times = [e["time"] for e in events["gyro"]]
+ min_gyro_time = min(gyro_times) * NSEC_TO_SEC
+ max_gyro_time = max(gyro_times) * NSEC_TO_SEC
+ if not (min_cam_time > min_gyro_time and max_cam_time < max_gyro_time):
+ print "Test failed: camera timestamps [%f,%f] " \
+ "are not enclosed by gyro timestamps [%f, %f]" % (
+ min_cam_time, max_cam_time, min_gyro_time, max_gyro_time)
+ assert(0)
+
# Compute the camera rotation displacements (rad) between each pair of
# adjacent frames.
- cam_times = get_cam_times(events["cam"])
cam_rots = get_cam_rotations(frames)
if max(abs(cam_rots)) < THRESH_MIN_ROT:
print "Device wasn't moved enough"
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index b56281d..2bbd387 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -37,11 +37,9 @@
],
"scene1":[
"test_ae_precapture_trigger",
- "test_black_white",
"test_crop_region_raw",
"test_ev_compensation_advanced",
"test_ev_compensation_basic",
- "test_locked_burst",
"test_yuv_plus_jpeg"
]
}
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 87f962f..dc5fda5 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 18fa658..17ddbac 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -18,7 +18,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.cts.verifier"
android:versionCode="5"
- android:versionName="5.0_r2.5">
+ android:versionName="5.1_r3">
<uses-sdk android:minSdkVersion="19" android:targetSdkVersion="21"/>
@@ -1297,10 +1297,28 @@
<action android:name="com.android.cts.verifier.managedprovisioning.BYOD_QUERY" />
<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.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" />
+ <action android:name="com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM" />
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
+ <activity android:name=".managedprovisioning.NfcTestActivity">
+ <meta-data android:name="test_required_features" android:value="android.hardware.nfc" />
+ </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" />
@@ -1447,6 +1465,54 @@
android:resource="@xml/mock_content_rating_systems" />
</receiver>
+ <activity android:name=".car.CarDockTestActivity"
+ android:label="@string/car_dock_test">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_car" />
+ <meta-data android:name="test_excluded_features"
+ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
+
+ </activity>
+
+ <activity android:name=".car.CarDockActivity"
+ android:launchMode="singleTask"
+ android:autoRemoveFromRecents="true"
+ android:exported="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+
+ <!-- See explaination in CarDockTestActivity.java -->
+ <activity-alias android:name=".car.CarDockActivity1"
+ android:targetActivity=".car.CarDockActivity" >
+ <meta-data
+ android:name="android.dock_home"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.CAR_DOCK" />
+ </intent-filter>
+ </activity-alias>
+
+ <activity-alias android:name=".car.CarDockActivity2"
+ android:targetActivity=".car.CarDockActivity"
+ android:enabled="false" >
+ <meta-data
+ android:name="android.dock_home"
+ android:value="true" />
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.CAR_DOCK" />
+ </intent-filter>
+ </activity-alias>
+
</application>
</manifest>
diff --git a/apps/CtsVerifier/res/layout-small/sensor_test.xml b/apps/CtsVerifier/res/layout-small/sensor_test.xml
new file mode 100644
index 0000000..eefa5fa
--- /dev/null
+++ b/apps/CtsVerifier/res/layout-small/sensor_test.xml
@@ -0,0 +1,45 @@
+<?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.
+-->
+<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ScrollView android:id="@+id/log_scroll_view"
+ app:layout_box="all"
+ android:fillViewport="true"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent">
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent">
+
+ <LinearLayout android:id="@+id/log_layout"
+ android:orientation="vertical"
+ android:layout_height="wrap_content"
+ android:layout_width="match_parent"/>
+
+ <android.opengl.GLSurfaceView android:id="@+id/gl_surface_view"
+ android:visibility="gone"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:layout_width="match_parent"/>
+
+ <include layout="@layout/snsr_next_button"/>
+ </LinearLayout>
+
+ </ScrollView>
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml b/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml
new file mode 100644
index 0000000..52251b4
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/byod_nfc_test_activity.xml
@@ -0,0 +1,31 @@
+<?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.
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+
+ <Button android:text="@string/provisioning_byod_send_manual_beam"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/manual_beam_button" />
+
+ <Button android:text="@string/provisioning_byod_send_share_intent"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/intent_share_button"
+ android:layout_below="@+id/manual_beam_button" />
+
+</RelativeLayout>
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/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/layout/car_dock_main.xml
similarity index 64%
copy from tests/tests/uirendering/res/layout/draw_activity_view.xml
copy to apps/CtsVerifier/res/layout/car_dock_main.xml
index 54a72e3..97f0712 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/layout/car_dock_main.xml
@@ -13,5 +13,14 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent" >
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:text="@string/car_dock_activity_text" />
+
+</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/car_dock_test_main.xml b/apps/CtsVerifier/res/layout/car_dock_test_main.xml
new file mode 100644
index 0000000..c568b54
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/car_dock_test_main.xml
@@ -0,0 +1,32 @@
+<?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.
+-->
+<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <LinearLayout app:layout_box="all"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+ <include layout="@layout/pass_fail_buttons" />
+ <Button
+ android:id="@+id/car_mode"
+ android:layout_height="100dp"
+ android:layout_width="500dp"
+ android:layout_gravity="center"
+ android:text="@string/car_mode_enable" />
+ </LinearLayout>
+</android.support.wearable.view.BoxInsetLayout>
diff --git a/apps/CtsVerifier/res/layout/fs_info.xml b/apps/CtsVerifier/res/layout/fs_info.xml
index 3fe6815..ea02fee 100644
--- a/apps/CtsVerifier/res/layout/fs_info.xml
+++ b/apps/CtsVerifier/res/layout/fs_info.xml
@@ -13,62 +13,44 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent" android:layout_height="wrap_content">
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent" android:layout_height="fill_parent">
+ <GridLayout
+ android:columnCount="2"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content">
- <ImageView android:id="@+id/fs_legend_good_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_good"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true" />
- <TextView android:id="@+id/fs_legend_good_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_good"
- android:layout_toRightOf="@id/fs_legend_good_image"
- android:layout_alignTop="@id/fs_legend_good_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_good_image"
+ android:src="@drawable/fs_good"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_good_text"
+ android:text="@string/fs_legend_good"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_indeterminate_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_indeterminate"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_good_image" />
- <TextView android:id="@+id/fs_legend_indeterminate_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_indeterminate"
- android:layout_toRightOf="@id/fs_legend_indeterminate_image"
- android:layout_alignTop="@id/fs_legend_indeterminate_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_indeterminate_image"
+ android:src="@drawable/fs_indeterminate"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_indeterminate_text"
+ android:text="@string/fs_legend_indeterminate"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_warning_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_warning"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_indeterminate_image" />
- <TextView android:id="@+id/fs_legend_warning_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_warning"
- android:layout_toRightOf="@id/fs_legend_warning_image"
- android:layout_alignTop="@id/fs_legend_warning_image"
- android:layout_marginLeft="3dip" />
+ <ImageView android:id="@+id/fs_legend_warning_image"
+ android:src="@drawable/fs_warning"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_warning_text"
+ android:text="@string/fs_legend_warning"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
- <ImageView android:id="@+id/fs_legend_error_image"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/fs_error"
- android:layout_alignParentLeft="true"
- android:layout_below="@id/fs_legend_warning_image" />
- <TextView android:id="@+id/fs_legend_error_text"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/fs_legend_error"
- android:layout_toRightOf="@id/fs_legend_error_image"
- android:layout_alignTop="@id/fs_legend_error_image"
- android:layout_marginLeft="3dip" />
-</RelativeLayout>
+ <ImageView android:id="@+id/fs_legend_error_image"
+ android:src="@drawable/fs_error"
+ android:layout_gravity="top|left" />
+ <TextView android:id="@+id/fs_legend_error_text"
+ android:text="@string/fs_legend_error"
+ android:layout_marginLeft="3dip"
+ android:layout_gravity="top|left" />
+ </GridLayout>
+</ScrollView>
diff --git a/apps/CtsVerifier/res/layout/pwa_widgets.xml b/apps/CtsVerifier/res/layout/pwa_widgets.xml
index 537fc32..6204d3e 100644
--- a/apps/CtsVerifier/res/layout/pwa_widgets.xml
+++ b/apps/CtsVerifier/res/layout/pwa_widgets.xml
@@ -56,20 +56,6 @@
android:layout_weight="1"
android:text="@string/pwa_button_down" />
- <Button
- android:id="@+id/left_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:text="@string/pwa_button_left" />
-
- <Button
- android:id="@+id/right_button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/pwa_button_right" />
-
</LinearLayout>
</LinearLayout>
diff --git a/apps/CtsVerifier/res/layout/snsr_next_button.xml b/apps/CtsVerifier/res/layout/snsr_next_button.xml
index e7a6d72..391c394 100644
--- a/apps/CtsVerifier/res/layout/snsr_next_button.xml
+++ b/apps/CtsVerifier/res/layout/snsr_next_button.xml
@@ -13,9 +13,9 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
- android:gravity="center"
+ android:columnCount="@integer/test_list_footer_button_count"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/snsr_view_padding_bottom"
@@ -25,6 +25,7 @@
<Button android:id="@+id/next_button"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
+ android:layout_columnWeight="1"
android:text="@string/next_button_text"
/>
@@ -32,6 +33,7 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_columnWeight="1"
android:drawableTop="@drawable/fs_good"
/>
@@ -39,7 +41,8 @@
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_columnWeight="1"
android:drawableTop="@drawable/fs_error"
/>
-</LinearLayout>
+</GridLayout>
diff --git a/apps/CtsVerifier/res/layout/test_list_footer.xml b/apps/CtsVerifier/res/layout/test_list_footer.xml
index fdb8e43..cb73ed1 100644
--- a/apps/CtsVerifier/res/layout/test_list_footer.xml
+++ b/apps/CtsVerifier/res/layout/test_list_footer.xml
@@ -17,22 +17,26 @@
-->
<GridLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
+ android:columnCount="@integer/test_list_footer_button_count"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/clear"
android:text="@string/clear"
+ android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/view"
android:text="@string/view"
+ android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<Button
android:id="@+id/export"
android:text="@string/export"
+ android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</GridLayout>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/values-small/integers.xml
similarity index 78%
rename from tests/tests/uirendering/res/layout/draw_activity_view.xml
rename to apps/CtsVerifier/res/values-small/integers.xml
index 54a72e3..274db44 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/values-small/integers.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -13,5 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+<resources>
+ <integer name="test_list_footer_button_count">1</integer>
+</resources>
diff --git a/tests/tests/uirendering/res/layout/draw_activity_view.xml b/apps/CtsVerifier/res/values/integers.xml
similarity index 78%
copy from tests/tests/uirendering/res/layout/draw_activity_view.xml
copy to apps/CtsVerifier/res/values/integers.xml
index 54a72e3..2ced54b 100644
--- a/tests/tests/uirendering/res/layout/draw_activity_view.xml
+++ b/apps/CtsVerifier/res/values/integers.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
+<!-- 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.
@@ -13,5 +13,6 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-
-<android.uirendering.cts.CanvasClientView xmlns:android="http://schemas.android.com/apk/res/android"/>
+<resources>
+ <integer name="test_list_footer_button_count">3</integer>
+</resources>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index bd748ac..07f7654 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -26,6 +26,7 @@
<!-- Strings for TestListActivity -->
<string name="test_category_audio">Audio</string>
<string name="test_category_camera">Camera</string>
+ <string name="test_category_car">Car</string>
<string name="test_category_device_admin">Device Administration</string>
<string name="test_category_hardware">Hardware</string>
<string name="test_category_networking">Networking</string>
@@ -86,6 +87,17 @@
device and verify that all rows in the policy list are green. Red items indicate policy
settings that were not loaded properly.
</string>
+ <string name="car_dock_test">Car Dock Test</string>
+ <string name="car_dock_test_desc">This test ensures that car mode opens the app associated with
+ car dock when going into car mode.\n\n
+ Click on "Enable Car Mode" to start the test. Clicking on the button will either bring up a
+ disambiguation dialog asking which app to open or immediately open the CAR_DOCK application.
+ Select the "CTS Verifier" app and then "Always" if the dialog pops up.
+ This will open the CAR_DOCK application.\n\n
+ In the CAR_DOCK application, press the home button, which will enable the pass button if the
+ framework correctly tries to open the CAR_DOCK app again.</string>
+ <string name="car_mode_enable">Enable Car Mode</string>
+ <string name="car_dock_activity_text">Press the Home button</string>
<string name="da_no_policy">1. Press the \"Generate Policy\" to create a random device
policy\n\n2. Press \"Apply Policy\" to put the policy into effect.\n\n3. Reboot your
device and return to this test in the CTS Verifier.
@@ -979,6 +991,7 @@
<string name="package_priority_info">This test checks that the NotificationManagerService respects
user preferences about relative package priorities.
</string>
+ <string name="package_priority_bot">Verifying that the CTS Robot helper package is installed.</string>
<string name="package_priority_high">Find \"%s\" under \"App notifications\" in the \"Sound & notifications\" settings panel, and mark it as having notification priority.</string>
<string name="package_priority_default">Find \"%s\" under \"App notifications\" in the \"Sound & notifications\" settings panel, and make sure it has default priority.</string>
<string name="package_priority_user_order">Check that ranker respects user priorities.</string>
@@ -1033,7 +1046,8 @@
Location Provider API when the device is in High Accuracy location mode.
</string>
<string name="location_mode_select_high_accuracy">
- Please select the \"High accuracy\" mode at Settings > Location and return here.
+ Please select the \"High accuracy\" mode at Settings > Location
+ (hint: tap the "Mode" item) and return here.
</string>
<string name="location_mode_battery_saving_test">Battery Saving Mode Test</string>
<string name="location_mode_battery_saving_info">
@@ -1041,7 +1055,8 @@
Location Provider API when the device is in Battery Saving location mode.
</string>
<string name="location_mode_select_battery_saving">
- Please select the \"Battery Saving\" mode at Settings > Location and return here.
+ Please select the \"Battery Saving\" mode at Settings > Location
+ (hint: tap the "Mode" item) and return here.
</string>
<string name="location_mode_device_only_test">Device Only Mode Test</string>
<string name="location_mode_device_only_info">
@@ -1050,7 +1065,7 @@
</string>
<string name="location_mode_select_device_only">
Please select the \"Device Only\" mode at
- Settings > Location and return here.
+ Settings > Location (hint: tap the "Mode" item) and return here.
</string>
<string name="location_mode_off_test">Location Mode Off Test</string>
<string name="location_mode_off_info">
@@ -1117,6 +1132,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">
@@ -1361,6 +1409,27 @@
Then use the Back button to return to this test and mark accordingly.
</string>
+ <string name="provisioning_byod_nfc_beam">Disable Nfc beam</string>
+ <string name="provisioning_byod_nfc_beam_allowed_instruction">
+ Please press the Go button to test if Nfc beam can be triggered in the work profile.\n
+ \n
+ For the first test, press \"Send manual beam\" to trigger a beam, then bump into another device to send the file. Verify that the file is successfully received.\n
+ \n
+ For the second test, press \"Send share intent\" to trigger a beam, then bump into another device to send the file. Verify that the file is successfully received.\n
+ \n
+ Then use the Back button to return to this test and mark accordingly.
+ </string>
+ <string name="provisioning_byod_nfc_beam_disallowed_instruction">
+ Please press the Go button to test if Nfc beam is disallowed by policy
+ \n
+ Verify that Nfc beam is not triggered when pressing the button.\n
+ \n
+ Then use the Back button to return to this test and mark accordingly.
+ </string>
+ <string name="provisioning_byod_send_manual_beam">Send manual beam</string>
+ <string name="provisioning_byod_send_share_intent">Send share intent</string>
+ <string name="provisioning_byod_cannot_resolve_beam_activity">Cannot find beam activity</string>
+
<string name="provisioning_byod_no_activity">Cannot communicate with activity in the work profile.</string>
<string name="provisioning_byod_delete_profile">Initiate deletion of work profile.</string>
<string name="provisioning_byod_profile_deleted">Work profile deleted.</string>
@@ -1402,80 +1471,90 @@
<string name="js_any_connectivity_test">Device with no connectivity will not execute a job with an unmetered connectivity constraint.</string>
<string name="js_no_connectivity_test">Device with no connectivity will still execute a job with no connectivity constraints.</string>
- <!-- String for Live Channels app Tests -->
+ <!-- String for the bundled TV app Tests -->
- <string name="tv_input_discover_test">3rd-party TV input app discoverability test</string>
+ <string name="tv_input_discover_test">3rd-party TV input test</string>
<string name="tv_input_discover_test_info">
- This test verifies that the pre-loaded Live Channels app is invoked via intents and issues
- appropriate calls to framework APIs, so that TV input apps work properly with the pre-loaded
- Live Channels app.
+ Verify that the bundled TV app launches via Intent and calls the proper API to discover
+ 3rd-party TV inputs.
</string>
<string name="tv_input_discover_test_go_to_setup">
- Press the \"Launch Live Channels\" button, and set up the newly installed TV input:
+ Select the \"Launch TV app\" button and set up the newly installed TV input:
\"CTS Verifier\".
</string>
<string name="tv_input_discover_test_verify_setup">
Setup activity must have been started.
</string>
<string name="tv_input_discover_test_tune_to_channel">
- Press the \"Launch Live Channels\" button, and tune to the channel named \"Dummy\" from
- \"CTS Verifier\" input. If necessary, configure the channel to be visible.
+ Select the \"Launch TV app\" button and tune to the \"Dummy\" channel from \"CTS Verifier\"
+ input. If necessary, configure the channel to be visible.
</string>
<string name="tv_input_discover_test_verify_tune">
Tune command must be called.
</string>
<string name="tv_input_discover_test_verify_overlay_view">
- Overlay view must be shown. Verify that there is a text view displaying \"Overlay View Dummy Text\"
- when you tune to the \"Dummy\" channel.
+ Verify that the overlay appears and displays the text \"Overlay View Dummy Text\" when you tune
+ to the \"Dummy\" channel.
</string>
+ <string name="tv_input_discover_test_verify_global_search">
+ Verify the TV app provides query results for 3rd-party input\'s channels and programs in
+ global search results.
+ </string>
+ <string name="tv_input_discover_test_go_to_epg">
+ Select the \"Launch EPG\" button and locate the \"Dummy\" channel.
+ </string>
+ <string name="tv_input_discover_test_verify_epg">
+ Do you see the programs named \"Dummy Program\" and their descriptions
+ "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">TV app parental controls test</string>
<string name="tv_parental_control_test_info">
- This test verifies that the default Live Channels app invokes proper parental control APIs in
- the framework.
+ Verify that the bundled TV app calls the parental controls API.
</string>
<string name="tv_parental_control_test_turn_on_parental_control">
- Press the \"Launch Live Channels\" button, and turn on the parental control. If it\'s on
- already, turn it off and on again.
+ Select the \"Launch TV app\" button and turn on the parental controls. If parental controls are
+ on already, turn it off and on again.
</string>
<string name="tv_parental_control_test_verify_receive_broadcast1">
TV input service must have received ACTION_PARENTAL_CONTROLS_ENABLED_CHANGED broadcast.
</string>
<string name="tv_parental_control_test_block_tv_ma">
- Press the \"Launch Live Channels\" button, and block \"Fake\" rating for \"CtsVerifier\" rating
- system in the parental control settings. You may have to enable the rating system if it is
- disabled by default. If it\'s already blocked, unblock, save, and then block again.
+ Select the \"Launch TV app\" button and block the \"Fake\" rating for \"CtsVerifier\" rating
+ system in the parental control settings. If the rating system is disabled by default, enable it.
+ If the \"Fake\" rating is already blocked, unblock it, save, and then block again.
</string>
<string name="tv_parental_control_test_verify_receive_broadcast2">
TV input service must have received ACTION_BLOCKED_RATINGS_CHANGED broadcast.
</string>
<string name="tv_parental_control_test_block_unblock">
- Press the \"Launch Live Channels\" button; verify that the channel is blocked visually.
- Try unblock the screen by entering PIN; verify that it\'s unblocked visually.
+ Select the \"Launch TV app\" button; verify that the channel is blocked.
+ Try to unblock the screen by entering PIN; verify that it\'s unblocked.
</string>
- <string name="tv_launch_tv_app">Launch Live Channels</string>
+ <string name="tv_launch_tv_app">Launch TV app</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">TV 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.
+ Verify that the bundled TV app calls the multi-track API.
</string>
<string name="tv_multiple_tracks_test_select_subtitle">
- Press the \"Launch Live Channels\" button. Verify that the closed caption is off by default.
- Set closed caption to English.
+ Select the \"Launch TV app\" button. Verify that closed captions are off by default. Set closed
+ caption language to English.
</string>
<string name="tv_multiple_tracks_test_verify_set_caption_enabled">
- Caption should be enabled.
+ Captions are enabled.
</string>
<string name="tv_multiple_tracks_test_verify_select_subtitle">
- The English subtitle track should be selected.
+ The English closed caption track should be selected.
</string>
<string name="tv_multiple_tracks_test_select_audio">
- Press the \"Launch Live Channels\" button. Verify that the audio track is English by default.
+ Select the \"Launch TV app\" button. Verify that the audio track is English by default.
Select Spanish audio track.
</string>
<string name="tv_multiple_tracks_test_verify_select_audio">
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/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
index 6495a05..409b0db 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java
@@ -20,8 +20,10 @@
import android.app.ListActivity;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.view.View;
+import android.view.Window;
import android.widget.ListView;
/** {@link ListActivity} that displays a list of manual tests. */
@@ -57,6 +59,10 @@
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+ == Configuration.UI_MODE_TYPE_TELEVISION) {
+ getWindow().requestFeature(Window.FEATURE_OPTIONS_PANEL);
+ }
setContentView(R.layout.list_content);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
index 8b989e1..959e98f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/fov/DetermineFovActivity.java
@@ -145,7 +145,7 @@
mTargetDistanceCm = getTargetDistance();
mReportedFovDegrees = PhotoCaptureActivity.getReportedFovDegrees();
- mFovDegrees = mReportedFovDegrees > 80 ? 60 : mReportedFovDegrees;
+ mFovDegrees = mReportedFovDegrees > 120 ? 60 : mReportedFovDegrees;
mFovMaxDegrees = mFovDegrees + FOV_ADJUSTMENT_RANGE / 2;
mFovMinDegrees = mFovDegrees - FOV_ADJUSTMENT_RANGE / 2;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index e3d0b6d..ce10535 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -1094,25 +1094,34 @@
}
// Initiate the captures.
+ long maxExpTimeNs = -1;
for (int i = 0; i < requests.size(); i++) {
+ CaptureRequest.Builder req = requests.get(i);
// For DNG captures, need the LSC map to be available.
if (mCaptureRawIsDng) {
- requests.get(i).set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 1);
+ req.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE, 1);
+ }
+ Long expTimeNs = req.get(CaptureRequest.SENSOR_EXPOSURE_TIME);
+ if (expTimeNs != null && expTimeNs > maxExpTimeNs) {
+ maxExpTimeNs = expTimeNs;
}
- CaptureRequest.Builder req = requests.get(i);
for (int j = 0; j < numSurfaces; j++) {
req.addTarget(mCaptureReaders[j].getSurface());
}
mSession.capture(req.build(), mCaptureResultListener, mResultHandler);
}
+ long timeout = TIMEOUT_CALLBACK * 1000;
+ if (maxExpTimeNs > 0) {
+ timeout += maxExpTimeNs / 1000000; // ns to ms
+ }
// Make sure all callbacks have been hit (wait until captures are done).
// If no timeouts are received after a timeout, then fail.
int currentCount = mCountCallbacksRemaining.get();
while (currentCount > 0) {
try {
- Thread.sleep(TIMEOUT_CALLBACK*1000);
+ Thread.sleep(timeout);
} catch (InterruptedException e) {
throw new ItsException("Timeout failure", e);
}
@@ -1232,13 +1241,32 @@
StringBuilder logMsg = new StringBuilder();
logMsg.append(String.format(
- "Capt result: AE=%d, AF=%d, AWB=%d, sens=%d, exp=%.1fms, dur=%.1fms, ",
+ "Capt result: AE=%d, AF=%d, AWB=%d, ",
result.get(CaptureResult.CONTROL_AE_STATE),
result.get(CaptureResult.CONTROL_AF_STATE),
- result.get(CaptureResult.CONTROL_AWB_STATE),
- result.get(CaptureResult.SENSOR_SENSITIVITY),
- result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue() / 1000000.0f,
- result.get(CaptureResult.SENSOR_FRAME_DURATION).intValue() / 1000000.0f));
+ result.get(CaptureResult.CONTROL_AWB_STATE)));
+ int[] capabilities = mCameraCharacteristics.get(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ if (capabilities == null) {
+ throw new ItsException("Failed to get capabilities");
+ }
+ boolean readSensorSettings = false;
+ for (int capability : capabilities) {
+ if (capability ==
+ CameraCharacteristics.
+ REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS) {
+ readSensorSettings = true;
+ break;
+ }
+ }
+ if (readSensorSettings) {
+ logMsg.append(String.format(
+ "sens=%d, exp=%.1fms, dur=%.1fms, ",
+ result.get(CaptureResult.SENSOR_SENSITIVITY),
+ result.get(CaptureResult.SENSOR_EXPOSURE_TIME).intValue() / 1000000.0f,
+ result.get(CaptureResult.SENSOR_FRAME_DURATION).intValue() /
+ 1000000.0f));
+ }
if (result.get(CaptureResult.COLOR_CORRECTION_GAINS) != null) {
logMsg.append(String.format(
"gains=[%.1f, %.1f, %.1f, %.1f], ",
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
index 2541142..2011314 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java
@@ -166,7 +166,9 @@
int length = w * bytesPerPixel;
buffer.get(data, offset, length);
// Advance buffer the remainder of the row stride
- buffer.position(buffer.position() + rowStride - length);
+ if (row < h - 1) {
+ buffer.position(buffer.position() + rowStride - length);
+ }
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java
new file mode 100644
index 0000000..cdee037
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockActivity.java
@@ -0,0 +1,53 @@
+/*
+ * 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.verifier.car;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+/**
+ * Dummy activity which notifies {@link CarDockTestActivity} that the CAR_DOCK application
+ * successfully opened.
+ */
+public class CarDockActivity extends Activity {
+ public static Runnable sOnHomePressedRunnable;
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.car_dock_main);
+ Toast.makeText(this, "CAR_DOCK app started from entering car mode!",
+ Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ /**
+ * Pressing the home button while already in this app will trigger this callback.
+ */
+ protected void onNewIntent(Intent intent) {
+ if (sOnHomePressedRunnable != null) {
+ sOnHomePressedRunnable.run();
+ sOnHomePressedRunnable = null;
+ Toast.makeText(this, "CAR_DOCK app started from home button press!",
+ Toast.LENGTH_SHORT).show();
+ }
+ finish();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java
new file mode 100644
index 0000000..4473a3c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/CarDockTestActivity.java
@@ -0,0 +1,112 @@
+/*
+ * 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.verifier.car;
+
+import android.app.UiModeManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestResult;
+
+/**
+ * Tests that CAR_DOCK mode opens the app associated with car dock when going into
+ * car mode.
+ */
+public class CarDockTestActivity extends PassFailButtons.Activity {
+
+ private static final String CAR_DOCK1 =
+ "com.android.cts.verifier.car.CarDockActivity1";
+ private static final String CAR_DOCK2 =
+ "com.android.cts.verifier.car.CarDockActivity2";
+
+ private UiModeManager mManager;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ View view = getLayoutInflater().inflate(R.layout.car_dock_test_main, null);
+ setContentView(view);
+ setInfoResources(R.string.car_dock_test, R.string.car_dock_test_desc, -1);
+ setPassFailButtonClickListeners();
+ getPassButton().setEnabled(false);
+ mManager = (UiModeManager) getSystemService(Context.UI_MODE_SERVICE);
+ CarDockActivity.sOnHomePressedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ TestResult.setPassedResult(CarDockTestActivity.this, getTestId(),
+ getTestDetails(), getReportLog());
+ mManager.disableCarMode(0);
+ finish();
+ }
+ };
+ Button button = (Button) view.findViewById(R.id.car_mode);
+ button.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mManager.enableCarMode(UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME);
+ }
+ });
+ Intent i = new Intent(Intent.ACTION_MAIN, null);
+ i.addCategory(Intent.CATEGORY_CAR_DOCK);
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+ Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ ActivityInfo ai = null;
+ ResolveInfo info = getPackageManager().resolveActivity(i,
+ PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA);
+ if (info != null) {
+ ai = info.activityInfo;
+ // Check if we are the default CAR_DOCK handler
+ if (!ai.packageName.equals(getPackageName())) {
+ // Switch components to fake new CAR_DOCK install to force bringing up the
+ // disambiguation dialog.
+ PackageManager pm = getApplicationContext().getPackageManager();
+ ComponentName component1 = new ComponentName(getPackageName(), CAR_DOCK1);
+ ComponentName component2 = new ComponentName(getPackageName(), CAR_DOCK2);
+
+ if (pm.getComponentEnabledSetting(component1) ==
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED) {
+ swapCarDockHandler(component2, component1);
+ } else {
+ swapCarDockHandler(component1, component2);
+ }
+ }
+ }
+ }
+
+ private void swapCarDockHandler(
+ ComponentName toBeDisabledComponent, ComponentName toBeEnabledComponent) {
+ PackageManager pm = getApplicationContext().getPackageManager();
+
+ pm.setComponentEnabledSetting(toBeDisabledComponent,
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+ PackageManager.DONT_KILL_APP);
+
+ pm.setComponentEnabledSetting(toBeEnabledComponent,
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
+ PackageManager.DONT_KILL_APP);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
old mode 100644
new mode 100755
index e97539d..8d10bda
--- a/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/jobscheduler/ConnectivityConstraintTestActivity.java
@@ -31,6 +31,8 @@
ConnectivityConstraintTestActivity.class.hashCode() + 1;
private static final int NO_CONNECTIVITY_JOB_ID =
ConnectivityConstraintTestActivity.class.hashCode() + 2;
+ private static final int NO_CONNECTIVITY_JOB_ID_2 =
+ ConnectivityConstraintTestActivity.class.hashCode() + 3;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -89,18 +91,25 @@
}
private void testNoConnectivityConstraintExecutes_noConnectivity() {
- JobInfo testJob = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
+ JobInfo testJob1 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID, mMockComponent)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
.setOverrideDeadline(100000L) // Will not expire.
.build();
+ JobInfo testJob2 = new JobInfo.Builder(NO_CONNECTIVITY_JOB_ID_2, mMockComponent)
+ .setRequiredNetworkType(JobInfo.NETWORK_TYPE_NONE)
+ .setOverrideDeadline(100000L) // Will not expire.
+ .build();
mTestEnvironment.setUp();
- mTestEnvironment.setExpectedExecutions(1);
+ mTestEnvironment.setExpectedExecutions(2);
- mJobScheduler.schedule(testJob);
+ mJobScheduler.schedule(testJob1);
+ mJobScheduler.schedule(testJob2);
+ /*
// Send intent to kick off ready jobs that the JobScheduler might be lazily holding on to.
sendBroadcastAndBlockForResult(EXPEDITE_STABLE_CHARGING);
+ */
boolean testPassed;
try {
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 c1cc1f9..af3a7d5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java
@@ -80,6 +80,10 @@
private TestItem mLocationSettingsVisibleTest;
private TestItem mCredSettingsVisibleTest;
private TestItem mPrintSettingsVisibleTest;
+ private TestItem mCrossProfileImageCaptureSupportTest;
+ private TestItem mCrossProfileVideoCaptureSupportTest;
+ private TestItem mCrossProfileAudioCaptureSupportTest;
+ private TestItem mDisableNfcBeamTest;
private int mCurrentTestPosition;
@@ -241,6 +245,79 @@
mTests.add(mCrossProfileIntentFiltersTest);
mTests.add(mDisableNonMarketTest);
mTests.add(mEnableNonMarketTest);
+
+ 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();
+ }
+
+ if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC)) {
+ mDisableNfcBeamTest = new TestItem(this, R.string.provisioning_byod_nfc_beam,
+ R.string.provisioning_byod_nfc_beam_allowed_instruction,
+ new Intent(ByodHelperActivity.ACTION_TEST_NFC_BEAM)) {
+ @Override
+ public void performTest(final ByodFlowTestActivity activity) {
+ activity.showManualTestDialog(mDisableNfcBeamTest,
+ new DefaultTestCallback(mDisableNfcBeamTest) {
+ @Override
+ public void onPass() {
+ // Start a second test with beam disallowed by policy.
+ Intent testNfcBeamIntent = new Intent(
+ ByodHelperActivity.ACTION_TEST_NFC_BEAM);
+ testNfcBeamIntent.putExtra(NfcTestActivity.EXTRA_DISALLOW_BY_POLICY,
+ true);
+ TestItem disableNfcBeamTest2 = new TestItem(activity,
+ R.string.provisioning_byod_nfc_beam,
+ R.string.provisioning_byod_nfc_beam_disallowed_instruction,
+ testNfcBeamIntent);
+ // The result should be reflected on the original test.
+ activity.showManualTestDialog(disableNfcBeamTest2,
+ new DefaultTestCallback(mDisableNfcBeamTest));
+ }
+ });
+ }
+ };
+ mTests.add(mDisableNfcBeamTest);
+ }
+
+ /* 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
@@ -251,7 +328,36 @@
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 class DefaultTestCallback implements TestItem.TestCallback {
+ final private TestItem mTest;
+
+ public DefaultTestCallback(TestItem test) {
+ mTest = test;
+ }
+
+ @Override
+ public void onPass() {
+ clearRemainingState(mTest);
+ setTestResult(mTest, TestResult.Passed);
+ }
+
+ @Override
+ public void onFail() {
+ clearRemainingState(mTest);
+ setTestResult(mTest, TestResult.Failed);
+ }
+ }
+
private void showManualTestDialog(final TestItem test) {
+ showManualTestDialog(test, new DefaultTestCallback(test));
+ }
+
+ private void showManualTestDialog(final TestItem test, final TestItem.TestCallback callback) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_info)
.setTitle(R.string.provisioning_byod)
@@ -259,15 +365,13 @@
.setPositiveButton(R.string.pass_button_text, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- clearRemainingState(test);
- setTestResult(test, TestResult.Passed);
+ callback.onPass();
}
})
.setNegativeButton(R.string.fail_button_text, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
- clearRemainingState(test);
- setTestResult(test, TestResult.Failed);
+ callback.onFail();
}
});
View customView = test.getCustomView();
@@ -383,6 +487,11 @@
static class TestItem {
+ public interface TestCallback {
+ void onPass();
+ void onFail();
+ }
+
private String mDisplayName;
private TestResult mPassed;
private boolean mManualTest;
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 277324c..800137a 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,13 @@
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 ACTION_TEST_NFC_BEAM = "com.android.cts.verifier.managedprovisioning.TEST_NFC_BEAM";
public static final String EXTRA_PROVISIONED = "extra_provisioned";
@@ -62,6 +77,9 @@
public static final String EXTRA_ALLOW_NON_MARKET_APPS = INSTALL_NON_MARKET_APPS;
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;
@@ -69,6 +87,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);
@@ -120,6 +143,46 @@
// Not yet ready to finish- wait until the result comes back
return;
+ } 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;
+ } else if (action.equals(ACTION_TEST_NFC_BEAM)) {
+ Intent testNfcBeamIntent = new Intent(this, NfcTestActivity.class);
+ testNfcBeamIntent.putExtras(intent);
+ startActivity(testNfcBeamIntent);
+ finish();
+ return;
}
// This activity has no UI and is only used to respond to CtsVerifier in the primary side.
finish();
@@ -145,6 +208,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;
@@ -152,6 +245,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());
@@ -181,4 +307,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 fa7bc4c..15349ab 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -49,6 +49,10 @@
filter.addAction(ByodHelperActivity.ACTION_QUERY_PROFILE_OWNER);
filter.addAction(ByodHelperActivity.ACTION_REMOVE_PROFILE_OWNER);
filter.addAction(ByodHelperActivity.ACTION_INSTALL_APK);
+ 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(ByodHelperActivity.ACTION_TEST_NFC_BEAM);
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/managedprovisioning/NfcTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
new file mode 100644
index 0000000..2f7619c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/NfcTestActivity.java
@@ -0,0 +1,158 @@
+/*
+ * 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.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.net.Uri;
+import android.nfc.NfcAdapter;
+import android.os.Bundle;
+import android.os.UserManager;
+import android.support.v4.content.FileProvider;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Toast;
+
+import com.android.cts.verifier.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+
+public class NfcTestActivity extends Activity {
+ private static final String TAG = "NfcTestActivity";
+
+ /* package */ static final String EXTRA_DISALLOW_BY_POLICY = "disallowByPolicy";
+
+ private static final String NFC_BEAM_PACKAGE = "com.android.nfc";
+ private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
+ private static final String SAMPLE_IMAGE_FILENAME = "image_to_share.jpg";
+ private static final String SAMPLE_IMAGE_CONTENT = "sample image";
+ private static final int MARGIN = 80;
+ private static final int TEXT_SIZE = 200;
+
+ private ComponentName mAdminReceiverComponent;
+ private DevicePolicyManager mDevicePolicyManager;
+ private UserManager mUserMangaer;
+ private NfcAdapter mNfcAdapter;
+ private boolean mDisallowByPolicy;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.byod_nfc_test_activity);
+
+ mAdminReceiverComponent = new ComponentName(this, DeviceAdminTestReceiver.class.getName());
+ mDevicePolicyManager = (DevicePolicyManager) getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ mUserMangaer = (UserManager) getSystemService(Context.USER_SERVICE);
+ mDisallowByPolicy = getIntent().getBooleanExtra(EXTRA_DISALLOW_BY_POLICY, false);
+ if (mDisallowByPolicy) {
+ mDevicePolicyManager.addUserRestriction(mAdminReceiverComponent,
+ UserManager.DISALLOW_OUTGOING_BEAM);
+ }
+
+ final Uri uri = createUriForImage(SAMPLE_IMAGE_FILENAME, SAMPLE_IMAGE_CONTENT);
+ Uri[] uris = new Uri[] { uri };
+
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(this);
+ mNfcAdapter.setBeamPushUris(uris, this);
+
+ findViewById(R.id.manual_beam_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ mNfcAdapter.invokeBeam(NfcTestActivity.this);
+ }
+ });
+ findViewById(R.id.intent_share_button).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ Intent shareIntent = new Intent(Intent.ACTION_SEND);
+ shareIntent.putExtra(Intent.EXTRA_STREAM, uri);
+ shareIntent.setType("image/jpg");
+ shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ // Specify the package name of NfcBeamActivity so that the tester don't need to
+ // select the activity manually.
+ shareIntent.setClassName(NFC_BEAM_PACKAGE, NFC_BEAM_ACTIVITY);
+ try {
+ startActivity(shareIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(NfcTestActivity.this,
+ R.string.provisioning_byod_cannot_resolve_beam_activity,
+ Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Nfc beam activity not found", e);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void finish() {
+ if (mUserMangaer.hasUserRestriction(UserManager.DISALLOW_OUTGOING_BEAM)) {
+ mDevicePolicyManager.clearUserRestriction(mAdminReceiverComponent,
+ UserManager.DISALLOW_OUTGOING_BEAM);
+ }
+ super.finish();
+ }
+
+ /**
+ * Creates a Bitmap image that contains red on white text with a specified margin.
+ * @param text Text to be displayed in the image.
+ * @return A Bitmap image with the above specification.
+ */
+ private Bitmap createSampleImage(String text) {
+ Paint paint = new Paint();
+ paint.setStyle(Paint.Style.FILL);
+ paint.setTextSize(TEXT_SIZE);
+ Rect rect = new Rect();
+ paint.getTextBounds(text, 0, text.length(), rect);
+ int w = 2 * MARGIN + rect.right - rect.left;
+ int h = 2 * MARGIN + rect.bottom - rect.top;
+ Bitmap dest = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
+ Canvas canvas = new Canvas();
+ canvas.setBitmap(dest);
+ paint.setColor(Color.WHITE);
+ canvas.drawPaint(paint);
+ paint.setColor(Color.RED);
+ canvas.drawText(text, MARGIN - rect.left, MARGIN - rect.top, paint);
+ return dest;
+ }
+
+ private Uri createUriForImage(String name, String text) {
+ final File file = new File(getFilesDir() + File.separator + "images"
+ + File.separator + name);
+ file.getParentFile().mkdirs(); //if the folder doesn't exists it is created
+ try {
+ createSampleImage(text).compress(Bitmap.CompressFormat.JPEG, 100,
+ new FileOutputStream(file));
+ } catch (FileNotFoundException e) {
+ return null;
+ }
+ return FileProvider.getUriForFile(this,
+ "com.android.cts.verifier.managedprovisioning.fileprovider", file);
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
index b658e4f..07d9cc9 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/InteractiveVerifierActivity.java
@@ -172,7 +172,8 @@
@Override
protected void onResume() {
super.onResume();
- if (mCurrentTest.autoStart()) {
+ //To avoid NPE during onResume,before start to iterate next test order
+ if (mCurrentTest!= null && mCurrentTest.autoStart()) {
mCurrentTest.status = READY;
}
next();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index 7207083..c80f371 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -241,7 +241,6 @@
mNotifications.remove(sbn.getKey());
mNotificationKeys.remove(sbn.getTag());
onNotificationRankingUpdate(rankingMap);
- mNotificationKeys.remove(sbn.getTag());
}
public static void resetListenerData(Context context) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
index a6affb3..5870981 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/PackagePriorityVerifierActivity.java
@@ -18,6 +18,7 @@
import android.app.Notification;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@@ -57,6 +58,7 @@
protected List<InteractiveTestCase> createTestItems() {
mAppLabel = getString(R.string.app_name);
List<InteractiveTestCase> tests = new ArrayList<>(17);
+ tests.add(new CheckForBot());
tests.add(new IsEnabledTest());
tests.add(new ServiceStartedTest());
tests.add(new WaitForSetPriorityDefault());
@@ -68,6 +70,27 @@
// Tests
+ /** Make sure the helper package is installed. */
+ protected class CheckForBot extends InteractiveTestCase {
+ @Override
+ View inflate(ViewGroup parent) {
+ return createAutoItem(parent, R.string.package_priority_bot);
+ }
+
+ @Override
+ void test() {
+ PackageManager pm = mContext.getPackageManager();
+ try {
+ pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0);
+ status = PASS;
+ } catch (PackageManager.NameNotFoundException e) {
+ status = FAIL;
+ logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE);
+ }
+ next();
+ }
+ }
+
/** Wait for the user to set the target package priority to default. */
protected class WaitForSetPriorityDefault extends InteractiveTestCase {
@Override
@@ -233,6 +256,7 @@
postIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
postIntent.putExtra(EXTRA_ID, 0);
postIntent.putExtra(EXTRA_NOTIFICATION, bob.build());
+ postIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(postIntent);
}
@@ -243,6 +267,7 @@
Intent cancelIntent = new Intent(ACTION_CANCEL);
cancelIntent.setPackage(NOTIFICATION_BOT_PACKAGE);
cancelIntent.putExtra(EXTRA_ID, 0);
+ cancelIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
sendBroadcast(cancelIntent);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
index 9b862de..9f8cb51 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/projection/widgets/ProjectionWidgetActivity.java
@@ -67,13 +67,5 @@
button = (Button) view.findViewById(R.id.down_button);
button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_DOWN));
}
- {
- button = (Button) view.findViewById(R.id.right_button);
- button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_RIGHT));
- }
- {
- button = (Button) view.findViewById(R.id.left_button);
- button.setOnClickListener(new InjectDPadClickListener(KeyEvent.KEYCODE_DPAD_LEFT));
- }
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
index 200865e..8e72ebb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/screenpinning/ScreenPinningTestActivity.java
@@ -104,6 +104,7 @@
TextView tv = new TextView(this);
tv.setPadding(10, 10, 10, 30);
tv.setText(id);
+ mInstructions.removeAllViews();
mInstructions.addView(tv);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index dfcf120..52b3dee 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -97,11 +97,11 @@
Sensor.TYPE_ACCELEROMETER,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyMeasurements =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
verifyMeasurements.addVerification(new MeanVerification(
expectations,
new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
- verifyMeasurements.execute();
+ verifyMeasurements.execute(getCurrentTestNode());
return null;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 6f0a7aa..7ef63d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -22,9 +22,7 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import java.util.concurrent.TimeUnit;
@@ -128,7 +126,7 @@
int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
TestSensorOperation operation =
- new TestSensorOperation(environment, testDurationSec,TimeUnit.SECONDS);
+ TestSensorOperation.createOperation(environment, testDurationSec,TimeUnit.SECONDS);
return executeTest(operation);
}
@@ -145,15 +143,14 @@
maxBatchReportLatencyUs);
int flushDurationSec = maxBatchReportLatencySec / 2;
- TestSensorFlushOperation operation =
- new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation operation = TestSensorOperation
+ .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
return executeTest(operation);
}
- private String executeTest(VerifiableSensorOperation operation) throws InterruptedException {
+ private String executeTest(TestSensorOperation operation) throws InterruptedException {
operation.addDefaultVerifications();
- operation.setLogEvents(true);
- operation.execute();
+ operation.execute(getCurrentTestNode());
return null;
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 4b2a7f4..7be0fb1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -156,8 +156,8 @@
getApplicationContext(),
Sensor.TYPE_GYROSCOPE,
SensorManager.SENSOR_DELAY_FASTEST);
- TestSensorOperation sensorOperation =
- new TestSensorOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
+ TestSensorOperation sensorOperation = TestSensorOperation
+ .createOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
int gyroscopeAxes = environment.getSensorAxesCount();
int[] expectationsDeg = getExpectationsDeg(gyroscopeAxes, rotationAxis, expectationDeg);
@@ -167,7 +167,7 @@
sensorOperation.addVerification(integrationVerification);
try {
- sensorOperation.execute();
+ sensorOperation.execute(getCurrentTestNode());
} finally {
playSound();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 553147b..229a9dc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -21,7 +21,6 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
import android.hardware.SensorManager;
import android.hardware.cts.helpers.SensorCalibratedUncalibratedVerifier;
import android.hardware.cts.helpers.SensorCtsHelper;
@@ -79,7 +78,7 @@
Sensor.TYPE_MAGNETIC_FIELD,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyNorm =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
float expectedMagneticFieldEarth =
(SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
@@ -88,7 +87,7 @@
verifyNorm.addVerification(new MagnitudeVerification(
expectedMagneticFieldEarth,
magneticFieldEarthThreshold));
- verifyNorm.execute();
+ verifyNorm.execute(getCurrentTestNode());
return null;
}
@@ -124,11 +123,11 @@
Sensor.TYPE_MAGNETIC_FIELD,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyStdDev =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
verifyStdDev.addVerification(new StandardDeviationVerification(
new float[]{2f, 2f, 2f} /* uT */));
- verifyStdDev.execute();
+ verifyStdDev.execute(getCurrentTestNode());
return null;
}
@@ -166,7 +165,12 @@
* A routine to help operators calibrate the magnetometer.
*/
private void calibrateMagnetometer() throws InterruptedException {
- SensorEventListener2 listener = new SensorEventListener2() {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getApplicationContext(),
+ Sensor.TYPE_MAGNETIC_FIELD,
+ SensorManager.SENSOR_DELAY_NORMAL);
+
+ TestSensorEventListener listener = new TestSensorEventListener(environment) {
@Override
public void onSensorChanged(SensorEvent event) {
clearText();
@@ -184,21 +188,11 @@
// TODO: automate finding out when the magnetometer is calibrated
logger.logInstructions(R.string.snsr_mag_calibration_complete);
}
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {}
-
- @Override
- public void onFlushCompleted(Sensor sensor) {}
};
- TestSensorEnvironment environment = new TestSensorEnvironment(
- getApplicationContext(),
- Sensor.TYPE_MAGNETIC_FIELD,
- SensorManager.SENSOR_DELAY_NORMAL);
TestSensorManager magnetometer = new TestSensorManager(environment);
try {
- magnetometer.registerListener(new TestSensorEventListener(listener));
+ magnetometer.registerListener(listener);
waitForUserToContinue();
} finally {
magnetometer.unregisterListener();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
index 5bbaaf7..380b282 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsTestResult.java
@@ -26,6 +26,8 @@
import android.content.Context;
import android.hardware.cts.SensorTestCase;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import java.util.Enumeration;
@@ -138,10 +140,24 @@
SensorTestCase sensorTestCase = (SensorTestCase) testCase;
sensorTestCase.setContext(mContext);
sensorTestCase.setEmulateSensorUnderLoad(false);
+ sensorTestCase.setCurrentTestNode(new TestNode(testCase));
// TODO: set delayed assertion provider
} else {
throw new IllegalStateException("TestCase must be an instance of SensorTestCase.");
}
super.run(testCase);
}
+
+ private class TestNode implements ISensorTestNode {
+ private final TestCase mTestCase;
+
+ public TestNode(TestCase testCase) {
+ mTestCase = testCase;
+ }
+
+ @Override
+ public String getName() throws SensorTestPlatformException {
+ return mTestCase.getClass().getSimpleName() + "_" + mTestCase.getName();
+ }
+ }
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
index a88abd0..8cf287a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/base/SensorCtsVerifierTestActivity.java
@@ -19,6 +19,8 @@
import com.android.cts.verifier.sensors.reporting.SensorTestDetails;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
@@ -35,6 +37,7 @@
private volatile int mTestPassedCounter;
private volatile int mTestSkippedCounter;
private volatile int mTestFailedCounter;
+ private volatile ISensorTestNode mCurrentTestNode;
/**
* {@inheritDoc}
@@ -63,8 +66,12 @@
mTestFailedCounter);
}
+ protected ISensorTestNode getCurrentTestNode() {
+ return mCurrentTestNode;
+ }
+
private List<Method> findTestMethods() {
- ArrayList<Method> testMethods = new ArrayList<Method>();
+ ArrayList<Method> testMethods = new ArrayList<>();
for (Method method : mTestClass.getDeclaredMethods()) {
if (Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
@@ -79,6 +86,7 @@
private SensorTestDetails executeTest(Method testMethod) throws InterruptedException {
String testMethodName = testMethod.getName();
String testName = String.format("%s#%s", getTestClassName(), testMethodName);
+ mCurrentTestNode = new TestNode(testMethod);
SensorTestDetails testDetails;
try {
@@ -112,4 +120,17 @@
return testDetails;
}
+
+ private class TestNode implements ISensorTestNode {
+ private final Method mTestMethod;
+
+ public TestNode(Method testMethod) {
+ mTestMethod = testMethod;
+ }
+
+ @Override
+ public String getName() {
+ return mTestClass.getSimpleName() + "_" + mTestMethod.getName();
+ }
+ }
}
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..c05b753 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/MockTvInputSetupActivity.java
@@ -21,17 +21,25 @@
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";
+ /* package-private */ static final String CHANNEL_NUMBER = "999-0";
+ /* package-private */ static final String CHANNEL_NAME = "Dummy";
+
+ /* package-private */ static final String PROGRAM_TITLE = "Dummy Program";
+ /* package-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/SearchUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
new file mode 100644
index 0000000..4513123
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/SearchUtil.java
@@ -0,0 +1,136 @@
+/*
+ * 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.verifier.tv;
+
+import android.app.SearchManager;
+import android.app.SearchableInfo;
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteException;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.os.RemoteException;
+import android.text.TextUtils;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * A utility class for verifying channel/program results on global search requests.
+ */
+public class SearchUtil {
+ private SearchUtil() {}
+
+ /**
+ * Returns {@code true} if one of the search results matches the given {@code expectedResult}.
+ *
+ * @param context The context object to be used for getting content resolver
+ * @param searchable The {@link android.app.SearchableInfo} the TV app implements
+ * @param query A query string to search for
+ * @param expectedResult The expected search result
+ */
+ public static boolean verifySearchResult(Context context, SearchableInfo searchable,
+ String query, String expectedResult) {
+ Uri.Builder uriBuilder = getSearchUri(searchable).buildUpon();
+ String selection = searchable.getSuggestSelection();
+ String[] selectionArg = null;
+ if (selection != null) {
+ selectionArg = new String[] { query };
+ } else {
+ uriBuilder.appendPath(query);
+ }
+
+ Uri uri = uriBuilder.build();
+ ContentProviderClient provider = context.getContentResolver()
+ .acquireUnstableContentProviderClient(uri);
+ try (Cursor c = provider.query(uri, null, selection, selectionArg, null, null)) {
+ while (c.moveToNext()) {
+ int index = c.getColumnIndex(SearchManager.SUGGEST_COLUMN_TEXT_1);
+ if (index >= 0) {
+ if (TextUtils.equals(expectedResult, c.getString(index))) {
+ return true;
+ }
+ }
+ }
+ } catch (SQLiteException | RemoteException e) {
+ return false;
+ } finally {
+ provider.release();
+ }
+ return false;
+ }
+
+ /**
+ * Returns the {@link android.app.SearchableInfo} instances which should provide search results
+ * for channels and programs in TvProvider.
+ *
+ * @param context The context object to used for accessing system services
+ */
+ public static List<SearchableInfo> getSearchableInfos(Context context) {
+ // Just in case EPG is provided by a separate package, collect all possible TV packages
+ // that can be searchable.
+ PackageManager pm = context.getPackageManager();
+ Set<String> tvPackages = new HashSet<>();
+ List<ResolveInfo> infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+ TvContract.Channels.CONTENT_URI), 0);
+ for (ResolveInfo info : infos) {
+ tvPackages.add(info.activityInfo.packageName);
+ }
+ infos = pm.queryIntentActivities(new Intent(Intent.ACTION_VIEW,
+ TvContract.Programs.CONTENT_URI), 0);
+ for (ResolveInfo info : infos) {
+ tvPackages.add(info.activityInfo.packageName);
+ }
+ SearchManager sm = (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
+ List<SearchableInfo> globalSearchableInfos = sm.getSearchablesInGlobalSearch();
+ List<SearchableInfo> tvSearchableInfos = new ArrayList<>();
+ for (SearchableInfo info : globalSearchableInfos) {
+ if (tvPackages.contains(info.getSearchActivity().getPackageName())) {
+ tvSearchableInfos.add(info);
+ }
+ }
+ return tvSearchableInfos;
+ }
+
+ private static Uri getSearchUri(SearchableInfo searchable) {
+ if (searchable == null) {
+ return null;
+ }
+ String authority = searchable.getSuggestAuthority();
+ if (authority == null) {
+ return null;
+ }
+ Uri.Builder uriBuilder = new Uri.Builder()
+ .scheme(ContentResolver.SCHEME_CONTENT)
+ .authority(authority);
+
+ final String contentPath = searchable.getSuggestPath();
+ if (contentPath != null) {
+ uriBuilder.appendEncodedPath(contentPath);
+ }
+
+ uriBuilder.appendPath(SearchManager.SUGGEST_URI_PATH_QUERY);
+ return uriBuilder.build();
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
index 3529237..12e9652 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvAppVerifierActivity.java
@@ -63,6 +63,7 @@
protected void setButtonEnabled(View item, boolean enabled) {
View button = item.findViewById(R.id.user_action_button);
+ button.setFocusable(enabled);
button.setClickable(enabled);
button.setEnabled(enabled);
}
@@ -70,9 +71,7 @@
protected void setPassState(View item, boolean passed) {
ImageView status = (ImageView) item.findViewById(R.id.status);
status.setImageResource(passed ? R.drawable.fs_good : R.drawable.fs_error);
- View button = item.findViewById(R.id.user_action_button);
- button.setClickable(false);
- button.setEnabled(false);
+ setButtonEnabled(item, false);
status.invalidate();
}
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..06f4f6f 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/TvInputDiscoveryTestActivity.java
@@ -16,12 +16,17 @@
package com.android.cts.verifier.tv;
+import android.app.SearchableInfo;
+import android.content.Context;
import android.content.Intent;
import android.media.tv.TvContract;
+import android.os.AsyncTask;
import android.view.View;
import com.android.cts.verifier.R;
+import java.util.List;
+
/**
* Tests for verifying TV app behavior for third-party TV input apps.
*/
@@ -30,7 +35,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,8 +46,12 @@
private View mTuneToChannelItem;
private View mVerifyTuneItem;
private View mVerifyOverlayViewItem;
+ private View mVerifyGlobalSearchItem;
+ private View mGoToEpgItem;
+ private View mVerifyEpgItem;
private boolean mTuneVerified;
private boolean mOverlayViewVerified;
+ private boolean mGlobalSearchVerified;
@Override
public void onClick(View v) {
@@ -63,6 +74,7 @@
setButtonEnabled(mTuneToChannelItem, true);
}
});
+ startActivity(TV_APP_INTENT);
} else if (containsButton(mTuneToChannelItem, v)) {
final Runnable failCallback = new Runnable() {
@Override
@@ -78,7 +90,7 @@
setPassState(mVerifyTuneItem, true);
mTuneVerified = true;
- updatePassState(postTarget, failCallback);
+ goToNextState(postTarget, failCallback);
}
});
MockTvInputService.expectOverlayView(postTarget, new Runnable() {
@@ -88,11 +100,19 @@
setPassState(mVerifyOverlayViewItem, true);
mOverlayViewVerified = true;
- updatePassState(postTarget, failCallback);
+ goToNextState(postTarget, failCallback);
}
});
+ verifyGlobalSearch(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,13 +126,12 @@
mVerifyTuneItem = createAutoItem(R.string.tv_input_discover_test_verify_tune);
mVerifyOverlayViewItem = createAutoItem(
R.string.tv_input_discover_test_verify_overlay_view);
- }
-
- private void updatePassState(View postTarget, Runnable failCallback) {
- if (mTuneVerified && mOverlayViewVerified) {
- postTarget.removeCallbacks(failCallback);
- getPassButton().setEnabled(true);
- }
+ mVerifyGlobalSearchItem = createAutoItem(
+ R.string.tv_input_discover_test_verify_global_search);
+ 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);
}
@Override
@@ -120,4 +139,39 @@
setInfoResources(R.string.tv_input_discover_test,
R.string.tv_input_discover_test_info, -1);
}
+
+ private void goToNextState(View postTarget, Runnable failCallback) {
+ if (mTuneVerified && mOverlayViewVerified && mGlobalSearchVerified) {
+ postTarget.removeCallbacks(failCallback);
+ setButtonEnabled(mGoToEpgItem, true);
+ }
+ }
+
+ private void verifyGlobalSearch(final View postTarget, final Runnable failCallback) {
+ new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ Context context = TvInputDiscoveryTestActivity.this;
+ for (SearchableInfo info : SearchUtil.getSearchableInfos(context)) {
+ if (SearchUtil.verifySearchResult(context, info,
+ MockTvInputSetupActivity.CHANNEL_NAME,
+ MockTvInputSetupActivity.PROGRAM_TITLE)
+ && SearchUtil.verifySearchResult(context, info,
+ MockTvInputSetupActivity.PROGRAM_TITLE,
+ MockTvInputSetupActivity.PROGRAM_TITLE)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ super.onPostExecute(result);
+ setPassState(mVerifyGlobalSearchItem, result);
+ mGlobalSearchVerified = result;
+ goToNextState(postTarget, failCallback);
+ }
+ }.execute();
+ }
}
diff --git a/build/test_executable.mk b/build/test_executable.mk
index e0352ba..02b3e4c 100644
--- a/build/test_executable.mk
+++ b/build/test_executable.mk
@@ -43,3 +43,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-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_xml)
diff --git a/build/test_gtest_package.mk b/build/test_gtest_package.mk
index 6868081..dd1269b 100644
--- a/build/test_gtest_package.mk
+++ b/build/test_gtest_package.mk
@@ -52,3 +52,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-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_apk) $(cts_package_xml)
diff --git a/build/test_host_java_library.mk b/build/test_host_java_library.mk
index 8e071e4..8ed5670 100644
--- a/build/test_host_java_library.mk
+++ b/build/test_host_java_library.mk
@@ -42,3 +42,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-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_xml)
diff --git a/build/test_package.mk b/build/test_package.mk
index 72972b2..7589787 100644
--- a/build/test_package.mk
+++ b/build/test_package.mk
@@ -64,3 +64,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-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_apk) $(cts_package_xml)
diff --git a/build/test_target_java_library.mk b/build/test_target_java_library.mk
index c0d7a2a..04fffb9 100644
--- a/build/test_target_java_library.mk
+++ b/build/test_target_java_library.mk
@@ -44,3 +44,6 @@
-a $(CTS_TARGET_ARCH) \
-x "runtimeArgs->$(PRIVATE_RUNTIME_ARGS)" \
-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)
diff --git a/build/test_uiautomator.mk b/build/test_uiautomator.mk
index 085d672..cad6e4f 100644
--- a/build/test_uiautomator.mk
+++ b/build/test_uiautomator.mk
@@ -55,3 +55,6 @@
-b $(CTS_UNSUPPORTED_ABIS) \
-a $(CTS_TARGET_ARCH) \
-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)
diff --git a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
index 9f69242..a036382 100644
--- a/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
+++ b/hostsidetests/aadb/src/com/android/cts/aadb/TestDeviceFuncTest.java
@@ -294,7 +294,11 @@
String deviceTimezone = mTestDevice.getProperty("persist.sys.timezone");
if (deviceTimezone != null) {
TimeZone tz = TimeZone.getTimeZone(deviceTimezone);
- tmpFile.setLastModified(tmpFile.lastModified() + tz.getRawOffset());
+ long timestamp = tmpFile.lastModified() + tz.getRawOffset();
+ if (tz.observesDaylightTime()) {
+ timestamp += tz.getDSTSavings();
+ }
+ tmpFile.setLastModified(timestamp);
}
assertTrue(mTestDevice.syncFiles(tmpDir, externalStorePath));
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
index 9dc51c9..4d9ef00 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/AppSecurityTests.java
@@ -181,7 +181,7 @@
true /* reinstall */, options);
assertNotNull("app upgrade with different cert than existing app installed " +
"successfully", installResult);
- assertEquals("INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES", installResult);
+ assertEquals("INSTALL_FAILED_UPDATE_INCOMPATIBLE", installResult);
}
finally {
getDevice().uninstallPackage(SIMPLE_APP_PKG);
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
index 264c0b1..90cbed9 100644
--- a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/SplitTests.java
@@ -57,7 +57,13 @@
private static final String APK_mips64 = "CtsSplitApp_mips64.apk";
private static final String APK_mips = "CtsSplitApp_mips.apk";
+ private static final String APK_DIFF_REVISION = "CtsSplitAppDiffRevision.apk";
+ private static final String APK_DIFF_REVISION_v7 = "CtsSplitAppDiffRevision_v7.apk";
+
+ private static final String APK_DIFF_VERSION = "CtsSplitAppDiffVersion.apk";
private static final String APK_DIFF_VERSION_v7 = "CtsSplitAppDiffVersion_v7.apk";
+
+ private static final String APK_DIFF_CERT = "CtsSplitAppDiffCert.apk";
private static final String APK_DIFF_CERT_v7 = "CtsSplitAppDiffCert_v7.apk";
private static final String APK_FEATURE = "CtsSplitAppFeature.apk";
@@ -218,8 +224,6 @@
public void testDiffCertInherit() throws Exception {
new InstallMultiple().addApk(APK).run();
- // TODO: remove this once we fix 17900178
- runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_CERT_v7).runExpectingFailure();
}
@@ -229,11 +233,33 @@
public void testDiffVersionInherit() throws Exception {
new InstallMultiple().addApk(APK).run();
- // TODO: remove this once we fix 17900178
- runDeviceTests(PKG, ".SplitAppTest", "testSingleBase");
new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_VERSION_v7).runExpectingFailure();
}
+ public void testDiffRevision() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+ }
+
+ public void testDiffRevisionInheritBase() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_12");
+ }
+
+ public void testDiffRevisionInheritSplit() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_v7).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision0_0");
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_DIFF_REVISION).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testRevision12_0");
+ }
+
+ public void testDiffRevisionDowngrade() throws Exception {
+ new InstallMultiple().addApk(APK).addApk(APK_DIFF_REVISION_v7).run();
+ new InstallMultiple().inheritFrom(PKG).addApk(APK_v7).runExpectingFailure();
+ }
+
public void testFeatureBase() throws Exception {
new InstallMultiple().addApk(APK).addApk(APK_FEATURE).run();
runDeviceTests(PKG, ".SplitAppTest", "testFeatureBase");
@@ -252,6 +278,16 @@
// TODO: flesh out this test
}
+ /**
+ * Verify that installing a new version of app wipes code cache.
+ */
+ public void testClearCodeCache() throws Exception {
+ new InstallMultiple().addApk(APK).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheWrite");
+ new InstallMultiple().addArg("-r").addApk(APK_DIFF_VERSION).run();
+ runDeviceTests(PKG, ".SplitAppTest", "testCodeCacheRead");
+ }
+
class InstallMultiple {
private List<String> mArgs = new ArrayList<>();
private List<File> mApks = new ArrayList<>();
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index 98610a0..f308211 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -33,6 +33,7 @@
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
import android.text.format.DateUtils;
+import android.util.Log;
import com.android.cts.documentclient.MyActivity.Result;
@@ -47,10 +48,12 @@
* like {@link Intent#ACTION_OPEN_DOCUMENT}.
*/
public class DocumentsClientTest extends InstrumentationTestCase {
+ private static final String TAG = "DocumentsClientTest";
+
private UiDevice mDevice;
private MyActivity mActivity;
- private static final long TIMEOUT = 10 * DateUtils.SECOND_IN_MILLIS;
+ private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS;
@Override
public void setUp() throws Exception {
@@ -73,6 +76,15 @@
"com.android.documentsui:id/container_roots").childSelector(
new UiSelector().resourceId("android:id/list"));
+ // We might need to expand drawer if not visible
+ if (!new UiObject(rootsList).waitForExists(TIMEOUT)) {
+ Log.d(TAG, "Failed to find roots list; trying to expand");
+ final UiSelector hamburger = new UiSelector().resourceId(
+ "com.android.documentsui:id/toolbar").childSelector(
+ new UiSelector().className("android.widget.ImageButton").clickable(true));
+ new UiObject(hamburger).click();
+ }
+
// Wait for the first list item to appear
assertTrue("First list item",
new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
index 8b25f4b..bf89576 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/Android.mk
@@ -30,7 +30,31 @@
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
+
+
+#################################################
+# Define a variant with a different revision code
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsSplitAppDiffRevision
+LOCAL_PACKAGE_SPLITS := v7
+
+LOCAL_MANIFEST_FILE := revision/AndroidManifest.xml
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundredRevisionTwelve --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
@@ -53,7 +77,7 @@
LOCAL_PACKAGE_SPLITS := v7
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 101 --version-name OneHundredOne --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
@@ -76,7 +100,7 @@
LOCAL_PACKAGE_SPLITS := v7
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_DEX_PREOPT := false
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
index e93f6c3..809a6b8 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/feature/Android.mk
@@ -24,7 +24,7 @@
LOCAL_ASSET_DIR := $(LOCAL_PATH)/assets
LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey1
-LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version -c mdpi -c hdpi -c xhdpi -c xxhdpi
+LOCAL_AAPT_FLAGS := --version-code 100 --version-name OneHundred --replace-version
LOCAL_MODULE_TAGS := tests
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
new file mode 100644
index 0000000..8e053ba
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/revision/AndroidManifest.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.splitapp"
+ android:revisionCode="12">
+
+ <uses-permission android:name="android.permission.CAMERA" />
+
+ <application android:label="SplitApp">
+ <activity android:name=".MyActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.service.wallpaper" android:resource="@xml/my_activity_meta" />
+ </activity>
+ <receiver android:name=".MyReceiver"
+ android:enabled="@bool/my_receiver_enabled">
+ <intent-filter>
+ <action android:name="android.intent.action.DATE_CHANGED" />
+ </intent-filter>
+ </receiver>
+
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.splitapp" />
+
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
index 277a1a2..3d6cee7 100644
--- a/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
+++ b/hostsidetests/appsecurity/test-apps/SplitApp/src/com/android/cts/splitapp/SplitAppTest.java
@@ -21,6 +21,7 @@
import android.content.Intent;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
@@ -30,6 +31,7 @@
import android.graphics.Canvas;
import android.graphics.drawable.Drawable;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -37,6 +39,7 @@
import org.xmlpull.v1.XmlPullParserException;
import java.io.BufferedReader;
+import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
@@ -211,9 +214,8 @@
assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta)));
// And that we can access resources from feature
- // TODO: enable these once 17924027 is fixed
-// assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
-// assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+ assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG)));
+ assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -292,8 +294,7 @@
assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled));
// And that we can access resources from feature
- // TODO: enable these once 17924027 is fixed
-// assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
+ assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG)));
final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR");
final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null);
@@ -310,6 +311,40 @@
assertEquals(0, result.size());
}
+ public void testCodeCacheWrite() throws Exception {
+ assertTrue(new File(getContext().getFilesDir(), "normal.raw").createNewFile());
+ assertTrue(new File(getContext().getCodeCacheDir(), "cache.raw").createNewFile());
+ }
+
+ public void testCodeCacheRead() throws Exception {
+ assertTrue(new File(getContext().getFilesDir(), "normal.raw").exists());
+ assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists());
+ }
+
+ public void testRevision0_0() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(0, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(0, info.splitRevisionCodes[0]);
+ }
+
+ public void testRevision12_0() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(12, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(0, info.splitRevisionCodes[0]);
+ }
+
+ public void testRevision0_12() throws Exception {
+ final PackageInfo info = getContext().getPackageManager()
+ .getPackageInfo(getContext().getPackageName(), 0);
+ assertEquals(0, info.baseRevisionCode);
+ assertEquals(1, info.splitRevisionCodes.length);
+ assertEquals(12, info.splitRevisionCodes[0]);
+ }
+
private static void updateDpi(Resources r, int densityDpi) {
final Configuration c = new Configuration(r.getConfiguration());
c.densityDpi = densityDpi;
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
index eb71540..715905a 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/permDef/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningA
@@ -31,7 +30,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetPermDefSigningB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
index 000b12a..eceea38 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/permUse/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningA
@@ -31,7 +30,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetPermUseSigningB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
index 6220790..efba345 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uA/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeA
@@ -30,7 +29,6 @@
#apks signed by cts-keyset-test-b
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeA
@@ -43,7 +41,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndBUpgradeA
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
index 534dba3..219689e 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAB/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAAndB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
index 75729e0..040c378 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uAuB/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeAOrB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
index 121c342..62b5461 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uB/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeB
@@ -31,7 +30,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningBUpgradeB
@@ -44,7 +42,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAAndCUpgradeB
diff --git a/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
index a8746ec..5fc4ab9 100644
--- a/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
+++ b/hostsidetests/appsecurity/test-apps/keysets/uNone/Android.mk
@@ -18,7 +18,6 @@
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := tests
-LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_SDK_VERSION := current
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
LOCAL_PACKAGE_NAME := CtsKeySetSigningAUpgradeNone
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
index e1f6886..5bbdf76 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -24,9 +24,10 @@
<application>
<activity android:name="com.android.cts.intent.receiver.IntentReceiverActivity">
<intent-filter>
+ <action android:name="com.android.cts.action.COPY_TO_CLIPBOARD" />
<action android:name="com.android.cts.action.READ_FROM_URI" />
- <action android:name="com.android.cts.action.WRITE_TO_URI" />
<action android:name="com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION" />
+ <action android:name="com.android.cts.action.WRITE_TO_URI" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index 59f0752..294c678 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -16,6 +16,8 @@
package com.android.cts.intent.receiver;
import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -35,21 +37,36 @@
private static final String TAG = "IntentReceiverActivity";
- private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+ private static final String ACTION_COPY_TO_CLIPBOARD =
+ "com.android.cts.action.COPY_TO_CLIPBOARD";
- private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+ private static final String ACTION_READ_FROM_URI =
+ "com.android.cts.action.READ_FROM_URI";
private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
"com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
+ private static final String ACTION_WRITE_TO_URI =
+ "com.android.cts.action.WRITE_TO_URI";
+
+
private static final String EXTRA_CAUGHT_SECURITY_EXCEPTION = "extra_caught_security_exception";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Intent received = getIntent();
- String action = received.getAction();
- Uri uri = getIntent().getClipData().getItemAt(0).getUri();
- if (ACTION_READ_FROM_URI.equals(action)) {
+ final Intent received = getIntent();
+ final String action = received.getAction();
+ final ClipData clipData = getIntent().getClipData();
+ final Uri uri = clipData != null ? clipData.getItemAt(0).getUri() : null;
+ if (ACTION_COPY_TO_CLIPBOARD.equals(action)) {
+ String text = received.getStringExtra("extra_text");
+ Log.i(TAG, "Copying \"" + text + "\" to the clipboard");
+ ClipData clip = ClipData.newPlainText("", text);
+ ClipboardManager clipboard =
+ (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(clip);
+ setResult(Activity.RESULT_OK);
+ } else if (ACTION_READ_FROM_URI.equals(action)) {
Intent result = new Intent();
String message = null;
try {
@@ -63,6 +80,11 @@
Log.i(TAG, "Message received in reading test: " + message);
result.putExtra("extra_response", message);
setResult(Activity.RESULT_OK, result);
+ } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
+ Log.i(TAG, "Taking persistable uri permission to " + uri);
+ getContentResolver().takePersistableUriPermission(uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ setResult(Activity.RESULT_OK);
} else if (ACTION_WRITE_TO_URI.equals(action)) {
Intent result = new Intent();
String message = received.getStringExtra("extra_message");
@@ -76,11 +98,6 @@
Log.i(TAG, "Caught a IOException while trying to write to " + uri, e);
}
setResult(Activity.RESULT_OK, result);
- } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
- Log.i(TAG, "Taking persistable uri permission to " + uri);
- getContentResolver().takePersistableUriPermission(uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- setResult(Activity.RESULT_OK);
}
finish();
}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
similarity index 94%
rename from hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
rename to hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
index 47de0da..1aaa5ab 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
@@ -32,7 +32,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
-public class IntentSenderTest extends InstrumentationTestCase {
+public class ContentTest extends InstrumentationTestCase {
private static final String MESSAGE = "Sample Message";
@@ -57,8 +57,8 @@
@Override
public void tearDown() throws Exception {
- super.tearDown();
mActivity.finish();
+ super.tearDown();
}
/**
@@ -73,7 +73,7 @@
intent.setClipData(ClipData.newRawUri("", uri));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
assertNotNull(result);
assertEquals(MESSAGE, result.getStringExtra("extra_response"));
}
@@ -95,7 +95,7 @@
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
- mActivity.getResult(intent);
+ mActivity.getCrossProfileResult(intent);
assertEquals(MESSAGE, getFirstLineFromUri(uri));
}
@@ -107,7 +107,7 @@
Intent intent = new Intent(ACTION_READ_FROM_URI);
intent.setClipData(ClipData.newRawUri("", uri));
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
assertNotNull(result);
assertEquals(MESSAGE, result.getStringExtra("extra_response"));
}
@@ -136,7 +136,7 @@
Intent notGrant = new Intent(ACTION_READ_FROM_URI);
notGrant.setClipData(ClipData.newRawUri("", uriNotGranted));
- final Intent result = mActivity.getResult(notGrant);
+ final Intent result = mActivity.getCrossProfileResult(notGrant);
assertNotNull(result);
// The receiver did not have permission to read the uri. So it should have caught a security
// exception.
@@ -155,7 +155,7 @@
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// We're expecting to run into a security exception
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
if (result == null) {
// This is fine; probably of a SecurityException when off in the
// system somewhere.
@@ -170,7 +170,7 @@
grantPersistable.setClipData(ClipData.newRawUri("", uri));
grantPersistable.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- mActivity.getResult(grantPersistable);
+ mActivity.getCrossProfileResult(grantPersistable);
}
private Uri getBasicContentProviderUri(String path) {
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
new file mode 100644
index 0000000..a5d83db
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.intent.sender;
+
+import android.content.ClipboardManager;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CopyPasteTest extends InstrumentationTestCase
+ implements ClipboardManager.OnPrimaryClipChangedListener {
+
+ private IntentSenderActivity mActivity;
+ private ClipboardManager mClipboard;
+ private Semaphore mNotified;
+
+ private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
+ private static String INITIAL_TEXT = "initial text";
+ private static String NEW_TEXT = "sample text";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Context context = getInstrumentation().getTargetContext();
+ mActivity = launchActivity(context.getPackageName(), IntentSenderActivity.class, null);
+ mClipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mActivity.finish();
+ super.tearDown();
+ }
+
+ public void testCanReadAcrossProfiles() throws Exception {
+ ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+ mClipboard.setPrimaryClip(clip);
+ assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ assertEquals(NEW_TEXT, getTextFromClipboard());
+ }
+
+ public void testCannotReadAcrossProfiles() throws Exception {
+ ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+ mClipboard.setPrimaryClip(clip);
+ assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ String clipboardText = getTextFromClipboard();
+ assertTrue("The clipboard text is " + clipboardText + " but should be <null> or "
+ + INITIAL_TEXT, clipboardText == null || clipboardText.equals(INITIAL_TEXT));
+ }
+
+ public void testIsNotified() throws Exception {
+ try {
+ mNotified = new Semaphore(0);
+ mActivity.addPrimaryClipChangedListener(this);
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ assertTrue(mNotified.tryAcquire(5, TimeUnit.SECONDS));
+ } finally {
+ mActivity.removePrimaryClipChangedListener(this);
+ }
+ }
+
+ private void askCrossProfileReceiverToCopy(String text) throws Exception {
+ Intent intent = new Intent(ACTION_COPY_TO_CLIPBOARD);
+ intent.putExtra("extra_text", text);
+ mActivity.getCrossProfileResult(intent);
+ }
+
+ private String getTextFromClipboard() {
+ ClipData clip = mClipboard.getPrimaryClip();
+ if (clip == null) {
+ return null;
+ }
+ ClipData.Item item = clip.getItemAt(0);
+ if (item == null) {
+ return null;
+ }
+ CharSequence text = item.getText();
+ if (text == null) {
+ return null;
+ }
+ return text.toString();
+ }
+
+
+ @Override
+ public void onPrimaryClipChanged() {
+ mNotified.release();
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
index 00fa6b7..fd421ac 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
@@ -17,15 +17,27 @@
package com.android.cts.intent.sender;
import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipboardManager.OnPrimaryClipChangedListener;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.util.Log;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+import java.util.List;
public class IntentSenderActivity extends Activity {
+ private static String TAG = "IntentSenderActivity";
+
private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+ private ClipboardManager mClipboardManager;
+
public static class Result {
public final int resultCode;
public final Intent data;
@@ -37,6 +49,12 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ }
+
+ @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
try {
@@ -52,4 +70,38 @@
final Result result = mResult.poll(30, TimeUnit.SECONDS);
return (result != null) ? result.data : null;
}
+
+ /**
+ * This method will send an intent accross profiles to IntentReceiverActivity, and return the
+ * result intent set by IntentReceiverActivity.
+ */
+ public Intent getCrossProfileResult(Intent intent)
+ throws Exception {
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ // There should be two matches:
+ // - com.android.cts.intent.receiver (on the current profile).
+ // - One that will send the intent to the other profile.
+ // It's the second one we want to send the intent to.
+
+ for (ResolveInfo ri : ris) {
+ if (!ri.activityInfo.packageName.equals("com.android.cts.intent.receiver")) {
+ intent.setComponent(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name));
+ return getResult(intent);
+ }
+ }
+ Log.e(TAG, "The intent " + intent + " cannot be sent accross profiles");
+ return null;
+ }
+
+ public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) {
+ mClipboardManager.addPrimaryClipChangedListener(listener);
+ }
+
+ public void removePrimaryClipChangedListener(
+ OnPrimaryClipChangedListener listener) {
+ mClipboardManager.removePrimaryClipChangedListener(listener);
+ }
+
}
diff --git a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
index 3d44ecd..e076fc3 100644
--- a/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
+++ b/hostsidetests/devicepolicy/app/LauncherTests/src/com/android/cts/launchertests/LauncherAppsTests.java
@@ -61,7 +61,7 @@
public static final int MSG_CHECK_PACKAGE_ADDED = 1;
public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
- public static final int MSG_CHECK_NO_CALLBACK = 4;
+ public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
public static final int RESULT_PASS = 1;
public static final int RESULT_FAIL = 2;
@@ -139,8 +139,8 @@
assertEquals(RESULT_PASS, result);
}
- public void testNoCallbackForUser() throws Throwable {
- int result = sendMessageToCallbacksService(MSG_CHECK_NO_CALLBACK,
+ public void testNoPackageAddedCallbackForUser() throws Throwable {
+ int result = sendMessageToCallbacksService(MSG_CHECK_NO_PACKAGE_ADDED,
mUser, SIMPLE_APP_PACKAGE);
assertEquals(RESULT_PASS, result);
}
@@ -238,8 +238,8 @@
public int waitForResult() {
try {
- if (mSemaphore.tryAcquire(5, TimeUnit.SECONDS)) {
- return result;
+ if (mSemaphore.tryAcquire(120, TimeUnit.SECONDS)) {
+ return result;
}
} catch (InterruptedException e) {
}
diff --git a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
index 8d61496..195a18b 100644
--- a/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
+++ b/hostsidetests/devicepolicy/app/LauncherTestsSupport/src/com/android/cts/launchertests/support/LauncherCallbackTestsService.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Bundle;
import android.os.Handler;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
@@ -31,6 +32,9 @@
import android.util.Pair;
import java.util.ArrayList;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
import java.util.List;
/**
@@ -49,23 +53,28 @@
public static final int MSG_CHECK_PACKAGE_ADDED = 1;
public static final int MSG_CHECK_PACKAGE_REMOVED = 2;
public static final int MSG_CHECK_PACKAGE_CHANGED = 3;
- public static final int MSG_CHECK_NO_CALLBACK = 4;
+ public static final int MSG_CHECK_NO_PACKAGE_ADDED = 4;
public static final int RESULT_PASS = 1;
public static final int RESULT_FAIL = 2;
private static final String TAG = "LauncherCallbackTests";
- private static List<Pair<String, UserHandle>> mPackagesAdded
- = new ArrayList<Pair<String, UserHandle>>();
- private static List<Pair<String, UserHandle>> mPackagesRemoved
- = new ArrayList<Pair<String, UserHandle>>();
- private static List<Pair<String, UserHandle>> mPackagesChanged
- = new ArrayList<Pair<String, UserHandle>>();
- private static Object mPackagesLock = new Object();
+ private static BlockingQueue<Pair<String, UserHandle>> mPackagesAdded
+ = new LinkedBlockingQueue();
+ private static BlockingQueue<Pair<String, UserHandle>> mPackagesRemoved
+ = new LinkedBlockingQueue();
+ private static BlockingQueue<Pair<String, UserHandle>> mPackagesChanged
+ = new LinkedBlockingQueue();
private TestCallback mCallback;
+ private Object mCallbackLock = new Object();
private final Messenger mMessenger = new Messenger(new CheckHandler());
+ private final HandlerThread mCallbackThread = new HandlerThread("callback");
+
+ public LauncherCallbackTestsService() {
+ mCallbackThread.start();
+ }
class CheckHandler extends Handler {
@Override
@@ -77,6 +86,7 @@
try {
switch (msg.what) {
case MSG_CHECK_PACKAGE_ADDED: {
+ Log.i(TAG, "MSG_CHECK_PACKAGE_ADDED");
boolean exists = eventExists(params, mPackagesAdded);
teardown();
msg.replyTo.send(Message.obtain(null, MSG_RESULT,
@@ -84,6 +94,7 @@
break;
}
case MSG_CHECK_PACKAGE_REMOVED: {
+ Log.i(TAG, "MSG_CHECK_PACKAGE_REMOVED");
boolean exists = eventExists(params, mPackagesRemoved);
teardown();
msg.replyTo.send(Message.obtain(null, MSG_RESULT,
@@ -91,16 +102,16 @@
break;
}
case MSG_CHECK_PACKAGE_CHANGED: {
+ Log.i(TAG, "MSG_CHECK_PACKAGE_CHANGED");
boolean exists = eventExists(params, mPackagesChanged);
teardown();
msg.replyTo.send(Message.obtain(null, MSG_RESULT,
exists ? RESULT_PASS : RESULT_FAIL, 0));
break;
}
- case MSG_CHECK_NO_CALLBACK: {
- boolean exists = eventExists(params, mPackagesAdded)
- || eventExists(params, mPackagesRemoved)
- || eventExists(params, mPackagesChanged);
+ case MSG_CHECK_NO_PACKAGE_ADDED: {
+ Log.i(TAG, "MSG_CHECK_NO_PACKAGE_ADDED");
+ boolean exists = eventExists(params, mPackagesAdded);
teardown();
msg.replyTo.send(Message.obtain(null, MSG_RESULT,
exists ? RESULT_FAIL : RESULT_PASS, 0));
@@ -129,24 +140,26 @@
private void setup() {
LauncherApps launcherApps = (LauncherApps) getSystemService(
Context.LAUNCHER_APPS_SERVICE);
- synchronized (mPackagesLock) {
- mPackagesAdded.clear();
- mPackagesRemoved.clear();
- mPackagesChanged.clear();
+ synchronized (mCallbackLock) {
if (mCallback != null) {
launcherApps.unregisterCallback(mCallback);
}
+ mPackagesAdded.clear();
+ mPackagesRemoved.clear();
+ mPackagesChanged.clear();
mCallback = new TestCallback();
- launcherApps.registerCallback(mCallback);
+ launcherApps.registerCallback(mCallback, new Handler(mCallbackThread.getLooper()));
+ Log.i(TAG, "started listening for events");
}
}
private void teardown() {
LauncherApps launcherApps = (LauncherApps) getSystemService(
Context.LAUNCHER_APPS_SERVICE);
- synchronized (mPackagesLock) {
+ synchronized (mCallbackLock) {
if (mCallback != null) {
launcherApps.unregisterCallback(mCallback);
+ Log.i(TAG, "stopped listening for events");
mCallback = null;
}
mPackagesAdded.clear();
@@ -155,20 +168,23 @@
}
}
- private boolean eventExists(Bundle params, List<Pair<String, UserHandle>> events) {
+ private boolean eventExists(Bundle params, BlockingQueue<Pair<String, UserHandle>> events) {
UserHandle user = params.getParcelable(USER_EXTRA);
String packageName = params.getString(PACKAGE_EXTRA);
- synchronized (mPackagesLock) {
- if (events != null) {
- for (Pair<String, UserHandle> added : events) {
- if (added.first.equals(packageName) && added.second.equals(user)) {
- Log.i(TAG, "Event exists " + packageName + " for user " + user);
- return true;
- }
+ Log.i(TAG, "checking for " + packageName + " " + user);
+ try {
+ Pair<String, UserHandle> event = events.poll(60, TimeUnit.SECONDS);
+ while (event != null) {
+ if (event.first.equals(packageName) && event.second.equals(user)) {
+ Log.i(TAG, "Event exists " + packageName + " for user " + user);
+ return true;
}
+ event = events.poll(20, TimeUnit.SECONDS);
}
- return false;
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed checking for event", e);
}
+ return false;
}
@Override
@@ -178,20 +194,29 @@
private class TestCallback extends LauncherApps.Callback {
public void onPackageRemoved(String packageName, UserHandle user) {
- synchronized (mPackagesLock) {
- mPackagesRemoved.add(new Pair<String, UserHandle>(packageName, user));
+ Log.i(TAG, "package removed event " + packageName + " " + user);
+ try {
+ mPackagesRemoved.put(new Pair(packageName, user));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed saving event", e);
}
}
public void onPackageAdded(String packageName, UserHandle user) {
- synchronized (mPackagesLock) {
- mPackagesAdded.add(new Pair<String, UserHandle>(packageName, user));
+ Log.i(TAG, "package added event " + packageName + " " + user);
+ try {
+ mPackagesAdded.put(new Pair(packageName, user));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed saving event", e);
}
}
public void onPackageChanged(String packageName, UserHandle user) {
- synchronized (mPackagesLock) {
- mPackagesChanged.add(new Pair<String, UserHandle>(packageName, user));
+ Log.i(TAG, "package changed event " + packageName + " " + user);
+ try {
+ mPackagesChanged.put(new Pair(packageName, user));
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Failed saving event", e);
}
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index 008ed38..e430785 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -18,6 +18,10 @@
package="com.android.cts.managedprofile">
<uses-sdk android:minSdkVersion="20"/>
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png b/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png
new file mode 100644
index 0000000..37b558b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/raw/ic_contact_picture.png
Binary files differ
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
new file mode 100644
index 0000000..4d7ddeb
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothTest.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.managedprofile;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothServerSocket;
+import android.test.AndroidTestCase;
+
+import java.io.IOException;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Test that the basic bluetooth API is callable in managed profiles.
+ * These tests should only be executed if the device supports bluetooth,
+ * i.e. if it has the {@link android.content.pm.PackageManager#FEATURE_BLUETOOTH} feature.
+ *
+ * This includes tests for the {@link BluetoothAdapter}.
+ * The corresponding CTS tests in the primary profile are in
+ * {@link android.bluetooth.cts.BasicAdapterTest}.
+ * TODO: Merge the primary and managed profile tests into one.
+ */
+public class BluetoothTest extends AndroidTestCase {
+ private static final int DISABLE_TIMEOUT_MS = 8000;
+ private static final int ENABLE_TIMEOUT_MS = 10000;
+ private static final int POLL_TIME_MS = 400;
+ private static final int CHECK_WAIT_TIME_MS = 1000;
+
+ private BluetoothAdapter mAdapter;
+ private boolean mBtWasEnabled;
+
+ public void setUp() throws Exception {
+ super.setUp();
+ mAdapter = BluetoothAdapter.getDefaultAdapter();
+ assertNotNull(mAdapter);
+ mBtWasEnabled = mAdapter.isEnabled();
+ }
+
+ public void tearDown() throws Exception {
+ if (mBtWasEnabled != mAdapter.isEnabled()) {
+ if (mBtWasEnabled) {
+ enable();
+ } else {
+ disable();
+ }
+ }
+ super.tearDown();
+ }
+
+ /**
+ * Checks enable(), disable(), getState(), isEnabled()
+ */
+ public void testEnableDisable() {
+ disable();
+ enable();
+ }
+
+ /**
+ * Test the getAddress() function.
+ */
+ public void testGetAddress() {
+ assertTrue(BluetoothAdapter.checkBluetoothAddress(mAdapter.getAddress()));
+ }
+
+ /**
+ * Tests the listenUsingRfcommWithServiceRecord function.
+ */
+ public void testListenUsingRfcommWithServiceRecord() throws IOException {
+ enable();
+ BluetoothServerSocket socket = mAdapter.listenUsingRfcommWithServiceRecord(
+ "test", UUID.randomUUID());
+ assertNotNull(socket);
+ socket.close();
+ }
+
+ /**
+ * Test the getRemoteDevice() function.
+ */
+ public void testGetRemoteDevice() {
+ // getRemoteDevice() should work even with Bluetooth disabled
+ disable();
+
+ // test bad addresses
+ try {
+ mAdapter.getRemoteDevice((String)null);
+ fail("IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ mAdapter.getRemoteDevice("00:00:00:00:00:00:00:00");
+ fail("IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ mAdapter.getRemoteDevice((byte[])null);
+ fail("IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ mAdapter.getRemoteDevice(new byte[] {0x00, 0x00, 0x00, 0x00, 0x00});
+ fail("IllegalArgumentException not thrown");
+ } catch (IllegalArgumentException e) {
+ }
+
+ // test success
+ BluetoothDevice device = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
+ assertNotNull(device);
+ assertEquals("00:11:22:AA:BB:CC", device.getAddress());
+ device = mAdapter.getRemoteDevice(
+ new byte[] {0x01, 0x02, 0x03, 0x04, 0x05, 0x06});
+ assertNotNull(device);
+ assertEquals("01:02:03:04:05:06", device.getAddress());
+ }
+
+ /**
+ * Helper to turn BT off.
+ * This method will either fail on an assert, or return with BT turned off.
+ * Behavior of getState() and isEnabled() are validated along the way.
+ */
+ private void disable() {
+ sleep(CHECK_WAIT_TIME_MS);
+ if (mAdapter.getState() == BluetoothAdapter.STATE_OFF) {
+ assertFalse(mAdapter.isEnabled());
+ return;
+ }
+
+ assertEquals(BluetoothAdapter.STATE_ON, mAdapter.getState());
+ assertTrue(mAdapter.isEnabled());
+ assertTrue(mAdapter.disable());
+ boolean turnOff = false;
+ for (int i=0; i<DISABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
+ sleep(POLL_TIME_MS);
+ int state = mAdapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_OFF:
+ assertFalse(mAdapter.isEnabled());
+ return;
+ default:
+ if (state != BluetoothAdapter.STATE_ON || turnOff) {
+ assertEquals(BluetoothAdapter.STATE_TURNING_OFF, state);
+ turnOff = true;
+ }
+ break;
+ }
+ }
+ fail("disable() timeout");
+ }
+
+ /**
+ * Helper to turn BT on.
+ * This method will either fail on an assert, or return with BT turned on.
+ * Behavior of getState() and isEnabled() are validated along the way.
+ */
+ private void enable() {
+ sleep(CHECK_WAIT_TIME_MS);
+ if (mAdapter.getState() == BluetoothAdapter.STATE_ON) {
+ assertTrue(mAdapter.isEnabled());
+ return;
+ }
+
+ assertEquals(BluetoothAdapter.STATE_OFF, mAdapter.getState());
+ assertFalse(mAdapter.isEnabled());
+ assertTrue(mAdapter.enable());
+ boolean turnOn = false;
+ for (int i=0; i<ENABLE_TIMEOUT_MS/POLL_TIME_MS; i++) {
+ sleep(POLL_TIME_MS);
+ int state = mAdapter.getState();
+ switch (state) {
+ case BluetoothAdapter.STATE_ON:
+ assertTrue(mAdapter.isEnabled());
+ return;
+ default:
+ if (state != BluetoothAdapter.STATE_OFF || turnOn) {
+ assertEquals(BluetoothAdapter.STATE_TURNING_ON, state);
+ turnOn = true;
+ }
+ break;
+ }
+ }
+ fail("enable() timeout");
+ }
+
+ private void sleep(long t) {
+ try {
+ Thread.sleep(t);
+ } catch (InterruptedException e) {}
+ }
+}
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
new file mode 100644
index 0000000..faee6dc
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/ContactsTest.java
@@ -0,0 +1,285 @@
+/*
+ * 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.managedprofile;
+
+import android.annotation.TargetApi;
+import android.app.admin.DevicePolicyManager;
+import android.content.ContentProviderOperation;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.OperationApplicationException;
+import android.content.res.Resources.NotFoundException;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.RemoteException;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.Photo;
+import android.provider.ContactsContract.PhoneLookup;
+import android.provider.ContactsContract.RawContacts;
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class ContactsTest extends AndroidTestCase {
+
+ private static final String TEST_ACCOUNT_NAME = "CTS";
+ private static final String TEST_ACCOUNT_TYPE = "com.android.cts.test";
+ private static final String PRIMARY_CONTACT_DISPLAY_NAME = "Primary";
+ private static final String PRIMARY_CONTACT_PHONE = "00000001";
+ private static final String MANAGED_CONTACT_DISPLAY_NAME = "Managed";
+ private static final String MANAGED_CONTACT_PHONE = "6891999";
+
+ private DevicePolicyManager mDevicePolicyManager;
+ private ContentResolver mResolver;
+
+ private static class ContactInfo {
+ String contactId;
+ String displayName;
+ String photoUri;
+ String photoThumbnailUri;
+ String photoId;
+
+ public ContactInfo(String contactId, String displayName, String photoUri,
+ String photoThumbnailUri, String photoId) {
+ this.contactId = contactId;
+ this.displayName = displayName;
+ this.photoUri = photoUri;
+ this.photoThumbnailUri = photoThumbnailUri;
+ this.photoId = photoId;
+ }
+
+ private boolean hasPhotoUri() {
+ return photoUri != null && photoThumbnailUri != null;
+ }
+
+ private boolean hasPhotoId() {
+ return photoId != null;
+ }
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mResolver = getContext().getContentResolver();
+ mDevicePolicyManager = (DevicePolicyManager) mContext
+ .getSystemService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ public void testPrimaryProfilePhoneLookup_insertedAndfound() throws RemoteException,
+ OperationApplicationException, NotFoundException, IOException {
+ assertFalse(isManagedProfile());
+ // Do not insert to primary contact
+ insertContact(PRIMARY_CONTACT_DISPLAY_NAME, PRIMARY_CONTACT_PHONE, 0);
+
+ ContactInfo contactInfo = getContactInfo(PRIMARY_CONTACT_PHONE);
+ 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,
+ OperationApplicationException, NotFoundException, IOException {
+ assertTrue(isManagedProfile());
+ // Insert ic_contact_picture as photo in managed contact
+ insertContact(MANAGED_CONTACT_DISPLAY_NAME, MANAGED_CONTACT_PHONE,
+ com.android.cts.managedprofile.R.raw.ic_contact_picture);
+
+ ContactInfo contactInfo = getContactInfo(MANAGED_CONTACT_PHONE);
+ assertNotNull(contactInfo);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+ 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 testPrimaryProfilePhoneLookup_canAccessPrimaryContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+ assertEquals(PRIMARY_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertFalse(contactInfo.hasPhotoUri());
+ assertFalse(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testManagedProfilePhoneLookup_canAccessEnterpriseContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+ assertEquals(MANAGED_CONTACT_DISPLAY_NAME, contactInfo.displayName);
+ assertTrue(contactInfo.hasPhotoUri());
+ assertTrue(contactInfo.hasPhotoId());
+ assertFalse(isEnterpriseContactId(contactInfo.contactId));
+ }
+
+ public void testPrimaryProfilePhoneLookup_canNotAccessEnterpriseContact() {
+ assertFalse(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(MANAGED_CONTACT_PHONE);
+ assertNull(contactInfo);
+ }
+
+ public void testManagedProfilePhoneLookup_canNotAccessPrimaryContact() {
+ assertTrue(isManagedProfile());
+ ContactInfo contactInfo = getEnterpriseContactInfo(PRIMARY_CONTACT_PHONE);
+ assertNull(contactInfo);
+ }
+
+ public void testSetCrossProfileCallerIdDisabled_true() {
+ assertTrue(isManagedProfile());
+ mDevicePolicyManager.setCrossProfileCallerIdDisabled(
+ BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, true);
+ }
+
+ public void testSetCrossProfileCallerIdDisabled_false() {
+ assertTrue(isManagedProfile());
+ mDevicePolicyManager.setCrossProfileCallerIdDisabled(
+ BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT, false);
+ }
+
+ public void testCurrentProfileContacts_removeContacts() {
+ removeAllTestContactsInProfile();
+ }
+
+ private boolean isManagedProfile() {
+ String adminPackage = BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT.getPackageName();
+ return mDevicePolicyManager.isProfileOwnerApp(adminPackage);
+ }
+
+ private void insertContact(String displayName, String phoneNumber, int photoResId)
+ throws RemoteException,
+ OperationApplicationException, NotFoundException, IOException {
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.RawContacts.CONTENT_URI)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, TEST_ACCOUNT_TYPE)
+ .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, TEST_ACCOUNT_NAME)
+ .build());
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
+ .withValue(
+ ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME,
+ displayName)
+ .build());
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
+ .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER,
+ phoneNumber)
+ .withValue(ContactsContract.CommonDataKinds.Phone.TYPE,
+ Phone.TYPE_MOBILE)
+ .build());
+ if (photoResId != 0) {
+ InputStream phoneInputStream = mContext.getResources().openRawResource(photoResId);
+ byte[] rawPhoto = getByteFromStream(phoneInputStream);
+ ops.add(ContentProviderOperation
+ .newInsert(ContactsContract.Data.CONTENT_URI)
+ .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
+ .withValue(
+ ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)
+ .withValue(Photo.PHOTO, rawPhoto)
+ .build());
+ }
+
+ mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+ }
+
+ private ContactInfo getContactInfoFromUri(Uri phoneLookupUri, String phoneNumber) {
+ Uri uri = Uri.withAppendedPath(phoneLookupUri,
+ Uri.encode(phoneNumber));
+ Cursor cursor = mResolver.query(uri,
+ new String[] {
+ PhoneLookup._ID, PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_URI,
+ PhoneLookup.PHOTO_THUMBNAIL_URI, PhoneLookup.PHOTO_ID
+ }, null, null, null);
+ if (cursor == null) {
+ return null;
+ }
+ ContactInfo result = null;
+ if (cursor.moveToFirst()) {
+ result = new ContactInfo(
+ cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup._ID)),
+ cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.DISPLAY_NAME)),
+ cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_URI)),
+ cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_THUMBNAIL_URI)),
+ cursor.getString(cursor.getColumnIndexOrThrow(PhoneLookup.PHOTO_ID)));
+ }
+ cursor.close();
+ return result;
+ }
+
+ private ContactInfo getContactInfo(String phoneNumber) {
+ return getContactInfoFromUri(PhoneLookup.CONTENT_FILTER_URI,
+ phoneNumber);
+ }
+
+ private ContactInfo getEnterpriseContactInfo(String phoneNumber) {
+ return getContactInfoFromUri(
+ PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
+ phoneNumber);
+ }
+
+ private void removeAllTestContactsInProfile() {
+ ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
+ ops.add(ContentProviderOperation.newDelete(RawContacts.CONTENT_URI)
+ .withSelection(RawContacts.ACCOUNT_TYPE + "=?", new String[] {TEST_ACCOUNT_TYPE})
+ .build());
+ try {
+ mResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+ } catch (Exception e) {
+ // Catch all exceptions to let tearDown() run smoothly
+ e.printStackTrace();
+ }
+ }
+
+ private static byte[] getByteFromStream(InputStream is) throws IOException {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ byte[] buf = new byte[1024 * 10];
+ int i = 0;
+ while ((i = is.read(buf, 0, buf.length)) > 0) {
+ outputStream.write(buf, 0, i);
+ }
+ return outputStream.toByteArray();
+ }
+
+ private boolean isEnterpriseContactId(String contactId) {
+ return ContactsContract.Contacts.isEnterpriseContactId(Long.valueOf(contactId));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
index 9615991..49001e9 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
@@ -21,8 +21,15 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.IntentFilter;
+import android.os.UserManager;
import android.test.AndroidTestCase;
+/**
+ * The methods in this class are not really tests.
+ * They are just performing an action that is needed for a test.
+ * But we're still using an AndroidTestCase because it's an easy way to call
+ * device-side methods from the host.
+ */
public class CrossProfileUtils extends AndroidTestCase {
private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
@@ -31,16 +38,14 @@
private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
"com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
+ private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
public void addParentCanAccessManagedFilters() {
removeAllFilters();
final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
Context.DEVICE_POLICY_SERVICE);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_READ_FROM_URI);
- intentFilter.addAction(ACTION_WRITE_TO_URI);
- intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
- dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
+ dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
}
@@ -49,12 +54,17 @@
final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
+ DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+ }
+
+ public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_READ_FROM_URI);
intentFilter.addAction(ACTION_WRITE_TO_URI);
intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
- dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
- DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+ intentFilter.addAction(ACTION_COPY_TO_CLIPBOARD);
+ return intentFilter;
}
public void removeAllFilters() {
@@ -62,4 +72,18 @@
Context.DEVICE_POLICY_SERVICE);
dpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
}
+
+ public void disallowCrossProfileCopyPaste() {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ }
+
+ public void allowCrossProfileCopyPaste() {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ }
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java
new file mode 100644
index 0000000..c74211d
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/NfcTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.managedprofile;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class NfcTest extends AndroidTestCase {
+ private static final String SAMPLE_TEXT = "This is my text to send.";
+ private static final String TEXT_MIME_TYPE = "text/plain";
+ private static final String NFC_BEAM_ACTIVITY = "com.android.nfc.BeamShareActivity";
+
+ public void testNfcShareDisabled() throws Exception {
+ Intent intent = getTextShareIntent();
+ assertFalse("Nfc beam activity should not be resolved", isNfcBeamActivityResolved(intent));
+ }
+
+ public void testNfcShareEnabled() throws Exception {
+ Intent intent = getTextShareIntent();
+ assertTrue("Nfc beam activity should be resolved", isNfcBeamActivityResolved(intent));
+ }
+
+ private Intent getTextShareIntent() {
+ Intent intent = new Intent();
+ intent.setAction(Intent.ACTION_SEND);
+ intent.putExtra(Intent.EXTRA_TEXT, SAMPLE_TEXT);
+ intent.setType(TEXT_MIME_TYPE);
+ return intent;
+ }
+
+ private boolean isNfcBeamActivityResolved(Intent intent) {
+ PackageManager pm = mContext.getPackageManager();
+ for (ResolveInfo resolveInfo : pm.queryIntentActivities(intent, 0)) {
+ if (NFC_BEAM_ACTIVITY.equals(resolveInfo.activityInfo.name)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
+
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
index 0af38a4..8da189f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsMultiUserTest.java
@@ -81,7 +81,7 @@
try {
assertTrue(runDeviceTests(LAUNCHER_TESTS_PKG,
LAUNCHER_TESTS_CLASS,
- "testNoCallbackForUser",
+ "testNoPackageAddedCallbackForUser",
0, "-e testUser " + mSecondaryUserSerialNumber));
} finally {
getDevice().uninstallPackage(SIMPLE_APP_PKG);
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 7da4228..f8fa222 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -37,19 +37,24 @@
private static final String ADMIN_RECEIVER_TEST_CLASS =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
+ private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
private int mUserId;
-
+ private boolean mHasNfcFeature;
+
@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) && hasDeviceFeature(
+ mHasFeature = mHasFeature && hasDeviceFeature(
"android.software.managed_users");
+ mHasNfcFeature = hasDeviceFeature("android.hardware.nfc");
if (mHasFeature) {
mUserId = createManagedProfile();
installApp(MANAGED_PROFILE_APK);
+ installApp(INTENT_RECEIVER_APK);
+ installApp(INTENT_SENDER_APK);
setProfileOwner(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
startUser(mUserId);
}
@@ -60,8 +65,9 @@
if (mHasFeature) {
removeUser(mUserId);
getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+ getDevice().uninstallPackage(INTENT_SENDER_PKG);
+ getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
}
-
super.tearDown();
}
@@ -88,14 +94,6 @@
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 testCrossProfileIntentFilters() throws Exception {
if (!mHasFeature) {
return;
@@ -130,30 +128,58 @@
return;
}
- try {
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- installAppAsUser(INTENT_SENDER_APK, 0);
- installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+ // Test from parent to managed
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "addManagedCanAccessParentFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", 0));
- // Test from parent to managed
- assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "addManagedCanAccessParentFilters", mUserId));
- assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", 0));
+ // Test from managed to parent
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "addParentCanAccessManagedFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mUserId));
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- installAppAsUser(INTENT_SENDER_APK, mUserId);
- installAppAsUser(INTENT_RECEIVER_APK, 0);
+ }
- // Test from managed to parent
- assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "addParentCanAccessManagedFilters", mUserId));
- assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", mUserId));
+ public void testCrossProfileCopyPaste() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
- } finally {
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "allowCrossProfileCopyPaste", mUserId));
+ // Test that managed can see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mUserId, true);
+ // Test that the parent can see what is copied in managed.
+ testCrossProfileCopyPasteInternal(0, true);
+
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "disallowCrossProfileCopyPaste", mUserId));
+ // Test that managed can still see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mUserId, true);
+ // Test that the parent cannot see what is copied in managed.
+ testCrossProfileCopyPasteInternal(0, false);
+ }
+
+ private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ final String direction = (userId == 0) ? "addManagedCanAccessParentFilters"
+ : "addParentCanAccessManagedFilters";
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ direction, mUserId));
+ if (shouldSucceed) {
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCanReadAcrossProfiles", userId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testIsNotified", userId));
+ } else {
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCannotReadAcrossProfiles", userId));
}
}
@@ -183,6 +209,97 @@
addRestrictionCommandOutput.contains("SecurityException"));
}
+ // Test the bluetooth API from a managed profile.
+ public void testBluetooth() throws Exception {
+ boolean mHasBluetooth = hasDeviceFeature(FEATURE_BLUETOOTH);
+ if (!mHasFeature || !mHasBluetooth) {
+ return ;
+ }
+
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+ "testEnableDisable", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+ "testGetAddress", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+ "testListenUsingRfcommWithServiceRecord", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".BluetoothTest",
+ "testGetRemoteDevice", mUserId));
+ }
+
+ public void testManagedContacts() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+
+ try {
+ // Insert Primary profile Contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfilePhoneLookup_insertedAndfound", 0));
+ // Insert Managed profile Contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfilePhoneLookup_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
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfilePhoneLookup_canAccessEnterpriseContact", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+
+ // Primary user can use ENTERPRISE_CONTENT_FILTER_URI
+ // To access both primary and managed contacts
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfileEnterprisePhoneLookup_canAccessEnterpriseContact", 0));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testPrimaryProfilePhoneLookup_canAccessPrimaryContact", 0));
+
+ // 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
+ 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",
+ "testManagedProfilePhoneLookup_canNotAccessPrimaryContact", mUserId));
+ } finally {
+ // Clean up in managed profile and primary profile
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testCurrentProfileContacts_removeContacts", mUserId);
+ runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".ContactsTest",
+ "testCurrentProfileContacts_removeContacts", 0);
+ }
+ }
+
+ public void testNfcRestriction() throws Exception {
+ if (!mHasFeature || !mHasNfcFeature) {
+ return;
+ }
+
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+ "testNfcShareEnabled", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+ "testNfcShareEnabled", 0));
+
+ String restriction = "no_outgoing_beam"; // UserManager.DISALLOW_OUTGOING_BEAM
+ String command = "add-restriction";
+
+ String addRestrictionCommandOutput =
+ changeUserRestrictionForUser(restriction, command, mUserId);
+ assertTrue("Command was expected to succeed " + addRestrictionCommandOutput,
+ addRestrictionCommandOutput.contains("Status: ok"));
+
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+ "testNfcShareDisabled", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".NfcTest",
+ "testNfcShareEnabled", 0));
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index 4668faf..e12135a 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -523,7 +523,7 @@
}
private void checkProcess(String[] parts) {
- assertEquals(9, parts.length);
+ assertTrue(parts.length >= 9);
assertNotNull(parts[4]); // process
assertInteger(parts[5]); // userMillis
assertInteger(parts[6]); // systemMillis
@@ -586,10 +586,10 @@
}
private void checkKernelWakelock(String[] parts) {
- assertEquals(7, parts.length);
- assertNotNull(parts[4]); // kernel wakelock
- assertInteger(parts[5]); // totalTime
- assertInteger(parts[6]); // count
+ assertTrue(parts.length >= 7);
+ assertNotNull(parts[4]); // Kernel wakelock
+ assertInteger(parts[parts.length-2]); // totalTime
+ assertInteger(parts[parts.length-1]); // count
}
private void checkWakeupReason(String[] parts) {
@@ -658,7 +658,7 @@
}
private void checkMisc(String[] parts) {
- assertEquals(20, parts.length);
+ assertTrue(parts.length >= 20);
assertInteger(parts[4]); // screenOnTime
assertInteger(parts[5]); // phoneOnTime
assertInteger(parts[6]); // wifiOnTime
@@ -699,7 +699,7 @@
}
private void checkSignalStrength(String[] parts) {
- assertEquals(9, parts.length);
+ assertTrue(parts.length >= 9);
assertInteger(parts[4]); // none
assertInteger(parts[5]); // poor
assertInteger(parts[6]); // moderate
diff --git a/hostsidetests/net/Android.mk b/hostsidetests/net/Android.mk
new file mode 100644
index 0000000..6637d61
--- /dev/null
+++ b/hostsidetests/net/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsHostsideNetworkTests
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.net.hostsidenetwork
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+
+# Build the test APKs using their own makefiles
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/net/app/Android.mk b/hostsidetests/net/app/Android.mk
new file mode 100644
index 0000000..29b620d
--- /dev/null
+++ b/hostsidetests/net/app/Android.mk
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsHostsideNetworkTestsApp
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_PACKAGE)
diff --git a/hostsidetests/net/app/AndroidManifest.xml b/hostsidetests/net/app/AndroidManifest.xml
new file mode 100644
index 0000000..cdde7dc
--- /dev/null
+++ b/hostsidetests/net/app/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.net.hostside">
+
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ <activity android:name=".MyActivity" />
+ <service android:name=".MyVpnService"
+ android:permission="android.permission.BIND_VPN_SERVICE">
+ <intent-filter>
+ <action android:name="android.net.VpnService"/>
+ </intent-filter>
+ </service>
+ </application>
+
+ <instrumentation
+ android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.net.hostside" />
+
+</manifest>
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
new file mode 100644
index 0000000..375c852
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyActivity.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.view.WindowManager;
+
+import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MyActivity extends Activity {
+ private final LinkedBlockingQueue<Integer> mResult = new LinkedBlockingQueue<>(1);
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (mResult.offer(resultCode) == false) {
+ throw new RuntimeException("Queue is full! This should never happen");
+ }
+ }
+
+ public Integer getResult(int timeoutMs) throws InterruptedException {
+ return mResult.poll(timeoutMs, TimeUnit.MILLISECONDS);
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
new file mode 100644
index 0000000..a3f400c
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/MyVpnService.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.content.Intent;
+import android.net.VpnService;
+import android.os.ParcelFileDescriptor;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+
+public class MyVpnService extends VpnService {
+
+ private static String TAG = "MyVpnService";
+ private static int MTU = 1799;
+
+ private ParcelFileDescriptor mFd = null;
+ private PacketReflector mPacketReflector = null;
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String packageName = getPackageName();
+ String cmd = intent.getStringExtra(packageName + ".cmd");
+ if ("disconnect".equals(cmd)) {
+ stop();
+ } else if ("connect".equals(cmd)) {
+ start(packageName, intent);
+ }
+
+ return START_NOT_STICKY;
+ }
+
+ private void start(String packageName, Intent intent) {
+ Builder builder = new Builder();
+
+ String addresses = intent.getStringExtra(packageName + ".addresses");
+ if (addresses != null) {
+ String[] addressArray = addresses.split(",");
+ for (int i = 0; i < addressArray.length; i++) {
+ String[] prefixAndMask = addressArray[i].split("/");
+ try {
+ InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+ int prefixLength = Integer.parseInt(prefixAndMask[1]);
+ builder.addAddress(address, prefixLength);
+ } catch (UnknownHostException|NumberFormatException|
+ ArrayIndexOutOfBoundsException e) {
+ continue;
+ }
+ }
+ }
+
+ String routes = intent.getStringExtra(packageName + ".routes");
+ if (routes != null) {
+ String[] routeArray = routes.split(",");
+ for (int i = 0; i < routeArray.length; i++) {
+ String[] prefixAndMask = routeArray[i].split("/");
+ try {
+ InetAddress address = InetAddress.getByName(prefixAndMask[0]);
+ int prefixLength = Integer.parseInt(prefixAndMask[1]);
+ builder.addRoute(address, prefixLength);
+ } catch (UnknownHostException|NumberFormatException|
+ ArrayIndexOutOfBoundsException e) {
+ continue;
+ }
+ }
+ }
+
+ String allowed = intent.getStringExtra(packageName + ".allowedapplications");
+ if (allowed != null) {
+ String[] packageArray = allowed.split(",");
+ for (int i = 0; i < packageArray.length; i++) {
+ String allowedPackage = packageArray[i];
+ if (!TextUtils.isEmpty(allowedPackage)) {
+ try {
+ builder.addAllowedApplication(allowedPackage);
+ } catch(NameNotFoundException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ String disallowed = intent.getStringExtra(packageName + ".disallowedapplications");
+ if (disallowed != null) {
+ String[] packageArray = disallowed.split(",");
+ for (int i = 0; i < packageArray.length; i++) {
+ String disallowedPackage = packageArray[i];
+ if (!TextUtils.isEmpty(disallowedPackage)) {
+ try {
+ builder.addDisallowedApplication(disallowedPackage);
+ } catch(NameNotFoundException e) {
+ continue;
+ }
+ }
+ }
+ }
+
+ builder.setMtu(MTU);
+ builder.setBlocking(true);
+ builder.setSession("MyVpnService");
+
+ Log.i(TAG, "Establishing VPN,"
+ + " addresses=" + addresses
+ + " routes=" + routes
+ + " allowedApplications=" + allowed
+ + " disallowedApplications=" + disallowed);
+
+ mFd = builder.establish();
+ Log.i(TAG, "Established, fd=" + (mFd == null ? "null" : mFd.getFd()));
+
+ mPacketReflector = new PacketReflector(mFd.getFileDescriptor(), MTU);
+ mPacketReflector.start();
+ }
+
+ private void stop() {
+ if (mPacketReflector != null) {
+ mPacketReflector.interrupt();
+ mPacketReflector = null;
+ }
+ try {
+ if (mFd != null) {
+ Log.i(TAG, "Closing filedescriptor");
+ mFd.close();
+ }
+ } catch(IOException e) {
+ } finally {
+ mFd = null;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ stop();
+ super.onDestroy();
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java
new file mode 100644
index 0000000..dd0f792
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/PacketReflector.java
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import android.system.ErrnoException;
+import android.system.Os;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+public class PacketReflector extends Thread {
+
+ private static int IPV4_HEADER_LENGTH = 20;
+ private static int IPV6_HEADER_LENGTH = 40;
+
+ private static int IPV4_ADDR_OFFSET = 12;
+ private static int IPV6_ADDR_OFFSET = 8;
+ private static int IPV4_ADDR_LENGTH = 4;
+ private static int IPV6_ADDR_LENGTH = 16;
+
+ private static int IPV4_PROTO_OFFSET = 9;
+ private static int IPV6_PROTO_OFFSET = 6;
+
+ private static final byte IPPROTO_ICMP = 1;
+ private static final byte IPPROTO_TCP = 6;
+ private static final byte IPPROTO_UDP = 17;
+ private static final byte IPPROTO_ICMPV6 = 58;
+
+ private static int ICMP_HEADER_LENGTH = 8;
+ private static int TCP_HEADER_LENGTH = 20;
+ private static int UDP_HEADER_LENGTH = 8;
+
+ private static final byte ICMP_ECHO = 8;
+ private static final byte ICMP_ECHOREPLY = 0;
+ private static final byte ICMPV6_ECHO_REQUEST = (byte) 128;
+ private static final byte ICMPV6_ECHO_REPLY = (byte) 129;
+
+ private static String TAG = "PacketReflector";
+
+ private FileDescriptor mFd;
+ private byte[] mBuf;
+
+ public PacketReflector(FileDescriptor fd, int mtu) {
+ super("PacketReflector");
+ mFd = fd;
+ mBuf = new byte[mtu];
+ }
+
+ private static void swapBytes(byte[] buf, int pos1, int pos2, int len) {
+ for (int i = 0; i < len; i++) {
+ byte b = buf[pos1 + i];
+ buf[pos1 + i] = buf[pos2 + i];
+ buf[pos2 + i] = b;
+ }
+ }
+
+ private static void swapAddresses(byte[] buf, int version) {
+ int addrPos, addrLen;
+ switch(version) {
+ case 4:
+ addrPos = IPV4_ADDR_OFFSET;
+ addrLen = IPV4_ADDR_LENGTH;
+ break;
+ case 6:
+ addrPos = IPV6_ADDR_OFFSET;
+ addrLen = IPV6_ADDR_LENGTH;
+ break;
+ default:
+ throw new IllegalArgumentException();
+ }
+ swapBytes(buf, addrPos, addrPos + addrLen, addrLen);
+ }
+
+ // Reflect TCP packets: swap the source and destination addresses, but don't change the ports.
+ // This is used by the test to "connect to itself" through the VPN.
+ private void processTcpPacket(byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + TCP_HEADER_LENGTH) {
+ return;
+ }
+
+ // Swap src and dst IP addresses.
+ swapAddresses(buf, version);
+
+ // Send the packet back.
+ writePacket(buf, len);
+ }
+
+ // Echo UDP packets: swap source and destination addresses, and source and destination ports.
+ // This is used by the test to check that the bytes it sends are echoed back.
+ private void processUdpPacket(byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + UDP_HEADER_LENGTH) {
+ return;
+ }
+
+ // Swap src and dst IP addresses.
+ swapAddresses(buf, version);
+
+ // Swap dst and src ports.
+ int portOffset = hdrLen;
+ swapBytes(buf, portOffset, portOffset + 2, 2);
+
+ // Send the packet back.
+ writePacket(buf, len);
+ }
+
+ private void processIcmpPacket(byte[] buf, int version, int len, int hdrLen) {
+ if (len < hdrLen + ICMP_HEADER_LENGTH) {
+ return;
+ }
+
+ byte type = buf[hdrLen];
+ if (!(version == 4 && type == ICMP_ECHO) &&
+ !(version == 6 && type == ICMPV6_ECHO_REQUEST)) {
+ return;
+ }
+
+ // Save the ping packet we received.
+ byte[] request = buf.clone();
+
+ // Swap src and dst IP addresses, and send the packet back.
+ // This effectively pings the device to see if it replies.
+ swapAddresses(buf, version);
+ writePacket(buf, len);
+
+ // The device should have replied, and buf should now contain a ping response.
+ int received = readPacket(buf);
+ if (received != len) {
+ Log.i(TAG, "Reflecting ping did not result in ping response: " +
+ "read=" + received + " expected=" + len);
+ return;
+ }
+
+ // Compare the response we got with the original packet.
+ // The only thing that should have changed are addresses, type and checksum.
+ // Overwrite them with the received bytes and see if the packet is otherwise identical.
+ request[hdrLen] = buf[hdrLen]; // Type.
+ request[hdrLen + 2] = buf[hdrLen + 2]; // Checksum byte 1.
+ request[hdrLen + 3] = buf[hdrLen + 3]; // Checksum byte 2.
+ for (int i = 0; i < len; i++) {
+ if (buf[i] != request[i]) {
+ Log.i(TAG, "Received non-matching packet when expecting ping response.");
+ return;
+ }
+ }
+
+ // Now swap the addresses again and reflect the packet. This sends a ping reply.
+ swapAddresses(buf, version);
+ writePacket(buf, len);
+ }
+
+ private void writePacket(byte[] buf, int len) {
+ try {
+ Os.write(mFd, buf, 0, len);
+ } catch (ErrnoException|IOException e) {
+ Log.e(TAG, "Error writing packet: " + e.getMessage());
+ }
+ }
+
+ private int readPacket(byte[] buf) {
+ int len;
+ try {
+ len = Os.read(mFd, buf, 0, buf.length);
+ } catch (ErrnoException|IOException e) {
+ Log.e(TAG, "Error reading packet: " + e.getMessage());
+ len = -1;
+ }
+ return len;
+ }
+
+ // Reads one packet from our mFd, and possibly writes the packet back.
+ private void processPacket() {
+ int len = readPacket(mBuf);
+ if (len < 1) {
+ return;
+ }
+
+ int version = mBuf[0] >> 4;
+ int addrPos, protoPos, hdrLen, addrLen;
+ if (version == 4) {
+ hdrLen = IPV4_HEADER_LENGTH;
+ protoPos = IPV4_PROTO_OFFSET;
+ addrPos = IPV4_ADDR_OFFSET;
+ addrLen = IPV4_ADDR_LENGTH;
+ } else if (version == 6) {
+ hdrLen = IPV6_HEADER_LENGTH;
+ protoPos = IPV6_PROTO_OFFSET;
+ addrPos = IPV6_ADDR_OFFSET;
+ addrLen = IPV6_ADDR_LENGTH;
+ } else {
+ return;
+ }
+
+ if (len < hdrLen) {
+ return;
+ }
+
+ byte proto = mBuf[protoPos];
+ switch (proto) {
+ case IPPROTO_ICMP:
+ case IPPROTO_ICMPV6:
+ processIcmpPacket(mBuf, version, len, hdrLen);
+ break;
+ case IPPROTO_TCP:
+ processTcpPacket(mBuf, version, len, hdrLen);
+ break;
+ case IPPROTO_UDP:
+ processUdpPacket(mBuf, version, len, hdrLen);
+ break;
+ }
+ }
+
+ public void run() {
+ Log.i(TAG, "PacketReflector starting fd=" + mFd + " valid=" + mFd.valid());
+ while (!interrupted() && mFd.valid()) {
+ processPacket();
+ }
+ Log.i(TAG, "PacketReflector exiting fd=" + mFd + " valid=" + mFd.valid());
+ }
+}
diff --git a/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
new file mode 100755
index 0000000..5045cc2
--- /dev/null
+++ b/hostsidetests/net/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -0,0 +1,488 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside;
+
+import static android.system.OsConstants.*;
+
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkCapabilities;
+import android.net.NetworkRequest;
+import android.net.VpnService;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructPollfd;
+import android.test.InstrumentationTestCase;
+import android.test.MoreAsserts;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Random;
+
+/**
+ * Tests for the VpnService API.
+ *
+ * These tests establish a VPN via the VpnService API, and have the service reflect the packets back
+ * to the device without causing any network traffic. This allows testing the local VPN data path
+ * without a network connection or a VPN server.
+ *
+ * Note: in Lollipop, VPN functionality relies on kernel support for UID-based routing. If these
+ * tests fail, it may be due to the lack of kernel support. The necessary patches can be
+ * cherry-picked from the Android common kernel trees:
+ *
+ * android-3.10:
+ * https://android-review.googlesource.com/#/c/99220/
+ * https://android-review.googlesource.com/#/c/100545/
+ *
+ * android-3.4:
+ * https://android-review.googlesource.com/#/c/99225/
+ * https://android-review.googlesource.com/#/c/100557/
+ *
+ */
+public class VpnTest extends InstrumentationTestCase {
+
+ public static String TAG = "VpnTest";
+ public static int TIMEOUT_MS = 3 * 1000;
+ public static int SOCKET_TIMEOUT_MS = 100;
+
+ private UiDevice mDevice;
+ private MyActivity mActivity;
+ private String mPackageName;
+ private ConnectivityManager mCM;
+ Network mNetwork;
+ NetworkCallback mCallback;
+ final Object mLock = new Object();
+ final Object mLockShutdown = new Object();
+
+ private boolean supportedHardware() {
+ final PackageManager pm = getInstrumentation().getContext().getPackageManager();
+ return !pm.hasSystemFeature("android.hardware.type.television") &&
+ !pm.hasSystemFeature("android.hardware.type.watch");
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mNetwork = null;
+ mCallback = null;
+
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+ MyActivity.class, null);
+ mPackageName = mActivity.getPackageName();
+ mCM = (ConnectivityManager) mActivity.getSystemService(mActivity.CONNECTIVITY_SERVICE);
+ mDevice.waitForIdle();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ if (mCallback != null) {
+ mCM.unregisterNetworkCallback(mCallback);
+ }
+ Log.i(TAG, "Stopping VPN");
+ stopVpn();
+ mActivity.finish();
+ super.tearDown();
+ }
+
+ private void prepareVpn() throws Exception {
+ final int REQUEST_ID = 42;
+
+ // Attempt to prepare.
+ Log.i(TAG, "Preparing VPN");
+ Intent intent = VpnService.prepare(mActivity);
+
+ if (intent != null) {
+ // Start the confirmation dialog and click OK.
+ mActivity.startActivityForResult(intent, REQUEST_ID);
+ mDevice.waitForIdle();
+
+ String packageName = intent.getComponent().getPackageName();
+ String resourceIdRegex = "android:id/button1$|button_start_vpn";
+ final UiObject okButton = new UiObject(new UiSelector()
+ .className("android.widget.Button")
+ .packageName(packageName)
+ .resourceIdMatches(resourceIdRegex));
+ if (okButton.waitForExists(TIMEOUT_MS) == false) {
+ mActivity.finishActivity(REQUEST_ID);
+ fail("VpnService.prepare returned an Intent for '" + intent.getComponent() + "' " +
+ "to display the VPN confirmation dialog, but this test could not find the " +
+ "button to allow the VPN application to connect. Please ensure that the " +
+ "component displays a button with a resource ID matching the regexp: '" +
+ resourceIdRegex + "'.");
+ }
+
+ // Click the button and wait for RESULT_OK.
+ okButton.click();
+ try {
+ int result = mActivity.getResult(TIMEOUT_MS);
+ if (result != MyActivity.RESULT_OK) {
+ fail("The VPN confirmation dialog did not return RESULT_OK when clicking on " +
+ "the button matching the regular expression '" + resourceIdRegex +
+ "' of " + intent.getComponent() + "'. Please ensure that clicking on " +
+ "that button allows the VPN application to connect. " +
+ "Return value: " + result);
+ }
+ } catch (InterruptedException e) {
+ fail("VPN confirmation dialog did not return after " + TIMEOUT_MS + "ms");
+ }
+
+ // Now we should be prepared.
+ intent = VpnService.prepare(mActivity);
+ if (intent != null) {
+ fail("VpnService.prepare returned non-null even after the VPN dialog " +
+ intent.getComponent() + "returned RESULT_OK.");
+ }
+ }
+ }
+
+ private void startVpn(
+ String[] addresses, String[] routes,
+ String allowedApplications, String disallowedApplications) throws Exception {
+
+ prepareVpn();
+
+ // Register a callback so we will be notified when our VPN comes up.
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ mCallback = new NetworkCallback() {
+ public void onAvailable(Network network) {
+ synchronized (mLock) {
+ Log.i(TAG, "Got available callback for network=" + network);
+ mNetwork = network;
+ mLock.notify();
+ }
+ }
+ };
+ mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown.
+
+ // Start the service and wait up for TIMEOUT_MS ms for the VPN to come up.
+ Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", "connect")
+ .putExtra(mPackageName + ".addresses", TextUtils.join(",", addresses))
+ .putExtra(mPackageName + ".routes", TextUtils.join(",", routes))
+ .putExtra(mPackageName + ".allowedapplications", allowedApplications)
+ .putExtra(mPackageName + ".disallowedapplications", disallowedApplications);
+ mActivity.startService(intent);
+ synchronized (mLock) {
+ if (mNetwork == null) {
+ Log.i(TAG, "bf mLock");
+ mLock.wait(TIMEOUT_MS);
+ Log.i(TAG, "af mLock");
+ }
+ }
+
+ if (mNetwork == null) {
+ fail("VPN did not become available after " + TIMEOUT_MS + "ms");
+ }
+
+ // Unfortunately, when the available callback fires, the VPN UID ranges are not yet
+ // configured. Give the system some time to do so. http://b/18436087 .
+ try { Thread.sleep(3000); } catch(InterruptedException e) {}
+ }
+
+ private void stopVpn() {
+ // Register a callback so we will be notified when our VPN comes up.
+ final NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
+ .removeCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
+ .build();
+ mCallback = new NetworkCallback() {
+ public void onLost(Network network) {
+ synchronized (mLockShutdown) {
+ Log.i(TAG, "Got lost callback for network=" + network + ",mNetwork = " + mNetwork);
+ if( mNetwork == network){
+ mLockShutdown.notify();
+ }
+ }
+ }
+ };
+ mCM.registerNetworkCallback(request, mCallback); // Unregistered in tearDown.
+ // Simply calling mActivity.stopService() won't stop the service, because the system binds
+ // to the service for the purpose of sending it a revoke command if another VPN comes up,
+ // and stopping a bound service has no effect. Instead, "start" the service again with an
+ // Intent that tells it to disconnect.
+ Intent intent = new Intent(mActivity, MyVpnService.class)
+ .putExtra(mPackageName + ".cmd", "disconnect");
+ mActivity.startService(intent);
+ synchronized (mLockShutdown) {
+ try {
+ Log.i(TAG, "bf mLockShutdown");
+ mLockShutdown.wait(TIMEOUT_MS);
+ Log.i(TAG, "af mLockShutdown");
+ } catch(InterruptedException e) {}
+ }
+ }
+
+ private static void closeQuietly(Closeable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+
+ private static void checkPing(String to) throws IOException, ErrnoException {
+ InetAddress address = InetAddress.getByName(to);
+ FileDescriptor s;
+ final int LENGTH = 64;
+ byte[] packet = new byte[LENGTH];
+ byte[] header;
+
+ // Construct a ping packet.
+ Random random = new Random();
+ random.nextBytes(packet);
+ if (address instanceof Inet6Address) {
+ s = Os.socket(AF_INET6, SOCK_DGRAM, IPPROTO_ICMPV6);
+ header = new byte[] { (byte) 0x80, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
+ } else {
+ // Note that this doesn't actually work due to http://b/18558481 .
+ s = Os.socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ header = new byte[] { (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00 };
+ }
+ System.arraycopy(header, 0, packet, 0, header.length);
+
+ // Send the packet.
+ int port = random.nextInt(65534) + 1;
+ Os.connect(s, address, port);
+ Os.write(s, packet, 0, packet.length);
+
+ // Expect a reply.
+ StructPollfd pollfd = new StructPollfd();
+ pollfd.events = (short) POLLIN; // "error: possible loss of precision"
+ pollfd.fd = s;
+ int ret = Os.poll(new StructPollfd[] { pollfd }, SOCKET_TIMEOUT_MS);
+ assertEquals("Expected reply after sending ping", 1, ret);
+
+ byte[] reply = new byte[LENGTH];
+ int read = Os.read(s, reply, 0, LENGTH);
+ assertEquals(LENGTH, read);
+
+ // Find out what the kernel set the ICMP ID to.
+ InetSocketAddress local = (InetSocketAddress) Os.getsockname(s);
+ port = local.getPort();
+ packet[4] = (byte) ((port >> 8) & 0xff);
+ packet[5] = (byte) (port & 0xff);
+
+ // Check the contents.
+ if (packet[0] == (byte) 0x80) {
+ packet[0] = (byte) 0x81;
+ } else {
+ packet[0] = 0;
+ }
+ // Zero out the checksum in the reply so it matches the uninitialized checksum in packet.
+ reply[2] = reply[3] = 0;
+ MoreAsserts.assertEquals(packet, reply);
+ }
+
+ // Writes data to out and checks that it appears identically on in.
+ private static void writeAndCheckData(
+ OutputStream out, InputStream in, byte[] data) throws IOException {
+ out.write(data, 0, data.length);
+ out.flush();
+
+ byte[] read = new byte[data.length];
+ int bytesRead = 0, totalRead = 0;
+ do {
+ bytesRead = in.read(read, totalRead, read.length - totalRead);
+ totalRead += bytesRead;
+ } while (bytesRead >= 0 && totalRead < data.length);
+ assertEquals(totalRead, data.length);
+ MoreAsserts.assertEquals(data, read);
+ }
+
+ private static void checkTcpReflection(String to, String expectedFrom) throws IOException {
+ // Exercise TCP over the VPN by "connecting to ourselves". We open a server socket and a
+ // client socket, and connect the client socket to a remote host, with the port of the
+ // server socket. The PacketReflector reflects the packets, changing the source addresses
+ // but not the ports, so our client socket is connected to our server socket, though both
+ // sockets think their peers are on the "remote" IP address.
+
+ // Open a listening socket.
+ ServerSocket listen = new ServerSocket(0, 10, InetAddress.getByName("::"));
+
+ // Connect the client socket to it.
+ InetAddress toAddr = InetAddress.getByName(to);
+ Socket client = new Socket();
+ try {
+ client.connect(new InetSocketAddress(toAddr, listen.getLocalPort()), SOCKET_TIMEOUT_MS);
+ if (expectedFrom == null) {
+ closeQuietly(listen);
+ closeQuietly(client);
+ fail("Expected connection to fail, but it succeeded.");
+ }
+ } catch (IOException e) {
+ if (expectedFrom != null) {
+ closeQuietly(listen);
+ fail("Expected connection to succeed, but it failed.");
+ } else {
+ // We expected the connection to fail, and it did, so there's nothing more to test.
+ return;
+ }
+ }
+
+ // The connection succeeded, and we expected it to succeed. Send some data; if things are
+ // working, the data will be sent to the VPN, reflected by the PacketReflector, and arrive
+ // at our server socket. For good measure, send some data in the other direction.
+ Socket server = null;
+ try {
+ // Accept the connection on the server side.
+ listen.setSoTimeout(SOCKET_TIMEOUT_MS);
+ server = listen.accept();
+
+ // Check that the source and peer addresses are as expected.
+ assertEquals(expectedFrom, client.getLocalAddress().getHostAddress());
+ assertEquals(expectedFrom, server.getLocalAddress().getHostAddress());
+ assertEquals(
+ new InetSocketAddress(toAddr, client.getLocalPort()),
+ server.getRemoteSocketAddress());
+ assertEquals(
+ new InetSocketAddress(toAddr, server.getLocalPort()),
+ client.getRemoteSocketAddress());
+
+ // Now write some data.
+ final int LENGTH = 32768;
+ byte[] data = new byte[LENGTH];
+ new Random().nextBytes(data);
+
+ // Make sure our writes don't block or time out, because we're single-threaded and can't
+ // read and write at the same time.
+ server.setReceiveBufferSize(LENGTH * 2);
+ client.setSendBufferSize(LENGTH * 2);
+ client.setSoTimeout(SOCKET_TIMEOUT_MS);
+ server.setSoTimeout(SOCKET_TIMEOUT_MS);
+
+ // Send some data from client to server, then from server to client.
+ writeAndCheckData(client.getOutputStream(), server.getInputStream(), data);
+ writeAndCheckData(server.getOutputStream(), client.getInputStream(), data);
+ } finally {
+ closeQuietly(listen);
+ closeQuietly(client);
+ closeQuietly(server);
+ }
+ }
+
+ private static void checkUdpEcho(String to, String expectedFrom) throws IOException {
+ DatagramSocket s;
+ InetAddress address = InetAddress.getByName(to);
+ if (address instanceof Inet6Address) { // http://b/18094870
+ s = new DatagramSocket(0, InetAddress.getByName("::"));
+ } else {
+ s = new DatagramSocket();
+ }
+ s.setSoTimeout(SOCKET_TIMEOUT_MS);
+
+ Random random = new Random();
+ byte[] data = new byte[random.nextInt(1650)];
+ random.nextBytes(data);
+ DatagramPacket p = new DatagramPacket(data, data.length);
+ s.connect(address, 7);
+
+ if (expectedFrom != null) {
+ assertEquals("Unexpected source address: ",
+ expectedFrom, s.getLocalAddress().getHostAddress());
+ }
+
+ try {
+ if (expectedFrom != null) {
+ s.send(p);
+ s.receive(p);
+ MoreAsserts.assertEquals(data, p.getData());
+ } else {
+ try {
+ s.send(p);
+ s.receive(p);
+ fail("Received unexpected reply");
+ } catch(IOException expected) {}
+ }
+ } finally {
+ s.close();
+ }
+ }
+
+ private void checkTrafficOnVpn() throws IOException, ErrnoException {
+ checkUdpEcho("192.0.2.251", "192.0.2.2");
+ checkUdpEcho("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+ checkPing("2001:db8:dead:beef::f00");
+ checkTcpReflection("192.0.2.252", "192.0.2.2");
+ checkTcpReflection("2001:db8:dead:beef::f00", "2001:db8:1:2::ffe");
+ }
+
+ private void checkNoTrafficOnVpn() throws IOException, ErrnoException {
+ checkUdpEcho("192.0.2.251", null);
+ checkUdpEcho("2001:db8:dead:beef::f00", null);
+ checkTcpReflection("192.0.2.252", null);
+ checkTcpReflection("2001:db8:dead:beef::f00", null);
+ }
+
+ public void testDefault() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"0.0.0.0/0", "::/0"},
+ "", "");
+
+ checkTrafficOnVpn();
+ }
+
+ public void testAppAllowed() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ mPackageName, "");
+
+ checkTrafficOnVpn();
+ }
+
+ public void testAppDisallowed() throws Exception {
+ if (!supportedHardware()) return;
+
+ startVpn(new String[] {"192.0.2.2/32", "2001:db8:1:2::ffe/128"},
+ new String[] {"192.0.2.0/24", "2001:db8::/32"},
+ "", mPackageName);
+
+ checkNoTrafficOnVpn();
+ }
+}
diff --git a/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
new file mode 100644
index 0000000..a7698f3
--- /dev/null
+++ b/hostsidetests/net/src/com/android/cts/net/HostsideNetworkTests.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.ddmlib.testrunner.RemoteAndroidTestRunner;
+import com.android.ddmlib.testrunner.TestIdentifier;
+import com.android.ddmlib.testrunner.TestResult;
+import com.android.ddmlib.testrunner.TestResult.TestStatus;
+import com.android.ddmlib.testrunner.TestRunResult;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.result.CollectingTestListener;
+
+import java.util.Map;
+
+public class HostsideNetworkTests extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+ private static final String TEST_PKG = "com.android.cts.net.hostside";
+ private static final String TEST_APK = "CtsHostsideNetworkTestsApp.apk";
+
+ private IAbi mAbi;
+ private CtsBuildHelper mCtsBuild;
+
+ @Override
+ public void setAbi(IAbi abi) {
+ mAbi = abi;
+ }
+
+ @Override
+ public void setBuild(IBuildInfo buildInfo) {
+ mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ assertNotNull(mAbi);
+ assertNotNull(mCtsBuild);
+
+ getDevice().uninstallPackage(TEST_PKG);
+
+ assertNull(getDevice().installPackage(mCtsBuild.getTestApp(TEST_APK), false));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+
+ getDevice().uninstallPackage(TEST_PKG);
+ }
+
+ public void testVpn() throws Exception {
+ runDeviceTests(TEST_PKG, ".VpnTest");
+ }
+
+ public void runDeviceTests(String packageName, String testClassName)
+ throws DeviceNotAvailableException {
+ RemoteAndroidTestRunner testRunner = new RemoteAndroidTestRunner(packageName,
+ "android.support.test.runner.AndroidJUnitRunner", getDevice().getIDevice());
+
+ final CollectingTestListener listener = new CollectingTestListener();
+ getDevice().runInstrumentationTests(testRunner, listener);
+
+ final TestRunResult result = listener.getCurrentRunResults();
+ if (result.isRunFailure()) {
+ throw new AssertionError("Failed to successfully run device tests for "
+ + result.getName() + ": " + result.getRunFailureMessage());
+ }
+
+ if (result.hasFailedTests()) {
+ // build a meaningful error message
+ StringBuilder errorBuilder = new StringBuilder("on-device tests failed:\n");
+ for (Map.Entry<TestIdentifier, TestResult> resultEntry :
+ result.getTestResults().entrySet()) {
+ if (!resultEntry.getValue().getStatus().equals(TestStatus.PASSED)) {
+ errorBuilder.append(resultEntry.getKey().toString());
+ errorBuilder.append(":\n");
+ errorBuilder.append(resultEntry.getValue().getStackTrace());
+ }
+ }
+ throw new AssertionError(errorBuilder.toString());
+ }
+ }
+}
diff --git a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
index 96845b1..c93295a 100644
--- a/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/cts/security/SELinuxHostTest.java
@@ -77,8 +77,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);
}
/**
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/assets/22/400dpi.zip b/hostsidetests/theme/assets/22/400dpi.zip
new file mode 100644
index 0000000..6d62e5b
--- /dev/null
+++ b/hostsidetests/theme/assets/22/400dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/560dpi.zip b/hostsidetests/theme/assets/22/560dpi.zip
new file mode 100644
index 0000000..eff363c
--- /dev/null
+++ b/hostsidetests/theme/assets/22/560dpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/hdpi.zip b/hostsidetests/theme/assets/22/hdpi.zip
new file mode 100644
index 0000000..0fa67b7
--- /dev/null
+++ b/hostsidetests/theme/assets/22/hdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/ldpi.zip b/hostsidetests/theme/assets/22/ldpi.zip
new file mode 100644
index 0000000..e33f2f0
--- /dev/null
+++ b/hostsidetests/theme/assets/22/ldpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/mdpi.zip b/hostsidetests/theme/assets/22/mdpi.zip
new file mode 100644
index 0000000..f74739e
--- /dev/null
+++ b/hostsidetests/theme/assets/22/mdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/tvdpi.zip b/hostsidetests/theme/assets/22/tvdpi.zip
new file mode 100644
index 0000000..fbe1781
--- /dev/null
+++ b/hostsidetests/theme/assets/22/tvdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xhdpi.zip b/hostsidetests/theme/assets/22/xhdpi.zip
new file mode 100644
index 0000000..de6e2e1
--- /dev/null
+++ b/hostsidetests/theme/assets/22/xhdpi.zip
Binary files differ
diff --git a/hostsidetests/theme/assets/22/xxhdpi.zip b/hostsidetests/theme/assets/22/xxhdpi.zip
new file mode 100644
index 0000000..9f0d778
--- /dev/null
+++ b/hostsidetests/theme/assets/22/xxhdpi.zip
Binary files differ
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/deviceutil/src/android/cts/util/MediaUtils.java b/libs/deviceutil/src/android/cts/util/MediaUtils.java
old mode 100644
new mode 100755
index eab4808..5908923
--- a/libs/deviceutil/src/android/cts/util/MediaUtils.java
+++ b/libs/deviceutil/src/android/cts/util/MediaUtils.java
@@ -17,6 +17,7 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
@@ -37,14 +38,13 @@
private static final MediaCodecList sMCL = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
-
/**
- * Finds test name (heuristically) and prints out standard skip message.
+ * Returns the test name (heuristically).
*
* Since it uses heuristics, this method has only been verified for media
- * tests. This centralizes the way to signal a skipped test.
+ * tests. This centralizes the way to signal errors during a test.
*/
- public static void skipTest(String tag, String reason) {
+ public static String getTestName() {
int bestScore = -1;
String testName = "test???";
Map<Thread, StackTraceElement[]> traces = Thread.getAllStackTraces();
@@ -110,8 +110,17 @@
}
}
}
+ return testName;
+ }
- Log.i(tag, "SKIPPING " + testName + "(): " + reason);
+ /**
+ * Finds test name (heuristically) and prints out standard skip message.
+ *
+ * Since it uses heuristics, this method has only been verified for media
+ * tests. This centralizes the way to signal a skipped test.
+ */
+ public static void skipTest(String tag, String reason) {
+ Log.i(tag, "SKIPPING " + getTestName() + "(): " + reason);
}
/**
@@ -131,6 +140,17 @@
return result;
}
+ public static MediaCodec getDecoder(MediaFormat format) {
+ String decoder = sMCL.findDecoderForFormat(format);
+ if (decoder != null) {
+ try {
+ return MediaCodec.createByCodecName(decoder);
+ } catch (IOException e) {
+ }
+ }
+ return null;
+ }
+
public static boolean canDecode(MediaFormat format) {
if (sMCL.findDecoderForFormat(format) == null) {
Log.i(TAG, "no decoder for " + format);
diff --git a/suite/cts/deviceTests/tvproviderperf/Android.mk b/suite/cts/deviceTests/tvproviderperf/Android.mk
new file mode 100644
index 0000000..e268955
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/Android.mk
@@ -0,0 +1,30 @@
+# 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
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsDeviceTvProviderPerf
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+
diff --git a/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml b/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml
new file mode 100644
index 0000000..d345ab2
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/AndroidManifest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.tvproviderperf">
+
+ <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+ <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+ <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.tvproviderperf"
+ android:label="TvProvider performance measurement" />
+</manifest>
diff --git a/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
new file mode 100644
index 0000000..f9daa3c
--- /dev/null
+++ b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.tvproviderperf;
+
+import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.content.OperationApplicationException;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.cts.util.CtsAndroidTestCase;
+import android.media.tv.TvContract;
+import android.media.tv.TvContract.Channels;
+import android.media.tv.TvContract.Programs;
+import android.net.Uri;
+import android.os.RemoteException;
+
+import com.android.cts.util.MeasureRun;
+import com.android.cts.util.MeasureTime;
+import com.android.cts.util.ResultType;
+import com.android.cts.util.ResultUnit;
+import com.android.cts.util.ReportLog;
+import com.android.cts.util.TimeoutReq;
+import com.android.cts.util.Stat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test performance of TvProvider on a device. TvProvider typically handles hundreds of
+ * thousands of records periodically, so it is desirable to have performance under a reasonable
+ * bar.
+ */
+public class TvProviderPerfTest extends CtsAndroidTestCase {
+ private static final int TRANSACTION_RUNS = 100;
+ private static final int QUERY_RUNS = 10;
+
+ private ContentResolver mContentResolver;
+ private String mInputId;
+ private boolean mHasTvInputFramework;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHasTvInputFramework = getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_LIVE_TV);
+ if (!mHasTvInputFramework) return;
+ mContentResolver = getContext().getContentResolver();
+ mInputId = TvContract.buildInputId(new ComponentName(getContext(), getClass()));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ if (!mHasTvInputFramework) return;
+ mContentResolver.delete(Programs.CONTENT_URI, null, null);
+ mContentResolver.delete(Channels.CONTENT_URI, null, null);
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ @TimeoutReq(minutes = 8)
+ public void testChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ double[] averages = new double[5];
+
+ // Insert
+ final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ final int TRANSACTION_SIZE = 1000;
+ double[] applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ operations.clear();
+ for (int j = 0; j < TRANSACTION_SIZE; ++j) {
+ ContentValues values = new ContentValues();
+ values.put(Channels.COLUMN_INPUT_ID, mInputId);
+ values.put(Channels.COLUMN_SERVICE_TYPE,
+ Channels.SERVICE_TYPE_AUDIO_VIDEO);
+ values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER);
+ operations.add(
+ ContentProviderOperation.newInsert(Channels.CONTENT_URI)
+ .withValues(values).build());
+ }
+ try {
+ mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (OperationApplicationException | RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for insert: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[0] = Stat.getAverage(applyBatchTimes);
+
+ // Update
+ final String[] projection = { Channels._ID };
+ try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
+ projection, null, null, null)) {
+ applyBatchTimes = MeasureTime.measure(TRANSACTION_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ operations.clear();
+ for (int j = 0; j < TRANSACTION_SIZE && cursor.moveToNext(); ++j) {
+ Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
+ String number = Integer.toString(i * TRANSACTION_SIZE + j);
+ operations.add(
+ ContentProviderOperation.newUpdate(channelUri)
+ .withValue(Channels.COLUMN_DISPLAY_NUMBER, number)
+ .build());
+ }
+ try {
+ mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (OperationApplicationException | RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ }
+ getReportLog().printArray("Elapsed time for update: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[1] = Stat.getAverage(applyBatchTimes);
+
+ // Query channels
+ applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ try (Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, null, null,
+ null, null)) {
+ while (cursor.moveToNext()) {
+ // Do nothing. Just iterate all the items.
+ }
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for query (channels): ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[2] = Stat.getAverage(applyBatchTimes);
+
+ // Query a channel
+ try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
+ projection, null, null, null)) {
+ final Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
+ applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ assertTrue(cursor.moveToNext());
+ try (Cursor c = mContentResolver.query(channelUri, null, null, null, null)) {
+ while (c.moveToNext()) {
+ // Do nothing. Just iterate all the items.
+ }
+ }
+ }
+ });
+ }
+ getReportLog().printArray("Elapsed time for query (a channel): ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[3] = Stat.getAverage(applyBatchTimes);
+
+ // Delete
+ applyBatchTimes = MeasureTime.measure(1, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ mContentResolver.delete(TvContract.buildChannelsUriForInput(mInputId), null, null);
+ }
+ });
+ getReportLog().printArray("Elapsed time for delete: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[4] = Stat.getAverage(applyBatchTimes);
+
+ getReportLog().printArray("Average elapsed time for insert, update, query (channels), "
+ + "query (a channel), delete: ",
+ averages, ResultType.LOWER_BETTER, ResultUnit.MS);
+ }
+
+ @TimeoutReq(minutes = 12)
+ public void testPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ double[] averages = new double[7];
+
+ // Prepare (insert channels)
+ final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
+ final int TRANSACTION_SIZE = 1000;
+ final int NUM_CHANNELS = 100;
+ final List<Uri> channelUris = new ArrayList<>();
+
+ operations.clear();
+ for (int i = 0; i < NUM_CHANNELS; ++i) {
+ ContentValues values = new ContentValues();
+ values.put(Channels.COLUMN_INPUT_ID, mInputId);
+ values.put(Channels.COLUMN_SERVICE_TYPE,
+ Channels.SERVICE_TYPE_AUDIO_VIDEO);
+ values.put(Channels.COLUMN_TYPE, Channels.TYPE_OTHER);
+ operations.add(
+ ContentProviderOperation.newInsert(Channels.CONTENT_URI)
+ .withValues(values).build());
+ }
+ try {
+ ContentProviderResult[] results =
+ mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ for (ContentProviderResult result : results) {
+ channelUris.add(result.uri);
+ }
+ } catch (OperationApplicationException | RemoteException e) {
+ throw new RuntimeException(e);
+ }
+
+ // Insert
+ double[] applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ operations.clear();
+ Uri channelUri = channelUris.get(i);
+ long channelId = ContentUris.parseId(channelUri);
+ for (int j = 0; j < TRANSACTION_SIZE; ++j) {
+ ContentValues values = new ContentValues();
+ values.put(Programs.COLUMN_CHANNEL_ID, channelId);
+ operations.add(
+ ContentProviderOperation.newInsert(Programs.CONTENT_URI)
+ .withValues(values).build());
+ }
+ try {
+ mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (OperationApplicationException | RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for insert: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[0] = Stat.getAverage(applyBatchTimes);
+
+ // Update
+ final long PROGRAM_DURATION_MS = 60 * 1000;
+ final String[] projection = { Programs._ID };
+ applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ Uri channelUri = channelUris.get(i);
+ operations.clear();
+ try (Cursor cursor = mContentResolver.query(
+ TvContract.buildProgramsUriForChannel(channelUri),
+ projection, null, null, null)) {
+ long startTimeMs = 0;
+ long endTimeMs = 0;
+ while (cursor.moveToNext()) {
+ Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
+ endTimeMs += PROGRAM_DURATION_MS;
+ operations.add(
+ ContentProviderOperation.newUpdate(programUri)
+ .withValue(Programs.COLUMN_START_TIME_UTC_MILLIS, startTimeMs)
+ .withValue(Programs.COLUMN_END_TIME_UTC_MILLIS, endTimeMs)
+ .build());
+ startTimeMs = endTimeMs;
+ }
+ }
+ try {
+ mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+ } catch (OperationApplicationException | RemoteException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for update: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[1] = Stat.getAverage(applyBatchTimes);
+
+ // Query programs
+ applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ try (Cursor cursor = mContentResolver.query(Programs.CONTENT_URI, null, null,
+ null, null)) {
+ while (cursor.moveToNext()) {
+ // Do nothing. Just iterate all the items.
+ }
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for query (programs): ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[2] = Stat.getAverage(applyBatchTimes);
+
+ // Query programs with selection
+ applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ Uri channelUri = channelUris.get(i);
+ try (Cursor cursor = mContentResolver.query(
+ TvContract.buildProgramsUriForChannel(
+ channelUri, 0,
+ PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2),
+ null, null, null, null)) {
+ while (cursor.moveToNext()) {
+ // Do nothing. Just iterate all the items.
+ }
+ }
+ }
+ });
+ getReportLog().printArray("Elapsed time for query (programs with selection): ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[3] = Stat.getAverage(applyBatchTimes);
+
+ // Query a program
+ try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI,
+ projection, null, null, null)) {
+ final Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
+ applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ assertTrue(cursor.moveToNext());
+ try (Cursor c = mContentResolver.query(programUri, null, null, null, null)) {
+ while (c.moveToNext()) {
+ // Do nothing. Just iterate all the items.
+ }
+ }
+ }
+ });
+ }
+ getReportLog().printArray("Elapsed time for query (a program): ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[4] = Stat.getAverage(applyBatchTimes);
+
+ // Delete programs
+ applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ Uri channelUri = channelUris.get(i);
+ mContentResolver.delete(
+ TvContract.buildProgramsUriForChannel(
+ channelUri,
+ PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2,
+ PROGRAM_DURATION_MS * TRANSACTION_SIZE),
+ null, null);
+ }
+ });
+ getReportLog().printArray("Elapsed time for delete programs: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[5] = Stat.getAverage(applyBatchTimes);
+
+ // Delete channels
+ applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
+ @Override
+ public void run(int i) {
+ Uri channelUri = channelUris.get(i);
+ mContentResolver.delete(channelUri, null, null);
+ }
+ });
+ getReportLog().printArray("Elapsed time for delete channels: ",
+ applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+ averages[6] = Stat.getAverage(applyBatchTimes);
+
+ getReportLog().printArray("Average elapsed time for insert, update, query (programs), "
+ + "query (programs with selection), query (a channel), delete (channels), "
+ + "delete (programs): ",
+ averages, ResultType.LOWER_BETTER, ResultUnit.MS);
+ }
+}
diff --git a/suite/cts/deviceTests/videoperf/Android.mk b/suite/cts/deviceTests/videoperf/Android.mk
index cd82dde..a393683 100644
--- a/suite/cts/deviceTests/videoperf/Android.mk
+++ b/suite/cts/deviceTests/videoperf/Android.mk
@@ -17,8 +17,15 @@
# don't include this package in any target
LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+# include both the 32 and 64 bit versions
+LOCAL_MULTILIB := both
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctsmediautil ctsdeviceutil ctstestrunner
+
+LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni
LOCAL_SRC_FILES := $(call all-java-files-under, src)
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
index b7d1d27..462794b 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/CodecInfo.java
@@ -66,7 +66,12 @@
break;
}
}
- VideoCapabilities vidCap = cap.getVideoCapabilities();
+
+ if (cap.colorFormats.length == 0) {
+ Log.w(TAG, "no supported color format");
+ return null;
+ }
+
CodecInfo info = new CodecInfo();
for (int color : cap.colorFormats) {
if (color == CodecCapabilities.COLOR_FormatYUV420SemiPlanar) {
@@ -77,15 +82,25 @@
}
}
printIntArray("supported colors", cap.colorFormats);
- // either YUV420 planar or semiplanar should be supported
- if (!info.mSupportPlanar && !info.mSupportSemiPlanar) {
- Log.i(TAG, "no supported color format");
- return null;
- }
+ VideoCapabilities vidCap = cap.getVideoCapabilities();
if (mimeType.equals(VIDEO_AVC)) {
info.mFps = vidCap.getSupportedFrameRatesFor(w, h).getUpper().intValue();
info.mBitRate = vidCap.getBitrateRange().getUpper();
+
+ // we don't parse bitrate-range on L, so need to adjust bitrate to supported
+ // baseline or main profiles only.
+ int maxBitRate = 0;
+ for (CodecProfileLevel pl : cap.profileLevels) {
+ if (pl.profile == pl.AVCProfileBaseline || pl.profile == pl.AVCProfileMain) {
+ VideoCapabilities vidCapPL =
+ CodecCapabilities.createFromProfileLevel(mimeType, pl.profile, pl.level)
+ .getVideoCapabilities();
+ maxBitRate = Math.max(maxBitRate, vidCapPL.getBitrateRange().getUpper());
+ }
+ }
+ info.mBitRate = Math.min(info.mBitRate, maxBitRate);
+
Log.i(TAG, "AVC bit rate " + info.mBitRate + " fps " + info.mFps);
}
return info;
diff --git a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
index 5e76de9..643f773 100644
--- a/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
+++ b/suite/cts/deviceTests/videoperf/src/com/android/cts/videoperf/VideoEncoderDecoderTest.java
@@ -16,7 +16,12 @@
package com.android.cts.videoperf;
+import android.graphics.ImageFormat;
import android.graphics.Point;
+import android.media.cts.CodecImage;
+import android.media.cts.CodecUtils;
+import android.media.Image;
+import android.media.Image.Plane;
import android.media.MediaCodec;
import android.media.MediaCodecList;
import android.media.MediaCodecInfo.CodecCapabilities;
@@ -60,12 +65,10 @@
private static final int Y_CLAMP_MIN = 16;
private static final int Y_CLAMP_MAX = 235;
private static final int YUV_PLANE_ADDITIONAL_LENGTH = 200;
- private ByteBuffer mYBuffer;
- private ByteBuffer mUVBuffer;
- // if input raw data is semi-planar
- private boolean mSrcSemiPlanar;
- // if output raw data is semi-planar
- private boolean mDstSemiPlanar;
+ private ByteBuffer mYBuffer, mYDirectBuffer;
+ private ByteBuffer mUVBuffer, mUVDirectBuffer;
+ private int mSrcColorFormat;
+ private int mDstColorFormat;
private int mBufferWidth;
private int mBufferHeight;
private int mVideoWidth;
@@ -95,6 +98,8 @@
mEncodedOutputBuffer = null;
mYBuffer = null;
mUVBuffer = null;
+ mYDirectBuffer = null;
+ mUVDirectBuffer = null;
mRandom = null;
super.tearDown();
}
@@ -124,6 +129,33 @@
doTest(VIDEO_AVC, 1920, 1072, NUMBER_OF_REPEAT);
}
+ private boolean isSrcSemiPlanar() {
+ return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+ }
+
+ private boolean isSrcFlexYUV() {
+ return mSrcColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
+ }
+
+ private boolean isDstSemiPlanar() {
+ return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+ }
+
+ private boolean isDstFlexYUV() {
+ return mDstColorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible;
+ }
+
+ private static int getColorFormat(CodecInfo info) {
+ if (info.mSupportSemiPlanar) {
+ return CodecCapabilities.COLOR_FormatYUV420SemiPlanar;
+ } else if (info.mSupportPlanar) {
+ return CodecCapabilities.COLOR_FormatYUV420Planar;
+ } else {
+ // FlexYUV must be supported
+ return CodecCapabilities.COLOR_FormatYUV420Flexible;
+ }
+ }
+
/**
* Run encoding / decoding test for given mimeType of codec
* @param mimeType like video/avc
@@ -144,8 +176,14 @@
}
mVideoWidth = w;
mVideoHeight = h;
- initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH,
- infoEnc.mSupportSemiPlanar, infoDec.mSupportSemiPlanar);
+
+ mSrcColorFormat = getColorFormat(infoEnc);
+ mDstColorFormat = getColorFormat(infoDec);
+ Log.i(TAG, "Testing video resolution " + w + "x" + h +
+ ": enc format " + mSrcColorFormat +
+ ", dec format " + mDstColorFormat);
+
+ initYUVPlane(w + YUV_PLANE_ADDITIONAL_LENGTH, h + YUV_PLANE_ADDITIONAL_LENGTH);
double[] encoderFpsResults = new double[numberRepeat];
double[] decoderFpsResults = new double[numberRepeat];
double[] totalFpsResults = new double[numberRepeat];
@@ -157,9 +195,7 @@
format.setInteger(MediaFormat.KEY_BIT_RATE, infoEnc.mBitRate);
format.setInteger(MediaFormat.KEY_WIDTH, w);
format.setInteger(MediaFormat.KEY_HEIGHT, h);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- infoEnc.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
- CodecCapabilities.COLOR_FormatYUV420Planar);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mSrcColorFormat);
format.setInteger(MediaFormat.KEY_FRAME_RATE, infoEnc.mFps);
mFrameRate = infoEnc.mFps;
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, KEY_I_FRAME_INTERVAL);
@@ -169,9 +205,7 @@
format.setString(MediaFormat.KEY_MIME, mimeType);
format.setInteger(MediaFormat.KEY_WIDTH, w);
format.setInteger(MediaFormat.KEY_HEIGHT, h);
- format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- infoDec.mSupportSemiPlanar ? CodecCapabilities.COLOR_FormatYUV420SemiPlanar :
- CodecCapabilities.COLOR_FormatYUV420Planar);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, mDstColorFormat);
double[] decoderResult = runDecoder(VIDEO_AVC, format);
if (decoderResult == null) {
success = false;
@@ -231,7 +265,6 @@
return Double.NaN;
}
codec.start();
- ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
int numBytesSubmitted = 0;
@@ -244,10 +277,24 @@
if (inFramesCount < totalFrames) {
index = codec.dequeueInputBuffer(VIDEO_CODEC_WAIT_TIME_US /* timeoutUs */);
if (index != MediaCodec.INFO_TRY_AGAIN_LATER) {
- int size = queueInputBufferEncoder(
- codec, codecInputBuffers, index, inFramesCount,
- (inFramesCount == (totalFrames - 1)) ?
- MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ int size;
+ // when encoder only supports flexYUV, use Image only; otherwise,
+ // use ByteBuffer & Image each on half of the frames to test both
+ if (isSrcFlexYUV() || inFramesCount % 2 == 0) {
+ Image image = codec.getInputImage(index);
+ // image should always be available
+ assertTrue(image != null);
+ size = queueInputImageEncoder(
+ codec, image, index, inFramesCount,
+ (inFramesCount == (totalFrames - 1)) ?
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ } else {
+ ByteBuffer buffer = codec.getInputBuffer(index);
+ size = queueInputBufferEncoder(
+ codec, buffer, index, inFramesCount,
+ (inFramesCount == (totalFrames - 1)) ?
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM : 0);
+ }
inFramesCount++;
numBytesSubmitted += size;
if (VERBOSE) {
@@ -293,8 +340,7 @@
* @return size of enqueued data.
*/
private int queueInputBufferEncoder(
- MediaCodec codec, ByteBuffer[] inputBuffers, int index, int frameCount, int flags) {
- ByteBuffer buffer = inputBuffers[index];
+ MediaCodec codec, ByteBuffer buffer, int index, int frameCount, int flags) {
buffer.clear();
Point origin = getOrigin(frameCount);
@@ -305,7 +351,7 @@
buffer.put(yBuffer, srcOffsetY, mVideoWidth);
srcOffsetY += mBufferWidth;
}
- if (mSrcSemiPlanar) {
+ if (isSrcSemiPlanar()) {
int srcOffsetU = origin.y / 2 * mBufferWidth + origin.x / 2 * 2;
final byte[] uvBuffer = mUVBuffer.array();
for (int i = 0; i < mVideoHeight / 2; i++) {
@@ -316,16 +362,161 @@
int srcOffsetU = origin.y / 2 * mBufferWidth / 2 + origin.x / 2;
int srcOffsetV = srcOffsetU + mBufferWidth / 2 * mBufferHeight / 2;
final byte[] uvBuffer = mUVBuffer.array();
- for (int i = 0; i < mVideoHeight /2; i++) { //U only
+ for (int i = 0; i < mVideoHeight / 2; i++) { //U only
buffer.put(uvBuffer, srcOffsetU, mVideoWidth / 2);
srcOffsetU += mBufferWidth / 2;
}
- for (int i = 0; i < mVideoHeight /2; i++) { //V only
+ for (int i = 0; i < mVideoHeight / 2; i++) { //V only
buffer.put(uvBuffer, srcOffsetV, mVideoWidth / 2);
srcOffsetV += mBufferWidth / 2;
}
}
- int size = mVideoHeight * mVideoWidth * 3 /2;
+ int size = mVideoHeight * mVideoWidth * 3 / 2;
+ long ptsUsec = computePresentationTime(frameCount);
+
+ codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
+ if (VERBOSE && (frameCount == 0)) {
+ printByteArray("Y ", mYBuffer.array(), 0, 20);
+ printByteArray("UV ", mUVBuffer.array(), 0, 20);
+ printByteArray("UV ", mUVBuffer.array(), mBufferWidth * 60, 20);
+ }
+ return size;
+ }
+
+ class YUVImage extends CodecImage {
+ private final int mImageWidth;
+ private final int mImageHeight;
+ private final Plane[] mPlanes;
+
+ YUVImage(
+ Point origin,
+ int imageWidth, int imageHeight,
+ int arrayWidth, int arrayHeight,
+ boolean semiPlanar,
+ ByteBuffer bufferY, ByteBuffer bufferUV) {
+ mImageWidth = imageWidth;
+ mImageHeight = imageHeight;
+ ByteBuffer dupY = bufferY.duplicate();
+ ByteBuffer dupUV = bufferUV.duplicate();
+ mPlanes = new Plane[3];
+
+ int srcOffsetY = origin.x + origin.y * arrayWidth;
+
+ mPlanes[0] = new YUVPlane(
+ mImageWidth, mImageHeight, arrayWidth, 1,
+ dupY, srcOffsetY);
+
+ if (semiPlanar) {
+ int srcOffsetUV = origin.y / 2 * arrayWidth + origin.x / 2 * 2;
+
+ mPlanes[1] = new YUVPlane(
+ mImageWidth / 2, mImageHeight / 2, arrayWidth, 2,
+ dupUV, srcOffsetUV);
+ mPlanes[2] = new YUVPlane(
+ mImageWidth / 2, mImageHeight / 2, arrayWidth, 2,
+ dupUV, srcOffsetUV + 1);
+ } else {
+ int srcOffsetU = origin.y / 2 * arrayWidth / 2 + origin.x / 2;
+ int srcOffsetV = srcOffsetU + arrayWidth / 2 * arrayHeight / 2;
+
+ mPlanes[1] = new YUVPlane(
+ mImageWidth / 2, mImageHeight / 2, arrayWidth / 2, 1,
+ dupUV, srcOffsetU);
+ mPlanes[2] = new YUVPlane(
+ mImageWidth / 2, mImageHeight / 2, arrayWidth / 2, 1,
+ dupUV, srcOffsetV);
+ }
+ }
+
+ @Override
+ public int getFormat() {
+ return ImageFormat.YUV_420_888;
+ }
+
+ @Override
+ public int getWidth() {
+ return mImageWidth;
+ }
+
+ @Override
+ public int getHeight() {
+ return mImageHeight;
+ }
+
+ @Override
+ public long getTimestamp() {
+ return 0;
+ }
+
+ @Override
+ public Plane[] getPlanes() {
+ return mPlanes;
+ }
+
+ @Override
+ public void close() {
+ mPlanes[0] = null;
+ mPlanes[1] = null;
+ mPlanes[2] = null;
+ }
+
+ class YUVPlane extends CodecImage.Plane {
+ private final int mRowStride;
+ private final int mPixelStride;
+ private final ByteBuffer mByteBuffer;
+
+ YUVPlane(int w, int h, int rowStride, int pixelStride,
+ ByteBuffer buffer, int offset) {
+ mRowStride = rowStride;
+ mPixelStride = pixelStride;
+
+ // only safe to access length bytes starting from buffer[offset]
+ int length = (h - 1) * rowStride + (w - 1) * pixelStride + 1;
+
+ buffer.position(offset);
+ mByteBuffer = buffer.slice();
+ mByteBuffer.limit(length);
+ }
+
+ @Override
+ public int getRowStride() {
+ return mRowStride;
+ }
+
+ @Override
+ public int getPixelStride() {
+ return mPixelStride;
+ }
+
+ @Override
+ public ByteBuffer getBuffer() {
+ return mByteBuffer;
+ }
+ }
+ }
+
+ /**
+ * Fills input image for encoder from YUV buffers.
+ * @return size of enqueued data.
+ */
+ private int queueInputImageEncoder(
+ MediaCodec codec, Image image, int index, int frameCount, int flags) {
+ assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+
+
+ Point origin = getOrigin(frameCount);
+
+ // Y color first
+ CodecImage srcImage = new YUVImage(
+ origin,
+ mVideoWidth, mVideoHeight,
+ mBufferWidth, mBufferHeight,
+ isSrcSemiPlanar(),
+ mYDirectBuffer, mUVDirectBuffer);
+
+ CodecUtils.copyFlexYUVImage(image, srcImage);
+
+ int size = mVideoHeight * mVideoWidth * 3 / 2;
long ptsUsec = computePresentationTime(frameCount);
codec.queueInputBuffer(index, 0 /* offset */, size, ptsUsec /* timeUs */, flags);
@@ -371,7 +562,6 @@
codec.configure(format, null /* surface */, null /* crypto */, 0 /* flags */);
codec.start();
ByteBuffer[] codecInputBuffers = codec.getInputBuffers();
- ByteBuffer[] codecOutputBuffers = codec.getOutputBuffers();
double totalErrorSquared = 0;
@@ -410,22 +600,49 @@
// only do YUV compare on EOS frame if the buffer size is none-zero
if (info.size > 0) {
- ByteBuffer buf = codecOutputBuffers[outputBufIndex];
- if (VERBOSE && (outFrameCount == 0)) {
- printByteBuffer("Y ", buf, 0, 20);
- printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
- printByteBuffer("UV ", buf,
- mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
- }
Point origin = getOrigin(outFrameCount);
- for (int i = 0; i < PIXEL_CHECK_PER_FRAME; i++) {
+ int i;
+
+ // if decoder supports planar or semiplanar, check output with
+ // ByteBuffer & Image each on half of the points
+ int pixelCheckPerFrame = PIXEL_CHECK_PER_FRAME;
+ if (!isDstFlexYUV()) {
+ pixelCheckPerFrame /= 2;
+ ByteBuffer buf = codec.getOutputBuffer(outputBufIndex);
+ if (VERBOSE && (outFrameCount == 0)) {
+ printByteBuffer("Y ", buf, 0, 20);
+ printByteBuffer("UV ", buf, mVideoWidth * mVideoHeight, 20);
+ printByteBuffer("UV ", buf,
+ mVideoWidth * mVideoHeight + mVideoWidth * 60, 20);
+ }
+ for (i = 0; i < pixelCheckPerFrame; i++) {
+ int w = mRandom.nextInt(mVideoWidth);
+ int h = mRandom.nextInt(mVideoHeight);
+ getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
+ getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+ if (VERBOSE) {
+ Log.i(TAG, outFrameCount + "-" + i + "- th round: ByteBuffer:"
+ + " expected "
+ + expected.mY + "," + expected.mU + "," + expected.mV
+ + " decoded "
+ + decoded.mY + "," + decoded.mU + "," + decoded.mV);
+ }
+ totalErrorSquared += expected.calcErrorSquared(decoded);
+ }
+ }
+
+ Image image = codec.getOutputImage(outputBufIndex);
+ assertTrue(image != null);
+ for (i = 0; i < pixelCheckPerFrame; i++) {
int w = mRandom.nextInt(mVideoWidth);
int h = mRandom.nextInt(mVideoHeight);
getPixelValuesFromYUVBuffers(origin.x, origin.y, w, h, expected);
- getPixelValuesFromOutputBuffer(buf, w, h, decoded);
+ getPixelValuesFromImage(image, w, h, decoded);
if (VERBOSE) {
- Log.i(TAG, outFrameCount + "-" + i + "- th round expcted " + expected.mY
- + "," + expected.mU + "," + expected.mV + " decoded "
+ Log.i(TAG, outFrameCount + "-" + i + "- th round: FlexYUV:"
+ + " expcted "
+ + expected.mY + "," + expected.mU + "," + expected.mV
+ + " decoded "
+ decoded.mY + "," + decoded.mU + "," + decoded.mV);
}
totalErrorSquared += expected.calcErrorSquared(decoded);
@@ -437,23 +654,17 @@
Log.d(TAG, "saw output EOS.");
sawOutputEOS = true;
}
- } else if (res == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
- codecOutputBuffers = codec.getOutputBuffers();
- Log.d(TAG, "output buffers have changed.");
} else if (res == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat oformat = codec.getOutputFormat();
Log.d(TAG, "output format has changed to " + oformat);
int colorFormat = oformat.getInteger(MediaFormat.KEY_COLOR_FORMAT);
- if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar ) {
- mDstSemiPlanar = true;
- } else if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar ) {
- mDstSemiPlanar = false;
+ if (colorFormat == CodecCapabilities.COLOR_FormatYUV420SemiPlanar
+ || colorFormat == CodecCapabilities.COLOR_FormatYUV420Planar) {
+ mDstColorFormat = colorFormat;
} else {
+ mDstColorFormat = CodecCapabilities.COLOR_FormatYUV420Flexible;
Log.w(TAG, "output format changed to unsupported one " +
- Integer.toHexString(colorFormat));
- // give up and return as nothing can be done
- codec.release();
- return null;
+ Integer.toHexString(colorFormat) + ", using FlexYUV");
}
}
}
@@ -494,12 +705,12 @@
* @param semiPlanarEnc
* @param semiPlanarDec
*/
- private void initYUVPlane(int w, int h, boolean semiPlanarEnc, boolean semiPlanarDec) {
+ private void initYUVPlane(int w, int h) {
int bufferSizeY = w * h;
mYBuffer = ByteBuffer.allocate(bufferSizeY);
mUVBuffer = ByteBuffer.allocate(bufferSizeY / 2);
- mSrcSemiPlanar = semiPlanarEnc;
- mDstSemiPlanar = semiPlanarDec;
+ mYDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY);
+ mUVDirectBuffer = ByteBuffer.allocateDirect(bufferSizeY / 2);
mBufferWidth = w;
mBufferHeight = h;
final byte[] yArray = mYBuffer.array();
@@ -509,7 +720,7 @@
yArray[i * w + j] = clampY((i + j) & 0xff);
}
}
- if (semiPlanarEnc) {
+ if (isSrcSemiPlanar()) {
for (int i = 0; i < h/2; i++) {
for (int j = 0; j < w/2; j++) {
uvArray[i * w + 2 * j] = (byte) (i & 0xff);
@@ -525,6 +736,10 @@
}
}
}
+ mYDirectBuffer.put(yArray);
+ mUVDirectBuffer.put(uvArray);
+ mYDirectBuffer.rewind();
+ mUVDirectBuffer.rewind();
}
/**
@@ -559,7 +774,7 @@
private void getPixelValuesFromYUVBuffers(int originX, int originY, int x, int y,
YUVValue result) {
result.mY = mYBuffer.get((originY + y) * mBufferWidth + (originX + x));
- if (mSrcSemiPlanar) {
+ if (isSrcSemiPlanar()) {
int index = (originY + y) / 2 * mBufferWidth + (originX + x) / 2 * 2;
//Log.d(TAG, "YUV " + originX + "," + originY + "," + x + "," + y + "," + index);
result.mU = mUVBuffer.get(index);
@@ -580,7 +795,7 @@
*/
private void getPixelValuesFromOutputBuffer(ByteBuffer buffer, int x, int y, YUVValue result) {
result.mY = buffer.get(y * mVideoWidth + x);
- if (mDstSemiPlanar) {
+ if (isDstSemiPlanar()) {
int index = mVideoWidth * mVideoHeight + y / 2 * mVideoWidth + x / 2 * 2;
//Log.d(TAG, "Decoded " + x + "," + y + "," + index);
result.mU = buffer.get(index);
@@ -593,6 +808,22 @@
}
}
+ private void getPixelValuesFromImage(Image image, int x, int y, YUVValue result) {
+ assertTrue(image.getFormat() == ImageFormat.YUV_420_888);
+
+ Plane[] planes = image.getPlanes();
+ assertTrue(planes.length == 3);
+
+ result.mY = getPixelFromPlane(planes[0], x, y);
+ result.mU = getPixelFromPlane(planes[1], x / 2, y / 2);
+ result.mV = getPixelFromPlane(planes[2], x / 2, y / 2);
+ }
+
+ private byte getPixelFromPlane(Plane plane, int x, int y) {
+ ByteBuffer buf = plane.getBuffer();
+ return buf.get(y * plane.getRowStride() + x * plane.getPixelStride());
+ }
+
/**
* Y cannot have full range. clamp it to prevent invalid value.
*/
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
index a83f7a9..547b205 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java
@@ -51,6 +51,8 @@
private boolean mHasWifi;
/** Whether the device running these tests supports telephony. */
private boolean mHasTelephony;
+ /** Track whether WiFi was enabled in case we turn it off. */
+ private boolean mInitialWiFiState;
private JobInfo.Builder mBuilder;
@@ -67,6 +69,14 @@
mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY);
mBuilder =
new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent);
+
+ mInitialWiFiState = mWifiManager.isWifiEnabled();
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ // Ensure that we leave WiFi in its previous state.
+ mWifiManager.setWifiEnabled(mInitialWiFiState);
}
// --------------------------------------------------------------------------------------------
@@ -202,6 +212,14 @@
}
}
+ /**
+ * Disconnect from WiFi in an attempt to connect to cellular data. Worth noting that this is
+ * best effort - there are no public APIs to force connecting to cell data. We disable WiFi
+ * and wait for a broadcast that we're connected to cell.
+ * We will not call into this function if the device doesn't support telephony.
+ * @see #mHasTelephony
+ * @see #checkDeviceSupportsMobileData()
+ */
private void disconnectWifiToConnectToMobile() throws InterruptedException {
if (mHasWifi && mWifiManager.isWifiEnabled()) {
ConnectivityActionReceiver connectMobileReceiver =
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
index 080c08e..7564f32 100644
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -29,11 +29,16 @@
import org.junit.runner.notification.RunListener;
import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.lang.Class;
+import java.lang.ReflectiveOperationException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.net.Authenticator;
import java.net.CookieHandler;
import java.net.ResponseCache;
+import java.text.DateFormat;
import java.util.Locale;
import java.util.Properties;
import java.util.TimeZone;
@@ -148,11 +153,22 @@
// http://code.google.com/p/vogar/source/browse/trunk/src/vogar/target/TestEnvironment.java
static class TestEnvironment {
+ private static final Field sDateFormatIs24HourField;
+ static {
+ try {
+ Class<?> dateFormatClass = Class.forName("java.text.DateFormat");
+ sDateFormatIs24HourField = dateFormatClass.getDeclaredField("is24Hour");
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError("Missing DateFormat.is24Hour", e);
+ }
+ }
+
private final Locale mDefaultLocale;
private final TimeZone mDefaultTimeZone;
private final HostnameVerifier mHostnameVerifier;
private final SSLSocketFactory mSslSocketFactory;
private final Properties mProperties = new Properties();
+ private final Boolean mDefaultIs24Hour;
TestEnvironment(Context context) {
mDefaultLocale = Locale.getDefault();
@@ -167,7 +183,7 @@
PackageManager pm = context.getPackageManager();
mProperties.setProperty("android.cts.device.multicast",
Boolean.toString(pm.hasSystemFeature(PackageManager.FEATURE_WIFI)));
-
+ mDefaultIs24Hour = getDateFormatIs24Hour();
}
void reset() {
@@ -180,6 +196,23 @@
ResponseCache.setDefault(null);
HttpsURLConnection.setDefaultHostnameVerifier(mHostnameVerifier);
HttpsURLConnection.setDefaultSSLSocketFactory(mSslSocketFactory);
+ setDateFormatIs24Hour(mDefaultIs24Hour);
+ }
+
+ private static Boolean getDateFormatIs24Hour() {
+ try {
+ return (Boolean) sDateFormatIs24HourField.get(null);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError("Unable to get java.text.DateFormat.is24Hour", e);
+ }
+ }
+
+ private static void setDateFormatIs24Hour(Boolean value) {
+ try {
+ sDateFormatIs24HourField.set(null, value);
+ } catch (ReflectiveOperationException e) {
+ throw new AssertionError("Unable to set java.text.DateFormat.is24Hour", e);
+ }
}
}
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index 3abb1f7..76b528b 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -83,22 +83,18 @@
bug: 16720689
},
{
- description: "A few WebGL tests are known to fail in WebView",
+ description: "Disable WebGL conformance tests in CTS",
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"
+ "android.webgl.cts.WebGLTest"
],
- bug: 17748398
+ bug: 20937460
+},
+{
+ 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",
@@ -124,37 +120,6 @@
bug: 17530117
},
{
- description: "this test removes the stay-awake option, causing the screen to turn off during the execution of subsequent tests",
- names: [
- "android.admin.cts.DevicePolicyManagerTest#testMaximumTimeToLock"
- ],
- bug: 18002490
-},
-{
- description: "these tests locks the screen with an emtpy password or swipe-to-unlock, blocking subsequent test to dismiss keyguard",
- names: [
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_something",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_numeric",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_alphabetic",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_alphanumeric",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexUpperCase",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexLowerCase",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexLetters",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexNumeric",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexSymbols",
- "android.admin.cts.DevicePolicyManagerTest#testPasswordQuality_complexNonLetter",
- "android.admin.cts.DevicePolicyManagerTest#testGetMaximumFailedPasswordsForWipe"
- ],
- bug: 17496766
-},
-{
- description: "these tests locks the screen with an emtpy password or swipe-to-unlock, blocking subsequent test to dismiss keyguard",
- names: [
- "com.android.cts.devicepolicy.DeviceOwnerTest#testKeyManagement"
- ],
- bug: 17496766
-},
-{
description: "Current implementation of uninstallAllUserCaCerts does not throw expected security exception, wait for fix from framework",
names: [
"android.admin.cts.DevicePolicyManagerTest#testUninstallAllUserCaCerts_failIfNotProfileOwner"
@@ -162,24 +127,6 @@
bug: 17508787
},
{
- description: "New tests recently added for Android Enterprise. To be moved out of CTS-staging as soon as they show that they are stable",
- names: [
- "com.android.cts.devicepolicy.LauncherAppsMultiUserTest#testGetActivitiesForNonProfileFails",
- "com.android.cts.devicepolicy.LauncherAppsMultiUserTest#testNoLauncherCallbackPackageAddedSecondaryUser",
- "com.android.cts.devicepolicy.LauncherAppsProfileTest#testGetActivitiesWithProfile",
- "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageAddedProfile",
- "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageRemovedProfile",
- "com.android.cts.devicepolicy.LauncherAppsProfileTest#testLauncherCallbackPackageChangedProfile",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testInstallAppMainUser",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageAddedMainUser",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageRemovedMainUser",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherCallbackPackageChangedMainUser",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLauncherNonExportedAppFails",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLaunchNonExportActivityFails",
- "com.android.cts.devicepolicy.LauncherAppsSingleUserTest#testLaunchMainActivity"
- ]
-},
-{
description: "These tests fail on some devices.",
names: [
"android.uirendering.cts.testclasses.ExactCanvasTests#testBlueRect",
@@ -361,10 +308,7 @@
"android.hardware.cts.SingleSensorTests#testLinearAcceleration_10hz",
"android.hardware.cts.SingleSensorTests#testLinearAcceleration_5hz",
"android.hardware.cts.SingleSensorTests#testLinearAcceleration_1hz",
- "android.hardware.cts.SensorTest#testValuesForAllSensors",
- "android.hardware.cts.SensorTest#testSensorTimeStamps",
- "android.hardware.cts.SensorTest#testBatchAndFlush",
- "android.hardware.cts.SensorTest#testBatchAndFlushWithHandler"
+ "android.hardware.cts.SensorTest#testSensorTimeStamps"
],
bug: 17675466
},
@@ -390,5 +334,19 @@
"com.android.org.conscrypt.SignatureTest#test_getInstance_OpenSSL_ENGINE"
],
bug: 18030049
+},
+{
+ description: "The new recording test is not yet passing on all devices",
+ names: [
+ "android.hardware.camera2.cts.RecordingTest#testRecordingFramerateLowToHigh"
+ ],
+ bug: 18705837
+},
+{
+ description: "The new image reader test is not yet passing on all devices",
+ names: [
+ "android.hardware.camera2.cts.ImageReaderTest#testAllOutputYUVResolutions"
+ ],
+ bug: 18689511
}
]
diff --git a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
index 87f0238..f5e1d48 100644
--- a/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
+++ b/tests/tests/accessibility/src/android/view/accessibility/cts/AccessibilityNodeInfoTest.java
@@ -35,7 +35,7 @@
public class AccessibilityNodeInfoTest extends AndroidTestCase {
/** The number of properties of the {@link AccessibilityNodeInfo} class. */
- private static final int NON_STATIC_FIELD_COUNT = 28;
+ private static final int NON_STATIC_FIELD_COUNT = 30;
@SmallTest
public void testMarshaling() throws Exception {
@@ -54,7 +54,7 @@
AccessibilityNodeInfo receivedInfo = AccessibilityNodeInfo.CREATOR.createFromParcel(parcel);
// make sure all fields properly marshaled
- assertEqualsAccessiblityNodeInfo(sentInfo, receivedInfo);
+ assertEqualsAccessibilityNodeInfo(sentInfo, receivedInfo);
}
/**
@@ -204,7 +204,7 @@
* <code>receviedInfo</code> to verify that the received node info is
* the one that is expected.
*/
- public static void assertEqualsAccessiblityNodeInfo(AccessibilityNodeInfo expectedInfo,
+ public static void assertEqualsAccessibilityNodeInfo(AccessibilityNodeInfo expectedInfo,
AccessibilityNodeInfo receivedInfo) {
Rect expectedBounds = new Rect();
Rect receivedBounds = new Rect();
diff --git a/tests/tests/accounts/AndroidManifest.xml b/tests/tests/accounts/AndroidManifest.xml
index 22362ab..c671ff0 100644
--- a/tests/tests/accounts/AndroidManifest.xml
+++ b/tests/tests/accounts/AndroidManifest.xml
@@ -35,6 +35,9 @@
</intent-filter>
</activity>
+ <activity android:name="android.accounts.cts.AccountRemovalDummyActivity" >
+ </activity>
+
<service android:name="MockAccountService" android:exported="true"
android:process="android.accounts.cts">
<intent-filter>
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
index e5cc45a..b2a48e6 100644
--- a/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountManagerTest.java
@@ -26,6 +26,7 @@
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -81,6 +82,8 @@
public static final String USERDATA_VALUE_2 = "user.data.value.2";
public static final Account ACCOUNT = new Account(ACCOUNT_NAME, ACCOUNT_TYPE);
+ public static final Account ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API = new Account(
+ MockAccountAuthenticator.ACCOUNT_NAME_FOR_NEW_REMOVE_API, ACCOUNT_TYPE);
public static final Account ACCOUNT_SAME_TYPE = new Account(ACCOUNT_NAME_OTHER, ACCOUNT_TYPE);
private static MockAccountAuthenticator mockAuthenticator;
@@ -122,8 +125,10 @@
mockAuthenticator.clearData();
// Need to clean up created account
- assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
- assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, null /* callback */));
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
+ assertTrue(removeAccount(am, ACCOUNT_SAME_TYPE, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
// need to clean up the authenticator cached data
mockAuthenticator.clearData();
@@ -174,7 +179,7 @@
assertTrue(options.containsKey(AccountManager.KEY_CALLER_UID));
assertTrue(options.containsKey(AccountManager.KEY_CALLER_PID));
}
-
+
private void validateCredentials() {
assertEquals(ACCOUNT, mockAuthenticator.getAccount());
}
@@ -229,6 +234,38 @@
return resultBoolean;
}
+ private Bundle removeAccountWithIntentLaunch(AccountManager am, Account account,
+ Activity activity, AccountManagerCallback<Bundle> callback) throws IOException,
+ AuthenticatorException, OperationCanceledException {
+
+ AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
+ activity,
+ callback,
+ null /* handler */);
+ Bundle resultBundle = futureBundle.getResult();
+ assertTrue(futureBundle.isDone());
+
+ return resultBundle;
+ }
+
+ private Bundle removeAccount(AccountManager am, Account account, Activity activity,
+ AccountManagerCallback<Bundle> callback) throws IOException, AuthenticatorException,
+ OperationCanceledException {
+
+ AccountManagerFuture<Bundle> futureBundle = am.removeAccount(account,
+ activity,
+ callback,
+ null /* handler */);
+ Bundle resultBundle = futureBundle.getResult();
+ assertTrue(futureBundle.isDone());
+
+ return resultBundle;
+ }
+
+ private boolean removeAccountExplicitly(AccountManager am, Account account) {
+ return am.removeAccountExplicitly(account);
+ }
+
private void addAccountExplicitly(Account account, String password, Bundle userdata) {
assertTrue(am.addAccountExplicitly(account, password, userdata));
}
@@ -380,6 +417,61 @@
assertEquals(1 + expectedAccountsCount, accounts.length);
assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
// Need to clean up
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
+
+ // and verify that we go back to the initial state
+ accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(expectedAccountsCount, accounts.length);
+ }
+
+ /**
+ * Test addAccountExplicitly(), renameAccount() and removeAccount().
+ */
+ public void testAddAccountExplicitlyAndRemoveAccountWithNewApi() throws IOException,
+ AuthenticatorException, OperationCanceledException {
+
+ final int expectedAccountsCount = getAccountsCount();
+
+ addAccountExplicitly(ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, ACCOUNT_PASSWORD, null /* userData */);
+
+ // Assert that we have one more account
+ Account[] accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(1 + expectedAccountsCount, accounts.length);
+ assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
+ // Deprecated API should not work
+ assertFalse(removeAccount(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, null /* callback */));
+ accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(1 + expectedAccountsCount, accounts.length);
+ assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API));
+ // Check removal of account
+ assertTrue(removeAccountWithIntentLaunch(am, ACCOUNT_FOR_NEW_REMOVE_ACCOUNT_API, mActivity, null /* callback */)
+ .getBoolean(AccountManager.KEY_BOOLEAN_RESULT));
+ // and verify that we go back to the initial state
+ accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(expectedAccountsCount, accounts.length);
+ }
+
+ /**
+ * Test addAccountExplicitly(), renameAccount() and removeAccount().
+ */
+ public void testAddAccountExplicitlyAndRemoveAccountWithDeprecatedApi() throws IOException,
+ AuthenticatorException, OperationCanceledException {
+
+ final int expectedAccountsCount = getAccountsCount();
+
+ addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+ // Assert that we have one more account
+ Account[] accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(1 + expectedAccountsCount, accounts.length);
+ assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
+ // Need to clean up
assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
// and verify that we go back to the initial state
@@ -389,6 +481,30 @@
}
/**
+ * Test addAccountExplicitly() and removeAccountExplictly().
+ */
+ public void testAddAccountExplicitlyAndRemoveAccountExplicitly() throws IOException,
+ AuthenticatorException, OperationCanceledException {
+
+ final int expectedAccountsCount = getAccountsCount();
+
+ addAccountExplicitly(ACCOUNT, ACCOUNT_PASSWORD, null /* userData */);
+
+ // Assert that we have one more account
+ Account[] accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(1 + expectedAccountsCount, accounts.length);
+ assertTrue(isAccountPresent(am.getAccounts(), ACCOUNT));
+ // Need to clean up
+ assertTrue(removeAccountExplicitly(am, ACCOUNT));
+
+ // and verify that we go back to the initial state
+ accounts = am.getAccounts();
+ assertNotNull(accounts);
+ assertEquals(expectedAccountsCount, accounts.length);
+ }
+
+ /**
* Test setUserData() and getUserData().
*/
public void testAccountRenameAndGetPreviousName()
@@ -430,7 +546,8 @@
assertEquals(ACCOUNT.name, am.getPreviousName(renamedAccount));
// Need to clean up
- assertTrue(removeAccount(am, renamedAccount, null /* callback */));
+ assertTrue(removeAccount(am, renamedAccount, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
}
/**
@@ -1148,19 +1265,22 @@
false /* updateImmediately */);
// Need to cleanup intermediate state
- assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(null /* handler */,
true /* updateImmediately */);
// Need to cleanup intermediate state
- assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
false /* updateImmediately */);
// Need to cleanup intermediate state
- assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
testAddOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()),
true /* updateImmediately */);
@@ -1204,7 +1324,8 @@
testRemoveOnAccountsUpdatedListenerWithHandler(null /* handler */);
// Need to cleanup intermediate state
- assertTrue(removeAccount(am, ACCOUNT, null /* callback */));
+ assertTrue(removeAccount(am, ACCOUNT, mActivity, null /* callback */).getBoolean(
+ AccountManager.KEY_BOOLEAN_RESULT));
testRemoveOnAccountsUpdatedListenerWithHandler(new Handler(Looper.getMainLooper()));
}
diff --git a/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java b/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java
new file mode 100644
index 0000000..3ad9429
--- /dev/null
+++ b/tests/tests/accounts/src/android/accounts/cts/AccountRemovalDummyActivity.java
@@ -0,0 +1,49 @@
+/*
+ * 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.accounts.cts;
+
+import android.accounts.AccountAuthenticatorResponse;
+import android.accounts.AccountManager;
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+
+/**
+ * An activity.
+ */
+public class AccountRemovalDummyActivity extends Activity {
+
+ public static Intent createIntent(Context context) {
+ return new Intent(context, AccountRemovalDummyActivity.class);
+ }
+
+ private AccountAuthenticatorResponse mResponse;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Bundle input = (savedInstanceState == null) ? getIntent().getExtras() : savedInstanceState;
+ mResponse = input
+ .getParcelable(MockAccountAuthenticator.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
+ // Do verification and then remove the account
+ final Bundle result = new Bundle();
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+ mResponse.onResult(result);
+ finish();
+ }
+}
diff --git a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
index 2d5c395..b5804d9 100644
--- a/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
+++ b/tests/tests/accounts/src/android/accounts/cts/MockAccountAuthenticator.java
@@ -22,6 +22,7 @@
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.Context;
+import android.content.Intent;
import android.os.Bundle;
import java.util.ArrayList;
@@ -31,21 +32,27 @@
*/
public class MockAccountAuthenticator extends AbstractAccountAuthenticator {
- private AccountAuthenticatorResponse mResponse;
- private String mAccountType;
- private String mAuthTokenType;
- private String[] mRequiredFeatures;
+ public static String KEY_ACCOUNT_INFO = "key_account_info";
+ public static String KEY_ACCOUNT_AUTHENTICATOR_RESPONSE = "key_account_authenticator_response";
+ public static String ACCOUNT_NAME_FOR_NEW_REMOVE_API = "call new removeAccount api";
+
+ private final Context mContext;
+ AccountAuthenticatorResponse mResponse;
+ String mAccountType;
+ String mAuthTokenType;
+ String[] mRequiredFeatures;
public Bundle mOptionsUpdateCredentials;
public Bundle mOptionsConfirmCredentials;
public Bundle mOptionsAddAccount;
public Bundle mOptionsGetAuthToken;
- private Account mAccount;
- private String[] mFeatures;
+ Account mAccount;
+ String[] mFeatures;
- private final ArrayList<String> mockFeatureList = new ArrayList<String>();
+ final ArrayList<String> mockFeatureList = new ArrayList<String>();
public MockAccountAuthenticator(Context context) {
super(context);
+ mContext = context;
// Create some mock features
mockFeatureList.add(AccountManagerTest.FEATURE_1);
@@ -213,4 +220,26 @@
}
return result;
}
+
+ @Override
+ public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response,
+ Account account) throws NetworkErrorException {
+ final Bundle result = new Bundle();
+ if (ACCOUNT_NAME_FOR_NEW_REMOVE_API.equals(account.name)) {
+ Intent intent = AccountRemovalDummyActivity.createIntent(mContext);
+ // Pass in the authenticator response, so that account removal can
+ // be
+ // completed
+ intent.putExtra(KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
+ intent.putExtra(KEY_ACCOUNT_INFO, account);
+ result.putParcelable(AccountManager.KEY_INTENT, intent);
+ // Adding this following line to reject account installation
+ // requests
+ // coming from old removeAccount API.
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, false);
+ } else {
+ result.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, true);
+ }
+ return result;
+ }
}
\ No newline at end of file
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 1f61b27..513b67e 100644
--- a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -494,24 +494,6 @@
}
}
- public void testMaximumTimeToLock() {
- if (!mDeviceAdmin) {
- Log.w(TAG, "Skipping testMaximumTimeToLock");
- return;
- }
- long originalValue = mDevicePolicyManager.getMaximumTimeToLock(mComponent);
- try {
- for (long testLength : new long[] {
- 5000L /* 5 sec */, 60000L /* 1 min */, 1800000 /* 30 min */}) {
- mDevicePolicyManager.setMaximumTimeToLock(mComponent, testLength);
- assertEquals(testLength,
- mDevicePolicyManager.getMaximumTimeToLock(mComponent));
- }
- } finally {
- mDevicePolicyManager.setMaximumTimeToLock(mComponent, originalValue);
- }
- }
-
public void testCreateUser_failIfNotDeviceOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testCreateUser_failIfNotDeviceOwner");
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 35a0b4c..0163a58 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -16,8 +16,11 @@
package android.animation.cts;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -25,6 +28,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
public class AnimatorSetTest extends
ActivityInstrumentationTestCase2<AnimationActivity> {
@@ -34,6 +38,7 @@
private Object object;
private ObjectAnimator yAnimator;
private ObjectAnimator xAnimator;
+ Set<Integer> identityHashes = new HashSet<Integer>();
public AnimatorSetTest() {
super(AnimationActivity.class);
@@ -158,4 +163,84 @@
}
});
}
+
+ private void assertUnique(Object object) {
+ assertUnique(object, "");
+ }
+
+ private void assertUnique(Object object, String msg) {
+ final int code = System.identityHashCode(object);
+ assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+ }
+
+ public void testClone() throws Throwable {
+ final AnimatorSet set1 = new AnimatorSet();
+ final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
+ set1.addListener(setListener);
+ ObjectAnimator animator1 = new ObjectAnimator();
+ animator1.setDuration(100);
+ animator1.setPropertyName("x");
+ animator1.setIntValues(5);
+ animator1.setInterpolator(new LinearInterpolator());
+ AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){};
+ AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){};
+ animator1.addListener(listener1);
+
+ ObjectAnimator animator2 = new ObjectAnimator();
+ animator2.setDuration(100);
+ animator2.setInterpolator(new LinearInterpolator());
+ animator2.addListener(listener2);
+ animator2.setPropertyName("y");
+ animator2.setIntValues(10);
+
+ set1.playTogether(animator1, animator2);
+
+ AnimateObject target = new AnimateObject();
+ set1.setTarget(target);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ set1.start();
+ }
+ });
+ assertTrue(set1.isStarted());
+
+ animator1.getListeners();
+ AnimatorSet set2 = set1.clone();
+ assertFalse(set2.isStarted());
+
+ assertUnique(set1);
+ assertUnique(animator1);
+ assertUnique(animator2);
+
+ assertUnique(set2);
+ assertEquals(2, set2.getChildAnimations().size());
+
+ Animator clone1 = set2.getChildAnimations().get(0);
+ Animator clone2 = set2.getChildAnimations().get(1);
+
+ for (Animator animator : set2.getChildAnimations()) {
+ assertUnique(animator);
+ }
+
+ assertTrue(clone1.getListeners().contains(listener1));
+ assertTrue(clone2.getListeners().contains(listener2));
+
+ assertTrue(set2.getListeners().contains(setListener));
+
+ for (Animator.AnimatorListener listener : set1.getListeners()) {
+ assertTrue(set2.getListeners().contains(listener));
+ }
+
+ assertEquals(animator1.getDuration(), clone1.getDuration());
+ assertEquals(animator2.getDuration(), clone2.getDuration());
+ assertSame(animator1.getInterpolator(), clone1.getInterpolator());
+ assertSame(animator2.getInterpolator(), clone2.getInterpolator());
+ }
+
+ class AnimateObject {
+ int x = 1;
+ int y = 2;
+ }
}
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
index fac9ff9..a08a5eb 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
@@ -182,6 +182,27 @@
assertNull(listListenersTwo);
}
+ public void testNullObjectAnimator() throws Throwable {
+ Object object = mActivity.view.newBall;
+ final ObjectAnimator animator = ObjectAnimator.ofFloat(object, "y", 0, 100);
+ MyListener listener = new MyListener();
+ animator.addListener(listener);
+ mActivity.view.newBall.setY(0);
+ startAnimation(animator);
+ int sleepCount = 0;
+ while (mActivity.view.newBall.getY() == 0 && sleepCount++ < 50) {
+ Thread.sleep(1);
+ }
+ assertNotSame(0, mActivity.view.newBall.getY());
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ animator.setTarget(null);
+ }
+ });
+ assertTrue(listener.mCancel);
+ }
+
class MyListener implements Animator.AnimatorListener{
boolean mStart = false;
boolean mEnd = false;
diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
index 5b1909a..d3890df 100644
--- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
+++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java
@@ -132,11 +132,12 @@
Activities.ActivityThree.class,
};
+ final long startTime = System.currentTimeMillis() - MINUTE;
+
// Launch the series of Activities.
launchSubActivities(activitySequence);
final long endTime = System.currentTimeMillis();
- final long startTime = endTime - DAY;
UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
// Consume all the events.
@@ -147,15 +148,26 @@
eventList.add(event);
}
+ // Find the last Activity's MOVE_TO_FOREGROUND event.
+ int end = eventList.size();
+ while (end > 0) {
+ UsageEvents.Event event = eventList.get(end - 1);
+ if (event.getClassName().equals(activitySequence[activitySequence.length - 1].getName())
+ && event.getEventType() == UsageEvents.Event.MOVE_TO_FOREGROUND) {
+ break;
+ }
+ end--;
+ }
+
// We expect 2 events per Activity launched (foreground + background)
// except for the last Activity, which was in the foreground when
// we queried the event log.
- assertTrue(eventList.size() >= (activitySequence.length * 2) - 1);
- final int offset = eventList.size() - ((activitySequence.length * 2) - 1);
+ final int start = end - ((activitySequence.length * 2) - 1);
+ assertTrue("Not enough events", start >= 0);
final int activityCount = activitySequence.length;
for (int i = 0; i < activityCount; i++) {
- int index = offset + (i * 2);
+ final int index = start + (i * 2);
// Check for foreground event.
UsageEvents.Event event = eventList.get(index);
@@ -163,10 +175,10 @@
assertEquals(activitySequence[i].getName(), event.getClassName());
assertEquals(UsageEvents.Event.MOVE_TO_FOREGROUND, event.getEventType());
- index += 1;
+ // Only check for the background event if this is not the
+ // last activity.
if (i < activityCount - 1) {
- // Check for background event.
- event = eventList.get(index);
+ event = eventList.get(index + 1);
assertEquals(mTargetPackage, event.getPackageName());
assertEquals(activitySequence[i].getName(), event.getClassName());
assertEquals(UsageEvents.Event.MOVE_TO_BACKGROUND, event.getEventType());
@@ -182,6 +194,7 @@
@Ignore
public void ignore_testStatsAreShiftedInTimeWhenSystemTimeChanges() throws Exception {
launchSubActivity(Activities.ActivityOne.class);
+ launchSubActivity(Activities.ActivityThree.class);
long endTime = System.currentTimeMillis();
long startTime = endTime - MINUTE;
@@ -214,18 +227,19 @@
}
public void testUsageEventsParceling() throws Exception {
- final long startTime = System.currentTimeMillis();
+ final long startTime = System.currentTimeMillis() - MINUTE;
+ // Ensure some data is in the UsageStats log.
@SuppressWarnings("unchecked")
Class<? extends Activity>[] activityClasses = new Class[] {
+ Activities.ActivityTwo.class,
Activities.ActivityOne.class,
Activities.ActivityThree.class,
- Activities.ActivityTwo.class,
};
launchSubActivities(activityClasses);
final long endTime = System.currentTimeMillis();
- UsageEvents events = mUsageStatsManager.queryEvents(startTime - TIME_DIFF_THRESHOLD, endTime);
+ UsageEvents events = mUsageStatsManager.queryEvents(startTime, endTime);
assertTrue(events.getNextEvent(new UsageEvents.Event()));
Parcel p = Parcel.obtain();
@@ -255,6 +269,7 @@
// Launch an Activity.
launchSubActivity(Activities.ActivityFour.class);
+ launchSubActivity(Activities.ActivityThree.class);
final long endTime = System.currentTimeMillis();
@@ -287,10 +302,12 @@
}
public void testNoAccessSilentlyFails() throws Exception {
+ final long startTime = System.currentTimeMillis() - MINUTE;
+
launchSubActivity(Activities.ActivityOne.class);
+ launchSubActivity(Activities.ActivityThree.class);
final long endTime = System.currentTimeMillis();
- final long startTime = endTime - MINUTE;
List<UsageStats> stats = mUsageStatsManager.queryUsageStats(UsageStatsManager.INTERVAL_BEST,
startTime, endTime);
assertFalse(stats.isEmpty());
diff --git a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
index 904a056..5deff5a 100644
--- a/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
+++ b/tests/tests/app/src/android/app/cts/ActivityManagerMemoryClassTest.java
@@ -53,6 +53,7 @@
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_TV, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_HIGH, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XHIGH, 48);
+ expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_280, 48);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_400, 96);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_XXHIGH, 128);
expectedMemorySizeForSmallNormalScreen.put(DisplayMetrics.DENSITY_560, 192);
@@ -65,6 +66,7 @@
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_TV, 80);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 80);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 128);
+ expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_280, 96);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_400, 192);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_XXHIGH, 256);
expectedMemorySizeForLargeScreen.put(DisplayMetrics.DENSITY_560, 384);
@@ -77,6 +79,7 @@
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_TV, 96);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_HIGH, 96);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XHIGH, 192);
+ expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_280, 144);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_400, 288);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_XXHIGH, 384);
expectedMemorySizeForXLargeScreen.put(DisplayMetrics.DENSITY_560, 576);
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/app/src/android/app/cts/AlarmManagerTest.java b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
index bfc3e1d..b85c616 100644
--- a/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
+++ b/tests/tests/app/src/android/app/cts/AlarmManagerTest.java
@@ -18,14 +18,17 @@
import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.cts.util.PollingCheck;
+import android.os.Build;
import android.os.SystemClock;
import android.test.AndroidTestCase;
import android.util.Log;
+import android.test.MoreAsserts;
public class AlarmManagerTest extends AndroidTestCase {
public static final String MOCKACTION = "android.app.AlarmManagerTest.TEST_ALARMRECEIVER";
@@ -47,6 +50,7 @@
private static final int TIME_DELTA = 1000;
private static final int TIME_DELAY = 10000;
+ private static final int REPEAT_PERIOD = 60000;
// Receiver registration/unregistration between tests races with the system process, so
// we add a little buffer time here to allow the system to process before we proceed.
@@ -221,46 +225,54 @@
public void testSetRepeating() throws Exception {
mMockAlarmReceiver.setAlarmedFalse();
- mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
- mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, TIME_DELAY / 2, mSender);
- new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
+ mWakeupTime = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+ mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, REPEAT_PERIOD, mSender);
+
+ // wait beyond the initial alarm's possible delivery window to verify that it fires the first time
+ new PollingCheck(TEST_ALARM_FUTURITY + REPEAT_PERIOD) {
@Override
protected boolean check() {
return mMockAlarmReceiver.alarmed;
}
}.run();
+ assertTrue(mMockAlarmReceiver.alarmed);
+
+ // Now reset the receiver and wait for the intended repeat alarm to fire as expected
mMockAlarmReceiver.setAlarmedFalse();
- new PollingCheck(TIME_DELAY) {
+ new PollingCheck(REPEAT_PERIOD*2) {
@Override
protected boolean check() {
return mMockAlarmReceiver.alarmed;
}
}.run();
+ assertTrue(mMockAlarmReceiver.alarmed);
+
mAm.cancel(mSender);
}
public void testCancel() throws Exception {
mMockAlarmReceiver.setAlarmedFalse();
- mWakeupTime = System.currentTimeMillis() + SNOOZE_DELAY;
- mAm.setRepeating(AlarmManager.RTC_WAKEUP, mWakeupTime, 1000, mSender);
- new PollingCheck(SNOOZE_DELAY + TIME_DELAY) {
- @Override
- protected boolean check() {
- return mMockAlarmReceiver.alarmed;
- }
- }.run();
- mMockAlarmReceiver.setAlarmedFalse();
+ mMockAlarmReceiver2.setAlarmedFalse();
+
+ // set two alarms
+ final long when1 = System.currentTimeMillis() + TEST_ALARM_FUTURITY;
+ mAm.setExact(AlarmManager.RTC_WAKEUP, when1, mSender);
+ final long when2 = when1 + TIME_DELTA; // will fire after when1's target time
+ mAm.setExact(AlarmManager.RTC_WAKEUP, when2, mSender2);
+
+ // cancel the earlier one
+ mAm.cancel(mSender);
+
+ // and verify that only the later one fired
new PollingCheck(TIME_DELAY) {
@Override
protected boolean check() {
- return mMockAlarmReceiver.alarmed;
+ return mMockAlarmReceiver2.alarmed;
}
}.run();
- mAm.cancel(mSender);
- Thread.sleep(TIME_DELAY);
- mMockAlarmReceiver.setAlarmedFalse();
- Thread.sleep(TIME_DELAY * 5);
+
assertFalse(mMockAlarmReceiver.alarmed);
+ assertTrue(mMockAlarmReceiver2.alarmed);
}
public void testSetInexactRepeating() throws Exception {
@@ -273,4 +285,66 @@
// " Unable to open alarm driver: Permission denied". But still fail
// after tried many permission.
}
+
+ public void testSetAlarmClock() throws Exception {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ mMockAlarmReceiver.setAlarmedFalse();
+ mMockAlarmReceiver2.setAlarmedFalse();
+
+ // Set first alarm clock.
+ final long wakeupTimeFirst = System.currentTimeMillis()
+ + 2 * TEST_ALARM_FUTURITY;
+ mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeFirst, null), mSender);
+
+ // Verify getNextAlarmClock returns first alarm clock.
+ AlarmClockInfo nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+ assertNull(nextAlarmClock.getShowIntent());
+
+ // Set second alarm clock, earlier than first.
+ final long wakeupTimeSecond = System.currentTimeMillis()
+ + TEST_ALARM_FUTURITY;
+ PendingIntent showIntentSecond = PendingIntent.getBroadcast(getContext(), 0,
+ new Intent(getContext(), AlarmManagerTest.class).setAction("SHOW_INTENT"), 0);
+ mAm.setAlarmClock(new AlarmClockInfo(wakeupTimeSecond, showIntentSecond),
+ mSender2);
+
+ // Verify getNextAlarmClock returns second alarm clock now.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeSecond, nextAlarmClock.getTriggerTime());
+ assertEquals(showIntentSecond, nextAlarmClock.getShowIntent());
+
+ // Cancel second alarm.
+ mAm.cancel(mSender2);
+
+ // Verify getNextAlarmClock returns first alarm clock again.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ assertEquals(wakeupTimeFirst, nextAlarmClock.getTriggerTime());
+ assertNull(nextAlarmClock.getShowIntent());
+
+ // Wait for first alarm to trigger.
+ assertFalse(mMockAlarmReceiver.alarmed);
+ new PollingCheck(2 * TEST_ALARM_FUTURITY + TIME_DELAY) {
+ @Override
+ protected boolean check() {
+ return mMockAlarmReceiver.alarmed;
+ }
+ }.run();
+
+ // Verify first alarm fired at the right time.
+ assertEquals(mMockAlarmReceiver.rtcTime, wakeupTimeFirst, TIME_DELTA);
+
+ // Verify second alarm didn't fire.
+ assertFalse(mMockAlarmReceiver2.alarmed);
+
+ // Verify the next alarm is not returning neither the first nor the second alarm.
+ nextAlarmClock = mAm.getNextAlarmClock();
+ MoreAsserts.assertNotEqual(wakeupTimeFirst, nextAlarmClock != null
+ ? nextAlarmClock.getTriggerTime()
+ : null);
+ MoreAsserts.assertNotEqual(wakeupTimeSecond, nextAlarmClock != null
+ ? nextAlarmClock.getTriggerTime()
+ : null);
+ }
+ }
}
diff --git a/tests/tests/app/src/android/app/cts/DialogTest.java b/tests/tests/app/src/android/app/cts/DialogTest.java
index 6df2eee..feb4940 100644
--- a/tests/tests/app/src/android/app/cts/DialogTest.java
+++ b/tests/tests/app/src/android/app/cts/DialogTest.java
@@ -673,19 +673,36 @@
mInstrumentation.waitForIdleSync();
}
- public void testSetFeatureDrawableUri() {
+ public void testSetFeatureDrawableUri() throws Throwable {
startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
- mActivity.getDialog().setFeatureDrawableUri(0, Uri.parse("http://www.google.com"));
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mActivity.getDialog().setFeatureDrawableUri(Window.FEATURE_LEFT_ICON,
+ Uri.parse("http://www.google.com"));
+ }
+ });
+ mInstrumentation.waitForIdleSync();
}
- public void testSetFeatureDrawable() {
+ public void testSetFeatureDrawable() throws Throwable {
startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
- mActivity.getDialog().setFeatureDrawable(0, new MockDrawable());
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mActivity.getDialog().setFeatureDrawable(Window.FEATURE_LEFT_ICON,
+ new MockDrawable());
+ }
+ });
+ mInstrumentation.waitForIdleSync();
}
- public void testSetFeatureDrawableAlpha() {
+ public void testSetFeatureDrawableAlpha() throws Throwable {
startDialogActivity(DialogStubActivity.TEST_ONSTART_AND_ONSTOP);
- mActivity.getDialog().setFeatureDrawableAlpha(0, 0);
+ runTestOnUiThread(new Runnable() {
+ public void run() {
+ mActivity.getDialog().setFeatureDrawableAlpha(Window.FEATURE_LEFT_ICON, 0);
+ }
+ });
+ mInstrumentation.waitForIdleSync();
}
public void testGetLayoutInflater() {
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index 0c2e9fa..b21148e 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -196,10 +196,12 @@
public void testInvokeMenuActionSync() throws Exception {
final int resId = R.id.goto_menu_id;
- mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
- mInstrumentation.waitForIdleSync();
-
- assertEquals(resId, mActivity.getMenuID());
+ if (mActivity.getWindow().hasFeature(Window.FEATURE_OPTIONS_PANEL)) {
+ mInstrumentation.invokeMenuActionSync(mActivity, resId, 0);
+ mInstrumentation.waitForIdleSync();
+
+ assertEquals(resId, mActivity.getMenuID());
+ }
}
public void testCallActivityOnPostCreate() throws Throwable {
diff --git a/tests/tests/app/src/android/app/cts/ServiceTest.java b/tests/tests/app/src/android/app/cts/ServiceTest.java
index b66f4c8..8ba1816 100644
--- a/tests/tests/app/src/android/app/cts/ServiceTest.java
+++ b/tests/tests/app/src/android/app/cts/ServiceTest.java
@@ -448,4 +448,17 @@
// expected
}
}
+
+ @MediumTest
+ public void testImplicitIntentFailsOnApiLevel21() throws Exception {
+ Intent intent = new Intent(LocalService.SERVICE_LOCAL);
+ EmptyConnection conn = new EmptyConnection();
+ try {
+ mContext.bindService(intent, conn, 0);
+ mContext.unbindService(conn);
+ fail("Implicit intents should be disallowed for apps targeting API 21+");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ }
}
diff --git a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
index 5e72a78..a385ebe 100644
--- a/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
+++ b/tests/tests/appwidget/src/android/appwidget/cts/AppWidgetTest.java
@@ -95,7 +95,17 @@
private static final String REVOKE_BIND_APP_WIDGET_PERMISSION_COMMAND =
"appwidget revokebind --package android.cts.appwidget --user 0";
+
+ private boolean hasAppWidgets() {
+ return getInstrumentation().getTargetContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS);
+ }
+
public void testGetAppInstalledProvidersForCurrentUserLegacy() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// By default we should get only providers for the current user.
List<AppWidgetProviderInfo> providers = getAppWidgetManager().getInstalledProviders();
@@ -104,6 +114,10 @@
}
public void testGetAppInstalledProvidersForCurrentUserNewCurrentProfile() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We ask only for providers for the current user.
List<AppWidgetProviderInfo> providers = getAppWidgetManager()
.getInstalledProvidersForProfile(Process.myUserHandle());
@@ -113,6 +127,10 @@
}
public void testGetAppInstalledProvidersForCurrentUserNewAllProfiles() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We ask only for providers for all current user's profiles
UserManager userManager = (UserManager) getInstrumentation()
.getTargetContext().getSystemService(Context.USER_SERVICE);
@@ -133,6 +151,10 @@
}
public void testBindAppWidget() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// Create a host and start listening.
AppWidgetHost host = new AppWidgetHost(getInstrumentation().getTargetContext(), 0);
host.deleteHost();
@@ -170,6 +192,10 @@
}
public void testAppWidgetProviderCallbacks() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
AtomicInteger invocationCounter = new AtomicInteger();
// Set a mock to intercept provider callbacks.
@@ -277,6 +303,10 @@
}
public void testTwoAppWidgetProviderCallbacks() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
AtomicInteger invocationCounter = new AtomicInteger();
// Set a mock to intercept first provider callbacks.
@@ -368,6 +398,10 @@
}
public void testGetAppWidgetIds() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -415,6 +449,10 @@
}
public void testGetAppWidgetInfo() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -467,6 +505,10 @@
}
public void testGetAppWidgetOptions() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -512,6 +554,10 @@
}
public void testDeleteHost() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -553,6 +599,10 @@
}
public void testDeleteHosts() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -609,6 +659,10 @@
}
public void testOnProvidersChanged() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -671,6 +725,10 @@
}
public void testUpdateAppWidgetViaComponentName() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -761,6 +819,10 @@
}
public void testUpdateAppWidgetViaWidgetId() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -831,6 +893,10 @@
}
public void testUpdateAppWidgetViaWidgetIds() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -923,6 +989,10 @@
}
public void testPartiallyUpdateAppWidgetViaWidgetId() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -997,6 +1067,10 @@
}
public void testPartiallyUpdateAppWidgetViaWidgetIds() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
@@ -1107,6 +1181,10 @@
}
public void testCollectionWidgets() throws Exception {
+ if (!hasAppWidgets()) {
+ return;
+ }
+
// We want to bind widgets.
grantBindAppWidgetPermission();
diff --git a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
index f3a4ba1..187fe06 100644
--- a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
+++ b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
@@ -22,13 +22,11 @@
public class HighPriorityBroadcastReceiver extends ResultReceiver {
@Override
- public void onReceive(Context context, Intent intent) {
+ public synchronized void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
try {
- synchronized (this) {
- wait();
- }
+ wait();
} catch (InterruptedException e) {
throw new RuntimeException("Got interrupted during wait()", e);
}
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index d4fac55..0f9a5cc 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -45,6 +45,7 @@
import java.io.Serializable;
import java.net.URISyntaxException;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.Set;
public class IntentTest extends AndroidTestCase {
@@ -676,7 +677,7 @@
final String compnent =
"component(" + mContext.getPackageName() + "!" + MockActivity.class.getName() + ")";
- uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(1)" + compnent
+ uri = "testdata#action(test)categories(test!test2)type(mtype)launchFlags(5)" + compnent
+ "extras(Stest=testString!btestbyte=1!"
+ "Btestboolean=true!ctestchar=a!dtestdouble=1d!"
+ "itestint=1!ltestlong=1!stestshort=1!ftestfloat=1f)";
@@ -686,7 +687,7 @@
assertEquals(mComponentName, mIntent.getComponent());
assertEquals("test", (String) (mIntent.getCategories().toArray()[0]));
assertEquals("mtype", mIntent.getType());
- assertEquals(1, mIntent.getFlags());
+ assertEquals(4, mIntent.getFlags());
assertEquals("testString", mIntent.getStringExtra("test"));
assertTrue(mIntent.getBooleanExtra("testboolean", false));
final byte b = 1;
@@ -812,10 +813,13 @@
target = Intent.getIntent(uri);
assertEquals(TEST_TYPE, target.getType());
- mIntent.setFlags(1);
+ mIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT
+ | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION
+ | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
+ | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
uri = mIntent.toURI();
target = Intent.getIntent(uri);
- assertEquals(1, target.getFlags());
+ assertEquals(Intent.FLAG_ACTIVITY_NEW_DOCUMENT, target.getFlags());
String stringValue = "testString";
mIntent.putExtra(TEST_EXTRA_NAME, stringValue);
@@ -869,8 +873,8 @@
}
public void testToURI() {
- mIntent.setFlags(0);
- assertEquals("#Intent;end", mIntent.toURI());
+ mIntent = new Intent();
+ assertEquals("", mIntent.toURI());
mIntent.setData(TEST_URI);
assertTrue(mIntent.toURI().indexOf(TEST_URI.toString()) != -1);
@@ -937,6 +941,321 @@
return uri.toString();
}
+ static Intent makeSelector(Intent baseIntent, Intent selectorIntent) {
+ baseIntent.setSelector(selectorIntent);
+ return baseIntent;
+ }
+
+ public void testUris() {
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;end",
+ null,
+ new Intent().setAction("android.test.FOO"));
+ checkIntentUri(
+ "intent:#Intent;category=android.test.FOO;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;launchFlags=0x20;end",
+ null,
+ new Intent().setAction("android.test.FOO").setFlags(0x20));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah")));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;component=com.exfoo/com.argh.Bar;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar")));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah#fragment")));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;action=android.test.foo;end",
+ null,
+ new Intent().setAction("android.test.foo")
+ .setData(Uri.parse("http://www.example.com/blah")));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;type=image/foo;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.parse("mailto:foo"), "image/foo"));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;S.string=text;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("string", "text"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;S.string=text;end",
+ null,
+ new Intent().setAction("android.test.FOO").putExtra("string", "text"));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;i.int=1000;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("int", 1000));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;l.long=1000;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("long", (long) 1000));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;B.boolean=true;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("boolean", true));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;f.float=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("float", 10.4f));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;d.double=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("mailto:foo"))
+ .putExtra("double", (double) 10.4));
+ checkIntentUri(
+ "intent:#Intent;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ null,
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .putExtra("int", 1000).putExtra("long", (long) 1000)
+ .putExtra("boolean", true).putExtra("float", 10.4f));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;scheme=foobar;action=android.test.FOO;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO").setData(Uri.parse("foobar:"))));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;package=com.myapp;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO").setPackage("com.myapp")));
+ checkIntentUri(
+ "intent:foo#Intent;scheme=mailto;SEL;action=android.test.FOO;component=com.exfoo/com.argh.Bar;end",
+ null,
+ makeSelector(new Intent(Intent.ACTION_VIEW).setData(Uri.parse("mailto:foo")),
+ new Intent("android.test.FOO")
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))));
+
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;end",
+ new Intent().setAction("android.test.FOO").setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.intent.action.MAIN;package=com.myapp;end",
+ "android-app://com.myapp",
+ new Intent().setAction(Intent.ACTION_MAIN).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;end",
+ new Intent().setAction(Intent.ACTION_VIEW).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;category=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;category=android.test.FOO;end",
+ new Intent().setAction(Intent.ACTION_VIEW).addCategory("android.test.FOO")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;launchFlags=0x20;package=com.myapp;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;launchFlags=0x20;end",
+ new Intent().setAction("android.test.FOO").setFlags(0x20)
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;component=com.exfoo/com.argh.Bar;end",
+ "android-app://com.myapp/http/www.example.com/blah#Intent;component=com.exfoo/com.argh.Bar;end",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setComponent(new ComponentName("com.exfoo", "com.argh.Bar"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah#fragment",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah#fragment"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#fragment#Intent;scheme=http;action=android.test.FOO;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah#fragment#Intent;action=android.test.FOO;end",
+ new Intent().setAction("android.test.FOO")
+ .setData(Uri.parse("http://www.example.com/blah#fragment"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent://www.example.com/blah#Intent;scheme=http;package=com.myapp;end",
+ "android-app://com.myapp/http/www.example.com/blah",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setData(Uri.parse("http://www.example.com/blah"))
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;type=image/foo;package=com.myapp;end",
+ "android-app://com.myapp/mailto#Intent;type=image/foo;end",
+ new Intent().setAction(Intent.ACTION_VIEW)
+ .setDataAndType(Uri.parse("mailto:"), "image/foo")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;S.string=text;end",
+ "android-app://com.myapp/mailto#Intent;S.string=text;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;action=android.test.FOO;package=com.myapp;S.string=text;end",
+ "android-app://com.myapp#Intent;action=android.test.FOO;S.string=text;end",
+ new Intent().setAction("android.test.FOO").putExtra("string", "text")
+ .setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;i.int=1000;end",
+ "android-app://com.myapp/mailto#Intent;i.int=1000;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("int", 1000)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;l.long=1000;end",
+ "android-app://com.myapp/mailto#Intent;l.long=1000;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("long", (long) 1000)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;B.boolean=true;end",
+ "android-app://com.myapp/mailto#Intent;B.boolean=true;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("boolean", true)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;f.float=10.4;end",
+ "android-app://com.myapp/mailto#Intent;f.float=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("float", 10.4f)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;scheme=mailto;package=com.myapp;d.double=10.4;end",
+ "android-app://com.myapp/mailto#Intent;d.double=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("double", (double) 10.4)
+ .setData(Uri.parse("mailto:")).setPackage("com.myapp"));
+ checkIntentUri(
+ "intent:#Intent;package=com.myapp;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ "android-app://com.myapp#Intent;action=android.intent.action.VIEW;S.string=text;i.int=1000;l.long=1000;B.boolean=true;f.float=10.4;end",
+ new Intent().setAction(Intent.ACTION_VIEW).putExtra("string", "text")
+ .putExtra("int", 1000).putExtra("long", (long) 1000)
+ .putExtra("boolean", true).putExtra("float", 10.4f)
+ .setPackage("com.myapp"));
+ }
+
+ private boolean compareIntents(Intent expected, Intent actual) {
+ if (!Objects.equals(expected.getAction(), actual.getAction())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getData(), actual.getData())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getType(), actual.getType())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getPackage(), actual.getPackage())) {
+ return false;
+ }
+ if (!Objects.equals(expected.getComponent(), actual.getComponent())) {
+ return false;
+ }
+ if (expected.getFlags() != actual.getFlags()) {
+ return false;
+ }
+ Set<String> expectedCat = expected.getCategories();
+ Set<String> actualCat = actual.getCategories();
+ if (expectedCat != actualCat) {
+ if (expectedCat == null || actualCat == null) {
+ return false;
+ }
+ for (String cat : expectedCat) {
+ if (!actual.hasCategory(cat)) {
+ return false;
+ }
+ }
+ for (String cat : actualCat) {
+ if (!expected.hasCategory(cat)) {
+ return false;
+ }
+ }
+ }
+ Bundle extras1 = expected.getExtras();
+ Bundle extras2 = actual.getExtras();
+ if (extras1 != extras2) {
+ if (extras1 == null || extras2 == null) {
+ return false;
+ }
+ for (String key : extras1.keySet()) {
+ if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+ return false;
+ }
+ }
+ for (String key : extras2.keySet()) {
+ if (!Objects.equals(extras1.get(key), extras2.get(key))) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void assertEqualsIntent(String msg, Intent expected, Intent actual) {
+ if (!compareIntents(expected, actual)) {
+ failNotEquals(msg, expected, actual);
+ }
+ Intent expectedSel = expected.getSelector();
+ Intent actualSel = actual.getSelector();
+ if (expectedSel != actualSel) {
+ if (expectedSel == null || actualSel == null) {
+ failNotEquals(msg, expected, actual);
+ }
+ if (!compareIntents(expectedSel, actualSel)) {
+ failNotEquals(msg, expected, actual);
+ }
+ }
+ }
+
+ private void checkIntentUri(String intentSchemeUri, String androidAppSchemeUri, Intent intent) {
+ if (intentSchemeUri != null) {
+ try {
+ Intent genIntent = Intent.parseUri(intentSchemeUri, 0);
+ assertEqualsIntent("Implicitly converting " + intentSchemeUri + " to Intent",
+ intent, genIntent);
+ genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_INTENT_SCHEME);
+ assertEqualsIntent("Explicitly converting " + intentSchemeUri + " to Intent",
+ intent, genIntent);
+ } catch (URISyntaxException e) {
+ fail("Failure parsing " + intentSchemeUri + ": " + e);
+ }
+ String genUri = intent.toUri(Intent.URI_INTENT_SCHEME);
+ assertEquals("Converting " + intent + " to intent: uri",
+ intentSchemeUri, genUri);
+ }
+ if (androidAppSchemeUri != null) {
+ try {
+ Intent genIntent = Intent.parseUri(androidAppSchemeUri, 0);
+ assertEqualsIntent("Implicitly converting " + androidAppSchemeUri + " to Intent",
+ intent, genIntent);
+ genIntent = Intent.parseUri(intentSchemeUri, Intent.URI_ANDROID_APP_SCHEME);
+ assertEqualsIntent("Explicitly converting " + androidAppSchemeUri + " to Intent",
+ intent, genIntent);
+ } catch (URISyntaxException e) {
+ fail("Failure parsing " + androidAppSchemeUri + ": " + e);
+ }
+ String genUri = intent.toUri(Intent.URI_ANDROID_APP_SCHEME);
+ assertEquals("Converting " + intent + " to android-app: uri",
+ androidAppSchemeUri, genUri);
+ }
+ }
+
public void testAccessFlags() {
int expected = 1;
mIntent.setFlags(expected);
diff --git a/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
new file mode 100644
index 0000000..35466be
--- /dev/null
+++ b/tests/tests/content/src/android/content/res/cts/PrivateAttributeTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.content.res.cts;
+
+import android.content.res.Resources;
+import android.test.AndroidTestCase;
+import android.util.TypedValue;
+
+/**
+ * Tests that private attributes are correctly placed in a separate type to
+ * prevent future releases from stomping over private attributes with new public ones.
+ */
+public class PrivateAttributeTest extends AndroidTestCase {
+
+ private static final int sLastPublicAttr = 0x010104d5;
+
+ public void testNoAttributesAfterLastPublicAttribute() throws Exception {
+ final Resources res = getContext().getResources();
+
+ final String lastPublicName;
+ try {
+ lastPublicName = res.getResourceEntryName(sLastPublicAttr);
+ } catch (Resources.NotFoundException e) {
+ throw new AssertionError("Last public resource was not found", e);
+ }
+
+ int currentAttr = sLastPublicAttr;
+ while (currentAttr < 0x0101ffff) {
+ currentAttr++;
+ try {
+ final String name = res.getResourceEntryName(currentAttr);
+ throw new AssertionError("Found attribute '" + name + "'"
+ + " (0x" + Integer.toHexString(currentAttr) + ")"
+ + " after last public framework attribute "
+ + "'" + lastPublicName + "'"
+ + " (0x" + Integer.toHexString(sLastPublicAttr) + ")");
+ } catch (Resources.NotFoundException e) {
+ // continue
+ }
+ }
+ }
+}
diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java
index 4c7116d..bea99ed 100644
--- a/tests/tests/display/src/android/display/cts/DisplayTest.java
+++ b/tests/tests/display/src/android/display/cts/DisplayTest.java
@@ -28,10 +28,11 @@
public class DisplayTest extends AndroidTestCase {
// This test is called from DisplayTestRunner which brings up an overlay display on the target
// device. The overlay display parameters must match the ones defined there which are
- // 1281x721/214 (wxh/dpi).
+ // 181x161/214 (wxh/dpi). It only matters that these values are different from any real
+ // display.
- private static final int SECONDARY_DISPLAY_WIDTH = 1281;
- private static final int SECONDARY_DISPLAY_HEIGHT = 721;
+ private static final int SECONDARY_DISPLAY_WIDTH = 181;
+ private static final int SECONDARY_DISPLAY_HEIGHT = 161;
private static final int SECONDARY_DISPLAY_DPI = 214;
private static final float SCALE_DENSITY_LOWER_BOUND =
(float)(SECONDARY_DISPLAY_DPI - 1) / DisplayMetrics.DENSITY_DEFAULT;
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
index 82e3204..238abec 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationTest.java
@@ -57,6 +57,7 @@
allowedDensities.add(DisplayMetrics.DENSITY_MEDIUM);
allowedDensities.add(DisplayMetrics.DENSITY_TV);
allowedDensities.add(DisplayMetrics.DENSITY_HIGH);
+ allowedDensities.add(DisplayMetrics.DENSITY_280);
allowedDensities.add(DisplayMetrics.DENSITY_XHIGH);
allowedDensities.add(DisplayMetrics.DENSITY_400);
allowedDensities.add(DisplayMetrics.DENSITY_XXHIGH);
diff --git a/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml b/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml
deleted file mode 100644
index 25d2dfe..0000000
--- a/tests/tests/graphics/res/xml/anim_list_missing_list_attrs.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
- * Copyright (C) 2008 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.
- */
--->
-
-<animation-list xmlns:android="http://schemas.android.com/apk/res/android" >
- <item android:drawable="@drawable/testimage"
- android:duration="2000" />
-</animation-list>
-
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
index c278ed2..28a3c6b 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/AnimationDrawableTest.java
@@ -76,7 +76,7 @@
// Check the values set in the constructor
assertNotNull(mAnimationDrawable.getConstantState());
assertFalse(mAnimationDrawable.isRunning());
- assertTrue(mAnimationDrawable.isOneShot());
+ assertFalse(mAnimationDrawable.isOneShot());
}
public void testSetVisible() throws Throwable {
@@ -277,68 +277,62 @@
assertStoppedAnimation(THIRD_FRAME_INDEX, THIRD_FRAME_DURATION);
}
- public void testInflate() throws XmlPullParserException, IOException {
- mAnimationDrawable = new AnimationDrawable();
- DrawableContainerState drawableContainerState =
- (DrawableContainerState) mAnimationDrawable.getConstantState();
-
+ public void testInflateCorrect() throws XmlPullParserException, IOException {
XmlResourceParser parser = getResourceParser(R.xml.anim_list_correct);
- mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+ AnimationDrawable dr = new AnimationDrawable();
+ dr.inflate(mResources, parser, Xml.asAttributeSet(parser));
// android:visible="false"
- assertFalse(mAnimationDrawable.isVisible());
+ assertFalse(dr.isVisible());
// android:oneShot="true"
- assertTrue(mAnimationDrawable.isOneShot());
+ assertTrue(dr.isOneShot());
// android:variablePadding="true"
- assertNull(drawableContainerState.getConstantPadding());
- assertEquals(2, mAnimationDrawable.getNumberOfFrames());
- assertEquals(2000, mAnimationDrawable.getDuration(0));
- assertEquals(1000, mAnimationDrawable.getDuration(1));
- assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
+ DrawableContainerState state =
+ (DrawableContainerState) dr.getConstantState();
+ assertNull(state.getConstantPadding());
+ assertEquals(2, dr.getNumberOfFrames());
+ assertEquals(2000, dr.getDuration(0));
+ assertEquals(1000, dr.getDuration(1));
+ assertSame(dr.getFrame(0), dr.getCurrent());
+ }
- parser = getResourceParser(R.xml.anim_list_missing_list_attrs);
- mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
- // use current the visibility
- assertFalse(mAnimationDrawable.isVisible());
- // default value of android:oneShot is false
- assertFalse(mAnimationDrawable.isOneShot());
- // default value of android:variablePadding is false
- // TODO: its not clear what the value of constant padding should be when variablePadding
- // is false
- //assertNotNull(drawableContainerState.getConstantPadding());
- // add a new frame from xml
- assertEquals(3, mAnimationDrawable.getNumberOfFrames());
- assertEquals(2000, mAnimationDrawable.getDuration(0));
- assertEquals(1000, mAnimationDrawable.getDuration(1));
- assertEquals(2000, mAnimationDrawable.getDuration(2));
- assertSame(mAnimationDrawable.getFrame(0), mAnimationDrawable.getCurrent());
-
- parser = getResourceParser(R.xml.anim_list_missing_item_drawable);
+ public void testInflateMissingDrawable() throws XmlPullParserException, IOException {
+ XmlResourceParser parser = getResourceParser(R.xml.anim_list_missing_item_drawable);
+ AnimationDrawable dr = new AnimationDrawable();
try {
- mAnimationDrawable.inflate(mResources, parser, Xml.asAttributeSet(parser));
+ dr.inflate(mResources, parser, Xml.asAttributeSet(parser));
fail("Should throw XmlPullParserException if drawable of item is missing");
} catch (XmlPullParserException e) {
// expected
}
}
- public void testInflateWithNullParameters() throws XmlPullParserException, IOException {
+ public void testInflateNullResources() throws XmlPullParserException, IOException {
XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+ AnimationDrawable dr = new AnimationDrawable();
try {
- mAnimationDrawable.inflate(null, parser, Xml.asAttributeSet(parser));
+ dr.inflate(null, parser, Xml.asAttributeSet(parser));
fail("Should throw NullPointerException if resource is null");
} catch (NullPointerException e) {
// expected
}
+ }
+ public void testInflateNullXmlPullParser() throws XmlPullParserException, IOException {
+ XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+ AnimationDrawable dr = new AnimationDrawable();
try {
- mAnimationDrawable.inflate(mResources, null, Xml.asAttributeSet(parser));
+ dr.inflate(mResources, null, Xml.asAttributeSet(parser));
fail("Should throw NullPointerException if parser is null");
} catch (NullPointerException e) {
// expected
}
+ }
+ public void testInflateNullAttributeSet() throws XmlPullParserException, IOException {
+ XmlResourceParser parser = getResourceParser(R.drawable.animationdrawable);
+ AnimationDrawable dr = new AnimationDrawable();
try {
- mAnimationDrawable.inflate(mResources, parser, null);
+ dr.inflate(mResources, parser, null);
fail("Should throw NullPointerException if AttributeSet is null");
} catch (NullPointerException e) {
// expected
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
index 190fffa..6281582 100644
--- a/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/ScaleDrawableTest.java
@@ -413,8 +413,8 @@
attrs = DrawableTestUtils.getAttributeSet(parser, "scale_nodrawable");
try {
scaleDrawable.inflate(res, parser, attrs);
- fail("Should throw IllegalArgumentException");
- } catch (IllegalArgumentException e) {
+ fail("Should throw XmlPullParserException");
+ } catch (XmlPullParserException e) {
}
try {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
index 92a5e58..d4fb235 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/AllocationTest.java
@@ -310,7 +310,7 @@
*/
private static float[] convertPixelYuvToRgb(byte[] yuvData) {
final int CHANNELS = 3; // yuv
- final float COLOR_RANGE = 256f;
+ final float COLOR_RANGE = 255f;
assertTrue("YUV pixel must be at least 3 bytes large", CHANNELS <= yuvData.length);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
new file mode 100644
index 0000000..5d0841e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/BurstCaptureTest.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.cts;
+
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.cts.testcases.Camera2SurfaceViewTestCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Log;
+import android.util.Range;
+import android.util.Size;
+
+import java.util.List;
+import java.util.ArrayList;
+
+public class BurstCaptureTest extends Camera2SurfaceViewTestCase {
+ private static final String TAG = "BurstCaptureTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Test YUV burst capture with full-AUTO control.
+ * Also verifies sensor settings operation if READ_SENSOR_SETTINGS is available.
+ */
+ public void testYuvBurst() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ String id = mCameraIds[i];
+ Log.i(TAG, "Testing YUV Burst for camera " + id);
+ openDevice(id);
+
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ Log.i(TAG, "Skip burst test on legacy devices.");
+ continue;
+ }
+
+ yuvBurstTestByCamera(id);
+ } finally {
+ closeDevice();
+ closeImageReader();
+ }
+ }
+ }
+
+ private void yuvBurstTestByCamera(String cameraId) throws Exception {
+ // Parameters
+ final int MAX_CONVERGENCE_FRAMES = 150; // 5 sec at 30fps
+ final long MAX_PREVIEW_RESULT_TIMEOUT_MS = 1000;
+ final int BURST_SIZE = 100;
+ final float FRAME_DURATION_MARGIN_FRACTION = 0.1f;
+
+ // Find a good preview size (bound to 1080p)
+ final Size previewSize = mOrderedPreviewSizes.get(0);
+
+ // Get maximum YUV_420_888 size
+ final Size stillSize = getMaxPreviewSize(cameraId, mCameraManager);
+
+ // Find max pipeline depth and sync latency
+ final int maxPipelineDepth = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.REQUEST_PIPELINE_MAX_DEPTH);
+ final int maxSyncLatency = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SYNC_MAX_LATENCY);
+
+ // Find minimum frame duration for full-res YUV_420_888
+ StreamConfigurationMap config = mStaticInfo.getCharacteristics().get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ final long minStillFrameDuration =
+ 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;
+ }
+ }
+ }
+ 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);
+
+ Log.i(TAG, String.format("Selected frame rate range %d - %d for YUV burst",
+ targetRange.getLower(), targetRange.getUpper()));
+
+ // Check if READ_SENSOR_SETTINGS is supported
+ final boolean checkSensorSettings = mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS);
+
+ // Configure basic preview and burst settings
+
+ CaptureRequest.Builder previewBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest.Builder burstBuilder =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+
+ previewBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ targetRange);
+ burstBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
+ targetRange);
+ burstBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ burstBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+
+ // Create session and start up preview
+
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ ImageDropperListener imageDropper = new ImageDropperListener();
+
+ prepareCaptureAndStartPreview(
+ previewBuilder, burstBuilder,
+ previewSize, stillSize,
+ ImageFormat.YUV_420_888, resultListener,
+ /*maxNumImages*/ 3, imageDropper);
+
+ // Create burst
+
+ List<CaptureRequest> burst = new ArrayList<>();
+ for (int i = 0; i < BURST_SIZE; i++) {
+ burst.add(burstBuilder.build());
+ }
+
+ // Converge AE/AWB
+
+ int frameCount = 0;
+ while (true) {
+ CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+ int aeState = result.get(CaptureResult.CONTROL_AE_STATE);
+ int awbState = result.get(CaptureResult.CONTROL_AWB_STATE);
+
+ if (DEBUG) {
+ Log.d(TAG, "aeState: " + aeState + ". awbState: " + awbState);
+ }
+
+ if ((aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED ||
+ aeState == CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED) &&
+ awbState == CaptureResult.CONTROL_AWB_STATE_CONVERGED) {
+ break;
+ }
+ frameCount++;
+ assertTrue(String.format("Cam %s: Can not converge AE and AWB within %d frames",
+ cameraId, MAX_CONVERGENCE_FRAMES),
+ frameCount < MAX_CONVERGENCE_FRAMES);
+ }
+
+ // Lock AF if there's a focuser
+
+ if (mStaticInfo.hasFocuser()) {
+ previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_TRIGGER_START);
+ mSession.capture(previewBuilder.build(), resultListener, mHandler);
+ previewBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
+ CaptureRequest.CONTROL_AF_TRIGGER_IDLE);
+
+ frameCount = 0;
+ while (true) {
+ CaptureResult result = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+ int afState = result.get(CaptureResult.CONTROL_AF_STATE);
+
+ if (DEBUG) {
+ Log.d(TAG, "afState: " + afState);
+ }
+
+ if (afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED ||
+ afState == CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED) {
+ break;
+ }
+ frameCount++;
+ assertTrue(String.format("Cam %s: Cannot lock AF within %d frames", cameraId,
+ MAX_CONVERGENCE_FRAMES),
+ frameCount < MAX_CONVERGENCE_FRAMES);
+ }
+ }
+
+ // Lock AE/AWB
+
+ previewBuilder.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ previewBuilder.set(CaptureRequest.CONTROL_AWB_LOCK, true);
+
+ CaptureRequest lockedRequest = previewBuilder.build();
+ mSession.setRepeatingRequest(lockedRequest, resultListener, mHandler);
+
+ // Wait for first result with locking
+
+ CaptureResult lockedResult =
+ resultListener.getCaptureResultForRequest(lockedRequest, maxPipelineDepth);
+
+ int pipelineDepth = lockedResult.get(CaptureResult.REQUEST_PIPELINE_DEPTH);
+
+ // Then start waiting on results to get the first result that should be synced
+ // up, and also fire the burst as soon as possible
+
+ if (maxSyncLatency == CameraCharacteristics.SYNC_MAX_LATENCY_PER_FRAME_CONTROL) {
+ // The locked result we have is already synchronized so start the burst
+ mSession.captureBurst(burst, resultListener, mHandler);
+ } else {
+ // Need to get a synchronized result, and may need to start burst later to
+ // be synchronized correctly
+
+ boolean burstSent = false;
+
+ // Calculate how many requests we need to still send down to camera before we
+ // know the settings have settled for the burst
+
+ int numFramesWaited = maxSyncLatency;
+ if (numFramesWaited == CameraCharacteristics.SYNC_MAX_LATENCY_UNKNOWN) {
+ numFramesWaited = NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY;
+ }
+
+ int requestsNeededToSync = numFramesWaited - pipelineDepth;
+ for (int i = 0; i < numFramesWaited; i++) {
+ if (!burstSent && requestsNeededToSync <= 0) {
+ mSession.captureBurst(burst, resultListener, mHandler);
+ burstSent = true;
+ }
+ lockedResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+ requestsNeededToSync--;
+ }
+
+ assertTrue("Cam " + cameraId + ": Burst failed to fire!", burstSent);
+ }
+
+ // Read in locked settings if supported
+
+ long burstExposure = 0;
+ long burstFrameDuration = 0;
+ int burstSensitivity = 0;
+ if (checkSensorSettings) {
+ burstExposure = lockedResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+ burstFrameDuration = lockedResult.get(CaptureResult.SENSOR_FRAME_DURATION);
+ burstSensitivity = lockedResult.get(CaptureResult.SENSOR_SENSITIVITY);
+
+ assertTrue(String.format("Cam %s: Frame duration %d ns too short compared to " +
+ "exposure time %d ns", cameraId, burstFrameDuration, burstExposure),
+ burstFrameDuration >= burstExposure);
+
+ assertTrue(String.format("Cam %s: Exposure time is not valid: %d",
+ cameraId, burstExposure),
+ burstExposure > 0);
+ assertTrue(String.format("Cam %s: Frame duration is not valid: %d",
+ cameraId, burstFrameDuration),
+ burstFrameDuration > 0);
+ assertTrue(String.format("Cam %s: Sensitivity is not valid: %d",
+ cameraId, burstSensitivity),
+ burstSensitivity > 0);
+ }
+
+ // Process burst results
+ int burstIndex = 0;
+ CaptureResult burstResult =
+ resultListener.getCaptureResultForRequest(burst.get(burstIndex),
+ maxPipelineDepth + 1);
+ long prevTimestamp = -1;
+ final long frameDurationBound = (long)
+ (minStillFrameDuration * (1 + FRAME_DURATION_MARGIN_FRACTION) );
+
+ List<Long> frameDurations = new ArrayList<>();
+
+ while(true) {
+ // Verify the result
+ assertTrue("Cam " + cameraId + ": Result doesn't match expected request",
+ burstResult.getRequest() == burst.get(burstIndex));
+
+ // Verify locked settings
+ if (checkSensorSettings) {
+ long exposure = burstResult.get(CaptureResult.SENSOR_EXPOSURE_TIME);
+ int sensitivity = burstResult.get(CaptureResult.SENSOR_SENSITIVITY);
+ assertTrue("Cam " + cameraId + ": Exposure not locked!",
+ exposure == burstExposure);
+ assertTrue("Cam " + cameraId + ": Sensitivity not locked!",
+ sensitivity == burstSensitivity);
+ }
+
+ // Collect inter-frame durations
+ long timestamp = burstResult.get(CaptureResult.SENSOR_TIMESTAMP);
+ if (prevTimestamp != -1) {
+ long frameDuration = timestamp - prevTimestamp;
+ frameDurations.add(frameDuration);
+ if (DEBUG) {
+ Log.i(TAG, String.format("Frame %03d Duration %.2f ms", burstIndex,
+ frameDuration/1e6));
+ }
+ }
+ prevTimestamp = timestamp;
+
+ // Get next result
+ burstIndex++;
+ if (burstIndex == BURST_SIZE) break;
+ burstResult = resultListener.getCaptureResult(MAX_PREVIEW_RESULT_TIMEOUT_MS);
+ }
+
+ // Verify inter-frame durations
+
+ long meanFrameSum = 0;
+ for (Long duration : frameDurations) {
+ meanFrameSum += duration;
+ }
+ float meanFrameDuration = (float) meanFrameSum / frameDurations.size();
+
+ float stddevSum = 0;
+ for (Long duration : frameDurations) {
+ stddevSum += (duration - meanFrameDuration) * (duration - meanFrameDuration);
+ }
+ float stddevFrameDuration = (float)
+ Math.sqrt(1.f / (frameDurations.size() - 1 ) * stddevSum);
+
+ Log.i(TAG, String.format("Cam %s: Burst frame duration mean: %.1f, stddev: %.1f", cameraId,
+ meanFrameDuration, stddevFrameDuration));
+
+ assertTrue(
+ String.format("Cam %s: Burst frame duration mean %.1f ns is larger than acceptable, " +
+ "expecting below %d ns, allowing below %d", cameraId,
+ meanFrameDuration, minStillFrameDuration, frameDurationBound),
+ meanFrameDuration <= frameDurationBound);
+
+ // Calculate upper 97.5% bound (assuming durations are normally distributed...)
+ float limit95FrameDuration = meanFrameDuration + 2 * stddevFrameDuration;
+
+ // Don't enforce this yet, but warn
+ if (limit95FrameDuration > frameDurationBound) {
+ Log.w(TAG,
+ String.format("Cam %s: Standard deviation is too large compared to limit: " +
+ "mean: %.1f ms, stddev: %.1f ms: 95%% bound: %f ms", cameraId,
+ meanFrameDuration/1e6, stddevFrameDuration/1e6,
+ limit95FrameDuration/1e6));
+ }
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
index 936883e..8a217fd 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/Camera2SurfaceViewCtsActivity.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import android.os.Bundle;
import android.os.ConditionVariable;
+import android.os.SystemClock;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
@@ -31,6 +32,7 @@
private SurfaceView mSurfaceView;
private int currentWidth = 0;
private int currentHeight = 0;
+ private final Object sizeLock = new Object();
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -54,10 +56,16 @@
timeOutMs, expectWidth, expectHeight));
}
+ synchronized(sizeLock) {
+ if (expectWidth == currentWidth && expectHeight == currentHeight) {
+ return true;
+ }
+ }
+
int waitTimeMs = timeOutMs;
boolean changeSucceeded = false;
while (!changeSucceeded && waitTimeMs > 0) {
- long startTimeMs = System.currentTimeMillis();
+ long startTimeMs = SystemClock.elapsedRealtime();
changeSucceeded = surfaceChangedDone.block(waitTimeMs);
if (!changeSucceeded) {
Log.e(TAG, "Wait for surface change timed out after " + timeOutMs + " ms");
@@ -72,7 +80,7 @@
// again.
changeSucceeded = false;
}
- waitTimeMs -= (System.currentTimeMillis() - startTimeMs);
+ waitTimeMs -= (SystemClock.elapsedRealtime() - startTimeMs);
}
// Couldn't get expected surface size change.
@@ -86,8 +94,10 @@
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i(TAG, "Surface Changed to: " + width + "x" + height);
- currentWidth = width;
- currentHeight = height;
+ synchronized (sizeLock) {
+ currentWidth = width;
+ currentHeight = height;
+ }
surfaceChangedDone.open();
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
index 9f50b43..29c7362 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraDeviceTest.java
@@ -28,6 +28,7 @@
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
@@ -1014,6 +1015,24 @@
}
}
+ private void checkAntiBandingMode(CaptureRequest.Builder request, int template) {
+ if (template == CameraDevice.TEMPLATE_MANUAL) {
+ return;
+ }
+
+ List<Integer> availableAntiBandingModes =
+ Arrays.asList(toObject(mStaticInfo.getAeAvailableAntiBandingModesChecked()));
+
+ if (availableAntiBandingModes.contains(CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO)) {
+ mCollector.expectKeyValueEquals(request, CONTROL_AE_ANTIBANDING_MODE,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
+ } else {
+ mCollector.expectKeyValueIsIn(request, CONTROL_AE_ANTIBANDING_MODE,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ,
+ CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ);
+ }
+ }
+
/**
* <p>Check if 3A metering settings are "up to HAL" in request template</p>
*
@@ -1058,6 +1077,7 @@
checkAfMode(request, template, props);
checkFpsRange(request, template, props);
+ checkAntiBandingMode(request, template);
if (template == CameraDevice.TEMPLATE_MANUAL) {
mCollector.expectKeyValueEquals(request, CONTROL_MODE, CaptureRequest.CONTROL_MODE_OFF);
@@ -1068,8 +1088,6 @@
} else {
mCollector.expectKeyValueEquals(request, CONTROL_AE_MODE,
CaptureRequest.CONTROL_AE_MODE_ON);
- mCollector.expectKeyValueNotEquals(request, CONTROL_AE_ANTIBANDING_MODE,
- CaptureRequest.CONTROL_AE_ANTIBANDING_MODE_OFF);
mCollector.expectKeyValueEquals(request, CONTROL_AE_EXPOSURE_COMPENSATION, 0);
mCollector.expectKeyValueEquals(request, CONTROL_AE_LOCK, false);
mCollector.expectKeyValueEquals(request, CONTROL_AE_PRECAPTURE_TRIGGER,
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 192fb85..27ff6d1 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -41,7 +41,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* <p>Basic test for CameraManager class.</p>
@@ -485,7 +487,132 @@
mCameraManager.registerAvailabilityCallback(mListener, mHandler);
mCameraManager.unregisterAvailabilityCallback(mListener);
mCameraManager.unregisterAvailabilityCallback(mListener);
-
- // TODO: test the listener callbacks
}
+
+ /**
+ * Test that the availability callbacks fire when expected
+ */
+ public void testCameraManagerListenerCallbacks() throws Exception {
+ final int AVAILABILITY_TIMEOUT_MS = 10;
+
+ final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
+
+ CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ availableEventQueue.offer(cameraId);
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ unavailableEventQueue.offer(cameraId);
+ }
+ };
+
+ mCameraManager.registerAvailabilityCallback(ac, mHandler);
+ String[] cameras = mCameraManager.getCameraIdList();
+
+ if (cameras.length == 0) {
+ Log.i(TAG, "No cameras present, skipping test");
+ return;
+ }
+
+ // Verify we received available for all cameras' initial state in a reasonable amount of time
+ HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
+ while (expectedAvailableCameras.size() > 0) {
+ String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue("Did not receive initial availability notices for some cameras",
+ id != null);
+ expectedAvailableCameras.remove(id);
+ }
+ // Verify no unavailable cameras were reported
+ assertTrue("Some camera devices are initially unavailable",
+ unavailableEventQueue.size() == 0);
+
+ // Verify transitions for individual cameras
+ for (String id : cameras) {
+ MockStateCallback mockListener = MockStateCallback.mock();
+ mCameraListener = new BlockingStateCallback(mockListener);
+
+ mCameraManager.openCamera(id, mCameraListener, mHandler);
+
+ // Block until opened
+ mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+ CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+ // Then verify only open happened, and get the camera handle
+ CameraDevice camera = verifyCameraStateOpened(id, mockListener);
+
+ // Verify that we see the expected 'unavailable' event.
+ String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received unavailability notice for wrong ID " +
+ "(expected %s, got %s)", id, candidateId),
+ id == candidateId);
+ assertTrue("Availability events received unexpectedly",
+ availableEventQueue.size() == 0);
+
+ // Verify that we see the expected 'available' event after closing the camera
+
+ camera.close();
+
+ mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+ CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+ candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received availability notice for wrong ID " +
+ "(expected %s, got %s)", id, candidateId),
+ id == candidateId);
+ assertTrue("Unavailability events received unexpectedly",
+ unavailableEventQueue.size() == 0);
+
+ }
+
+ // Verify that we can unregister the listener and see no more events
+ assertTrue("Availability events received unexpectedly",
+ availableEventQueue.size() == 0);
+ assertTrue("Unavailability events received unexpectedly",
+ unavailableEventQueue.size() == 0);
+
+ mCameraManager.unregisterAvailabilityCallback(ac);
+
+ {
+ // Open an arbitrary camera and make sure we don't hear about it
+
+ MockStateCallback mockListener = MockStateCallback.mock();
+ mCameraListener = new BlockingStateCallback(mockListener);
+
+ mCameraManager.openCamera(cameras[0], mCameraListener, mHandler);
+
+ // Block until opened
+ mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+ CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+ // Then verify only open happened, and close the camera
+ CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener);
+
+ camera.close();
+
+ mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+ CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+ // No unavailability or availability callback should have occured
+ String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ",
+ candidateId),
+ candidateId == null);
+
+ candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received availability notice for ID %s unexpectedly ",
+ candidateId),
+ candidateId == null);
+
+
+ }
+
+ } // testCameraManagerListenerCallbacks
+
}
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 0c36832..a17041d 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -18,6 +18,7 @@
import static com.android.ex.camera2.blocking.BlockingStateCallback.*;
+import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.PointF;
@@ -40,7 +41,9 @@
import android.media.Image.Plane;
import android.os.Handler;
import android.util.Log;
+import android.view.Display;
import android.view.Surface;
+import android.view.WindowManager;
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
@@ -63,6 +66,7 @@
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
/**
* A package private utility class for wrapping up the camera2 cts test common utility functions
@@ -199,6 +203,7 @@
public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback {
private final LinkedBlockingQueue<CaptureResult> mQueue =
new LinkedBlockingQueue<CaptureResult>();
+ private AtomicLong mNumFramesArrived = new AtomicLong(0);
@Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
@@ -210,6 +215,7 @@
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
try {
+ mNumFramesArrived.incrementAndGet();
mQueue.put(result);
} catch (InterruptedException e) {
throw new UnsupportedOperationException(
@@ -227,6 +233,10 @@
long frameNumber) {
}
+ public long getTotalNumFrames() {
+ return mNumFramesArrived.get();
+ }
+
public CaptureResult getCaptureResult(long timeout) {
try {
CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
@@ -439,22 +449,26 @@
assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
for (int row = 0; row < h; row++) {
int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+ int length;
if (pixelStride == bytesPerPixel) {
// Special case: optimized read of the entire row
- int length = w * bytesPerPixel;
+ length = w * bytesPerPixel;
buffer.get(data, offset, length);
- // Advance buffer the remainder of the row stride
- buffer.position(buffer.position() + rowStride - length);
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
// Use intermediate buffer to avoid read byte-by-byte from
// DirectByteBuffer, which is very bad for performance
- buffer.get(rowData, 0, rowStride);
+ length = (w - 1) * pixelStride + bytesPerPixel;
+ buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[offset++] = rowData[col * pixelStride];
}
}
+ // Advance buffer the remainder of the row stride
+ if (row < h - 1) {
+ buffer.position(buffer.position() + rowStride - length);
+ }
}
if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
buffer.rewind();
@@ -485,6 +499,23 @@
}
}
+ public static void dumpFile(String fileName, Bitmap data) {
+ FileOutputStream outStream;
+ try {
+ Log.v(TAG, "output will be saved as " + fileName);
+ outStream = new FileOutputStream(fileName);
+ } catch (IOException ioe) {
+ throw new RuntimeException("Unable to create debug output file " + fileName, ioe);
+ }
+
+ try {
+ data.compress(Bitmap.CompressFormat.JPEG, /*quality*/90, outStream);
+ outStream.close();
+ } catch (IOException ioe) {
+ throw new RuntimeException("failed writing data to file " + fileName, ioe);
+ }
+ }
+
public static void dumpFile(String fileName, byte[] data) {
FileOutputStream outStream;
try {
@@ -541,7 +572,27 @@
*/
static public List<Size> getSupportedPreviewSizes(String cameraId,
CameraManager cameraManager, Size bound) throws CameraAccessException {
- return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+ CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
+ StreamConfigurationMap config =
+ props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] rawSizes = config.getOutputSizes(android.view.SurfaceHolder.class);
+ assertArrayNotEmpty(rawSizes,
+ "Available sizes for SurfaceHolder class should not be empty");
+ if (VERBOSE) {
+ Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
+ }
+
+ if (bound == null) {
+ return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
+ }
+
+ List<Size> sizes = new ArrayList<Size>();
+ for (Size sz: rawSizes) {
+ if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+ sizes.add(sz);
+ }
+ }
+ return getAscendingOrderSizes(sizes, /*ascending*/false);
}
/**
@@ -576,7 +627,7 @@
* Get sorted (descending order) size list for given format. Remove the sizes larger than
* the bound. If the bound is null, don't do the size bound filtering.
*/
- static private List<Size> getSortedSizesForFormat(String cameraId,
+ static public List<Size> getSortedSizesForFormat(String cameraId,
CameraManager cameraManager, int format, Size bound) throws CameraAccessException {
Comparator<Size> comparator = new SizeComparator();
Size[] sizes = getSupportedSizeForFormat(format, cameraId, cameraManager);
@@ -610,7 +661,27 @@
*/
static public List<Size> getSupportedVideoSizes(String cameraId,
CameraManager cameraManager, Size bound) throws CameraAccessException {
- return getSortedSizesForFormat(cameraId, cameraManager, ImageFormat.YUV_420_888, bound);
+ CameraCharacteristics props = cameraManager.getCameraCharacteristics(cameraId);
+ StreamConfigurationMap config =
+ props.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ Size[] rawSizes = config.getOutputSizes(android.media.MediaRecorder.class);
+ assertArrayNotEmpty(rawSizes,
+ "Available sizes for MediaRecorder class should not be empty");
+ if (VERBOSE) {
+ Log.v(TAG, "Supported sizes are: " + Arrays.deepToString(rawSizes));
+ }
+
+ if (bound == null) {
+ return getAscendingOrderSizes(Arrays.asList(rawSizes), /*ascending*/false);
+ }
+
+ List<Size> sizes = new ArrayList<Size>();
+ for (Size sz: rawSizes) {
+ if (sz.getWidth() <= bound.getWidth() && sz.getHeight() <= bound.getHeight()) {
+ sizes.add(sz);
+ }
+ }
+ return getAscendingOrderSizes(sizes, /*ascending*/false);
}
/**
@@ -675,6 +746,21 @@
}
/**
+ * Returns true if the given {@code array} contains the given element.
+ *
+ * @param array {@code array} to check for {@code elem}
+ * @param elem {@code elem} to test for
+ * @return {@code true} if the given element is contained
+ */
+ public static boolean contains(int[] array, int elem) {
+ if (array == null) return false;
+ for (int i = 0; i < array.length; i++) {
+ if (elem == array[i]) return true;
+ }
+ return false;
+ }
+
+ /**
* Get object array from byte array.
*
* @param array Input byte array to be converted
@@ -772,6 +858,7 @@
validateJpegData(data, width, height, filePath);
break;
case ImageFormat.YUV_420_888:
+ case ImageFormat.YV12:
validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
break;
case ImageFormat.RAW_SENSOR:
@@ -973,4 +1060,22 @@
}
return resultRegions;
}
+
+ public static Size getPreviewSizeBound(WindowManager windowManager, Size bound) {
+ Display display = windowManager.getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ if (height > width) {
+ height = width;
+ width = display.getHeight();
+ }
+
+ if (bound.getWidth() <= width &&
+ bound.getHeight() <= height)
+ return bound;
+ else
+ return new Size(width, height);
+ }
}
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 f7d95f6..92eef80 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -147,7 +147,8 @@
changeExposure(requestBuilder, DEFAULT_EXP_TIME_NS, DEFAULT_SENSITIVITY);
Size previewSz =
- getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+ getMaxPreviewSize(mCamera.getId(), mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
startPreview(requestBuilder, previewSz, listener);
waitForSettingsApplied(listener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
@@ -202,7 +203,8 @@
STATISTICS_LENS_SHADING_MAP_MODE_ON);
Size previewSz =
- getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+ getMaxPreviewSize(mCamera.getId(), mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
listener = new SimpleCaptureCallback();
startPreview(requestBuilder, previewSz, listener);
@@ -256,7 +258,8 @@
int[] modes = mStaticInfo.getAeAvailableAntiBandingModesChecked();
Size previewSz =
- getMaxPreviewSize(mCamera.getId(), mCameraManager, PREVIEW_SIZE_BOUND);
+ getMaxPreviewSize(mCamera.getId(), mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
for (int mode : modes) {
antiBandingTestByMode(previewSz, mode);
@@ -351,11 +354,6 @@
try {
openDevice(mCameraIds[i]);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
faceDetectionTestByCamera();
} finally {
closeDevice();
@@ -471,6 +469,11 @@
try {
openDevice(id);
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ Log.i(TAG, "Skipping test on legacy devices");
+ continue;
+ }
+
awbModeAndLockTestByCamera();
} finally {
closeDevice();
@@ -1165,7 +1168,9 @@
changeExposure(requestBuilder, expTimes[i], sensitivities[j]);
mSession.capture(requestBuilder.build(), listener, mHandler);
- CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+ // make sure timeout is long enough for long exposure time
+ long timeout = WAIT_FOR_RESULT_TIMEOUT_MS + expTimes[i];
+ CaptureResult result = listener.getCaptureResult(timeout);
long resultExpTime = getValueNotNull(result, CaptureResult.SENSOR_EXPOSURE_TIME);
int resultSensitivity = getValueNotNull(result, CaptureResult.SENSOR_SENSITIVITY);
validateExposureTime(expTimes[i], resultExpTime);
@@ -2141,7 +2146,8 @@
prevCaptureTime = captureTime;
}
mCollector.expectInRange(
- "Frame duration must be in the range of " + Arrays.toString(frameDurationRange),
+ "Frame duration must be in the range of " +
+ Arrays.toString(frameDurationRange),
frameDuration,
(long) (frameDurationRange[0] * (1 - frameDurationErrorMargin)),
(long) (frameDurationRange[1] * (1 + frameDurationErrorMargin)));
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
index 9ec649e..67fad4e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/DngCreatorTest.java
@@ -16,20 +16,29 @@
package android.hardware.camera2.cts;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.DngCreator;
+import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
+import android.hardware.camera2.cts.rs.RawConverter;
+import android.hardware.camera2.cts.rs.RenderScriptSingleton;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.location.Location;
import android.media.ExifInterface;
import android.media.Image;
import android.media.ImageReader;
+import android.os.ConditionVariable;
import android.util.Log;
import android.util.Pair;
import android.util.Size;
@@ -37,10 +46,12 @@
import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream;
+import java.nio.channels.FileChannel;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
-import static android.hardware.camera2.cts.CameraTestUtils.configureCameraSession;
import static android.hardware.camera2.cts.helpers.AssertHelpers.*;
/**
@@ -51,6 +62,10 @@
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final String DEBUG_DNG_FILE = "raw16.dng";
+ private static final double IMAGE_DIFFERENCE_TOLERANCE = 65;
+ private static final int DEFAULT_PATCH_DIMEN = 512;
+ private static final int AE_TIMEOUT_MS = 2000;
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -61,6 +76,13 @@
super.tearDown();
}
+ @Override
+ public synchronized void setContext(Context context) {
+ super.setContext(context);
+
+ RenderScriptSingleton.setContext(context);
+ }
+
/**
* Test basic raw capture and DNG saving functionality for each of the available cameras.
*
@@ -111,7 +133,7 @@
captureReader = createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
captureListener);
Pair<Image, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
- captureReader, captureListener);
+ /*waitForAe*/false, captureReader, captureListener);
CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
// Test simple writeImage, no header checks
@@ -120,14 +142,15 @@
dngCreator.writeImage(outputStream, resultPair.first);
if (VERBOSE) {
- String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+ // Write DNG to file
+ String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_basic_" + deviceId + "_" +
DEBUG_DNG_FILE;
// Write out captured DNG file for the first camera device if setprop is enabled
- fileStream = new FileOutputStream(filePath);
+ fileStream = new FileOutputStream(dngFilePath);
fileStream.write(outputStream.toByteArray());
fileStream.flush();
fileStream.close();
- Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + filePath);
+ Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
}
} finally {
closeDevice(deviceId);
@@ -212,7 +235,7 @@
captureListeners.add(previewListener);
Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
- captureReaders, captureListeners);
+ captureReaders, /*waitForAe*/false, captureListeners);
CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
// Test simple writeImage, no header checks
@@ -231,7 +254,7 @@
dngCreator.writeImage(outputStream, resultPair.first.get(0));
if (VERBOSE) {
- String filePath = DEBUG_FILE_NAME_BASE + "camera_" + deviceId + "_" +
+ String filePath = DEBUG_FILE_NAME_BASE + "/camera_thumb_" + deviceId + "_" +
DEBUG_DNG_FILE;
// Write out captured DNG file for the first camera device if setprop is enabled
fileStream = new FileOutputStream(filePath);
@@ -257,33 +280,260 @@
}
}
- private Pair<Image, CaptureResult> captureSingleRawShot(Size s, ImageReader captureReader,
+ /**
+ * Test basic RAW capture, and ensure that the rendered RAW output is similar to the JPEG
+ * created for the same frame.
+ *
+ * <p>
+ * This test renders the RAW buffer into an RGB bitmap using a rendering pipeline
+ * similar to one in the Adobe DNG validation tool. JPEGs produced by the vendor hardware may
+ * have different tonemapping and saturation applied than the RGB bitmaps produced
+ * from this DNG rendering pipeline, and this test allows for fairly wide variations
+ * between the histograms for the RAW and JPEG buffers to avoid false positives.
+ * </p>
+ *
+ * <p>
+ * To ensure more subtle errors in the colorspace transforms returned for the HAL's RAW
+ * metadata, the DNGs and JPEGs produced here should also be manually compared using external
+ * DNG rendering tools. The DNG, rendered RGB bitmap, and JPEG buffer for this test can be
+ * dumped to the SD card for further examination by enabling the 'verbose' mode for this test
+ * using:
+ * adb shell setprop log.tag.DngCreatorTest VERBOSE
+ * </p>
+ */
+ public void testRaw16JpegConsistency() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ String deviceId = mCameraIds[i];
+ List<ImageReader> captureReaders = new ArrayList<ImageReader>();
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners =
+ new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
+ FileOutputStream fileStream = null;
+ ByteArrayOutputStream outputStream = null;
+ FileChannel fileChannel = null;
+ try {
+ openDevice(deviceId);
+
+ if (!mStaticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ Log.i(TAG, "RAW capability is not supported in camera " + mCameraIds[i] +
+ ". Skip the test.");
+ continue;
+ }
+
+ Size[] targetCaptureSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
+ StaticMetadata.StreamDirection.Output);
+
+ assertTrue("No capture sizes available for RAW format!",
+ targetCaptureSizes.length != 0);
+ Rect activeArray = mStaticInfo.getActiveArraySizeChecked();
+ Size activeArraySize = new Size(activeArray.width(), activeArray.height());
+ assertTrue("Active array has invalid size!", activeArray.width() > 0 &&
+ activeArray.height() > 0);
+ // TODO: Allow PixelArraySize also.
+ assertArrayContains("Available sizes for RAW format must include ActiveArraySize",
+ targetCaptureSizes, activeArraySize);
+
+ // Get largest jpeg size
+ Size[] targetJpegSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+ StaticMetadata.StreamDirection.Output);
+
+ Size largestJpegSize = Collections.max(Arrays.asList(targetJpegSizes),
+ new CameraTestUtils.SizeComparator());
+
+ // Create raw image reader and capture listener
+ CameraTestUtils.SimpleImageReaderListener rawListener
+ = new CameraTestUtils.SimpleImageReaderListener();
+ captureReaders.add(createImageReader(activeArraySize, ImageFormat.RAW_SENSOR, 2,
+ rawListener));
+ captureListeners.add(rawListener);
+
+
+ // Create jpeg image reader and capture listener
+ CameraTestUtils.SimpleImageReaderListener jpegListener
+ = new CameraTestUtils.SimpleImageReaderListener();
+ captureReaders.add(createImageReader(largestJpegSize, ImageFormat.JPEG, 2,
+ jpegListener));
+ captureListeners.add(jpegListener);
+
+ Pair<List<Image>, CaptureResult> resultPair = captureSingleRawShot(activeArraySize,
+ captureReaders, /*waitForAe*/true, captureListeners);
+ CameraCharacteristics characteristics = mStaticInfo.getCharacteristics();
+ Image raw = resultPair.first.get(0);
+ Image jpeg = resultPair.first.get(1);
+
+ Bitmap rawBitmap = Bitmap.createBitmap(raw.getWidth(), raw.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ byte[] rawPlane = new byte[raw.getPlanes()[0].getRowStride() * raw.getHeight()];
+
+ // Render RAW image to a bitmap
+ raw.getPlanes()[0].getBuffer().get(rawPlane);
+ raw.getPlanes()[0].getBuffer().rewind();
+ RawConverter.convertToSRGB(RenderScriptSingleton.getRS(), raw.getWidth(),
+ raw.getHeight(), raw.getPlanes()[0].getRowStride(), rawPlane,
+ characteristics, resultPair.second, /*offsetX*/0, /*offsetY*/0,
+ /*out*/rawBitmap);
+
+ // Decompress JPEG image to a bitmap
+ byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpeg);
+
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap fullSizeJpegBmap = BitmapFactory.decodeByteArray(compressedJpegData,
+ /*offset*/0, compressedJpegData.length, /*inout*/opt);
+ Rect jpegDimens = new Rect(0, 0, fullSizeJpegBmap.getWidth(),
+ fullSizeJpegBmap.getHeight());
+
+ if (VERBOSE) {
+ // Generate DNG file
+ DngCreator dngCreator = new DngCreator(characteristics, resultPair.second);
+ outputStream = new ByteArrayOutputStream();
+ dngCreator.writeImage(outputStream, raw);
+
+ // Write DNG to file
+ String dngFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_" +
+ DEBUG_DNG_FILE;
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileStream = new FileOutputStream(dngFilePath);
+ fileStream.write(outputStream.toByteArray());
+ fileStream.flush();
+ fileStream.close();
+ Log.v(TAG, "Test DNG file for camera " + deviceId + " saved to " + dngFilePath);
+
+ // Write JPEG to file
+ String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_jpeg.jpg";
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileChannel = new FileOutputStream(jpegFilePath).getChannel();
+ fileChannel.write(jpeg.getPlanes()[0].getBuffer());
+ fileChannel.close();
+ Log.v(TAG, "Test JPEG file for camera " + deviceId + " saved to " +
+ jpegFilePath);
+
+ // Write jpeg generated from demosaiced RAW frame to file
+ String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId + "_raw.jpg";
+ // Write out captured DNG file for the first camera device if setprop is enabled
+ fileStream = new FileOutputStream(rawFilePath);
+ rawBitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.v(TAG, "Test converted RAW file for camera " + deviceId + " saved to " +
+ rawFilePath);
+ }
+
+ Size rawBitmapSize = new Size(rawBitmap.getWidth(), rawBitmap.getHeight());
+ assertTrue("Raw bitmap size must be equal to active array size.",
+ rawBitmapSize.equals(activeArraySize));
+
+ // Get square center patch from JPEG and RAW bitmaps
+ RectF jpegRect = new RectF(jpegDimens);
+ RectF rawRect = new RectF(0, 0, rawBitmap.getWidth(), rawBitmap.getHeight());
+ int sideDimen = Math.min(Math.min(Math.min(Math.min(DEFAULT_PATCH_DIMEN,
+ jpegDimens.width()), jpegDimens.height()), rawBitmap.getWidth()),
+ rawBitmap.getHeight());
+
+ RectF jpegIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+ jpegIntermediate.offset(jpegRect.centerX() - jpegIntermediate.centerX(),
+ jpegRect.centerY() - jpegIntermediate.centerY());
+ RectF rawIntermediate = new RectF(0, 0, sideDimen, sideDimen);
+ rawIntermediate.offset(rawRect.centerX() - rawIntermediate.centerX(),
+ rawRect.centerY() - rawIntermediate.centerY());
+ Rect jpegFinal = new Rect();
+ jpegIntermediate.roundOut(jpegFinal);
+ Rect rawFinal = new Rect();
+ rawIntermediate.roundOut(rawFinal);
+
+ Bitmap jpegPatch = Bitmap.createBitmap(fullSizeJpegBmap, jpegFinal.left,
+ jpegFinal.top, jpegFinal.width(), jpegFinal.height());
+ Bitmap rawPatch = Bitmap.createBitmap(rawBitmap, rawFinal.left, rawFinal.top,
+ rawFinal.width(), rawFinal.height());
+
+ // Compare center patch from JPEG and rendered RAW bitmap
+ double difference = BitmapUtils.calcDifferenceMetric(jpegPatch, rawPatch);
+ if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+ // Write JPEG patch to file
+ String jpegFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+ "_jpeg_patch.jpg";
+ fileStream = new FileOutputStream(jpegFilePath);
+ jpegPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.e(TAG, "Failed JPEG patch file for camera " + deviceId + " saved to " +
+ jpegFilePath);
+
+ // Write RAW patch to file
+ String rawFilePath = DEBUG_FILE_NAME_BASE + "/camera_" + deviceId +
+ "_raw_patch.jpg";
+ fileStream = new FileOutputStream(rawFilePath);
+ rawPatch.compress(Bitmap.CompressFormat.JPEG, 90, fileStream);
+ fileStream.flush();
+ fileStream.close();
+ Log.e(TAG, "Failed RAW patch file for camera " + deviceId + " saved to " +
+ rawFilePath);
+
+ fail("Camera " + mCamera.getId() + ": RAW and JPEG image at for the same " +
+ "frame are not similar, center patches have difference metric of " +
+ difference);
+ }
+
+ } finally {
+ closeDevice(deviceId);
+ for (ImageReader r : captureReaders) {
+ closeImageReader(r);
+ }
+
+ if (fileChannel != null) {
+ fileChannel.close();
+ }
+
+ if (outputStream != null) {
+ outputStream.close();
+ }
+
+ if (fileStream != null) {
+ fileStream.close();
+ }
+ }
+ }
+ }
+
+ private Pair<Image, CaptureResult> captureSingleRawShot(Size s, boolean waitForAe,
+ ImageReader captureReader,
CameraTestUtils.SimpleImageReaderListener captureListener) throws Exception {
List<ImageReader> readers = new ArrayList<ImageReader>();
readers.add(captureReader);
List<CameraTestUtils.SimpleImageReaderListener> listeners =
new ArrayList<CameraTestUtils.SimpleImageReaderListener>();
listeners.add(captureListener);
- Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, listeners);
+ Pair<List<Image>, CaptureResult> res = captureSingleRawShot(s, readers, waitForAe,
+ listeners);
return new Pair<Image, CaptureResult>(res.first.get(0), res.second);
}
+ private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s,
+ List<ImageReader> captureReaders, boolean waitForAe,
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+ return captureRawShots(s, captureReaders, waitForAe, captureListeners, 1).get(0);
+ }
+
/**
- * Capture a single raw image.
+ * Capture raw images.
*
- * <p>Capture an raw image for a given size.</p>
+ * <p>Capture raw images for a given size.</p>
*
* @param s The size of the raw image to capture. Must be one of the available sizes for this
* device.
- * @return a pair containing the {@link Image} and {@link CaptureResult} used for this capture.
+ * @return a list of pairs containing a {@link Image} and {@link CaptureResult} used for
+ * each capture.
*/
- private Pair<List<Image>, CaptureResult> captureSingleRawShot(Size s, List<ImageReader> captureReaders,
- List<CameraTestUtils.SimpleImageReaderListener> captureListeners) throws Exception {
+ private List<Pair<List<Image>, CaptureResult>> captureRawShots(Size s,
+ List<ImageReader> captureReaders, boolean waitForAe,
+ List<CameraTestUtils.SimpleImageReaderListener> captureListeners,
+ int numShots) throws Exception {
if (VERBOSE) {
Log.v(TAG, "captureSingleRawShot - Capturing raw image.");
}
- Size maxYuvSz = mOrderedPreviewSizes.get(0);
Size[] targetCaptureSizes =
mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.RAW_SENSOR,
StaticMetadata.StreamDirection.Output);
@@ -298,37 +548,102 @@
}
assertTrue("Capture size is supported.", validSize);
-
// Capture images.
- List<Surface> outputSurfaces = new ArrayList<Surface>();
+ final List<Surface> outputSurfaces = new ArrayList<Surface>();
for (ImageReader captureReader : captureReaders) {
Surface captureSurface = captureReader.getSurface();
outputSurfaces.add(captureSurface);
}
- CaptureRequest.Builder request = prepareCaptureRequestForSurfaces(outputSurfaces);
+ // Set up still capture template targeting JPEG/RAW outputs
+ CaptureRequest.Builder request =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+ assertNotNull("Fail to get captureRequest", request);
+ for (Surface surface : outputSurfaces) {
+ request.addTarget(surface);
+ }
+
+ ImageReader previewReader = null;
+ if (waitForAe) {
+ // Also setup a small YUV output for AE metering if needed
+ Size yuvSize = (mOrderedPreviewSizes.size() == 0) ? null :
+ mOrderedPreviewSizes.get(mOrderedPreviewSizes.size() - 1);
+ assertNotNull("Must support at least one small YUV size.", yuvSize);
+ previewReader = createImageReader(yuvSize, ImageFormat.YUV_420_888,
+ /*maxNumImages*/2, new CameraTestUtils.ImageDropperListener());
+ outputSurfaces.add(previewReader.getSurface());
+ }
+
+ createSession(outputSurfaces);
+
+ if (waitForAe) {
+ CaptureRequest.Builder precaptureRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ assertNotNull("Fail to get captureRequest", precaptureRequest);
+ precaptureRequest.addTarget(previewReader.getSurface());
+ precaptureRequest.set(CaptureRequest.CONTROL_MODE,
+ CaptureRequest.CONTROL_MODE_AUTO);
+ precaptureRequest.set(CaptureRequest.CONTROL_AE_MODE,
+ CaptureRequest.CONTROL_AE_MODE_ON);
+
+ final ConditionVariable waitForAeCondition = new ConditionVariable(/*isOpen*/false);
+ CameraCaptureSession.CaptureCallback captureCallback =
+ new CameraCaptureSession.CaptureCallback() {
+ @Override
+ public void onCaptureProgressed(CameraCaptureSession session,
+ CaptureRequest request, CaptureResult partialResult) {
+ if (partialResult.get(CaptureResult.CONTROL_AE_STATE) ==
+ CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
+ waitForAeCondition.open();
+ }
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraCaptureSession session,
+ CaptureRequest request, TotalCaptureResult result) {
+ if (result.get(CaptureResult.CONTROL_AE_STATE) ==
+ CaptureRequest.CONTROL_AE_STATE_CONVERGED) {
+ waitForAeCondition.open();
+ }
+ }
+ };
+ startCapture(precaptureRequest.build(), /*repeating*/true, captureCallback, mHandler);
+
+ precaptureRequest.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
+ CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START);
+ startCapture(precaptureRequest.build(), /*repeating*/false, captureCallback, mHandler);
+ assertTrue("Timeout out waiting for AE to converge",
+ waitForAeCondition.block(AE_TIMEOUT_MS));
+ }
+
request.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
CameraTestUtils.SimpleCaptureCallback resultListener =
new CameraTestUtils.SimpleCaptureCallback();
- startCapture(request.build(), /*repeating*/false, resultListener, mHandler);
+ CaptureRequest request1 = request.build();
+ for (int i = 0; i < numShots; i++) {
+ startCapture(request1, /*repeating*/false, resultListener, mHandler);
+ }
+ List<Pair<List<Image>, CaptureResult>> ret = new ArrayList<>();
+ for (int i = 0; i < numShots; i++) {
+ // Verify capture result and images
+ CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
- // Verify capture result and images
- CaptureResult result = resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
-
- List<Image> resultImages = new ArrayList<Image>();
- for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
- Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ List<Image> resultImages = new ArrayList<Image>();
+ for (CameraTestUtils.SimpleImageReaderListener captureListener : captureListeners) {
+ Image captureImage = captureListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
/*CameraTestUtils.validateImage(captureImage, s.getWidth(), s.getHeight(),
ImageFormat.RAW_SENSOR, null);*/
- resultImages.add(captureImage);
+ resultImages.add(captureImage);
+ }
+ ret.add(new Pair<List<Image>, CaptureResult>(resultImages, result));
}
// Stop capture, delete the streams.
stopCapture(/*fast*/false);
- return new Pair<List<Image>, CaptureResult>(resultImages, result);
+ return ret;
}
private CaptureRequest.Builder prepareCaptureRequestForSurfaces(List<Surface> surfaces)
@@ -336,7 +651,7 @@
createSession(surfaces);
CaptureRequest.Builder captureBuilder =
- mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
assertNotNull("Fail to get captureRequest", captureBuilder);
for (Surface surface : surfaces) {
captureBuilder.addTarget(surface);
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 f08b60a..8619f73 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -18,6 +18,8 @@
import android.content.Context;
import android.graphics.ImageFormat;
+import android.graphics.Rect;
+import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraCharacteristics.Key;
import android.hardware.camera2.CameraManager;
@@ -25,10 +27,13 @@
import android.hardware.camera2.params.BlackLevelPattern;
import android.hardware.camera2.params.ColorSpaceTransform;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.media.ImageReader;
import android.test.AndroidTestCase;
import android.util.Log;
import android.util.Rational;
+import android.util.Range;
import android.util.Size;
+import android.view.Surface;
import java.util.ArrayList;
import java.util.Arrays;
@@ -269,7 +274,8 @@
int counter = 0;
for (CameraCharacteristics c : mCharacteristics) {
int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
- assertNotNull("android.request.availableCapabilities must never be null");
+ assertNotNull("android.request.availableCapabilities must never be null",
+ actualCapabilities);
if (!arrayContains(actualCapabilities,
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
Log.i(TAG, "RAW capability is not supported in camera " + counter++ +
@@ -341,6 +347,289 @@
}
/**
+ * Test values for static metadata used by the BURST capability.
+ */
+ public void testStaticBurstCharacteristics() {
+ int counter = 0;
+ final float SIZE_ERROR_MARGIN = 0.03f;
+ for (CameraCharacteristics c : mCharacteristics) {
+ int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertNotNull("android.request.availableCapabilities must never be null",
+ actualCapabilities);
+
+ // Check if the burst capability is defined
+ boolean haveBurstCapability = arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
+
+ StreamConfigurationMap config =
+ c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ assertNotNull(String.format("No stream configuration map found for: ID %s",
+ mIds[counter]), config);
+ Rect activeRect = c.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
+ Size sensorSize = new Size(activeRect.width(), activeRect.height());
+
+ // Ensure that max YUV size matches max JPEG size
+ Size maxYuvSize = CameraTestUtils.getMaxSize(
+ config.getOutputSizes(ImageFormat.YUV_420_888));
+ Size maxJpegSize = CameraTestUtils.getMaxSize(config.getOutputSizes(ImageFormat.JPEG));
+
+ boolean haveMaxYuv = maxYuvSize != null ?
+ (maxJpegSize.getWidth() <= maxYuvSize.getWidth() &&
+ maxJpegSize.getHeight() <= maxYuvSize.getHeight()) : false;
+
+ boolean maxYuvMatchSensor =
+ (maxYuvSize.getWidth() <= sensorSize.getWidth() * (1.0 + SIZE_ERROR_MARGIN) &&
+ maxYuvSize.getWidth() >= sensorSize.getWidth() * (1.0 - SIZE_ERROR_MARGIN) &&
+ maxYuvSize.getHeight() <= sensorSize.getHeight() * (1.0 + SIZE_ERROR_MARGIN) &&
+ maxYuvSize.getHeight() >= sensorSize.getHeight() * (1.0 - SIZE_ERROR_MARGIN));
+
+ // Ensure that YUV output is fast enough - needs to be at least 20 fps
+
+ long maxYuvRate =
+ config.getOutputMinFrameDuration(ImageFormat.YUV_420_888, maxYuvSize);
+ final long MIN_DURATION_BOUND_NS = 50000000; // 50 ms, 20 fps
+
+ boolean haveMaxYuvRate = maxYuvRate <= MIN_DURATION_BOUND_NS;
+
+ // Ensure that there's an FPS range that's fast enough to capture at above
+ // minFrameDuration, for full-auto bursts
+ Range[] fpsRanges = c.get(CameraCharacteristics.CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES);
+ float minYuvFps = 1.f / maxYuvRate;
+
+ boolean haveFastAeTargetFps = false;
+ for (Range<Integer> r : fpsRanges) {
+ if (r.getLower() >= minYuvFps) {
+ haveFastAeTargetFps = true;
+ break;
+ }
+ }
+
+ // Ensure that maximum sync latency is small enough for fast setting changes, even if
+ // it's not quite per-frame
+
+ Integer maxSyncLatencyValue = c.get(CameraCharacteristics.SYNC_MAX_LATENCY);
+ assertNotNull(String.format("No sync latency declared for ID %s", mIds[counter]),
+ maxSyncLatencyValue);
+
+ int maxSyncLatency = maxSyncLatencyValue;
+ final long MAX_LATENCY_BOUND = 4;
+ boolean haveFastSyncLatency =
+ (maxSyncLatency <= MAX_LATENCY_BOUND) && (maxSyncLatency >= 0);
+
+ if (haveBurstCapability) {
+ assertTrue(
+ String.format("BURST-capable camera device %s does not have maximum YUV " +
+ "size that is at least max JPEG size",
+ mIds[counter]),
+ haveMaxYuv);
+ assertTrue(
+ String.format("BURST-capable camera device %s YUV frame rate is too slow" +
+ "(%d ns min frame duration reported, less than %d ns expected)",
+ mIds[counter], maxYuvRate, MIN_DURATION_BOUND_NS),
+ haveMaxYuvRate);
+ assertTrue(
+ String.format("BURST-capable camera device %s does not list an AE target " +
+ " FPS range with min FPS >= %f, for full-AUTO bursts",
+ mIds[counter], minYuvFps),
+ haveFastAeTargetFps);
+ assertTrue(
+ String.format("BURST-capable camera device %s YUV sync latency is too long" +
+ "(%d frames reported, [0, %d] frames expected)",
+ mIds[counter], maxSyncLatency, MAX_LATENCY_BOUND),
+ haveFastSyncLatency);
+ assertTrue(
+ "Active array size and max YUV size should be similar",
+ maxYuvMatchSensor);
+ } else {
+ assertTrue(
+ String.format("Camera device %s has all the requirements for BURST" +
+ " capability but does not report it!", mIds[counter]),
+ !(haveMaxYuv && haveMaxYuvRate && haveFastAeTargetFps &&
+ haveFastSyncLatency && maxYuvMatchSensor));
+ }
+
+ counter++;
+ }
+ }
+
+ /**
+ * Cross-check StreamConfigurationMap output
+ */
+ public void testStreamConfigurationMap() {
+ int counter = 0;
+ for (CameraCharacteristics c : mCharacteristics) {
+ Log.i(TAG, "testStreamConfigurationMap: Testing camera ID " + mIds[counter]);
+ StreamConfigurationMap config =
+ c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ assertNotNull(String.format("No stream configuration map found for: ID %s",
+ mIds[counter]), config);
+
+ assertTrue("ImageReader must be supported",
+ config.isOutputSupportedFor(android.media.ImageReader.class));
+ assertTrue("MediaRecorder must be supported",
+ config.isOutputSupportedFor(android.media.MediaRecorder.class));
+ assertTrue("MediaCodec must be supported",
+ config.isOutputSupportedFor(android.media.MediaCodec.class));
+ assertTrue("Allocation must be supported",
+ config.isOutputSupportedFor(android.renderscript.Allocation.class));
+ assertTrue("SurfaceHolder must be supported",
+ config.isOutputSupportedFor(android.view.SurfaceHolder.class));
+ assertTrue("SurfaceTexture must be supported",
+ config.isOutputSupportedFor(android.graphics.SurfaceTexture.class));
+
+ assertTrue("YUV_420_888 must be supported",
+ config.isOutputSupportedFor(ImageFormat.YUV_420_888));
+ assertTrue("JPEG must be supported",
+ config.isOutputSupportedFor(ImageFormat.JPEG));
+
+ // Legacy YUV formats should not be listed
+ assertTrue("NV21 must not be supported",
+ !config.isOutputSupportedFor(ImageFormat.NV21));
+
+ int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
+ assertNotNull("android.request.availableCapabilities must never be null",
+ actualCapabilities);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW)) {
+ assertTrue("RAW_SENSOR must be supported if RAW capability is advertised",
+ config.isOutputSupportedFor(ImageFormat.RAW_SENSOR));
+ }
+
+ // Cross check public formats and sizes
+
+ int[] supportedFormats = config.getOutputFormats();
+ for (int format : supportedFormats) {
+ assertTrue("Format " + format + " fails cross check",
+ config.isOutputSupportedFor(format));
+ Size[] supportedSizes = config.getOutputSizes(format);
+ assertTrue("Supported format " + format + " has no sizes listed",
+ supportedSizes.length > 0);
+ for (Size size : supportedSizes) {
+ if (VERBOSE) {
+ Log.v(TAG,
+ String.format("Testing camera %s, format %d, size %s",
+ mIds[counter], format, size.toString()));
+ }
+
+ long stallDuration = config.getOutputStallDuration(format, size);
+ switch(format) {
+ case ImageFormat.YUV_420_888:
+ assertTrue("YUV_420_888 may not have a non-zero stall duration",
+ stallDuration == 0);
+ break;
+ default:
+ assertTrue("Negative stall duration for format " + format,
+ stallDuration >= 0);
+ break;
+ }
+ long minDuration = config.getOutputMinFrameDuration(format, size);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+ + "format " + format + " for size " + size + " minDuration " +
+ minDuration,
+ minDuration > 0);
+ } else {
+ assertTrue("Need non-negative min frame duration for format " + format,
+ minDuration >= 0);
+ }
+
+ ImageReader testReader = ImageReader.newInstance(
+ size.getWidth(),
+ size.getHeight(),
+ format,
+ 1);
+ Surface testSurface = testReader.getSurface();
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for config %s, format %d",
+ size.toString(), format),
+ config.isOutputSupportedFor(testSurface));
+
+ testReader.close();
+
+ } // sizes
+
+ // Try an invalid size in this format, should round
+ Size invalidSize = findInvalidSize(supportedSizes);
+ // WAR: the intended threshold is 1920, but to counter the bug
+ // in Lollipop framework, we need to set it to 1080 here.
+ // The threshold will be changed back to 1920 in next Android release.
+ int MAX_ROUNDING_WIDTH = 1080;
+ if (invalidSize.getWidth() <= MAX_ROUNDING_WIDTH) {
+ ImageReader testReader = ImageReader.newInstance(
+ invalidSize.getWidth(),
+ invalidSize.getHeight(),
+ format,
+ 1);
+ Surface testSurface = testReader.getSurface();
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for config %s, %d",
+ invalidSize.toString(), format),
+ config.isOutputSupportedFor(testSurface));
+
+ testReader.close();
+ }
+ } // formats
+
+ // Cross-check opaque format and sizes
+
+ SurfaceTexture st = new SurfaceTexture(1);
+ Surface surf = new Surface(st);
+
+ Size[] opaqueSizes = config.getOutputSizes(SurfaceTexture.class);
+ assertTrue("Opaque format has no sizes listed",
+ opaqueSizes.length > 0);
+ for (Size size : opaqueSizes) {
+ long stallDuration = config.getOutputStallDuration(SurfaceTexture.class, size);
+ assertTrue("Opaque output may not have a non-zero stall duration",
+ stallDuration == 0);
+
+ long minDuration = config.getOutputMinFrameDuration(SurfaceTexture.class, size);
+ if (arrayContains(actualCapabilities,
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ assertTrue("MANUAL_SENSOR capability, need positive min frame duration for"
+ + "opaque format",
+ minDuration > 0);
+ } else {
+ assertTrue("Need non-negative min frame duration for opaque format ",
+ minDuration >= 0);
+ }
+ st.setDefaultBufferSize(size.getWidth(), size.getHeight());
+
+ assertTrue(
+ String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+ size.toString()),
+ config.isOutputSupportedFor(surf));
+
+ } // opaque sizes
+
+ // Try invalid opaque size, should get rounded
+ Size invalidSize = findInvalidSize(opaqueSizes);
+ st.setDefaultBufferSize(invalidSize.getWidth(), invalidSize.getHeight());
+ assertTrue(
+ String.format("isOutputSupportedFor fails for SurfaceTexture config %s",
+ invalidSize.toString()),
+ config.isOutputSupportedFor(surf));
+
+ counter++;
+ } // mCharacteristics
+
+ }
+
+ /**
+ * Create an invalid size that's close to one of the good sizes in the list, but not one of them
+ */
+ private Size findInvalidSize(Size[] goodSizes) {
+ Size invalidSize = new Size(goodSizes[0].getWidth() + 1, goodSizes[0].getHeight());
+ while(arrayContains(goodSizes, invalidSize)) {
+ invalidSize = new Size(invalidSize.getWidth() + 1, invalidSize.getHeight());
+ }
+ return invalidSize;
+ }
+
+ /**
* Check key is present in characteristics if the hardware level is at least {@code hwLevel};
* check that the key is present if the actual capabilities are one of {@code capabilities}.
*
@@ -353,7 +642,8 @@
assertNotNull("android.info.supportedHardwareLevel must never be null", actualHwLevel);
int[] actualCapabilities = c.get(CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES);
- assertNotNull("android.request.availableCapabilities must never be null");
+ assertNotNull("android.request.availableCapabilities must never be null",
+ actualCapabilities);
List<Key<?>> allKeys = c.getKeys();
@@ -412,6 +702,20 @@
return false;
}
+ private static <T> boolean arrayContains(T[] arr, T needle) {
+ if (arr == null) {
+ return false;
+ }
+
+ for (T elem : arr) {
+ if (elem.equals(needle)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
private static boolean arrayContainsAnyOf(int[] arr, int[] needles) {
for (int needle : needles) {
if (arrayContains(arr, needle)) {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
index 61f25fb..a410775 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ImageReaderTest.java
@@ -16,26 +16,40 @@
package android.hardware.camera2.cts;
-import static android.hardware.camera2.cts.CameraTestUtils.*;
-
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.BitmapRegionDecoder;
+import android.graphics.Color;
import android.graphics.ImageFormat;
-import android.hardware.camera2.CameraDevice;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
-import android.util.Size;
import android.hardware.camera2.cts.helpers.StaticMetadata;
+import android.hardware.camera2.cts.rs.BitmapUtils;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.os.ConditionVariable;
import android.util.Log;
+import android.util.Size;
import android.view.Surface;
+import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
+import static android.hardware.camera2.cts.CameraTestUtils.CAPTURE_RESULT_TIMEOUT_MS;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleCaptureCallback;
+import static android.hardware.camera2.cts.CameraTestUtils.SimpleImageReaderListener;
+import static android.hardware.camera2.cts.CameraTestUtils.dumpFile;
+import static android.hardware.camera2.cts.CameraTestUtils.getValueNotNull;
+
/**
* <p>Basic test for ImageReader APIs. It uses CameraDevice as producer, camera
* sends the data to the surface provided by imageReader. Below image formats
@@ -49,10 +63,16 @@
public class ImageReaderTest extends Camera2AndroidTestCase {
private static final String TAG = "ImageReaderTest";
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
// number of frame (for streaming requests) to be verified.
private static final int NUM_FRAME_VERIFIED = 2;
// Max number of images can be accessed simultaneously from ImageReader.
private static final int MAX_NUM_IMAGES = 5;
+ // Max difference allowed between YUV and JPEG patches. This tolerance is intentionally very
+ // generous to avoid false positives due to punch/saturation operations vendors apply to the
+ // JPEG outputs.
+ private static final double IMAGE_DIFFERENCE_TOLERANCE = 30;
private SimpleImageListener mListener;
@@ -88,10 +108,6 @@
try {
Log.v(TAG, "Testing jpeg capture for Camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/false);
} finally {
closeDevice(id);
@@ -117,10 +133,6 @@
try {
Log.v(TAG, "Testing repeating jpeg capture for Camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
bufferFormatTestByCamera(ImageFormat.JPEG, /*repeating*/true);
} finally {
closeDevice(id);
@@ -141,9 +153,29 @@
}
}
- public void testInvalidAccessTest() {
- // TODO: test invalid access case, see if we can receive expected
- // exceptions
+ /**
+ * Test invalid access of image byte buffers: when an image is closed, further access
+ * of the image byte buffers will get an IllegalStateException. The basic assumption of
+ * this test is that the ImageReader always gives direct byte buffer, which is always true
+ * for camera case. For if the produced image byte buffer is not direct byte buffer, there
+ * is no guarantee to get an ISE for this invalid access case.
+ */
+ public void testInvalidAccessTest() throws Exception {
+ // Test byte buffer access after an image is released, it should throw ISE.
+ for (String id : mCameraIds) {
+ try {
+ Log.v(TAG, "Testing invalid image access for Camera " + id);
+ openDevice(id);
+ bufferAccessAfterRelease();
+ fail("ImageReader should throw IllegalStateException when accessing a byte buffer"
+ + " after the image is closed");
+ } catch (IllegalStateException e) {
+ // Expected.
+ } finally {
+ closeDevice(id);
+ }
+ }
+
}
/**
@@ -156,10 +188,7 @@
try {
Log.v(TAG, "YUV and JPEG testing for camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
+
bufferFormatWithYuvTestByCamera(ImageFormat.JPEG);
} finally {
closeDevice(id);
@@ -184,6 +213,302 @@
}
}
+ /**
+ * Check that the center patches for YUV and JPEG outputs for the same frame match for each YUV
+ * resolution and format supported.
+ */
+ public void testAllOutputYUVResolutions() throws Exception {
+ for (String id : mCameraIds) {
+ try {
+ Log.v(TAG, "Testing all YUV image resolutions for camera " + id);
+ openDevice(id);
+
+ // Skip warmup on FULL mode devices.
+ int warmupCaptureNumber = (mStaticInfo.isHardwareLevelLegacy()) ?
+ MAX_NUM_IMAGES - 1 : 0;
+
+ // NV21 isn't supported by ImageReader.
+ final int[] YUVFormats = new int[] {ImageFormat.YUV_420_888, ImageFormat.YV12};
+
+ CameraCharacteristics.Key<StreamConfigurationMap> key =
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP;
+ StreamConfigurationMap config = mStaticInfo.getValueFromKeyNonNull(key);
+ int[] supportedFormats = config.getOutputFormats();
+ List<Integer> supportedYUVFormats = new ArrayList<>();
+ for (int format : YUVFormats) {
+ if (CameraTestUtils.contains(supportedFormats, format)) {
+ supportedYUVFormats.add(format);
+ }
+ }
+
+ Size[] jpegSizes = mStaticInfo.getAvailableSizesForFormatChecked(ImageFormat.JPEG,
+ StaticMetadata.StreamDirection.Output);
+ assertFalse("JPEG output not supported for camera " + id +
+ ", at least one JPEG output is required.", jpegSizes.length == 0);
+
+ Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
+
+ for (int format : supportedYUVFormats) {
+ Size[] targetCaptureSizes =
+ mStaticInfo.getAvailableSizesForFormatChecked(format,
+ StaticMetadata.StreamDirection.Output);
+
+ for (Size captureSz : targetCaptureSizes) {
+ if (VERBOSE) {
+ Log.v(TAG, "Testing yuv size " + captureSz + " and jpeg size "
+ + maxJpegSize + " for camera " + mCamera.getId());
+ }
+
+ ImageReader jpegReader = null;
+ ImageReader yuvReader = null;
+ try {
+ // Create YUV image reader
+ SimpleImageReaderListener yuvListener = new SimpleImageReaderListener();
+ yuvReader = createImageReader(captureSz, format, MAX_NUM_IMAGES,
+ yuvListener);
+ Surface yuvSurface = yuvReader.getSurface();
+
+ // Create JPEG image reader
+ SimpleImageReaderListener jpegListener =
+ new SimpleImageReaderListener();
+ jpegReader = createImageReader(maxJpegSize,
+ ImageFormat.JPEG, MAX_NUM_IMAGES, jpegListener);
+ Surface jpegSurface = jpegReader.getSurface();
+
+ // Setup session
+ List<Surface> outputSurfaces = new ArrayList<Surface>();
+ outputSurfaces.add(yuvSurface);
+ outputSurfaces.add(jpegSurface);
+ createSession(outputSurfaces);
+
+ // Warm up camera preview (mainly to give legacy devices time to do 3A).
+ CaptureRequest.Builder warmupRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ warmupRequest.addTarget(yuvSurface);
+ assertNotNull("Fail to get CaptureRequest.Builder", warmupRequest);
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+
+ for (int i = 0; i < warmupCaptureNumber; i++) {
+ startCapture(warmupRequest.build(), /*repeating*/false,
+ resultListener, mHandler);
+ }
+ for (int i = 0; i < warmupCaptureNumber; i++) {
+ resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+ Image image = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ image.close();
+ }
+
+ // Capture image.
+ CaptureRequest.Builder mainRequest =
+ mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ for (Surface s : outputSurfaces) {
+ mainRequest.addTarget(s);
+ }
+
+ startCapture(mainRequest.build(), /*repeating*/false, resultListener,
+ mHandler);
+
+ // Verify capture result and images
+ resultListener.getCaptureResult(CAPTURE_WAIT_TIMEOUT_MS);
+
+ Image yuvImage = yuvListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+ Image jpegImage = jpegListener.getImage(CAPTURE_WAIT_TIMEOUT_MS);
+
+ //Validate captured images.
+ CameraTestUtils.validateImage(yuvImage, captureSz.getWidth(),
+ captureSz.getHeight(), format, /*filePath*/null);
+ CameraTestUtils.validateImage(jpegImage, maxJpegSize.getWidth(),
+ maxJpegSize.getHeight(), ImageFormat.JPEG, /*filePath*/null);
+
+ // Compare the image centers.
+ RectF jpegDimens = new RectF(0, 0, jpegImage.getWidth(),
+ jpegImage.getHeight());
+ RectF yuvDimens = new RectF(0, 0, yuvImage.getWidth(),
+ yuvImage.getHeight());
+
+ // Find scale difference between YUV and JPEG output
+ Matrix m = new Matrix();
+ m.setRectToRect(yuvDimens, jpegDimens, Matrix.ScaleToFit.START);
+ RectF scaledYuv = new RectF();
+ m.mapRect(scaledYuv, yuvDimens);
+ float scale = scaledYuv.width() / yuvDimens.width();
+
+ final int PATCH_DIMEN = 40; // pixels in YUV
+
+ // Find matching square patch of pixels in YUV and JPEG output
+ RectF tempPatch = new RectF(0, 0, PATCH_DIMEN, PATCH_DIMEN);
+ tempPatch.offset(yuvDimens.centerX() - tempPatch.centerX(),
+ yuvDimens.centerY() - tempPatch.centerY());
+ Rect yuvPatch = new Rect();
+ tempPatch.roundOut(yuvPatch);
+
+ tempPatch.set(0, 0, PATCH_DIMEN * scale, PATCH_DIMEN * scale);
+ tempPatch.offset(jpegDimens.centerX() - tempPatch.centerX(),
+ jpegDimens.centerY() - tempPatch.centerY());
+ Rect jpegPatch = new Rect();
+ tempPatch.roundOut(jpegPatch);
+
+ // Decode center patches
+ int[] yuvColors = convertPixelYuvToRgba(yuvPatch.width(),
+ yuvPatch.height(), yuvPatch.left, yuvPatch.top, yuvImage);
+ Bitmap yuvBmap = Bitmap.createBitmap(yuvColors, yuvPatch.width(),
+ yuvPatch.height(), Bitmap.Config.ARGB_8888);
+
+ byte[] compressedJpegData = CameraTestUtils.getDataFromImage(jpegImage);
+ BitmapRegionDecoder decoder = BitmapRegionDecoder.newInstance(
+ compressedJpegData, /*offset*/0, compressedJpegData.length,
+ /*isShareable*/true);
+ BitmapFactory.Options opt = new BitmapFactory.Options();
+ opt.inPreferredConfig = Bitmap.Config.ARGB_8888;
+ Bitmap fullSizeJpegBmap = decoder.decodeRegion(jpegPatch, opt);
+ Bitmap jpegBmap = Bitmap.createScaledBitmap(fullSizeJpegBmap,
+ yuvPatch.width(), yuvPatch.height(), /*filter*/true);
+
+ // Compare two patches using average of per-pixel differences
+ double difference = BitmapUtils.calcDifferenceMetric(yuvBmap, jpegBmap);
+
+ Log.i(TAG, "Difference for resolution " + captureSz + " is: " +
+ difference);
+ if (difference > IMAGE_DIFFERENCE_TOLERANCE) {
+ // Dump files if running in verbose mode
+ if (DEBUG) {
+ String jpegFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+ "_jpeg.jpg";
+ dumpFile(jpegFileName, jpegBmap);
+ String fullSizeJpegFileName = DEBUG_FILE_NAME_BASE + "/" +
+ captureSz + "_full_jpeg.jpg";
+ dumpFile(fullSizeJpegFileName, compressedJpegData);
+ String yuvFileName = DEBUG_FILE_NAME_BASE + "/" + captureSz +
+ "_yuv.jpg";
+ dumpFile(yuvFileName, yuvBmap);
+ String fullSizeYuvFileName = DEBUG_FILE_NAME_BASE + "/" +
+ captureSz + "_full_yuv.jpg";
+ int[] fullYUVColors = convertPixelYuvToRgba(yuvImage.getWidth(),
+ yuvImage.getHeight(), 0, 0, yuvImage);
+ Bitmap fullYUVBmap = Bitmap.createBitmap(fullYUVColors,
+ yuvImage.getWidth(), yuvImage.getHeight(),
+ Bitmap.Config.ARGB_8888);
+ dumpFile(fullSizeYuvFileName, fullYUVBmap);
+ }
+ fail("Camera " + mCamera.getId() + ": YUV and JPEG image at " +
+ "capture size " + captureSz + " for the same frame are " +
+ "not similar, center patches have difference metric of " +
+ difference);
+ }
+
+ // Stop capture, delete the streams.
+ stopCapture(/*fast*/false);
+ } finally {
+ closeImageReader(jpegReader);
+ jpegReader = null;
+ closeImageReader(yuvReader);
+ yuvReader = null;
+ }
+ }
+ }
+
+ } finally {
+ closeDevice(id);
+ }
+ }
+ }
+
+ /**
+ * Convert a rectangular patch in a YUV image to an ARGB color array.
+ *
+ * @param w width of the patch.
+ * @param h height of the patch.
+ * @param wOffset offset of the left side of the patch.
+ * @param hOffset offset of the top of the patch.
+ * @param yuvImage a YUV image to select a patch from.
+ * @return the image patch converted to RGB as an ARGB color array.
+ */
+ private static int[] convertPixelYuvToRgba(int w, int h, int wOffset, int hOffset,
+ Image yuvImage) {
+ final int CHANNELS = 3; // yuv
+ final float COLOR_RANGE = 255f;
+
+ assertTrue("Invalid argument to convertPixelYuvToRgba",
+ w > 0 && h > 0 && wOffset >= 0 && hOffset >= 0);
+ assertNotNull(yuvImage);
+
+ int imageFormat = yuvImage.getFormat();
+ assertTrue("YUV image must have YUV-type format",
+ imageFormat == ImageFormat.YUV_420_888 || imageFormat == ImageFormat.YV12 ||
+ imageFormat == ImageFormat.NV21);
+
+ int height = yuvImage.getHeight();
+ int width = yuvImage.getWidth();
+
+ Rect imageBounds = new Rect(/*left*/0, /*top*/0, /*right*/width, /*bottom*/height);
+ Rect crop = new Rect(/*left*/wOffset, /*top*/hOffset, /*right*/wOffset + w,
+ /*bottom*/hOffset + h);
+ assertTrue("Output rectangle" + crop + " must lie within image bounds " + imageBounds,
+ imageBounds.contains(crop));
+ Image.Plane[] planes = yuvImage.getPlanes();
+
+ Image.Plane yPlane = planes[0];
+ Image.Plane cbPlane = planes[1];
+ Image.Plane crPlane = planes[2];
+
+ ByteBuffer yBuf = yPlane.getBuffer();
+ int yPixStride = yPlane.getPixelStride();
+ int yRowStride = yPlane.getRowStride();
+ ByteBuffer cbBuf = cbPlane.getBuffer();
+ int cbPixStride = cbPlane.getPixelStride();
+ int cbRowStride = cbPlane.getRowStride();
+ ByteBuffer crBuf = crPlane.getBuffer();
+ int crPixStride = crPlane.getPixelStride();
+ int crRowStride = crPlane.getRowStride();
+
+ int[] output = new int[w * h];
+
+ // TODO: Optimize this with renderscript intrinsics
+ byte[] yRow = new byte[yPixStride * w];
+ byte[] cbRow = new byte[cbPixStride * w / 2];
+ byte[] crRow = new byte[crPixStride * w / 2];
+ yBuf.mark();
+ cbBuf.mark();
+ crBuf.mark();
+ int initialYPos = yBuf.position();
+ int initialCbPos = cbBuf.position();
+ int initialCrPos = crBuf.position();
+ int outputPos = 0;
+ for (int i = hOffset; i < hOffset + h; i++) {
+ yBuf.position(initialYPos + i * yRowStride + wOffset * yPixStride);
+ yBuf.get(yRow);
+ if ((i & 1) == (hOffset & 1)) {
+ cbBuf.position(initialCbPos + (i / 2) * cbRowStride + wOffset * cbPixStride / 2);
+ cbBuf.get(cbRow);
+ crBuf.position(initialCrPos + (i / 2) * crRowStride + wOffset * crPixStride / 2);
+ crBuf.get(crRow);
+ }
+ for (int j = 0, yPix = 0, crPix = 0, cbPix = 0; j < w; j++, yPix += yPixStride) {
+ float y = yRow[yPix] & 0xFF;
+ float cb = cbRow[cbPix] & 0xFF;
+ float cr = crRow[crPix] & 0xFF;
+
+ // convert YUV -> RGB (from JFIF's "Conversion to and from RGB" section)
+ int r = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.402f * (cr - 128)));
+ int g = (int) Math.max(0.0f,
+ Math.min(COLOR_RANGE, y - 0.34414f * (cb - 128) - 0.71414f * (cr - 128)));
+ int b = (int) Math.max(0.0f, Math.min(COLOR_RANGE, y + 1.772f * (cb - 128)));
+
+ // Convert to ARGB pixel color (use opaque alpha)
+ output[outputPos++] = Color.rgb(r, g, b);
+
+ if ((j & 1) == 1) {
+ crPix += crPixStride;
+ cbPix += cbPixStride;
+ }
+ }
+ }
+ yBuf.rewind();
+ cbBuf.rewind();
+ crBuf.rewind();
+
+ return output;
+ }
/**
* Test capture a given format stream with yuv stream simultaneously.
@@ -270,6 +595,36 @@
}
}
+ /**
+ * Test buffer access after release, YUV420_888 single capture is tested. This method
+ * should throw ISE.
+ */
+ private void bufferAccessAfterRelease() throws Exception {
+ final int FORMAT = ImageFormat.YUV_420_888;
+ Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(FORMAT,
+ StaticMetadata.StreamDirection.Output);
+
+ try {
+ // Create ImageReader.
+ mListener = new SimpleImageListener();
+ createDefaultImageReader(availableSizes[0], FORMAT, MAX_NUM_IMAGES, mListener);
+
+ // Start capture.
+ CaptureRequest request = prepareCaptureRequest();
+ SimpleCaptureCallback listener = new SimpleCaptureCallback();
+ startCapture(request, /* repeating */false, listener, mHandler);
+
+ mListener.waitForAnyImageAvailable(CAPTURE_WAIT_TIMEOUT_MS);
+ Image img = mReader.acquireNextImage();
+ ByteBuffer buffer = img.getPlanes()[0].getBuffer();
+ img.close();
+
+ byte data = buffer.get(); // An ISE should be thrown here.
+ } finally {
+ closeDefaultImageReader();
+ }
+ }
+
private void bufferFormatTestByCamera(int format, boolean repeating) throws Exception {
Size[] availableSizes = mStaticInfo.getAvailableSizesForFormatChecked(format,
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 8b8f2f6..e669007 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/PerformanceTest.java
@@ -195,11 +195,6 @@
try {
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
boolean partialsExpected = mStaticInfo.getPartialResultCount() > 1;
long startTimeMs;
boolean isPartialTimingValid = partialsExpected;
@@ -216,8 +211,8 @@
new SimpleTimingResultListener();
SimpleImageListener imageListener = new SimpleImageListener();
- Size maxYuvSize = CameraTestUtils.getSupportedPreviewSizes(id, mCameraManager,
- /*bound*/null).get(0);
+ Size maxYuvSize = CameraTestUtils.getSortedSizesForFormat(
+ id, mCameraManager, ImageFormat.YUV_420_888, /*bound*/null).get(0);
prepareCaptureAndStartPreview(previewBuilder, captureBuilder,
mOrderedPreviewSizes.get(0), maxYuvSize,
@@ -257,7 +252,7 @@
// simulate real scenario (preview runs a bit)
waitForNumResults(previewResultListener, NUM_RESULTS_WAIT);
- stopPreview();
+ blockingStopPreview();
}
mReportLog.printArray("Camera " + id
@@ -335,7 +330,9 @@
*/
private void initializeImageReader(String cameraId, int format) throws Exception {
mOrderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
- cameraId, mCameraManager, CameraTestUtils.PREVIEW_SIZE_BOUND);
+ cameraId, mCameraManager,
+ CameraTestUtils.getPreviewSizeBound(mWindowManager,
+ CameraTestUtils.PREVIEW_SIZE_BOUND));
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
createImageReader(maxPreviewSize, format, NUM_MAX_IMAGES, /*listener*/null);
updatePreviewSurface(maxPreviewSize);
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 21920b7..20a7834 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -30,8 +30,8 @@
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaCodecList;
+import android.media.MediaExtractor;
import android.media.MediaFormat;
-import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Environment;
import android.os.SystemClock;
@@ -66,15 +66,15 @@
private static final int VIDEO_FRAME_RATE = 30;
private final String VIDEO_FILE_PATH = Environment.getExternalStorageDirectory().getPath();
private static final int[] mCamcorderProfileList = {
+ CamcorderProfile.QUALITY_HIGH,
CamcorderProfile.QUALITY_2160P,
CamcorderProfile.QUALITY_1080P,
- CamcorderProfile.QUALITY_480P,
CamcorderProfile.QUALITY_720P,
+ CamcorderProfile.QUALITY_480P,
CamcorderProfile.QUALITY_CIF,
- CamcorderProfile.QUALITY_LOW,
- CamcorderProfile.QUALITY_HIGH,
CamcorderProfile.QUALITY_QCIF,
CamcorderProfile.QUALITY_QVGA,
+ CamcorderProfile.QUALITY_LOW,
};
private static final int MAX_VIDEO_SNAPSHOT_IMAGES = 5;
private static final int BURST_VIDEO_SNAPSHOT_NUM = 3;
@@ -86,6 +86,7 @@
private String mOutMediaFileName;
private int mVideoFrameRate;
private Size mVideoSize;
+ private long mRecordingStartTime;
@Override
protected void setUp() throws Exception {
@@ -119,7 +120,7 @@
initSupportedVideoSize(mCameraIds[i]);
- basicRecordingTestByCamera();
+ basicRecordingTestByCamera(mCamcorderProfileList);
} finally {
closeDevice();
releaseRecorder();
@@ -222,6 +223,53 @@
}
/**
+ * <p>
+ * Test recording framerate accuracy when switching from low FPS to high FPS.
+ * </p>
+ * <p>
+ * This test first record a video with profile of lowest framerate then record a video with
+ * profile of highest framerate. Make sure that the video framerate are still accurate.
+ * </p>
+ */
+ public void testRecordingFramerateLowToHigh() throws Exception {
+ for (int i = 0; i < mCameraIds.length; i++) {
+ try {
+ Log.i(TAG, "Testing basic recording for camera " + mCameraIds[i]);
+ // Re-use the MediaRecorder object for the same camera device.
+ mMediaRecorder = new MediaRecorder();
+ openDevice(mCameraIds[i]);
+
+ initSupportedVideoSize(mCameraIds[i]);
+
+ int minFpsProfileId = -1, minFps = 1000;
+ int maxFpsProfileId = -1, maxFps = 0;
+ int cameraId = Integer.valueOf(mCamera.getId());
+
+ for (int profileId : mCamcorderProfileList) {
+ if (!CamcorderProfile.hasProfile(cameraId, profileId)) {
+ continue;
+ }
+ CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
+ if (profile.videoFrameRate < minFps) {
+ minFpsProfileId = profileId;
+ minFps = profile.videoFrameRate;
+ }
+ if (profile.videoFrameRate > maxFps) {
+ maxFpsProfileId = profileId;
+ maxFps = profile.videoFrameRate;
+ }
+ }
+
+ int camcorderProfileList[] = new int[] {minFpsProfileId, maxFpsProfileId};
+ basicRecordingTestByCamera(camcorderProfileList);
+ } finally {
+ closeDevice();
+ releaseRecorder();
+ }
+ }
+ }
+
+ /**
* Test slow motion recording where capture rate (camera output) is different with
* video (playback) frame rate for each camera if high speed recording is supported
* by both camera and encoder.
@@ -288,8 +336,9 @@
updatePreviewSurfaceWithVideoSize(size);
// Start recording
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
- fpsRange);
+ fpsRange, resultListener);
long startTime = SystemClock.elapsedRealtime();
// Record certain duration.
@@ -297,10 +346,12 @@
// Stop recording and preview
stopRecording(/*useMediaRecorder*/true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ videoFramerate);
// Validation.
- validateRecording(size, duration * SLOWMO_SLOW_FACTOR);
+ validateRecording(size, durationMs);
}
@@ -317,7 +368,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;
}
@@ -330,7 +381,8 @@
}
private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
- int captureRate, Range<Integer> fpsRange) throws Exception {
+ int captureRate, Range<Integer> fpsRange,
+ CameraCaptureSession.CaptureCallback listener) throws Exception {
List<Surface> outputSurfaces = new ArrayList<Surface>(2);
assertTrue("Both preview and recording surfaces should be valid",
mPreviewSurface.isValid() && mRecordingSurface.isValid());
@@ -371,7 +423,7 @@
for (int i = 0; i < slowMotionFactor - 1; i++) {
slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
}
- mSession.setRepeatingBurst(slowMoRequests, null, null);
+ mSession.setRepeatingBurst(slowMoRequests, listener, mHandler);
if (useMediaRecorder) {
mMediaRecorder.start();
@@ -385,9 +437,9 @@
* Test camera recording by using each available CamcorderProfile for a
* given camera. preview size is set to the video size.
*/
- private void basicRecordingTestByCamera() throws Exception {
+ private void basicRecordingTestByCamera(int[] camcorderProfileList) throws Exception {
Size maxPreviewSize = mOrderedPreviewSizes.get(0);
- for (int profileId : mCamcorderProfileList) {
+ for (int profileId : camcorderProfileList) {
int cameraId = Integer.valueOf(mCamera.getId());
if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
allowedUnsupported(cameraId, profileId)) {
@@ -423,18 +475,25 @@
updatePreviewSurfaceWithVideoSize(videoSz);
// Start recording
- startRecording(/* useMediaRecorder */true);
- long startTime = SystemClock.elapsedRealtime();
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ startRecording(/* useMediaRecorder */true, resultListener);
// Record certain duration.
SystemClock.sleep(RECORDING_DURATION_MS);
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ profile.videoFrameRate);
+
+ if (VERBOSE) {
+ Log.v(TAG, "video frame rate: " + profile.videoFrameRate +
+ ", num of frames produced: " + resultListener.getTotalNumFrames());
+ }
// Validation.
- validateRecording(videoSz, duration);
+ validateRecording(videoSz, durationMs);
}
}
@@ -466,18 +525,20 @@
updatePreviewSurfaceWithVideoSize(sz);
// Start recording
- startRecording(/* useMediaRecorder */true);
- long startTime = SystemClock.elapsedRealtime();
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ startRecording(/* useMediaRecorder */true, resultListener);
// Record certain duration.
SystemClock.sleep(RECORDING_DURATION_MS);
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ VIDEO_FRAME_RATE);
// Validation.
- validateRecording(sz, duration);
+ validateRecording(sz, durationMs);
}
}
@@ -505,11 +566,6 @@
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
initSupportedVideoSize(id);
videoSnapshotTestByCamera(burstTest);
@@ -558,6 +614,10 @@
throws Exception {
final int NUM_SINGLE_SHOT_TEST = 5;
final int FRAMEDROP_TOLERANCE = 8;
+ final int FRAME_SIZE_15M = 15000000;
+ final float FRAME_DROP_TOLERENCE_FACTOR = 1.5f;
+ int kFrameDrop_Tolerence = FRAMEDROP_TOLERANCE;
+
for (int profileId : mCamcorderProfileList) {
int cameraId = Integer.valueOf(mCamera.getId());
if (!CamcorderProfile.hasProfile(cameraId, profileId) ||
@@ -567,14 +627,21 @@
CamcorderProfile profile = CamcorderProfile.get(cameraId, profileId);
Size videoSz = new Size(profile.videoFrameWidth, profile.videoFrameHeight);
+ 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;
+ }
+
if (!mSupportedVideoSizes.contains(videoSz)) {
mCollector.addMessage("Video size " + videoSz.toString() + " for profile ID " +
profileId + " must be one of the camera device supported video size!");
continue;
}
- Size maxPreviewSize = mOrderedPreviewSizes.get(0);
-
// 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.
@@ -586,6 +653,8 @@
videoSnapshotSz = candidateSize;
}
}
+ if (videoSnapshotSz.getWidth() * videoSnapshotSz.getHeight() > FRAME_SIZE_15M)
+ kFrameDrop_Tolerence = (int)(FRAMEDROP_TOLERANCE * FRAME_DROP_TOLERENCE_FACTOR);
/**
* Only test full res snapshot when below conditions are all true.
@@ -656,11 +725,17 @@
SystemClock.sleep(RECORDING_DURATION_MS / 2);
// Stop recording and preview
- stopRecording(/* useMediaRecorder */true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ int durationMs = stopRecording(/* useMediaRecorder */true);
+ // For non-burst test, use number of frames to also double check video frame rate.
+ // Burst video snapshot is allowed to cause frame rate drop, so do not use number
+ // of frames to estimate duration
+ if (!burstTest) {
+ durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ profile.videoFrameRate);
+ }
// Validation recorded video
- validateRecording(videoSz, duration);
+ validateRecording(videoSz, durationMs);
if (burstTest) {
for (int i = 0; i < BURST_VIDEO_SNAPSHOT_NUM; i++) {
@@ -693,8 +768,8 @@
"Camera %d Video size %s: Number of dropped frames %d must not"
+ " be larger than %d",
cameraId, videoSz.toString(), totalDroppedFrames,
- FRAMEDROP_TOLERANCE),
- FRAMEDROP_TOLERANCE, totalDroppedFrames);
+ kFrameDrop_Tolerence),
+ kFrameDrop_Tolerence, totalDroppedFrames);
}
closeImageReader();
}
@@ -810,6 +885,7 @@
} else {
// TODO: need implement MediaCodec path.
}
+ mRecordingStartTime = SystemClock.elapsedRealtime();
}
private void startRecording(boolean useMediaRecorder) throws Exception {
@@ -826,7 +902,9 @@
mSessionListener.getStateWaiter().waitForState(SESSION_CLOSED, SESSION_CLOSE_TIMEOUT_MS);
}
- private void stopRecording(boolean useMediaRecorder) throws Exception {
+ // Stop recording and return the estimated video duration in milliseconds.
+ private int stopRecording(boolean useMediaRecorder) throws Exception {
+ long stopRecordingTime = SystemClock.elapsedRealtime();
if (useMediaRecorder) {
stopCameraStreaming();
@@ -840,6 +918,7 @@
mRecordingSurface.release();
mRecordingSurface = null;
}
+ return (int) (stopRecordingTime - mRecordingStartTime);
}
private void releaseRecorder() {
@@ -853,14 +932,28 @@
File outFile = new File(mOutMediaFileName);
assertTrue("No video is recorded", outFile.exists());
- MediaPlayer mediaPlayer = new MediaPlayer();
+ MediaExtractor extractor = new MediaExtractor();
try {
- mediaPlayer.setDataSource(mOutMediaFileName);
- mediaPlayer.prepare();
- Size videoSz = new Size(mediaPlayer.getVideoWidth(), mediaPlayer.getVideoHeight());
+ extractor.setDataSource(mOutMediaFileName);
+ long durationUs = 0;
+ int width = -1, height = -1;
+ int numTracks = extractor.getTrackCount();
+ final String VIDEO_MIME_TYPE = "video";
+ for (int i = 0; i < numTracks; i++) {
+ MediaFormat format = extractor.getTrackFormat(i);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ if (mime.contains(VIDEO_MIME_TYPE)) {
+ Log.i(TAG, "video format is: " + format.toString());
+ durationUs = format.getLong(MediaFormat.KEY_DURATION);
+ width = format.getInteger(MediaFormat.KEY_WIDTH);
+ height = format.getInteger(MediaFormat.KEY_HEIGHT);
+ break;
+ }
+ }
+ Size videoSz = new Size(width, height);
assertTrue("Video size doesn't match, expected " + sz.toString() +
" got " + videoSz.toString(), videoSz.equals(sz));
- int duration = mediaPlayer.getDuration();
+ int duration = (int) (durationUs / 1000);
if (VERBOSE) {
Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
duration, durationMs));
@@ -869,11 +962,12 @@
// TODO: Don't skip this for video snapshot
if (!mStaticInfo.isHardwareLevelLegacy()) {
assertTrue(String.format(
- "Video duration doesn't match: recorded %dms, expected %dms", duration,
- durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+ "Camera %s: Video duration doesn't match: recorded %dms, expected %dms.",
+ mCamera.getId(), duration, durationMs),
+ Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
}
} finally {
- mediaPlayer.release();
+ extractor.release();
if (!DEBUG_DUMP) {
outFile.delete();
}
@@ -937,7 +1031,7 @@
));
}
- durationMs = (int) (nextTS - currentTS) / 1000000;
+ durationMs = (nextTS - currentTS) / 1000000.0;
mCollector.expectTrue(
String.format(
"Video %dx%d Frame drop detected after video snapshot: " +
@@ -951,7 +1045,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/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 7960200..abc0fea 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -19,31 +19,33 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.camera2.cts.RobustnessTest.MaxOutputSizes.*;
+import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
-import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.params.StreamConfigurationMap;
-import android.hardware.camera2.cts.CameraTestUtils;
import android.hardware.camera2.cts.helpers.StaticMetadata;
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.media.CamcorderProfile;
+import android.media.Image;
import android.media.ImageReader;
import android.util.Log;
import android.util.Size;
+import android.view.Display;
import android.view.Surface;
-
-import com.android.ex.camera2.blocking.BlockingSessionCallback;
+import android.view.WindowManager;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
+import static junit.framework.Assert.assertTrue;
import static org.mockito.Mockito.*;
/**
@@ -52,11 +54,14 @@
public class RobustnessTest extends Camera2AndroidTestCase {
private static final String TAG = "RobustnessTest";
- private static final int FAILED_CONFIGURE_TIMEOUT = 5000; //ms
+ private static final int CONFIGURE_TIMEOUT = 5000; //ms
+ private static final int CAPTURE_TIMEOUT = 1000; //ms
/**
- * Test that a {@link CameraCaptureSession} configured with a {@link Surface} with invalid
- * dimensions fails gracefully.
+ * Test that a {@link CameraCaptureSession} can be configured with a {@link Surface} containing
+ * a dimension other than one of the supported output dimensions. The buffers produced into
+ * this surface are expected have the dimensions of the closest possible buffer size in the
+ * available stream configurations for a surface with this format.
*/
public void testBadSurfaceDimensions() throws Exception {
for (String id : mCameraIds) {
@@ -64,9 +69,25 @@
Log.i(TAG, "Testing Camera " + id);
openDevice(id);
- // Setup Surface with unconfigured dimensions.
- SurfaceTexture surfaceTexture = new SurfaceTexture(0);
- Surface surface = new Surface(surfaceTexture);
+ // Find some size not supported by the camera
+ Size weirdSize = new Size(643, 577);
+ int count = 0;
+ while(mOrderedPreviewSizes.contains(weirdSize)) {
+ // Really, they can't all be supported...
+ weirdSize = new Size(weirdSize.getWidth() + 1, weirdSize.getHeight() + 1);
+ count++;
+ assertTrue("Too many exotic YUV_420_888 resolutions supported.", count < 100);
+ }
+
+ // Setup imageReader with invalid dimension
+ ImageReader imageReader = ImageReader.newInstance(weirdSize.getWidth(),
+ weirdSize.getHeight(), ImageFormat.YUV_420_888, 3);
+
+ // Setup ImageReaderListener
+ SimpleImageReaderListener imageListener = new SimpleImageReaderListener();
+ imageReader.setOnImageAvailableListener(imageListener, mHandler);
+
+ Surface surface = imageReader.getSurface();
List<Surface> surfaces = new ArrayList<>();
surfaces.add(surface);
@@ -74,19 +95,38 @@
CaptureRequest.Builder request =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
request.addTarget(surface);
- CameraCaptureSession.CaptureCallback mockCaptureListener =
- mock(CameraCaptureSession.CaptureCallback.class);
// Check that correct session callback is hit.
CameraCaptureSession.StateCallback sessionListener =
mock(CameraCaptureSession.StateCallback.class);
- mCamera.createCaptureSession(surfaces, sessionListener, mHandler);
- verify(sessionListener, timeout(FAILED_CONFIGURE_TIMEOUT).atLeastOnce()).
- onConfigureFailed(any(CameraCaptureSession.class));
- verify(sessionListener, never()).onConfigured(any(CameraCaptureSession.class));
+ CameraCaptureSession session = CameraTestUtils.configureCameraSession(mCamera,
+ surfaces, sessionListener, mHandler);
+
+ verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+ onConfigured(any(CameraCaptureSession.class));
+ verify(sessionListener, timeout(CONFIGURE_TIMEOUT).atLeastOnce()).
+ onReady(any(CameraCaptureSession.class));
+ verify(sessionListener, never()).onConfigureFailed(any(CameraCaptureSession.class));
verify(sessionListener, never()).onActive(any(CameraCaptureSession.class));
- verify(sessionListener, never()).onReady(any(CameraCaptureSession.class));
verify(sessionListener, never()).onClosed(any(CameraCaptureSession.class));
+
+ CameraCaptureSession.CaptureCallback captureListener =
+ mock(CameraCaptureSession.CaptureCallback.class);
+ session.capture(request.build(), captureListener, mHandler);
+
+ verify(captureListener, timeout(CAPTURE_TIMEOUT).atLeastOnce()).
+ onCaptureCompleted(any(CameraCaptureSession.class),
+ any(CaptureRequest.class), any(TotalCaptureResult.class));
+ verify(captureListener, never()).onCaptureFailed(any(CameraCaptureSession.class),
+ any(CaptureRequest.class), any(CaptureFailure.class));
+
+ Image image = imageListener.getImage(CAPTURE_TIMEOUT);
+ int imageWidth = image.getWidth();
+ int imageHeight = image.getHeight();
+ Size actualSize = new Size(imageWidth, imageHeight);
+
+ assertTrue("Camera does not contain outputted image resolution " + actualSize,
+ mOrderedPreviewSizes.contains(actualSize));
} finally {
closeDevice(id);
}
@@ -128,10 +168,13 @@
{YUV , PREVIEW, YUV, PREVIEW, JPEG, MAXIMUM } // Two-input in-app processing with still capture.
};
- final int[][] FULL_COMBINATIONS = {
+ final int[][] BURST_COMBINATIONS = {
{PRIV, PREVIEW, PRIV, MAXIMUM }, // Maximum-resolution GPU processing with preview.
{PRIV, PREVIEW, YUV, MAXIMUM }, // Maximum-resolution in-app processing with preview.
{YUV, PREVIEW, YUV, MAXIMUM }, // Maximum-resolution two-input in-app processsing.
+ };
+
+ final int[][] FULL_COMBINATIONS = {
{PRIV, PREVIEW, PRIV, PREVIEW, JPEG, MAXIMUM }, //Video recording with maximum-size video snapshot.
{YUV, VGA, PRIV, PREVIEW, YUV, MAXIMUM }, // Standard video recording plus maximum-resolution in-app processing.
{YUV, VGA, YUV, PREVIEW, YUV, MAXIMUM } // Preview plus two-input maximum-resolution in-app processing.
@@ -149,7 +192,7 @@
};
final int[][][] TABLES =
- { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
+ { LEGACY_COMBINATIONS, LIMITED_COMBINATIONS, BURST_COMBINATIONS, FULL_COMBINATIONS, RAW_COMBINATIONS };
// Sanity check the tables
int tableIdx = 0;
@@ -181,15 +224,10 @@
CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
- MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id);
+ MaxOutputSizes maxSizes = new MaxOutputSizes(cc, id, getContext());
final StaticMetadata staticInfo = new StaticMetadata(cc);
- if (staticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
openDevice(id);
// Always run legacy-level tests
@@ -208,7 +246,14 @@
testOutputCombination(id, config, maxSizes);
}
- // Check for FULL and RAW and run those if appropriate
+ // Check for BURST_CAPTURE, FULL and RAW and run those if appropriate
+
+ if (staticInfo.isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE)) {
+ for (int[] config : BURST_COMBINATIONS) {
+ testOutputCombination(id, config, maxSizes);
+ }
+ }
if (staticInfo.isHardwareLevelFull()) {
for (int[] config : FULL_COMBINATIONS) {
@@ -245,7 +290,7 @@
static final int VGA = 3;
static final int RESOLUTION_COUNT = 4;
- public MaxOutputSizes(CameraCharacteristics cc, String cameraId) {
+ public MaxOutputSizes(CameraCharacteristics cc, String cameraId, Context context) {
StreamConfigurationMap configs =
cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
Size[] privSizes = configs.getOutputSizes(SurfaceTexture.class);
@@ -253,11 +298,13 @@
Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
Size[] rawSizes = configs.getOutputSizes(ImageFormat.RAW_SENSOR);
+ Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
+
maxRawSize = (rawSizes != null) ? CameraTestUtils.getMaxSize(rawSizes) : null;
- maxPrivSizes[PREVIEW] = getMaxSize(privSizes, PREVIEW_SIZE_BOUND);
- maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, PREVIEW_SIZE_BOUND);
- maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, PREVIEW_SIZE_BOUND);
+ maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
+ maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, maxPreviewSize);
+ maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId);
maxYuvSizes[RECORD] = getMaxRecordingSize(cameraId);
@@ -330,9 +377,11 @@
Log.i(TAG, String.format("Testing Camera %s, config %s",
cameraId, MaxOutputSizes.configToString(config)));
- final int TIMEOUT_FOR_RESULT_MS = 1000;
+ // Timeout is relaxed by 500ms for LEGACY devices to reduce false positive rate in CTS
+ final int TIMEOUT_FOR_RESULT_MS = (mStaticInfo.isHardwareLevelLegacy()) ? 1500 : 1000;
final int MIN_RESULT_COUNT = 3;
+ ImageDropperListener imageDropperListener = new ImageDropperListener();
// Set up outputs
List<Object> outputTargets = new ArrayList<>();
List<Surface> outputSurfaces = new ArrayList<>();
@@ -358,6 +407,7 @@
Size targetSize = maxSizes.maxJpegSizes[sizeLimit];
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), JPEG, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
jpegTargets.add(target);
@@ -367,6 +417,7 @@
Size targetSize = maxSizes.maxYuvSizes[sizeLimit];
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), YUV, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
yuvTargets.add(target);
@@ -376,6 +427,7 @@
Size targetSize = maxSizes.maxRawSize;
ImageReader target = ImageReader.newInstance(
targetSize.getWidth(), targetSize.getHeight(), RAW, MIN_RESULT_COUNT);
+ target.setOnImageAvailableListener(imageDropperListener, mHandler);
outputTargets.add(target);
outputSurfaces.add(target.getSurface());
rawTargets.add(target);
@@ -502,4 +554,35 @@
return sz;
}
+ private static Size getMaxPreviewSize(Context context, String cameraId) {
+ try {
+ WindowManager windowManager =
+ (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ if (height > width) {
+ height = width;
+ width = display.getHeight();
+ }
+
+ CameraManager camMgr =
+ (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
+ cameraId, camMgr, PREVIEW_SIZE_BOUND);
+
+ if (orderedPreviewSizes != null) {
+ for (Size size : orderedPreviewSizes) {
+ if (width >= size.getWidth() &&
+ height >= size.getHeight())
+ return size;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString());
+ }
+ return PREVIEW_SIZE_BOUND;
+ }
}
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 dd1882e..b8c2f2e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StaticMetadataTest.java
@@ -21,6 +21,7 @@
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.CameraCharacteristics.Key;
@@ -29,11 +30,11 @@
import android.hardware.camera2.cts.testcases.Camera2AndroidTestCase;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.util.Log;
+import android.util.Pair;
import android.util.Size;
-import junit.framework.Assert;
-
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -55,6 +56,9 @@
private static final float MIN_FPS_FOR_FULL_DEVICE = 20.0f;
private String mCameraId;
+ // Last defined capability enum, for iterating over all of them
+ private static final int LAST_CAPABILITY_ENUM = REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE;
+
/**
* Test the available capability for different hardware support level devices.
*/
@@ -74,18 +78,13 @@
if (mStaticInfo.isHardwareLevelFull()) {
// Capability advertisement must be right.
- mCollector.expectTrue("Full device must contains MANUAL_SENSOR capability",
+ mCollector.expectTrue("Full device must contain MANUAL_SENSOR capability",
availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR));
- mCollector.expectTrue("Full device must contains MANUAL_POST_PROCESSING capability",
+ mCollector.expectTrue("Full device must contain MANUAL_POST_PROCESSING capability",
availableCaps.contains(
REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING));
-
- // Max yuv resolution must be very close to sensor resolution
- Size[] yuvSizes = configs.getOutputSizes(ImageFormat.YUV_420_888);
- Size maxYuvSize = CameraTestUtils.getMaxSize(yuvSizes);
- mCollector.expectSizesAreSimilar(
- "Active array size and max YUV size should be similar",
- sensorSize, maxYuvSize, SIZE_ERROR_MARGIN);
+ mCollector.expectTrue("Full device must contain BURST_CAPTURE capability",
+ availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE));
// Max resolution fps must be >= 20.
mCollector.expectTrue("Full device must support at least 20fps for max resolution",
@@ -96,6 +95,12 @@
mStaticInfo.isPerFrameControlSupported());
}
+ if (availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
+ mCollector.expectTrue("MANUAL_SENSOR capability always requires " +
+ "READ_SENSOR_SETTINGS capability as well",
+ availableCaps.contains(REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS));
+ }
+
// Max jpeg resolution must be very close to sensor resolution
Size[] jpegSizes = configs.getOutputSizes(ImageFormat.JPEG);
Size maxJpegSize = CameraTestUtils.getMaxSize(jpegSizes);
@@ -144,7 +149,7 @@
List<Integer> availableCaps = mStaticInfo.getAvailableCapabilitiesChecked();
for (Integer capability = REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
- capability <= REQUEST_AVAILABLE_CAPABILITIES_RAW; capability++) {
+ capability <= LAST_CAPABILITY_ENUM; capability++) {
boolean isCapabilityAvailable = availableCaps.contains(capability);
validateCapability(capability, isCapabilityAvailable);
}
@@ -250,6 +255,10 @@
private void validateCapability(Integer capability, boolean isCapabilityAvailable) {
List<CaptureRequest.Key<?>> requestKeys = new ArrayList<>();
+ Set<CaptureResult.Key<?>> resultKeys = new HashSet<>();
+ // Capability requirements other than key presences
+ List<Pair<String, Boolean>> additionalRequirements = new ArrayList<>();
+
/* For available capabilities, only check request keys in this test
Characteristics keys are tested in ExtendedCameraCharacteristicsTest
Result keys are tested in CaptureResultTest */
@@ -308,11 +317,31 @@
requestKeys.add(CaptureRequest.COLOR_CORRECTION_TRANSFORM);
requestKeys.add(CaptureRequest.SHADING_MODE);
requestKeys.add(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE);
- if (mStaticInfo.isHardwareLevelFull()) {
- requestKeys.add(CaptureRequest.TONEMAP_CURVE);
- requestKeys.add(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE);
- }
+ requestKeys.add(CaptureRequest.TONEMAP_CURVE);
+ requestKeys.add(CaptureRequest.COLOR_CORRECTION_ABERRATION_MODE);
+ // Legacy mode always doesn't support these requirements
+ Boolean contrastCurveModeSupported = false;
+ Boolean offColorAberrationModeSupported = false;
+ if (mStaticInfo.isHardwareLevelLimitedOrBetter()) {
+ int[] tonemapModes = mStaticInfo.getAvailableToneMapModesChecked();
+ List<Integer> modeList = (tonemapModes.length == 0) ?
+ new ArrayList<Integer>() :
+ Arrays.asList(CameraTestUtils.toObject(tonemapModes));
+ contrastCurveModeSupported =
+ modeList.contains(CameraMetadata.TONEMAP_MODE_CONTRAST_CURVE);
+ int[] colorAberrationModes =
+ mStaticInfo.getAvailableColorAberrationModesChecked();
+ modeList = (colorAberrationModes.length == 0) ?
+ new ArrayList<Integer>() :
+ Arrays.asList(CameraTestUtils.toObject(colorAberrationModes));
+ offColorAberrationModeSupported =
+ modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF);
+ }
+ additionalRequirements.add(new Pair<String, Boolean>(
+ "Tonemap mode must include CONTRAST_CURVE", contrastCurveModeSupported));
+ additionalRequirements.add(new Pair<String, Boolean>(
+ "Color aberration mode must include OFF", offColorAberrationModeSupported));
break;
case REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
capabilityName = "REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR";
@@ -321,6 +350,7 @@
requestKeys.add(CaptureRequest.SENSOR_SENSITIVITY);
if (mStaticInfo.hasFocuser()) {
requestKeys.add(CaptureRequest.LENS_APERTURE);
+ requestKeys.add(CaptureRequest.LENS_FOCUS_DISTANCE);
requestKeys.add(CaptureRequest.LENS_FILTER_DENSITY);
requestKeys.add(CaptureRequest.LENS_OPTICAL_STABILIZATION_MODE);
}
@@ -330,18 +360,68 @@
// RAW_CAPABILITY needs to check for not just capture request keys
validateRawCapability(isCapabilityAvailable);
return;
+ case REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
+ // Tested in ExtendedCameraCharacteristicsTest
+ return;
+ case REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
+ capabilityName = "REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS";
+ resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
+ resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+ resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+ if (mStaticInfo.hasFocuser()) {
+ resultKeys.add(CaptureResult.LENS_APERTURE);
+ resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+ resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+ }
+ break;
default:
capabilityName = "Unknown";
- Assert.fail(String.format("Unknown capability: %d", capability));
+ assertTrue(String.format("Unknown capability set: %d", capability),
+ !isCapabilityAvailable);
+ return;
}
- boolean matchExpectation =
- validateRequestKeysPresence(capabilityName, requestKeys, isCapabilityAvailable);
+ // Check additional requirements and exit early if possible
+ if (!isCapabilityAvailable) {
+ for (Pair<String, Boolean> p : additionalRequirements) {
+ String requirement = p.first;
+ Boolean meetRequirement = p.second;
+ // No further check is needed if we've found why capability cannot be advertised
+ if (!meetRequirement) {
+ Log.v(TAG, String.format(
+ "Camera %s doesn't list capability %s because of requirement: %s",
+ mCameraId, capabilityName, requirement));
+ return;
+ }
+ }
+ }
+
+ boolean matchExpectation = true;
+ if (!requestKeys.isEmpty()) {
+ matchExpectation &= validateRequestKeysPresence(
+ capabilityName, requestKeys, isCapabilityAvailable);
+ }
+ if(!resultKeys.isEmpty()) {
+ matchExpectation &= validateResultKeysPresence(
+ capabilityName, resultKeys, isCapabilityAvailable);
+ }
+
+ // Check additional requirements
+ for (Pair<String, Boolean> p : additionalRequirements) {
+ String requirement = p.first;
+ Boolean meetRequirement = p.second;
+ if (isCapabilityAvailable && !meetRequirement) {
+ mCollector.addMessage(String.format(
+ "Camera %s list capability %s but does not meet the requirement: %s",
+ mCameraId, capabilityName, requirement));
+ }
+ }
+
// In case of isCapabilityAvailable == true, error has been filed in
- // validateRequestKeysPresence
+ // validateRequest/ResultKeysPresence
if (!matchExpectation && !isCapabilityAvailable) {
mCollector.addMessage(String.format(
- "Camera %s doesn't list capability %s but contain all required keys",
+ "Camera %s doesn't list capability %s but meets all requirements",
mCameraId, capabilityName));
}
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index 37eff10..b4113e5 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -71,6 +71,7 @@
private static final Location sTestLocation0 = new Location(LocationManager.GPS_PROVIDER);
private static final Location sTestLocation1 = new Location(LocationManager.GPS_PROVIDER);
private static final Location sTestLocation2 = new Location(LocationManager.NETWORK_PROVIDER);
+ private static final int RELAXED_CAPTURE_IMAGE_TIMEOUT_MS = CAPTURE_IMAGE_TIMEOUT_MS + 1000;
static {
sTestLocation0.setTime(1199145600L);
sTestLocation0.setLatitude(37.736071);
@@ -263,11 +264,6 @@
Log.i(TAG, "Testing Still preview capture combination for Camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test for legacy devices");
- continue;
- }
-
previewStillCombinationTestByCamera();
} finally {
closeDevice();
@@ -314,11 +310,6 @@
Log.i(TAG, "Testing AE regions for Camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
boolean aeRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AE_INDEX);
if (!aeRegionsSupported) {
continue;
@@ -369,11 +360,6 @@
Log.i(TAG, "Testing AF regions for Camera " + id);
openDevice(id);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
boolean afRegionsSupported = isRegionsSupportedFor3A(MAX_REGIONS_AF_INDEX);
if (!afRegionsSupported) {
continue;
@@ -659,7 +645,8 @@
prepareStillCaptureAndStartPreview(previewRequest, stillRequest, previewSz,
stillSz, resultListener, imageListener);
mSession.capture(stillRequest.build(), resultListener, mHandler);
- Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
+ Image image = imageListener.getImage((mStaticInfo.isHardwareLevelLegacy()) ?
+ RELAXED_CAPTURE_IMAGE_TIMEOUT_MS : CAPTURE_IMAGE_TIMEOUT_MS);
validateJpegCapture(image, stillSz);
// Free image resources
@@ -1262,6 +1249,11 @@
private void aeCompensationTestByCamera() throws Exception {
Range<Integer> compensationRange = mStaticInfo.getAeCompensationRangeChecked();
+ // Skip the test if exposure compensation is not supported.
+ if (compensationRange.equals(Range.create(0, 0))) {
+ return;
+ }
+
Rational step = mStaticInfo.getAeCompensationStepChecked();
float stepF = (float) step.getNumerator() / step.getDenominator();
int stepsPerEv = (int) Math.round(1.0 / stepF);
@@ -1276,6 +1268,8 @@
CaptureRequest.Builder stillRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ CaptureResult normalResult;
+ CaptureResult compensatedResult;
// The following variables should only be read under the MANUAL_SENSOR capability guard:
long minExposureValue = -1;
@@ -1307,13 +1301,13 @@
// Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
- CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+ normalResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
long normalExposureValue = -1;
if (mStaticInfo.isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
// get and check if current exposure value is valid
- normalExposureValue = getExposureValue(result);
+ normalExposureValue = getExposureValue(normalResult);
mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
minExposureValue, maxExposureValuePreview);
@@ -1323,6 +1317,12 @@
expectedExposureValue > maxExposureValueStill) {
continue;
}
+ Log.v(TAG, "Expect ratio: " + expectedRatio +
+ " normalExposureValue: " + normalExposureValue +
+ " expectedExposureValue: " + expectedExposureValue +
+ " minExposureValue: " + minExposureValue +
+ " maxExposureValuePreview: " + maxExposureValuePreview +
+ " maxExposureValueStill: " + maxExposureValueStill);
}
// Now issue exposure compensation and wait for AE locked. AE could take a few
@@ -1343,29 +1343,39 @@
CaptureRequest request = stillRequest.build();
mSession.capture(request, resultListener, mHandler);
- result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
+ compensatedResult = resultListener.getCaptureResultForRequest(
+ request, WAIT_FOR_RESULT_TIMEOUT_MS);
if (mStaticInfo.isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
// Verify the exposure value compensates as requested
- long compensatedExposureValue = getExposureValue(result);
+ long compensatedExposureValue = getExposureValue(compensatedResult);
mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
minExposureValue, maxExposureValueStill);
double observedRatio = (double) compensatedExposureValue / normalExposureValue;
double error = observedRatio / expectedRatio;
- mCollector.expectInRange(String.format(
- "Exposure compensation ratio exceeds error tolerence:"
- + " expected(%f) observed(%f) ", expectedRatio, observedRatio),
- error,
+ String errorString = String.format(
+ "Exposure compensation ratio exceeds error tolerence:" +
+ " expected(%f) observed(%f)." +
+ " Normal exposure time %d us, sensitivity %d." +
+ " Compensated exposure time %d us, sensitivity %d",
+ expectedRatio, observedRatio,
+ (int) (getValueNotNull(
+ normalResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+ getValueNotNull(normalResult, CaptureResult.SENSOR_SENSITIVITY),
+ (int) (getValueNotNull(
+ compensatedResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+ getValueNotNull(compensatedResult, CaptureResult.SENSOR_SENSITIVITY));
+ mCollector.expectInRange(errorString, error,
1.0 - AE_COMPENSATION_ERROR_TOLERANCE,
1.0 + AE_COMPENSATION_ERROR_TOLERANCE);
}
mCollector.expectEquals("Exposure compensation result should match requested value.",
exposureCompensation,
- result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
+ compensatedResult.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
mCollector.expectTrue("Exposure lock should be set",
- result.get(CaptureResult.CONTROL_AE_LOCK));
+ compensatedResult.get(CaptureResult.CONTROL_AE_LOCK));
Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
validateJpegCapture(image, maxStillSz);
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 b3d4cf9..01da4c8 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/SurfaceViewPreviewTest.java
@@ -74,11 +74,6 @@
Log.i(TAG, "Testing preview for Camera " + mCameraIds[i]);
openDevice(mCameraIds[i]);
- if (mStaticInfo.isHardwareLevelLegacy()) {
- Log.i(TAG, "Skipping test on legacy devices");
- continue;
- }
-
previewTestByCamera();
} finally {
closeDevice();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
index f4859e5..0ee5ffc 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/CameraErrorCollector.java
@@ -859,8 +859,25 @@
*/
public <T> void expectKeyValueIsIn(CameraCharacteristics characteristics,
CameraCharacteristics.Key<T> key, T... expected) {
- T value;
- if ((value = expectKeyValueNotNull(characteristics, key)) == null) {
+ T value = expectKeyValueNotNull(characteristics, key);
+ if (value == null) {
+ return;
+ }
+ String reason = "Key " + key.getName() + " value " + value
+ + " isn't one of the expected values " + Arrays.deepToString(expected);
+ expectContains(reason, expected, value);
+ }
+
+ /**
+ * Check if the key is non-null, and the key value is one of the expected values.
+ *
+ * @param request The The {@link CaptureRequest#Builder} to get the key from.
+ * @param key The {@link CaptureRequest} key to be checked.
+ * @param expected The expected values of the CaptureRequest key.
+ */
+ public <T> void expectKeyValueIsIn(Builder request, CaptureRequest.Key<T> key, T... expected) {
+ T value = expectKeyValueNotNull(request, key);
+ if (value == null) {
return;
}
String reason = "Key " + key.getName() + " value " + value
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index a5c7083..2a654db 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -436,17 +436,23 @@
int[] modes = getValueFromKeyNonNull(key);
boolean foundAuto = false;
+ boolean found50Hz = false;
+ boolean found60Hz = false;
for (int mode : modes) {
checkTrueForKey(key, "mode value " + mode + " is out if range",
mode >= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_OFF ||
mode <= CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO);
if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_AUTO) {
foundAuto = true;
- return modes;
+ } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_50HZ) {
+ found50Hz = true;
+ } else if (mode == CameraMetadata.CONTROL_AE_ANTIBANDING_MODE_60HZ) {
+ found60Hz = true;
}
}
- // Must contain AUTO mode.
- checkTrueForKey(key, "AUTO mode is missing", foundAuto);
+ // Must contain AUTO mode or one of 50/60Hz mode.
+ checkTrueForKey(key, "Either AUTO mode or both 50HZ/60HZ mode should present",
+ foundAuto || (found50Hz && found60Hz));
return modes;
}
@@ -660,8 +666,9 @@
List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
checkTrueForKey(key, " Camera devices must always support FAST mode",
modeList.contains(CameraMetadata.TONEMAP_MODE_FAST));
- if (isHardwareLevelFull()) {
- checkTrueForKey(key, "Full-capability camera devices must support"
+ 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));
@@ -1302,12 +1309,13 @@
final Range<Integer> DEFAULT_RANGE = Range.create(
(int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MIN / compensationStepF),
(int)(CONTROL_AE_COMPENSATION_RANGE_DEFAULT_MAX / compensationStepF));
+ final Range<Integer> ZERO_RANGE = Range.create(0, 0);
if (compensationRange == null) {
- return DEFAULT_RANGE;
+ return ZERO_RANGE;
}
// Legacy devices don't have a minimum range requirement
- if (isHardwareLevelLimitedOrBetter()) {
+ if (isHardwareLevelLimitedOrBetter() && !compensationRange.equals(ZERO_RANGE)) {
checkTrueForKey(key, " range value must be at least " + DEFAULT_RANGE
+ ", actual " + compensationRange + ", compensation step " + compensationStep,
compensationRange.getLower() <= DEFAULT_RANGE.getLower() &&
@@ -1433,8 +1441,9 @@
}
List<Integer> modeList = Arrays.asList(CameraTestUtils.toObject(modes));
- checkTrueForKey(key, " Camera devices must always support OFF mode",
- modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF));
+ checkTrueForKey(key, " Camera devices must always support either OFF or FAST mode",
+ modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF) ||
+ modeList.contains(CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_FAST));
checkElementDistinct(key, modeList);
checkArrayValuesInRange(key, modes,
CameraMetadata.COLOR_CORRECTION_ABERRATION_MODE_OFF,
@@ -1480,7 +1489,7 @@
checkArrayValuesInRange(key, availableCaps,
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE,
- CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW);
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE);
capList = Arrays.asList(CameraTestUtils.toObject(availableCaps));
return capList;
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
new file mode 100644
index 0000000..744d2c7
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/BitmapUtils.java
@@ -0,0 +1,93 @@
+/*
+ * 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.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.RenderScript;
+import android.renderscript.ScriptIntrinsicHistogram;
+
+/**
+ * Utility class providing methods for various pixel-wise ARGB bitmap operations.
+ */
+public class BitmapUtils {
+ private static final String TAG = "BitmapUtils";
+ private static final int COLOR_BIT_DEPTH = 256;
+
+ public static int A = 3;
+ public static int R = 0;
+ public static int G = 1;
+ public static int B = 2;
+ public static int NUM_CHANNELS = 4;
+
+ /**
+ * Return the histograms for each color channel (interleaved).
+ *
+ * @param rs a {@link RenderScript} context to use.
+ * @param bmap a {@link Bitmap} to generate the histograms for.
+ * @return an array containing NUM_CHANNELS * COLOR_BIT_DEPTH histogram bucket values, with
+ * the color channels interleaved.
+ */
+ public static int[] calcHistograms(RenderScript rs, Bitmap bmap) {
+ ScriptIntrinsicHistogram hist = ScriptIntrinsicHistogram.create(rs, Element.U8_4(rs));
+ Allocation sums = Allocation.createSized(rs, Element.I32_4(rs), COLOR_BIT_DEPTH);
+
+ // Setup input allocation (ARGB 8888 bitmap).
+ Allocation input = Allocation.createFromBitmap(rs, bmap);
+
+ hist.setOutput(sums);
+ hist.forEach(input);
+ int[] output = new int[COLOR_BIT_DEPTH * NUM_CHANNELS];
+ sums.copyTo(output);
+ return output;
+ }
+
+ /**
+ * Find the difference between two bitmaps using average of per-pixel differences.
+ *
+ * @param a first {@link android.graphics.Bitmap}.
+ * @param b second {@link android.graphics.Bitmap}.
+ * @return the difference.
+ */
+ public static double calcDifferenceMetric(Bitmap a, Bitmap b) {
+ if (a.getWidth() != b.getWidth() || a.getHeight() != b.getHeight()) {
+ throw new IllegalArgumentException("Bitmap dimensions for arguments do not match a=" +
+ a.getWidth() + "x" + a.getHeight() + ", b=" + b.getWidth() + "x" +
+ b.getHeight());
+ }
+ // TODO: Optimize this in renderscript to avoid copy.
+ int[] aPixels = new int[a.getHeight() * a.getWidth()];
+ int[] bPixels = new int[aPixels.length];
+ a.getPixels(aPixels, /*offset*/0, /*stride*/a.getWidth(), /*x*/0, /*y*/0, a.getWidth(),
+ a.getHeight());
+ b.getPixels(bPixels, /*offset*/0, /*stride*/b.getWidth(), /*x*/0, /*y*/0, b.getWidth(),
+ b.getHeight());
+ double diff = 0;
+ for (int i = 0; i < aPixels.length; i++) {
+ int aPix = aPixels[i];
+ int bPix = bPixels[i];
+
+ diff += Math.abs(Color.red(aPix) - Color.red(bPix)); // red
+ diff += Math.abs(Color.green(aPix) - Color.green(bPix)); // green
+ diff += Math.abs(Color.blue(aPix) - Color.blue(bPix)); // blue
+ }
+ diff /= (aPixels.length * 3);
+ return diff;
+ }
+
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
new file mode 100644
index 0000000..33c212a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -0,0 +1,796 @@
+/*
+ * 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.hardware.camera2.cts.rs;
+
+import android.graphics.Bitmap;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.params.ColorSpaceTransform;
+import android.hardware.camera2.params.LensShadingMap;
+import android.renderscript.Allocation;
+import android.renderscript.Element;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+import android.renderscript.Int4;
+import android.renderscript.Matrix3f;
+import android.renderscript.RenderScript;
+import android.renderscript.Type;
+
+import android.hardware.camera2.cts.ScriptC_raw_converter;
+import android.util.Log;
+import android.util.Rational;
+import android.util.SparseIntArray;
+
+import java.util.Arrays;
+
+/**
+ * Utility class providing methods for rendering RAW16 images into other colorspaces.
+ */
+public class RawConverter {
+ private static final String TAG = "RawConverter";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ /**
+ * Matrix to convert from CIE XYZ colorspace to sRGB, Bradford-adapted to D65.
+ */
+ private static final float[] sXYZtoRGBBradford = new float[] {
+ 3.1338561f, -1.6168667f, -0.4906146f,
+ -0.9787684f, 1.9161415f, 0.0334540f,
+ 0.0719453f, -0.2289914f, 1.4052427f
+ };
+
+ /**
+ * Matrix to convert from the ProPhoto RGB colorspace to CIE XYZ colorspace.
+ */
+ private static final float[] sProPhotoToXYZ = new float[] {
+ 0.797779f, 0.135213f, 0.031303f,
+ 0.288000f, 0.711900f, 0.000100f,
+ 0.000000f, 0.000000f, 0.825105f
+ };
+
+ /**
+ * Matrix to convert from CIE XYZ colorspace to ProPhoto RGB colorspace.
+ */
+ private static final float[] sXYZtoProPhoto = new float[] {
+ 1.345753f, -0.255603f, -0.051025f,
+ -0.544426f, 1.508096f, 0.020472f,
+ 0.000000f, 0.000000f, 1.211968f
+ };
+
+ /**
+ * Coefficients for a 3rd order polynomial, ordered from highest to lowest power. This
+ * polynomial approximates the default tonemapping curve used for ACR3.
+ */
+ private static final float[] DEFAULT_ACR3_TONEMAP_CURVE_COEFFS = new float[] {
+ -1.087f, 1.643f, 0.443f, 0f
+ };
+
+ /**
+ * The D50 whitepoint coordinates in CIE XYZ colorspace.
+ */
+ private static final float[] D50_XYZ = new float[] { 0.9642f, 1, 0.8249f };
+
+ /**
+ * An array containing the color temperatures for standard reference illuminants.
+ */
+ private static final SparseIntArray sStandardIlluminants = new SparseIntArray();
+ private static final int NO_ILLUMINANT = -1;
+ static {
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT, 6504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D65, 6504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D50, 5003);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D55, 5503);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_D75, 7504);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_A, 2856);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_B, 4874);
+ sStandardIlluminants.append(CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_STANDARD_C, 6774);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_DAYLIGHT_FLUORESCENT, 6430);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_COOL_WHITE_FLUORESCENT, 4230);
+ sStandardIlluminants.append(
+ CameraMetadata.SENSOR_REFERENCE_ILLUMINANT1_WHITE_FLUORESCENT, 3450);
+ // TODO: Add the rest of the illuminants included in the LightSource EXIF tag.
+ }
+
+ /**
+ * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+ *
+ * <p> This function applies the operations roughly outlined in the Adobe DNG specification
+ * using the provided metadata about the image sensor. Sensor data for Android devices is
+ * assumed to be relatively linear, and no extra linearization step is applied here. The
+ * following operations are applied in the given order:</p>
+ *
+ * <ul>
+ * <li>
+ * Black level subtraction - the black levels given in the SENSOR_BLACK_LEVEL_PATTERN
+ * tag are subtracted from the corresponding raw pixels.
+ * </li>
+ * <li>
+ * Rescaling - each raw pixel is scaled by 1/(white level - black level).
+ * </li>
+ * <li>
+ * Lens shading correction - the interpolated gains from the gain map defined in the
+ * STATISTICS_LENS_SHADING_CORRECTION_MAP are applied to each raw pixel.
+ * </li>
+ * <li>
+ * Clipping - each raw pixel is clipped to a range of [0.0, 1.0].
+ * </li>
+ * <li>
+ * Demosaic - the RGB channels for each pixel are retrieved from the Bayer mosaic
+ * of raw pixels using a simple bilinear-interpolation demosaicing algorithm.
+ * </li>
+ * <li>
+ * Colorspace transform to wide-gamut RGB - each pixel is mapped into a
+ * wide-gamut colorspace (in this case ProPhoto RGB is used) from the sensor
+ * colorspace.
+ * </li>
+ * <li>
+ * Tonemapping - A basic tonemapping curve using the default from ACR3 is applied
+ * (no further exposure compensation is applied here, though this could be improved).
+ * </li>
+ * <li>
+ * Colorspace transform to final RGB - each pixel is mapped into linear sRGB colorspace.
+ * </li>
+ * <li>
+ * Gamma correction - each pixel is gamma corrected using γ=2.2 to map into sRGB
+ * colorspace for viewing.
+ * </li>
+ * <li>
+ * Packing - each pixel is scaled so that each color channel has a range of [0, 255],
+ * and is packed into an Android bitmap.
+ * </li>
+ * </ul>
+ *
+ * <p> Arguments given here are assumed to come from the values for the corresponding
+ * {@link CameraCharacteristics.Key}s defined for the camera that produced this RAW16 buffer.
+ * </p>
+ * @param rs a {@link RenderScript} context to use.
+ * @param inputWidth width of the input RAW16 image in pixels.
+ * @param inputHeight height of the input RAW16 image in pixels.
+ * @param inputStride stride of the input RAW16 image in bytes.
+ * @param rawImageInput a byte array containing a RAW16 image.
+ * @param staticMetadata the {@link CameraCharacteristics} for this RAW capture.
+ * @param dynamicMetadata the {@link CaptureResult} for this RAW capture.
+ * @param outputOffsetX the offset width into the raw image of the left side of the output
+ * rectangle.
+ * @param outputOffsetY the offset height into the raw image of the top side of the output
+ * rectangle.
+ * @param argbOutput a {@link Bitmap} to output the rendered RAW image into. The height and
+ * width of this bitmap along with the output offsets are used to determine
+ * the dimensions and offset of the output rectangle contained in the RAW
+ * image to be rendered.
+ */
+ public static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+ int inputStride, byte[] rawImageInput, CameraCharacteristics staticMetadata,
+ CaptureResult dynamicMetadata, int outputOffsetX, int outputOffsetY,
+ /*out*/Bitmap argbOutput) {
+ int cfa = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_COLOR_FILTER_ARRANGEMENT);
+ int[] blackLevelPattern = new int[4];
+ staticMetadata.get(CameraCharacteristics.SENSOR_BLACK_LEVEL_PATTERN).
+ copyTo(blackLevelPattern, /*offset*/0);
+ int whiteLevel = staticMetadata.get(CameraCharacteristics.SENSOR_INFO_WHITE_LEVEL);
+ int ref1 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT1);
+ int ref2 = staticMetadata.get(CameraCharacteristics.SENSOR_REFERENCE_ILLUMINANT2);
+ float[] calib1 = new float[9];
+ float[] calib2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM1), calib1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_CALIBRATION_TRANSFORM2), calib2);
+ float[] color1 = new float[9];
+ float[] color2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM1), color1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_COLOR_TRANSFORM2), color2);
+ float[] forward1 = new float[9];
+ float[] forward2 = new float[9];
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX1), forward1);
+ convertColorspaceTransform(
+ staticMetadata.get(CameraCharacteristics.SENSOR_FORWARD_MATRIX2), forward2);
+
+ Rational[] neutral = dynamicMetadata.get(CaptureResult.SENSOR_NEUTRAL_COLOR_POINT);
+
+ LensShadingMap shadingMap = dynamicMetadata.get(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP);
+
+ convertToSRGB(rs, inputWidth, inputHeight, inputStride, cfa, blackLevelPattern, whiteLevel,
+ rawImageInput, ref1, ref2, calib1, calib2, color1, color2,
+ forward1, forward2, neutral, shadingMap, outputOffsetX, outputOffsetY, argbOutput);
+ }
+
+ /**
+ * Convert a RAW16 buffer into an sRGB buffer, and write the result into a bitmap.
+ *
+ * @see #convertToSRGB
+ */
+ private static void convertToSRGB(RenderScript rs, int inputWidth, int inputHeight,
+ int inputStride, int cfa, int[] blackLevelPattern, int whiteLevel, byte[] rawImageInput,
+ int referenceIlluminant1, int referenceIlluminant2, float[] calibrationTransform1,
+ float[] calibrationTransform2, float[] colorMatrix1, float[] colorMatrix2,
+ float[] forwardTransform1, float[] forwardTransform2, Rational[/*3*/] neutralColorPoint,
+ LensShadingMap lensShadingMap, int outputOffsetX, int outputOffsetY,
+ /*out*/Bitmap argbOutput) {
+
+ // Validate arguments
+ if (argbOutput == null || rs == null || rawImageInput == null) {
+ throw new IllegalArgumentException("Null argument to convertToSRGB");
+ }
+ if (argbOutput.getConfig() != Bitmap.Config.ARGB_8888) {
+ throw new IllegalArgumentException(
+ "Output bitmap passed to convertToSRGB is not ARGB_8888 format");
+ }
+ if (outputOffsetX < 0 || outputOffsetY < 0) {
+ throw new IllegalArgumentException("Negative offset passed to convertToSRGB");
+ }
+ if ((inputStride / 2) < inputWidth) {
+ throw new IllegalArgumentException("Stride too small.");
+ }
+ if ((inputStride % 2) != 0) {
+ throw new IllegalArgumentException("Invalid stride for RAW16 format, see graphics.h.");
+ }
+ int outWidth = argbOutput.getWidth();
+ int outHeight = argbOutput.getHeight();
+ if (outWidth + outputOffsetX > inputWidth || outHeight + outputOffsetY > inputHeight) {
+ throw new IllegalArgumentException("Raw image with dimensions (w=" + inputWidth +
+ ", h=" + inputHeight + "), cannot converted into sRGB image with dimensions (w="
+ + outWidth + ", h=" + outHeight + ").");
+ }
+ if (cfa < 0 || cfa > 3) {
+ throw new IllegalArgumentException("Unsupported cfa pattern " + cfa + " used.");
+ }
+ if (DEBUG) {
+ Log.d(TAG, "Metadata Used:");
+ Log.d(TAG, "Input width,height: " + inputWidth + "," + inputHeight);
+ Log.d(TAG, "Output offset x,y: " + outputOffsetX + "," + outputOffsetY);
+ Log.d(TAG, "Output width,height: " + outWidth + "," + outHeight);
+ Log.d(TAG, "CFA: " + cfa);
+ Log.d(TAG, "BlackLevelPattern: " + Arrays.toString(blackLevelPattern));
+ Log.d(TAG, "WhiteLevel: " + whiteLevel);
+ Log.d(TAG, "ReferenceIlluminant1: " + referenceIlluminant1);
+ Log.d(TAG, "ReferenceIlluminant2: " + referenceIlluminant2);
+ Log.d(TAG, "CalibrationTransform1: " + Arrays.toString(calibrationTransform1));
+ Log.d(TAG, "CalibrationTransform2: " + Arrays.toString(calibrationTransform2));
+ Log.d(TAG, "ColorMatrix1: " + Arrays.toString(colorMatrix1));
+ Log.d(TAG, "ColorMatrix2: " + Arrays.toString(colorMatrix2));
+ Log.d(TAG, "ForwardTransform1: " + Arrays.toString(forwardTransform1));
+ Log.d(TAG, "ForwardTransform2: " + Arrays.toString(forwardTransform2));
+ Log.d(TAG, "NeutralColorPoint: " + Arrays.toString(neutralColorPoint));
+ }
+
+ Allocation gainMap = null;
+ if (lensShadingMap != null) {
+ float[] lsm = new float[lensShadingMap.getGainFactorCount()];
+ lensShadingMap.copyGainFactors(/*inout*/lsm, /*offset*/0);
+ gainMap = createFloat4Allocation(rs, lsm, lensShadingMap.getColumnCount(),
+ lensShadingMap.getRowCount());
+ }
+
+ float[] normalizedForwardTransform1 = Arrays.copyOf(forwardTransform1,
+ forwardTransform1.length);
+ normalizeFM(normalizedForwardTransform1);
+ float[] normalizedForwardTransform2 = Arrays.copyOf(forwardTransform2,
+ forwardTransform2.length);
+ normalizeFM(normalizedForwardTransform2);
+
+ float[] normalizedColorMatrix1 = Arrays.copyOf(colorMatrix1, colorMatrix1.length);
+ normalizeCM(normalizedColorMatrix1);
+ float[] normalizedColorMatrix2 = Arrays.copyOf(colorMatrix2, colorMatrix2.length);
+ normalizeCM(normalizedColorMatrix2);
+
+ if (DEBUG) {
+ Log.d(TAG, "Normalized ForwardTransform1: " + Arrays.toString(normalizedForwardTransform1));
+ Log.d(TAG, "Normalized ForwardTransform2: " + Arrays.toString(normalizedForwardTransform2));
+ Log.d(TAG, "Normalized ColorMatrix1: " + Arrays.toString(normalizedColorMatrix1));
+ Log.d(TAG, "Normalized ColorMatrix2: " + Arrays.toString(normalizedColorMatrix2));
+ }
+
+ // Calculate full sensor colorspace to sRGB colorspace transform.
+ double interpolationFactor = findDngInterpolationFactor(referenceIlluminant1,
+ referenceIlluminant2, calibrationTransform1, calibrationTransform2,
+ normalizedColorMatrix1, normalizedColorMatrix2, neutralColorPoint);
+ if (DEBUG) Log.d(TAG, "Interpolation factor used: " + interpolationFactor);
+ float[] sensorToXYZ = new float[9];
+ calculateCameraToXYZD50Transform(normalizedForwardTransform1, normalizedForwardTransform2,
+ calibrationTransform1, calibrationTransform2, neutralColorPoint,
+ interpolationFactor, /*out*/sensorToXYZ);
+ if (DEBUG) Log.d(TAG, "CameraToXYZ xform used: " + Arrays.toString(sensorToXYZ));
+ float[] sensorToProPhoto = new float[9];
+ multiply(sXYZtoProPhoto, sensorToXYZ, /*out*/sensorToProPhoto);
+ if (DEBUG) Log.d(TAG, "CameraToIntemediate xform used: " + Arrays.toString(sensorToProPhoto));
+ Allocation output = Allocation.createFromBitmap(rs, argbOutput);
+
+ float[] proPhotoToSRGB = new float[9];
+ multiply(sXYZtoRGBBradford, sProPhotoToXYZ, /*out*/proPhotoToSRGB);
+
+ // Setup input allocation (16-bit raw pixels)
+ Type.Builder typeBuilder = new Type.Builder(rs, Element.U16(rs));
+ typeBuilder.setX((inputStride / 2));
+ typeBuilder.setY(inputHeight);
+ Type inputType = typeBuilder.create();
+ Allocation input = Allocation.createTyped(rs, inputType);
+ input.copyFromUnchecked(rawImageInput);
+
+ // Setup RS kernel globals
+ ScriptC_raw_converter converterKernel = new ScriptC_raw_converter(rs);
+ converterKernel.set_inputRawBuffer(input);
+ converterKernel.set_whiteLevel(whiteLevel);
+ converterKernel.set_sensorToIntermediate(new Matrix3f(transpose(sensorToProPhoto)));
+ converterKernel.set_intermediateToSRGB(new Matrix3f(transpose(proPhotoToSRGB)));
+ converterKernel.set_offsetX(outputOffsetX);
+ converterKernel.set_offsetY(outputOffsetY);
+ converterKernel.set_rawHeight(inputHeight);
+ converterKernel.set_rawWidth(inputWidth);
+ converterKernel.set_neutralPoint(new Float3(neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()));
+ converterKernel.set_toneMapCoeffs(new Float4(DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[0],
+ DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[1], DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[2],
+ DEFAULT_ACR3_TONEMAP_CURVE_COEFFS[3]));
+ converterKernel.set_hasGainMap(gainMap != null);
+ if (gainMap != null) {
+ converterKernel.set_gainMap(gainMap);
+ converterKernel.set_gainMapWidth(lensShadingMap.getColumnCount());
+ converterKernel.set_gainMapHeight(lensShadingMap.getRowCount());
+ }
+
+ converterKernel.set_cfaPattern(cfa);
+ converterKernel.set_blackLevelPattern(new Int4(blackLevelPattern[0],
+ blackLevelPattern[1], blackLevelPattern[2], blackLevelPattern[3]));
+ converterKernel.forEach_convert_RAW_To_ARGB(output);
+ output.copyTo(argbOutput); // Force RS sync with bitmap (does not do an extra copy).
+ }
+
+ /**
+ * Create a float-backed renderscript {@link Allocation} with the given dimensions, containing
+ * the contents of the given float array.
+ *
+ * @param rs a {@link RenderScript} context to use.
+ * @param fArray the float array to copy into the {@link Allocation}.
+ * @param width the width of the {@link Allocation}.
+ * @param height the height of the {@link Allocation}.
+ * @return an {@link Allocation} containing the given floats.
+ */
+ private static Allocation createFloat4Allocation(RenderScript rs, float[] fArray,
+ int width, int height) {
+ if (fArray.length != width * height * 4) {
+ throw new IllegalArgumentException("Invalid float array of length " + fArray.length +
+ ", must be correct size for Allocation of dimensions " + width + "x" + height);
+ }
+ Type.Builder builder = new Type.Builder(rs, Element.F32_4(rs));
+ builder.setX(width);
+ builder.setY(height);
+ Allocation fAlloc = Allocation.createTyped(rs, builder.create());
+ fAlloc.copyFrom(fArray);
+ return fAlloc;
+ }
+
+ /**
+ * Calculate the correlated color temperature (CCT) for a given x,y chromaticity in CIE 1931 x,y
+ * chromaticity space using McCamy's cubic approximation algorithm given in:
+ *
+ * McCamy, Calvin S. (April 1992).
+ * "Correlated color temperature as an explicit function of chromaticity coordinates".
+ * Color Research & Application 17 (2): 142–144
+ *
+ * @param x x chromaticity component.
+ * @param y y chromaticity component.
+ *
+ * @return the CCT associated with this chromaticity coordinate.
+ */
+ private static double calculateColorTemperature(double x, double y) {
+ double n = (x - 0.332) / (y - 0.1858);
+ return -449 * Math.pow(n, 3) + 3525 * Math.pow(n, 2) - 6823.3 * n + 5520.33;
+ }
+
+ /**
+ * Calculate the x,y chromaticity coordinates in CIE 1931 x,y chromaticity space from the given
+ * CIE XYZ coordinates.
+ *
+ * @param X the CIE XYZ X coordinate.
+ * @param Y the CIE XYZ Y coordinate.
+ * @param Z the CIE XYZ Z coordinate.
+ *
+ * @return the [x, y] chromaticity coordinates as doubles.
+ */
+ private static double[] calculateCIExyCoordinates(double X, double Y, double Z) {
+ double[] ret = new double[] { 0, 0 };
+ ret[0] = X / (X + Y + Z);
+ ret[1] = Y / (X + Y + Z);
+ return ret;
+ }
+
+ /**
+ * Linearly interpolate between a and b given fraction f.
+ *
+ * @param a first term to interpolate between, a will be returned when f == 0.
+ * @param b second term to interpolate between, b will be returned when f == 1.
+ * @param f the fraction to interpolate by.
+ *
+ * @return interpolated result as double.
+ */
+ private static double lerp(double a, double b, double f) {
+ return (a * (1.0f - f)) + (b * f);
+ }
+
+ /**
+ * Linearly interpolate between 3x3 matrices a and b given fraction f.
+ *
+ * @param a first 3x3 matrix to interpolate between, a will be returned when f == 0.
+ * @param b second 3x3 matrix to interpolate between, b will be returned when f == 1.
+ * @param f the fraction to interpolate by.
+ * @param result will be set to contain the interpolated matrix.
+ */
+ private static void lerp(float[] a, float[] b, double f, /*out*/float[] result) {
+ for (int i = 0; i < 9; i++) {
+ result[i] = (float) lerp(a[i], b[i], f);
+ }
+ }
+
+ /**
+ * Convert a 9x9 {@link ColorSpaceTransform} to a matrix and write the matrix into the
+ * output.
+ *
+ * @param xform a {@link ColorSpaceTransform} to transform.
+ * @param output the 3x3 matrix to overwrite.
+ */
+ private static void convertColorspaceTransform(ColorSpaceTransform xform, /*out*/float[] output) {
+ for (int i = 0; i < 3; i++) {
+ for (int j = 0; j < 3; j++) {
+ output[i * 3 + j] = xform.getElement(j, i).floatValue();
+ }
+ }
+ }
+
+ /**
+ * Find the interpolation factor to use with the RAW matrices given a neutral color point.
+ *
+ * @param referenceIlluminant1 first reference illuminant.
+ * @param referenceIlluminant2 second reference illuminant.
+ * @param calibrationTransform1 calibration matrix corresponding to the first reference
+ * illuminant.
+ * @param calibrationTransform2 calibration matrix corresponding to the second reference
+ * illuminant.
+ * @param colorMatrix1 color matrix corresponding to the first reference illuminant.
+ * @param colorMatrix2 color matrix corresponding to the second reference illuminant.
+ * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+ *
+ * @return the interpolation factor corresponding to the given neutral color point.
+ */
+ private static double findDngInterpolationFactor(int referenceIlluminant1,
+ int referenceIlluminant2, float[] calibrationTransform1, float[] calibrationTransform2,
+ float[] colorMatrix1, float[] colorMatrix2, Rational[/*3*/] neutralColorPoint) {
+
+ int colorTemperature1 = sStandardIlluminants.get(referenceIlluminant1, NO_ILLUMINANT);
+ if (colorTemperature1 == NO_ILLUMINANT) {
+ throw new IllegalArgumentException("No such illuminant for reference illuminant 1: " +
+ referenceIlluminant1);
+ }
+
+ int colorTemperature2 = sStandardIlluminants.get(referenceIlluminant2, NO_ILLUMINANT);
+ if (colorTemperature2 == NO_ILLUMINANT) {
+ throw new IllegalArgumentException("No such illuminant for reference illuminant 2: " +
+ referenceIlluminant2);
+ }
+
+ if (DEBUG) Log.d(TAG, "ColorTemperature1: " + colorTemperature1);
+ if (DEBUG) Log.d(TAG, "ColorTemperature2: " + colorTemperature2);
+
+ double interpFactor = 0.5; // Initial guess for interpolation factor
+ double oldInterpFactor = interpFactor;
+
+ double lastDiff = Double.MAX_VALUE;
+ double tolerance = 0.0001;
+ float[] XYZToCamera1 = new float[9];
+ float[] XYZToCamera2 = new float[9];
+ multiply(calibrationTransform1, colorMatrix1, /*out*/XYZToCamera1);
+ multiply(calibrationTransform2, colorMatrix2, /*out*/XYZToCamera2);
+
+ float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+
+ float[] neutralGuess = new float[3];
+ float[] interpXYZToCamera = new float[9];
+ float[] interpXYZToCameraInverse = new float[9];
+
+
+ double lower = Math.min(colorTemperature1, colorTemperature2);
+ double upper = Math.max(colorTemperature1, colorTemperature2);
+
+ if(DEBUG) {
+ Log.d(TAG, "XYZtoCamera1: " + Arrays.toString(XYZToCamera1));
+ Log.d(TAG, "XYZtoCamera2: " + Arrays.toString(XYZToCamera2));
+ Log.d(TAG, "Finding interpolation factor, initial guess 0.5...");
+ }
+ // Iteratively guess xy value, find new CCT, and update interpolation factor.
+ int loopLimit = 30;
+ int count = 0;
+ while (lastDiff > tolerance && loopLimit > 0) {
+ if (DEBUG) Log.d(TAG, "Loop count " + count);
+ lerp(XYZToCamera1, XYZToCamera2, interpFactor, interpXYZToCamera);
+ if (!invert(interpXYZToCamera, /*out*/interpXYZToCameraInverse)) {
+ throw new IllegalArgumentException(
+ "Cannot invert XYZ to Camera matrix, input matrices are invalid.");
+ }
+
+ map(interpXYZToCameraInverse, cameraNeutral, /*out*/neutralGuess);
+ double[] xy = calculateCIExyCoordinates(neutralGuess[0], neutralGuess[1],
+ neutralGuess[2]);
+
+ double colorTemperature = calculateColorTemperature(xy[0], xy[1]);
+
+ if (colorTemperature <= lower) {
+ interpFactor = 1;
+ } else if (colorTemperature >= upper) {
+ interpFactor = 0;
+ } else {
+ double invCT = 1.0 / colorTemperature;
+ interpFactor = (invCT - 1.0 / upper) / ( 1.0 / lower - 1.0 / upper);
+ }
+
+ if (lower == colorTemperature1) {
+ interpFactor = 1.0 - interpFactor;
+ }
+
+ interpFactor = (interpFactor + oldInterpFactor) / 2;
+ lastDiff = Math.abs(oldInterpFactor - interpFactor);
+ oldInterpFactor = interpFactor;
+ loopLimit--;
+ count++;
+
+ if (DEBUG) {
+ Log.d(TAG, "CameraToXYZ chosen: " + Arrays.toString(interpXYZToCameraInverse));
+ Log.d(TAG, "XYZ neutral color guess: " + Arrays.toString(neutralGuess));
+ Log.d(TAG, "xy coordinate: " + Arrays.toString(xy));
+ Log.d(TAG, "xy color temperature: " + colorTemperature);
+ Log.d(TAG, "New interpolation factor: " + interpFactor);
+ }
+ }
+
+ if (loopLimit == 0) {
+ Log.w(TAG, "Could not converge on interpolation factor, using factor " + interpFactor +
+ " with remaining error factor of " + lastDiff);
+ }
+ return interpFactor;
+ }
+
+ /**
+ * Calculate the transform from the raw camera sensor colorspace to CIE XYZ colorspace with a
+ * D50 whitepoint.
+ *
+ * @param forwardTransform1 forward transform matrix corresponding to the first reference
+ * illuminant.
+ * @param forwardTransform2 forward transform matrix corresponding to the second reference
+ * illuminant.
+ * @param calibrationTransform1 calibration transform matrix corresponding to the first
+ * reference illuminant.
+ * @param calibrationTransform2 calibration transform matrix corresponding to the second
+ * reference illuminant.
+ * @param neutralColorPoint the neutral color point used to calculate the interpolation factor.
+ * @param interpolationFactor the interpolation factor to use for the forward and
+ * calibration transforms.
+ * @param outputTransform set to the full sensor to XYZ colorspace transform.
+ */
+ private static void calculateCameraToXYZD50Transform(float[] forwardTransform1,
+ float[] forwardTransform2, float[] calibrationTransform1, float[] calibrationTransform2,
+ Rational[/*3*/] neutralColorPoint, double interpolationFactor,
+ /*out*/float[] outputTransform) {
+ float[] cameraNeutral = new float[] { neutralColorPoint[0].floatValue(),
+ neutralColorPoint[1].floatValue(), neutralColorPoint[2].floatValue()};
+ if (DEBUG) Log.d(TAG, "Camera neutral: " + Arrays.toString(cameraNeutral));
+
+ float[] interpolatedCC = new float[9];
+ lerp(calibrationTransform1, calibrationTransform2, interpolationFactor,
+ interpolatedCC);
+ float[] inverseInterpolatedCC = new float[9];
+ if (!invert(interpolatedCC, /*out*/inverseInterpolatedCC)) {
+ throw new IllegalArgumentException( "Cannot invert interpolated calibration transform" +
+ ", input matrices are invalid.");
+ }
+ if (DEBUG) Log.d(TAG, "Inverted interpolated CalibrationTransform: " +
+ Arrays.toString(inverseInterpolatedCC));
+
+ float[] referenceNeutral = new float[3];
+ map(inverseInterpolatedCC, cameraNeutral, /*out*/referenceNeutral);
+ if (DEBUG) Log.d(TAG, "Reference neutral: " + Arrays.toString(referenceNeutral));
+ float maxNeutral = Math.max(Math.max(referenceNeutral[0], referenceNeutral[1]),
+ referenceNeutral[2]);
+ float[] D = new float[] { maxNeutral/referenceNeutral[0], 0, 0,
+ 0, maxNeutral/referenceNeutral[1], 0,
+ 0, 0, maxNeutral/referenceNeutral[2] };
+ if (DEBUG) Log.d(TAG, "Reference Neutral Diagonal: " + Arrays.toString(D));
+
+ float[] intermediate = new float[9];
+ float[] intermediate2 = new float[9];
+
+ lerp(forwardTransform1, forwardTransform2, interpolationFactor, /*out*/intermediate);
+ if (DEBUG) Log.d(TAG, "Interpolated ForwardTransform: " + Arrays.toString(intermediate));
+
+ multiply(D, inverseInterpolatedCC, /*out*/intermediate2);
+ multiply(intermediate, intermediate2, /*out*/outputTransform);
+ }
+
+ /**
+ * Map a 3d column vector using the given matrix.
+ *
+ * @param matrix float array containing 3x3 matrix to map vector by.
+ * @param input 3 dimensional vector to map.
+ * @param output 3 dimensional vector result.
+ */
+ private static void map(float[] matrix, float[] input, /*out*/float[] output) {
+ output[0] = input[0] * matrix[0] + input[1] * matrix[1] + input[2] * matrix[2];
+ output[1] = input[0] * matrix[3] + input[1] * matrix[4] + input[2] * matrix[5];
+ output[2] = input[0] * matrix[6] + input[1] * matrix[7] + input[2] * matrix[8];
+ }
+
+ /**
+ * Multiply two 3x3 matrices together: A * B
+ *
+ * @param a left matrix.
+ * @param b right matrix.
+ */
+ private static void multiply(float[] a, float[] b, /*out*/float[] output) {
+ output[0] = a[0] * b[0] + a[1] * b[3] + a[2] * b[6];
+ output[3] = a[3] * b[0] + a[4] * b[3] + a[5] * b[6];
+ output[6] = a[6] * b[0] + a[7] * b[3] + a[8] * b[6];
+ output[1] = a[0] * b[1] + a[1] * b[4] + a[2] * b[7];
+ output[4] = a[3] * b[1] + a[4] * b[4] + a[5] * b[7];
+ output[7] = a[6] * b[1] + a[7] * b[4] + a[8] * b[7];
+ output[2] = a[0] * b[2] + a[1] * b[5] + a[2] * b[8];
+ output[5] = a[3] * b[2] + a[4] * b[5] + a[5] * b[8];
+ output[8] = a[6] * b[2] + a[7] * b[5] + a[8] * b[8];
+ }
+
+ /**
+ * Transpose a 3x3 matrix in-place.
+ *
+ * @param m the matrix to transpose.
+ * @return the transposed matrix.
+ */
+ private static float[] transpose(/*inout*/float[/*9*/] m) {
+ float t = m[1];
+ m[1] = m[3];
+ m[3] = t;
+ t = m[2];
+ m[2] = m[6];
+ m[6] = t;
+ t = m[5];
+ m[5] = m[7];
+ m[7] = t;
+ return m;
+ }
+
+ /**
+ * Invert a 3x3 matrix, or return false if the matrix is singular.
+ *
+ * @param m matrix to invert.
+ * @param output set the output to be the inverse of m.
+ */
+ private static boolean invert(float[] m, /*out*/float[] output) {
+ double a00 = m[0];
+ double a01 = m[1];
+ double a02 = m[2];
+ double a10 = m[3];
+ double a11 = m[4];
+ double a12 = m[5];
+ double a20 = m[6];
+ double a21 = m[7];
+ double a22 = m[8];
+
+ double t00 = a11 * a22 - a21 * a12;
+ double t01 = a21 * a02 - a01 * a22;
+ double t02 = a01 * a12 - a11 * a02;
+ double t10 = a20 * a12 - a10 * a22;
+ double t11 = a00 * a22 - a20 * a02;
+ double t12 = a10 * a02 - a00 * a12;
+ double t20 = a10 * a21 - a20 * a11;
+ double t21 = a20 * a01 - a00 * a21;
+ double t22 = a00 * a11 - a10 * a01;
+
+ double det = a00 * t00 + a01 * t10 + a02 * t20;
+ if (Math.abs(det) < 1e-9) {
+ return false; // Inverse too close to zero, not invertible.
+ }
+
+ output[0] = (float) (t00 / det);
+ output[1] = (float) (t01 / det);
+ output[2] = (float) (t02 / det);
+ output[3] = (float) (t10 / det);
+ output[4] = (float) (t11 / det);
+ output[5] = (float) (t12 / det);
+ output[6] = (float) (t20 / det);
+ output[7] = (float) (t21 / det);
+ output[8] = (float) (t22 / det);
+ return true;
+ }
+
+ /**
+ * Scale each element in a matrix by the given scaling factor.
+ *
+ * @param factor factor to scale by.
+ * @param matrix the float array containing a 3x3 matrix to scale.
+ */
+ private static void scale(float factor, /*inout*/float[] matrix) {
+ for (int i = 0; i < 9; i++) {
+ matrix[i] *= factor;
+ }
+ }
+
+ /**
+ * Clamp a value to a given range.
+ *
+ * @param low lower bound to clamp to.
+ * @param high higher bound to clamp to.
+ * @param value the value to clamp.
+ * @return the clamped value.
+ */
+ private static double clamp(double low, double high, double value) {
+ return Math.max(low, Math.min(high, value));
+ }
+
+ /**
+ * Return the max float in the array.
+ *
+ * @param array array of floats to search.
+ * @return max float in the array.
+ */
+ private static float max(float[] array) {
+ float val = array[0];
+ for (float f : array) {
+ val = (f > val) ? f : val;
+ }
+ return val;
+ }
+
+ /**
+ * Normalize ColorMatrix to eliminate headroom for input space scaled to [0, 1] using
+ * the D50 whitepoint. This maps the D50 whitepoint into the colorspace used by the
+ * ColorMatrix, then uses the resulting whitepoint to renormalize the ColorMatrix so
+ * that the channel values in the resulting whitepoint for this operation are clamped
+ * to the range [0, 1].
+ *
+ * @param colorMatrix a 3x3 matrix containing a DNG ColorMatrix to be normalized.
+ */
+ private static void normalizeCM(/*inout*/float[] colorMatrix) {
+ float[] tmp = new float[3];
+ map(colorMatrix, D50_XYZ, /*out*/tmp);
+ float maxVal = max(tmp);
+ if (maxVal > 0) {
+ scale(1.0f / maxVal, colorMatrix);
+ }
+ }
+
+ /**
+ * Normalize ForwardMatrix to ensure that sensor whitepoint [1, 1, 1] maps to D50 in CIE XYZ
+ * colorspace.
+ *
+ * @param forwardMatrix a 3x3 matrix containing a DNG ForwardTransform to be normalized.
+ */
+ private static void normalizeFM(/*inout*/float[] forwardMatrix) {
+ float[] tmp = new float[] {1, 1, 1};
+ float[] xyz = new float[3];
+ map(forwardMatrix, tmp, /*out*/xyz);
+
+ float[] intermediate = new float[9];
+ float[] m = new float[] {1.0f / xyz[0], 0, 0, 0, 1.0f / xyz[1], 0, 0, 0, 1.0f / xyz[2]};
+
+ multiply(m, forwardMatrix, /*out*/ intermediate);
+ float[] m2 = new float[] {D50_XYZ[0], 0, 0, 0, D50_XYZ[1], 0, 0, 0, D50_XYZ[2]};
+ multiply(m2, intermediate, /*out*/forwardMatrix);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
new file mode 100644
index 0000000..c8b353e
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/raw_converter.rs
@@ -0,0 +1,369 @@
+/*
+ * 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.
+ */
+
+#include "../common.rs"
+
+// This file includes a conversion kernel for RGGB, GRBG, GBRG, and BGGR Bayer patterns.
+// Applying this script also will apply black-level subtraction, rescaling, clipping, tonemapping,
+// and color space transforms along with the Bayer demosaic. See RawConverter.java
+// for more information.
+
+// Input globals
+
+rs_allocation inputRawBuffer; // RAW16 buffer of dimensions (raw image stride) * (raw image height)
+rs_allocation gainMap; // Gainmap to apply to linearized raw sensor data.
+uint cfaPattern; // The Color Filter Arrangement pattern used
+uint gainMapWidth; // The width of the gain map
+uint gainMapHeight; // The height of the gain map
+bool hasGainMap; // Does gainmap exist?
+rs_matrix3x3 sensorToIntermediate; // Color transform from sensor to a wide-gamut colorspace
+rs_matrix3x3 intermediateToSRGB; // Color transform from wide-gamut colorspace to sRGB
+ushort4 blackLevelPattern; // Blacklevel to subtract for each channel, given in CFA order
+int whiteLevel; // Whitelevel of sensor
+uint offsetX; // X offset into inputRawBuffer
+uint offsetY; // Y offset into inputRawBuffer
+uint rawWidth; // Width of raw buffer
+uint rawHeight; // Height of raw buffer
+float3 neutralPoint; // The camera neutral
+float4 toneMapCoeffs; // Coefficients for a polynomial tonemapping curve
+
+// Interpolate gain map to find per-channel gains at a given pixel
+static float4 getGain(uint x, uint y) {
+ float interpX = (((float) x) / rawWidth) * gainMapWidth;
+ float interpY = (((float) y) / rawHeight) * gainMapHeight;
+ uint gX = (uint) interpX;
+ uint gY = (uint) interpY;
+ uint gXNext = (gX + 1 < gainMapWidth) ? gX + 1 : gX;
+ uint gYNext = (gY + 1 < gainMapHeight) ? gY + 1 : gY;
+
+ float4 tl = *((float4 *) rsGetElementAt(gainMap, gX, gY));
+ float4 tr = *((float4 *) rsGetElementAt(gainMap, gXNext, gY));
+ float4 bl = *((float4 *) rsGetElementAt(gainMap, gX, gYNext));
+ float4 br = *((float4 *) rsGetElementAt(gainMap, gXNext, gYNext));
+
+ float fracX = interpX - (float) gX;
+ float fracY = interpY - (float) gY;
+ float invFracX = 1.f - fracX;
+ float invFracY = 1.f - fracY;
+
+ return tl * invFracX * invFracY + tr * fracX * invFracY +
+ bl * invFracX * fracY + br * fracX * fracY;
+}
+
+// Apply gamma correction using sRGB gamma curve
+static float gammaEncode(float x) {
+ return (x <= 0.0031308f) ? x * 12.92f : 1.055f * pow(x, 0.4166667f) - 0.055f;
+}
+
+// Apply gamma correction to each color channel in RGB pixel
+static float3 gammaCorrectPixel(float3 rgb) {
+ float3 ret;
+ ret.x = gammaEncode(rgb.x);
+ ret.y = gammaEncode(rgb.y);
+ ret.z = gammaEncode(rgb.z);
+ return ret;
+}
+
+// Apply polynomial tonemapping curve to each color channel in RGB pixel.
+// This attempts to apply tonemapping without changing the hue of each pixel,
+// i.e.:
+//
+// For some RGB values:
+// M = max(R, G, B)
+// m = min(R, G, B)
+// m' = mid(R, G, B)
+// chroma = M - m
+// H = m' - m / chroma
+//
+// The relationship H=H' should be preserved, where H and H' are calculated from
+// the RGB and RGB' value at this pixel before and after this tonemapping
+// operation has been applied, respectively.
+static float3 tonemap(float3 rgb) {
+ float3 sorted = clamp(rgb, 0.f, 1.f);
+ float tmp;
+ int permutation = 0;
+
+ // Sort the RGB channels by value
+ if (sorted.z < sorted.y) {
+ tmp = sorted.z;
+ sorted.z = sorted.y;
+ sorted.y = tmp;
+ permutation |= 1;
+ }
+ if (sorted.y < sorted.x) {
+ tmp = sorted.y;
+ sorted.y = sorted.x;
+ sorted.x = tmp;
+ permutation |= 2;
+ }
+ if (sorted.z < sorted.y) {
+ tmp = sorted.z;
+ sorted.z = sorted.y;
+ sorted.y = tmp;
+ permutation |= 4;
+ }
+
+ float2 minmax;
+ minmax.x = sorted.x;
+ minmax.y = sorted.z;
+
+ // Apply tonemapping curve to min, max RGB channel values
+ minmax = native_powr(minmax, 3.f) * toneMapCoeffs.x +
+ native_powr(minmax, 2.f) * toneMapCoeffs.y +
+ minmax * toneMapCoeffs.z + toneMapCoeffs.w;
+
+ // Rescale middle value
+ float newMid;
+ if (sorted.z == sorted.x) {
+ newMid = minmax.y;
+ } else {
+ newMid = minmax.x + ((minmax.y - minmax.x) * (sorted.y - sorted.x) /
+ (sorted.z - sorted.x));
+ }
+
+ float3 finalRGB;
+ switch (permutation) {
+ case 0: // b >= g >= r
+ finalRGB.x = minmax.x;
+ finalRGB.y = newMid;
+ finalRGB.z = minmax.y;
+ break;
+ case 1: // g >= b >= r
+ finalRGB.x = minmax.x;
+ finalRGB.z = newMid;
+ finalRGB.y = minmax.y;
+ break;
+ case 2: // b >= r >= g
+ finalRGB.y = minmax.x;
+ finalRGB.x = newMid;
+ finalRGB.z = minmax.y;
+ break;
+ case 3: // g >= r >= b
+ finalRGB.z = minmax.x;
+ finalRGB.x = newMid;
+ finalRGB.y = minmax.y;
+ break;
+ case 6: // r >= b >= g
+ finalRGB.y = minmax.x;
+ finalRGB.z = newMid;
+ finalRGB.x = minmax.y;
+ break;
+ case 7: // r >= g >= b
+ finalRGB.z = minmax.x;
+ finalRGB.y = newMid;
+ finalRGB.x = minmax.y;
+ break;
+ case 4: // impossible
+ case 5: // impossible
+ default:
+ LOGD("raw_converter.rs: Logic error in tonemap.", 0);
+ break;
+ }
+ return clamp(finalRGB, 0.f, 1.f);
+}
+
+// Apply a colorspace transform to the intermediate colorspace, apply
+// a tonemapping curve, apply a colorspace transform to a final colorspace,
+// and apply a gamma correction curve.
+static float3 applyColorspace(float3 pRGB) {
+ pRGB.x = clamp(pRGB.x, 0.f, neutralPoint.x);
+ pRGB.y = clamp(pRGB.y, 0.f, neutralPoint.y);
+ pRGB.z = clamp(pRGB.z, 0.f, neutralPoint.z);
+
+ float3 intermediate = rsMatrixMultiply(&sensorToIntermediate, pRGB);
+ intermediate = tonemap(intermediate);
+ return gammaCorrectPixel(clamp(rsMatrixMultiply(&intermediateToSRGB, intermediate), 0.f, 1.f));
+}
+
+// Load a 3x3 patch of pixels into the output.
+static void load3x3(uint x, uint y, rs_allocation buf, /*out*/float* outputArray) {
+ outputArray[0] = *((ushort *) rsGetElementAt(buf, x - 1, y - 1));
+ outputArray[1] = *((ushort *) rsGetElementAt(buf, x, y - 1));
+ outputArray[2] = *((ushort *) rsGetElementAt(buf, x + 1, y - 1));
+ outputArray[3] = *((ushort *) rsGetElementAt(buf, x - 1, y));
+ outputArray[4] = *((ushort *) rsGetElementAt(buf, x, y));
+ outputArray[5] = *((ushort *) rsGetElementAt(buf, x + 1, y));
+ outputArray[6] = *((ushort *) rsGetElementAt(buf, x - 1, y + 1));
+ outputArray[7] = *((ushort *) rsGetElementAt(buf, x, y + 1));
+ outputArray[8] = *((ushort *) rsGetElementAt(buf, x + 1, y + 1));
+}
+
+// Blacklevel subtract, and normalize each pixel in the outputArray, and apply the
+// gain map.
+static void linearizeAndGainmap(uint x, uint y, ushort4 blackLevel, int whiteLevel,
+ uint cfa, /*inout*/float* outputArray) {
+ uint kk = 0;
+ for (uint j = y - 1; j <= y + 1; j++) {
+ for (uint i = x - 1; i <= x + 1; i++) {
+ uint index = (i & 1) | ((j & 1) << 1); // bits [0,1] are blacklevel offset
+ index |= (cfa << 2); // bits [2,3] are cfa
+ float bl = 0.f;
+ float g = 1.f;
+ float4 gains = 1.f;
+ if (hasGainMap) {
+ gains = getGain(i, j);
+ }
+ switch (index) {
+ // RGGB
+ case 0:
+ bl = blackLevel.x;
+ g = gains.x;
+ break;
+ case 1:
+ bl = blackLevel.y;
+ g = gains.y;
+ break;
+ case 2:
+ bl = blackLevel.z;
+ g = gains.z;
+ break;
+ case 3:
+ bl = blackLevel.w;
+ g = gains.w;
+ break;
+ // GRBG
+ case 4:
+ bl = blackLevel.x;
+ g = gains.y;
+ break;
+ case 5:
+ bl = blackLevel.y;
+ g = gains.x;
+ break;
+ case 6:
+ bl = blackLevel.z;
+ g = gains.w;
+ break;
+ case 7:
+ bl = blackLevel.w;
+ g = gains.z;
+ break;
+ // GBRG
+ case 8:
+ bl = blackLevel.x;
+ g = gains.y;
+ break;
+ case 9:
+ bl = blackLevel.y;
+ g = gains.w;
+ break;
+ case 10:
+ bl = blackLevel.z;
+ g = gains.x;
+ break;
+ case 11:
+ bl = blackLevel.w;
+ g = gains.z;
+ break;
+ // BGGR
+ case 12:
+ bl = blackLevel.x;
+ g = gains.w;
+ break;
+ case 13:
+ bl = blackLevel.y;
+ g = gains.y;
+ break;
+ case 14:
+ bl = blackLevel.z;
+ g = gains.z;
+ break;
+ case 15:
+ bl = blackLevel.w;
+ g = gains.x;
+ break;
+ }
+ outputArray[kk] = clamp(g * (outputArray[kk] - bl) / (whiteLevel - bl), 0.f, 1.f);
+ kk++;
+ }
+ }
+}
+
+// Apply bilinear-interpolation to demosaic
+static float3 demosaic(uint x, uint y, uint cfa, float* inputArray) {
+ uint index = (x & 1) | ((y & 1) << 1);
+ index |= (cfa << 2);
+ float3 pRGB;
+ switch (index) {
+ case 0:
+ case 5:
+ case 10:
+ case 15: // Red centered
+ // B G B
+ // G R G
+ // B G B
+ pRGB.x = inputArray[4];
+ pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+ pRGB.z = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+ break;
+ case 1:
+ case 4:
+ case 11:
+ case 14: // Green centered w/ horizontally adjacent Red
+ // G B G
+ // R G R
+ // G B G
+ pRGB.x = (inputArray[3] + inputArray[5]) / 2;
+ pRGB.y = inputArray[4];
+ pRGB.z = (inputArray[1] + inputArray[7]) / 2;
+ break;
+ case 2:
+ case 7:
+ case 8:
+ case 13: // Green centered w/ horizontally adjacent Blue
+ // G R G
+ // B G B
+ // G R G
+ pRGB.x = (inputArray[1] + inputArray[7]) / 2;
+ pRGB.y = inputArray[4];
+ pRGB.z = (inputArray[3] + inputArray[5]) / 2;
+ break;
+ case 3:
+ case 6:
+ case 9:
+ case 12: // Blue centered
+ // R G R
+ // G B G
+ // R G R
+ pRGB.x = (inputArray[0] + inputArray[2] + inputArray[6] + inputArray[8]) / 4;
+ pRGB.y = (inputArray[1] + inputArray[3] + inputArray[5] + inputArray[7]) / 4;
+ pRGB.z = inputArray[4];
+ break;
+ }
+
+ return pRGB;
+}
+
+// Full RAW->ARGB bitmap conversion kernel
+uchar4 RS_KERNEL convert_RAW_To_ARGB(uint x, uint y) {
+ float3 pRGB;
+ uint xP = x + offsetX;
+ uint yP = y + offsetY;
+ if (xP == 0) xP = 1;
+ if (yP == 0) yP = 1;
+ if (xP == rawWidth - 1) xP = rawWidth - 2;
+ if (yP == rawHeight - 1) yP = rawHeight - 2;
+
+ float patch[9];
+ // TODO: Once ScriptGroup and RS kernels have been updated to allow for iteration over 3x3 pixel
+ // patches, this can be optimized to avoid re-applying the pre-demosaic steps for each pixel,
+ // potentially achieving a 9x speedup here.
+ load3x3(xP, yP, inputRawBuffer, /*out*/ patch);
+ linearizeAndGainmap(xP, yP, blackLevelPattern, whiteLevel, cfaPattern, /*inout*/patch);
+ pRGB = demosaic(xP, yP, cfaPattern, patch);
+
+ return rsPackColorTo8888(applyColorspace(pRGB));
+}
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 5fc6321..ff69581 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
@@ -38,6 +38,7 @@
import android.test.AndroidTestCase;
import android.util.Log;
import android.view.Surface;
+import android.view.WindowManager;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
import com.android.ex.camera2.blocking.BlockingStateCallback;
@@ -70,11 +71,14 @@
protected List<Size> mOrderedVideoSizes; // In descending order.
protected List<Size> mOrderedStillSizes; // In descending order.
+ protected WindowManager mWindowManager;
+
@Override
public void setContext(Context context) {
super.setContext(context);
mCameraManager = (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
assertNotNull("Can't connect to camera manager!", mCameraManager);
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
/**
@@ -184,7 +188,8 @@
mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
CheckLevel.ASSERT, /*collector*/null);
mOrderedPreviewSizes = getSupportedPreviewSizes(
- cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ cameraId, mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
index 03e9647..5d832d6 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/testcases/Camera2MultiViewTestCase.java
@@ -43,6 +43,7 @@
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
+import android.view.WindowManager;
import com.android.ex.camera2.blocking.BlockingCameraManager;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
@@ -76,6 +77,8 @@
private CameraHolder[] mCameraHolders;
private HashMap<String, Integer> mCameraIdMap;
+ protected WindowManager mWindowManager;
+
public Camera2MultiViewTestCase() {
super(Camera2MultiViewCtsActivity.class);
}
@@ -104,6 +107,7 @@
mCameraHolders[i] = new CameraHolder(mCameraIds[i]);
mCameraIdMap.put(mCameraIds[i], i);
}
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
@Override
@@ -359,7 +363,8 @@
mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(mCameraId),
CheckLevel.ASSERT, /*collector*/null);
mOrderedPreviewSizes = getSupportedPreviewSizes(
- mCameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ mCameraId, mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
assertNotNull(String.format("Failed to open camera device ID: %s", mCameraId), mCamera);
}
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 bcc4061..00f7e1c 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
@@ -29,6 +29,7 @@
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
+import android.view.WindowManager;
import android.content.Context;
import android.graphics.ImageFormat;
import android.hardware.camera2.CameraAccessException;
@@ -102,6 +103,7 @@
protected List<Size> mOrderedStillSizes; // In descending order.
protected HashMap<Size, Long> mMinPreviewFrameDurationMap;
+ protected WindowManager mWindowManager;
public Camera2SurfaceViewTestCase() {
super(Camera2SurfaceViewCtsActivity.class);
@@ -131,6 +133,8 @@
mHandler = new Handler(mHandlerThread.getLooper());
mCameraListener = new BlockingStateCallback();
mCollector = new CameraErrorCollector();
+
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
}
@Override
@@ -559,7 +563,8 @@
mCollector.setCameraId(cameraId);
mStaticInfo = new StaticMetadata(mCameraManager.getCameraCharacteristics(cameraId),
CheckLevel.ASSERT, /*collector*/null);
- mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
+ mOrderedPreviewSizes = getSupportedPreviewSizes(cameraId, mCameraManager,
+ getPreviewSizeBound(mWindowManager, PREVIEW_SIZE_BOUND));
mOrderedVideoSizes = getSupportedVideoSizes(cameraId, mCameraManager, PREVIEW_SIZE_BOUND);
mOrderedStillSizes = getSupportedStillSizes(cameraId, mCameraManager, null);
// Use ImageFormat.YUV_420_888 for now. TODO: need figure out what's format for preview
@@ -612,8 +617,8 @@
mPreviewSize.getHeight());
assertTrue("wait for surface change to " + mPreviewSize.toString() + " timed out", res);
mPreviewSurface = holder.getSurface();
- assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
assertNotNull("Preview surface is null", mPreviewSurface);
+ assertTrue("Preview surface is invalid", mPreviewSurface.isValid());
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
index d8f8a1d..380e47d 100755
--- a/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraGLTest.java
@@ -497,7 +497,7 @@
* TODO: This should be made stricter once SurfaceTexture timestamps are generated by the drivers.
*/
@UiThreadTest
- @TimeoutReq(minutes = 20)
+ @TimeoutReq(minutes = 30)
public void testCameraToSurfaceTextureMetadata() throws Exception {
runForAllCameras(testCameraToSurfaceTextureMetadataByCamera);
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index 019bd21..cf616b6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -37,6 +37,7 @@
import android.os.ConditionVariable;
import android.os.Environment;
import android.os.Looper;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.MoreAsserts;
import android.test.UiThreadTest;
@@ -44,6 +45,7 @@
import android.util.Log;
import android.view.SurfaceHolder;
+import com.android.cts.util.TimeoutReq;
import java.io.File;
import java.io.FileOutputStream;
@@ -860,15 +862,16 @@
}
private void testJpegExifByCamera(boolean recording) throws Exception {
- Camera.Parameters parameters = mCamera.getParameters();
if (!recording) mCamera.startPreview();
- double focalLength = parameters.getFocalLength();
Date date = new Date(System.currentTimeMillis());
String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
waitForSnapshotDone();
+ Camera.Parameters parameters = mCamera.getParameters();
+ double focalLength = parameters.getFocalLength();
+
// Test various exif tags.
ExifInterface exif = new ExifInterface(JPEG_PATH);
StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
@@ -1768,6 +1771,7 @@
}
@UiThreadTest
+ @TimeoutReq(minutes = 30)
public void testPreviewPictureSizesCombination() throws Exception {
int nCameras = Camera.getNumberOfCameras();
for (int id = 0; id < nCameras; id++) {
@@ -1975,7 +1979,7 @@
// This method tests if the actual fps is between minimum and maximum.
// It also tests if the frame interval is too long.
public void onPreviewFrame(byte[] data, android.hardware.Camera camera) {
- long arrivalTime = System.currentTimeMillis();
+ long arrivalTime = SystemClock.elapsedRealtime();
camera.addCallbackBuffer(data);
if (firstFrameArrivalTime == 0) firstFrameArrivalTime = arrivalTime;
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
old mode 100644
new mode 100755
index 0e09e8c..68efef0
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -92,17 +92,20 @@
lessThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
lessThanDpi(density, DENSITY_LOW, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
- assertFalse("Device is not expected to be 64-bit", supports64Bit);
- assertMinMemoryMb(424);
- } else if (greaterThanDpi(density, DENSITY_XHIGH, screenSize,
+ if (supports64Bit) {
+ assertMinMemoryMb(704);
+ } else {
+ assertMinMemoryMb(424);
+ }
+ } else if (greaterThanDpi(density, DENSITY_560, screenSize,
SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
- greaterThanDpi(density, DENSITY_TV, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
- greaterThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
+ greaterThanDpi(density, DENSITY_400, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
+ greaterThanDpi(density, DENSITY_XHIGH, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
if (supports64Bit) {
- assertMinMemoryMb(832);
+ assertMinMemoryMb(1824);
} else {
- assertMinMemoryMb(512);
+ assertMinMemoryMb(1344);
}
} else if (greaterThanDpi(density, DENSITY_400, screenSize,
SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
@@ -114,15 +117,15 @@
} else {
assertMinMemoryMb(896);
}
- } else if (greaterThanDpi(density, DENSITY_560, screenSize,
+ } else if (greaterThanDpi(density, DENSITY_XHIGH, screenSize,
SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
- greaterThanDpi(density, DENSITY_400, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
- greaterThanDpi(density, DENSITY_XHIGH, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
+ greaterThanDpi(density, DENSITY_TV, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
+ greaterThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
if (supports64Bit) {
- assertMinMemoryMb(1824);
+ assertMinMemoryMb(832);
} else {
- assertMinMemoryMb(1344);
+ assertMinMemoryMb(512);
}
}
}
@@ -152,7 +155,7 @@
private void assertMinMemoryMb(long minMb) {
long totalMemoryMb = getTotalMemory() / ONE_MEGABYTE;
- boolean lowRam = totalMemoryMb <= minMb * 1.5;
+ boolean lowRam = totalMemoryMb <= 512;
boolean lowRamDevice = mActivityManager.isLowRamDevice();
Log.i(TAG, String.format("minMb=%,d", minMb));
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index 687826c..7640cd7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -20,9 +20,7 @@
import android.hardware.SensorManager;
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import android.hardware.cts.helpers.sensorverification.ISensorVerification;
import java.util.concurrent.TimeUnit;
@@ -263,7 +261,7 @@
rateUs,
maxBatchReportLatencyUs);
TestSensorOperation operation =
- new TestSensorOperation(environment, testDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation.createOperation(environment, testDurationSec, TimeUnit.SECONDS);
executeTest(environment, operation, false /* flushExpected */);
}
@@ -279,23 +277,23 @@
shouldEmulateSensorUnderLoad(),
rateUs,
maxBatchReportLatencyUs);
- TestSensorFlushOperation operation =
- new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation operation = TestSensorOperation
+ .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
executeTest(environment, operation, true /* flushExpected */);
}
private void executeTest(
TestSensorEnvironment environment,
- VerifiableSensorOperation operation,
+ TestSensorOperation operation,
boolean flushExpected) throws Throwable {
operation.addDefaultVerifications();
- operation.setLogEvents(true);
try {
- operation.execute();
+ operation.execute(getCurrentTestNode());
} finally {
- SensorStats.logStats(TAG, operation.getStats());
+ SensorStats stats = operation.getStats();
+ stats.log(TAG);
String sensorRate;
if (environment.getRequestedSamplingPeriodUs() == SensorManager.SENSOR_DELAY_FASTEST) {
@@ -311,7 +309,7 @@
sensorRate,
batching,
flush);
- SensorStats.logStatsToFile(fileName, operation.getStats());
+ stats.logToFile(fileName);
}
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 50cb12d..8c3fb7a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -18,13 +18,11 @@
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
-import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
import android.hardware.cts.helpers.sensoroperations.SequentialSensorOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
import java.util.Random;
@@ -81,7 +79,7 @@
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation continuousOperation =
- new TestSensorOperation(environment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(environment, 100 /* eventCount */);
continuousOperation.addVerification(new EventOrderingVerification());
operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
@@ -93,12 +91,12 @@
sensor.getMinDelay(),
MAX_REPORTING_LATENCY_US);
TestSensorOperation batchingOperation =
- new TestSensorOperation(batchingEnvironment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(batchingEnvironment, 100 /* eventCount */);
batchingOperation.addVerification(new EventOrderingVerification());
operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
}
- operation.execute();
- SensorStats.logStats(TAG, operation.getStats());
+ operation.execute(getCurrentTestNode());
+ operation.getStats().log(TAG);
}
/**
@@ -145,7 +143,7 @@
generateSamplingRateInUs(sensorType),
generateReportLatencyInUs());
TestSensorOperation sensorOperation =
- new TestSensorOperation(environment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(environment, 100 /* eventCount */);
sensorOperation.addVerification(new EventOrderingVerification());
sequentialOperation.add(sensorOperation);
}
@@ -153,8 +151,8 @@
}
}
- operation.execute();
- SensorStats.logStats(TAG, operation.getStats());
+ operation.execute(getCurrentTestNode());
+ operation.getStats().log(TAG);
}
/**
@@ -229,7 +227,7 @@
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation tester =
- new TestSensorOperation(testerEnvironment, 100 /* event count */);
+ TestSensorOperation.createOperation(testerEnvironment, 100 /* event count */);
tester.addVerification(new EventOrderingVerification());
TestSensorEnvironment testeeEnvironment = new TestSensorEnvironment(
@@ -237,18 +235,18 @@
sensorTypeTestee,
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
- VerifiableSensorOperation testee =
- new TestSensorOperation(testeeEnvironment, 100 /* event count */);
+ TestSensorOperation testee =
+ TestSensorOperation.createOperation(testeeEnvironment, 100 /* event count */);
testee.addVerification(new EventOrderingVerification());
ParallelSensorOperation operation = new ParallelSensorOperation();
operation.add(tester, testee);
- operation.execute();
- SensorStats.logStats(TAG, operation.getStats());
+ operation.execute(getCurrentTestNode());
+ operation.getStats().log(TAG);
testee = testee.clone();
- testee.execute();
- SensorStats.logStats(TAG, testee.getStats());
+ testee.execute(getCurrentTestNode());
+ testee.getStats().log(TAG);
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index e8e0f25..162474a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -18,6 +18,8 @@
import com.android.cts.util.TimeoutReq;
+import junit.framework.Assert;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
@@ -27,6 +29,16 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
@@ -39,33 +51,65 @@
import java.util.concurrent.TimeUnit;
public class SensorTest extends SensorTestCase {
- private SensorManager mSensorManager;
- private TriggerListener mTriggerListener;
- private SensorListener mSensorListener;
- private List<Sensor> mSensorList;
private static final String TAG = "SensorTest";
+
// Test only SDK defined sensors. Any sensors with type > 100 are ignored.
private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
- private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(5);
- private static final double MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE = 0.9;
+
private PowerManager.WakeLock mWakeLock;
+ private SensorManager mSensorManager;
+ private TestSensorManager mTestSensorManager;
+ private NullTriggerEventListener mNullTriggerEventListener;
+ private NullSensorEventListener mNullSensorEventListener;
+ private Sensor mTriggerSensor;
+ private List<Sensor> mSensorList;
@Override
protected void setUp() throws Exception {
- super.setUp();
- mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- mTriggerListener = new TriggerListener();
- mSensorListener = new SensorListener();
- mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+ Context context = getContext();
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mNullTriggerEventListener = new NullTriggerEventListener();
+ mNullSensorEventListener = new NullSensorEventListener();
+
+ mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ assertNotNull("SensorList was null.", mSensorList);
+ if (mSensorList.isEmpty()) {
+ // several devices will not have sensors, so we need to skip the tests in those cases
+ throw new SensorTestStateNotSupportedException(
+ "Sensors are not available in the system.");
+ }
+
+ mWakeLock.acquire();
}
+ @Override
+ protected void tearDown() {
+ if (mSensorManager != null) {
+ // SensorManager will check listener and status, so just unregister listener
+ mSensorManager.unregisterListener(mNullSensorEventListener);
+ if (mTriggerSensor != null) {
+ mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+ mTriggerSensor = null;
+ }
+ }
+
+ if (mTestSensorManager != null) {
+ mTestSensorManager.unregisterListener();
+ mTestSensorManager = null;
+ }
+
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
public void testSensorOperations() {
// Because we can't know every sensors unit details, so we can't assert
// get values with specified values.
- List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- assertNotNull(sensors);
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_ACCELEROMETER);
@@ -150,7 +194,6 @@
}
assertTrue(sensors.get(0).getName() + " defined as non-wake-up sensor",
sensors.get(0).isWakeUpSensor());
- return;
}
// Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -207,372 +250,144 @@
}
public void testRequestTriggerWithNonTriggerSensor() {
- Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.requestTriggerSensor(mTriggerListener, sensor);
- assertFalse(result);
+ mTriggerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (mTriggerSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+ boolean result =
+ mSensorManager.requestTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+ assertFalse(result);
}
public void testCancelTriggerWithNonTriggerSensor() {
- Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.cancelTriggerSensor(mTriggerListener, sensor);
- assertFalse(result);
+ mTriggerSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+ if (mTriggerSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+ boolean result =
+ mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, mTriggerSensor);
+ assertFalse(result);
}
public void testRegisterWithTriggerSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
}
+ boolean result = mSensorManager.registerListener(
+ mNullSensorEventListener,
+ sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertFalse(result);
}
public void testRegisterTwiceWithSameSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertTrue(result);
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+
+ boolean result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertTrue(result);
+
+ result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertFalse(result);
}
- class SensorEventTimeStampListener implements SensorEventListener {
- SensorEventTimeStampListener(long eventReportLatencyNs, CountDownLatch latch) {
- mEventReportLatencyNs = eventReportLatencyNs;
- mPrevTimeStampNs = -1;
- mLatch = latch;
- numErrors = 0;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mPrevTimeStampNs == -1) {
- mPrevTimeStampNs = event.timestamp;
- return;
- }
- long currTimeStampNs = event.timestamp;
- if (currTimeStampNs <= mPrevTimeStampNs) {
- Log.w(TAG, "Timestamps not monotonically increasing curr_ts_ns=" +
- event.timestamp + " prev_ts_ns=" + mPrevTimeStampNs);
- numErrors++;
- mPrevTimeStampNs = currTimeStampNs;
- return;
- }
- mLatch.countDown();
-
- final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
-
- if (elapsedRealtimeNs <= currTimeStampNs) {
- Log.w(TAG, "Timestamps into the future curr elapsedRealTimeNs=" + elapsedRealtimeNs
- + " current sensor ts_ns=" + currTimeStampNs);
- ++numErrors;
- } else if (elapsedRealtimeNs-currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
- Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
- " curr_ts_ns=" + currTimeStampNs +
- " diff_ns=" + (elapsedRealtimeNs - currTimeStampNs) +
- " SYNC_TOLERANCE_NS=" + SYNC_TOLERANCE +
- " eventReportLatencyNs=" + mEventReportLatencyNs);
- ++numErrors;
- }
- mPrevTimeStampNs = currTimeStampNs;
- }
-
- public int getNumErrors() {
- return numErrors;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- private int numErrors;
- private long mEventReportLatencyNs;
- private long mPrevTimeStampNs;
- private final CountDownLatch mLatch;
- private final long SYNC_TOLERANCE = 500000000L; // 500 milli seconds approx.
- }
-
- // Register for each sensor and compare the timestamps of SensorEvents that you get with
- // elapsedRealTimeNano.
+ // TODO: remove when parametized tests are supported and EventTimestampSynchronization
+ // verification is added to default verifications
@TimeoutReq(minutes=60)
public void testSensorTimeStamps() throws Exception {
- final int numEvents = 2000;
- try {
- mWakeLock.acquire();
- int numErrors = 0;
- for (Sensor sensor : mSensorList) {
- // Skip OEM defined sensors and non continuous sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
- continue;
- }
-
- for (int iterations = 0; iterations < 2; ++iterations) {
- // Test in both batch mode and non-batch mode for every sensor.
- long maxBatchReportLatencyNs = 10000000000L; // 10 secs
- if (iterations % 2 == 0) maxBatchReportLatencyNs = 0;
-
- final long samplingPeriodNs = (long)(TimeUnit.MICROSECONDS.toNanos(
- sensor.getMinDelay())/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE);
- // If there is a FIFO and a wake-lock is held, events will be reported when
- // the batch timeout expires or when the FIFO is full which ever occurs
- // earlier.
- final long eventReportLatencyNs = Math.min(maxBatchReportLatencyNs,
- sensor.getFifoMaxEventCount() * samplingPeriodNs);
-
- final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
- SensorEventTimeStampListener listener = new SensorEventTimeStampListener(
- eventReportLatencyNs, eventsRemaining);
-
- Log.i(TAG, "Running timeStamp test on " + sensor.getName());
- boolean result = mSensorManager.registerListener(listener, sensor,
- SensorManager.SENSOR_DELAY_FASTEST,
- (int)maxBatchReportLatencyNs/1000);
- assertTrue("Sensor registerListener failed ", result);
-
- long timeToWaitUs = samplingPeriodNs/1000 + eventReportLatencyNs/1000 +
- TIMEOUT_TOLERANCE_US;
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
- (int)(eventReportLatencyNs/1000), eventsRemaining);
-
- mSensorManager.unregisterListener(listener);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(sensor.getName(), (double) totalTimeWaitedUs/1000,
- numEvents, (double) sensor.getMinDelay()/1000,
- eventsRemaining.getCount(),
- numEvents - eventsRemaining.getCount());
- }
- if (listener.getNumErrors() > 5) {
- fail("Check logcat. Timestamp test failed. numErrors=" +
- listener.getNumErrors() + " " + sensor.getName() +
- " maxBatchReportLatencyNs=" + maxBatchReportLatencyNs +
- " samplingPeriodNs=" + sensor.getMinDelay());
- numErrors += listener.getNumErrors();
- } else {
- Log.i(TAG, "TimeStamp test PASS'd on " + sensor.getName());
- }
- }
- }
- } finally {
- mWakeLock.release();
+ ArrayList<Throwable> errorsFound = new ArrayList<>();
+ for (Sensor sensor : mSensorList) {
+ // test both continuous and batching mode sensors
+ verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, errorsFound);
+ verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), errorsFound);
}
+ assertOnErrors(errorsFound);
}
- private void failTimedOut(String sensorName, double totalTimeWaitedMs, int numEvents,
- double minDelayMs, long eventsRemaining, long eventsReceived) {
- final String TIMED_OUT_FORMAT = "Timed out waiting for events from %s " +
- "waited for time=%.1fms to receive totalEvents=%d at samplingRate=%.1fHz" +
- " remainingEvents=%d received events=%d";
- fail(String.format(TIMED_OUT_FORMAT, sensorName, totalTimeWaitedMs, numEvents,
- 1000/minDelayMs, eventsRemaining, eventsReceived));
- }
-
- // Register for updates from each continuous mode sensor, wait for N events, call flush and
- // wait for flushCompleteEvent before unregistering for the sensor.
+ // TODO: remove when parameterized tests are supported (see SensorBatchingTests.java)
@TimeoutReq(minutes=20)
public void testBatchAndFlush() throws Exception {
- try {
- mWakeLock.acquire();
- for (Sensor sensor : mSensorList) {
- // Skip ONLY one-shot sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
- registerListenerCallFlush(sensor, null);
- }
- }
- } finally {
- mWakeLock.release();
+ ArrayList<Throwable> errorsFound = new ArrayList<>();
+ for (Sensor sensor : mSensorList) {
+ verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound);
}
+ assertOnErrors(errorsFound);
}
- // Same as testBatchAndFlush but using Handler version of the API to register for sensors.
- // onSensorChanged is now called on a background thread.
+ /**
+ * Verifies that sensor events arrive in the given message queue (Handler).
+ */
@TimeoutReq(minutes=10)
public void testBatchAndFlushWithHandler() throws Exception {
- try {
- mWakeLock.acquire();
- HandlerThread handlerThread = new HandlerThread("sensorThread");
- handlerThread.start();
- Handler handler = new Handler(handlerThread.getLooper());
- for (Sensor sensor : mSensorList) {
- // Skip ONLY one-shot sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
- registerListenerCallFlush(sensor, handler);
- }
+ Sensor sensor = null;
+ for (Sensor s : mSensorList) {
+ if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+ sensor = s;
+ break;
}
- } finally {
- mWakeLock.release();
}
+ if (sensor == null) {
+ throw new SensorTestStateNotSupportedException(
+ "There are no Continuous sensors in the device.");
+ }
+
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ SensorManager.SENSOR_DELAY_FASTEST,
+ (int) TimeUnit.SECONDS.toMicros(5));
+ mTestSensorManager = new TestSensorManager(environment);
+
+ HandlerThread handlerThread = new HandlerThread("sensorThread");
+ handlerThread.start();
+ Handler handler = new Handler(handlerThread.getLooper());
+ TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
+
+ CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1);
+ listener.waitForEvents(eventLatch, 1);
+ CountDownLatch flushLatch = mTestSensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch);
+ listener.assertEventsReceivedInHandler();
}
- private void registerListenerCallFlush(Sensor sensor, Handler handler)
- throws InterruptedException {
- if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
- return;
- }
- final int numEvents = 500;
- final int rateUs = 0; // DELAY_FASTEST
- final int maxBatchReportLatencyUs = 10000000;
- final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
- final CountDownLatch flushReceived = new CountDownLatch(1);
- SensorEventListener2 listener = new SensorEventListener2() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- eventsRemaining.countDown();
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public void onFlushCompleted(Sensor sensor) {
- flushReceived.countDown();
- }
- };
- // Consider only continuous mode sensors for testing registerListener.
- // For on-change sensors, call registerListener() so that the listener is associated
- // with the sensor so that flush(listener) can be called on it.
- try {
- Log.i(TAG, "testBatch " + sensor.getName());
- boolean result = mSensorManager.registerListener(listener, sensor,
- rateUs, maxBatchReportLatencyUs, handler);
- assertTrue("registerListener failed " + sensor.getName(), result);
-
- // Wait for 500 events or N seconds before the test times out.
- if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
- // Wait for approximately the time required to generate these events + a tolerance
- // of 10 seconds.
- long timeToWaitUs =
- numEvents*(long)(sensor.getMinDelay()/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE)
- + maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
- maxBatchReportLatencyUs, eventsRemaining);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(sensor.getName(), (double)totalTimeWaitedUs/1000, numEvents,
- (double)sensor.getMinDelay()/1000,
- eventsRemaining.getCount(), numEvents - eventsRemaining.getCount());
- }
- }
- Log.i(TAG, "testFlush " + sensor.getName());
- result = mSensorManager.flush(listener);
- assertTrue("flush failed " + sensor.getName(), result);
- boolean collectedAllEvents = flushReceived.await(TIMEOUT_TOLERANCE_US,
- TimeUnit.MICROSECONDS);
- if (!collectedAllEvents) {
- fail("Timed out waiting for flushCompleteEvent from " + sensor.getName() +
- " waitedFor="+ TIMEOUT_TOLERANCE_US/1000 + "ms");
- }
- Log.i(TAG, "testBatchAndFlush PASS " + sensor.getName());
- } finally {
- mSensorManager.unregisterListener(listener);
- }
- }
-
- // Wait till the CountDownLatch counts down to zero. If the events are not delivered within
- // timetoWaitUs, wait an additional maxReportLantencyUs and check if the sensor is streaming
- // data or not. If the sensor is not streaming at all, fail the test or else wait in increments
- // of maxReportLantencyUs to collect sensor events.
- private long waitToCollectAllEvents(long timeToWaitUs, int maxReportLatencyUs,
- CountDownLatch eventsRemaining)
- throws InterruptedException {
- boolean collectedAllEvents = false;
- long totalTimeWaitedUs = 0;
- long remainingEvents;
- final long INCREMENTAL_WAIT_US = maxReportLatencyUs + TimeUnit.SECONDS.toMicros(1);
- do {
- totalTimeWaitedUs += timeToWaitUs;
- remainingEvents = eventsRemaining.getCount();
- collectedAllEvents = eventsRemaining.await(timeToWaitUs, TimeUnit.MICROSECONDS);
- timeToWaitUs = INCREMENTAL_WAIT_US;
- } while (!collectedAllEvents &&
- (remainingEvents - eventsRemaining.getCount() >=(long)INCREMENTAL_WAIT_US/1000000));
- return totalTimeWaitedUs;
- }
-
- // Call registerListener for multiple sensors at a time and call flush.
- public void testBatchAndFlushWithMutipleSensors() throws Exception {
- final int MAX_SENSORS = 3;
+ // TODO: after L release move to SensorBatchingTests and run in all sensors with default
+ // verifications enabled
+ public void testBatchAndFlushWithMultipleSensors() throws Exception {
+ final int maxSensors = 3;
+ final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
List<Sensor> sensorsToTest = new ArrayList<Sensor>();
for (Sensor sensor : mSensorList) {
if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
sensorsToTest.add(sensor);
- if (sensorsToTest.size() == MAX_SENSORS) break;
+ if (sensorsToTest.size() == maxSensors) break;
}
}
-
final int numSensorsToTest = sensorsToTest.size();
if (numSensorsToTest == 0) {
return;
}
- final int numEvents = 500;
- int rateUs = 0; // DELAY_FASTEST
- final int maxBatchReportLatencyUs = 10000000;
- final CountDownLatch eventsRemaining = new CountDownLatch(numSensorsToTest * numEvents);
- final CountDownLatch flushReceived = new CountDownLatch(numSensorsToTest);
- SensorEventListener2 listener = new SensorEventListener2() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- eventsRemaining.countDown();
- }
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public void onFlushCompleted(Sensor sensor) {
- flushReceived.countDown();
- }
- };
-
- try {
- mWakeLock.acquire();
- StringBuilder registeredSensors = new StringBuilder(30);
- for (Sensor sensor : sensorsToTest) {
- rateUs = Math.max(sensor.getMinDelay(), rateUs);
- boolean result = mSensorManager.registerListener(listener, sensor,
- SensorManager.SENSOR_DELAY_FASTEST, maxBatchReportLatencyUs);
- assertTrue("registerListener failed for " + sensor.getName(), result);
- registeredSensors.append(sensor.getName());
- registeredSensors.append(" ");
- }
-
- Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
- long timeToWaitUs =
- numEvents*(long)(rateUs/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE) +
- maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs, maxBatchReportLatencyUs,
- eventsRemaining);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(registeredSensors.toString(), (double)totalTimeWaitedUs/1000,
- numEvents, (double)rateUs/1000, eventsRemaining.getCount(),
- numEvents - eventsRemaining.getCount());
- }
- boolean result = mSensorManager.flush(listener);
- assertTrue("flush failed " + registeredSensors.toString(), result);
- boolean collectedFlushEvent =
- flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
- if (!collectedFlushEvent) {
- fail("Timed out waiting for flushCompleteEvent from " +
- registeredSensors.toString() + " waited for=" + timeToWaitUs/1000 + "ms");
- }
- Log.i(TAG, "testBatchAndFlushWithMutipleSensors PASS'd");
- } finally {
- mSensorManager.unregisterListener(listener);
- mWakeLock.release();
+ StringBuilder builder = new StringBuilder();
+ ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
+ for (Sensor sensor : sensorsToTest) {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ maxReportLatencyUs);
+ FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+ parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+ builder.append(sensor.getName()).append(", ");
}
+
+ Log.i(TAG, "Testing batch/flush for sensors: " + builder);
+ parallelSensorOperation.execute(getCurrentTestNode());
}
private void assertSensorValues(Sensor sensor) {
@@ -583,8 +398,8 @@
assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
" " + sensor.getName(), sensor.getResolution() >= 0);
assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
- assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
- sensor.getName(), sensor.getVersion() > 0);
+ assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
+ sensor.getName(), sensor.getVersion() > 0);
int fifoMaxEventCount = sensor.getFifoMaxEventCount();
int fifoReservedEventCount = sensor.getFifoReservedEventCount();
assertTrue(fifoMaxEventCount >= 0);
@@ -617,19 +432,142 @@
assertEquals(sensors, mSensorManager.getSensors());
}
- class TriggerListener extends TriggerEventListener {
- @Override
- public void onTrigger(TriggerEvent event) {
+ /**
+ * Verifies that a continuous sensor produces events that have timestamps synchronized with
+ * {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ private void verifyLongActivation(
+ Sensor sensor,
+ int maxReportLatencyUs,
+ ArrayList<Throwable> errorsFound) throws InterruptedException {
+ if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+ return;
+ }
+
+ try {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ maxReportLatencyUs);
+ TestSensorOperation operation =
+ TestSensorOperation.createOperation(environment, 20, TimeUnit.SECONDS);
+ operation.addVerification(EventGapVerification.getDefault(environment));
+ operation.addVerification(EventOrderingVerification.getDefault(environment));
+ operation.addVerification(
+ EventTimestampSynchronizationVerification.getDefault(environment));
+
+ Log.i(TAG, "Running timestamp test on: " + sensor.getName());
+ operation.execute(getCurrentTestNode());
+ } catch (InterruptedException e) {
+ // propagate so the test can stop
+ throw e;
+ } catch (Throwable e) {
+ errorsFound.add(e);
+ Log.e(TAG, e.getMessage());
}
}
- class SensorListener implements SensorEventListener {
- @Override
- public void onSensorChanged(SensorEvent event) {
+ /**
+ * Verifies that a client can listen for events, and that
+ * {@link SensorManager#flush(SensorEventListener)} will trigger the appropriate notification
+ * for {@link SensorEventListener2#onFlushCompleted(Sensor)}.
+ */
+ private void verifyRegisterListenerCallFlush(
+ Sensor sensor,
+ Handler handler,
+ ArrayList<Throwable> errorsFound)
+ throws InterruptedException {
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ return;
}
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ try {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ (int) TimeUnit.SECONDS.toMicros(10));
+ FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+ TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
+
+ Log.i(TAG, "Running flush test on: " + sensor.getName());
+ operation.execute(getCurrentTestNode());
+ } catch (InterruptedException e) {
+ // propagate so the test can stop
+ throw e;
+ } catch (Throwable e) {
+ errorsFound.add(e);
+ Log.e(TAG, e.getMessage());
}
}
+
+ private void assertOnErrors(List<Throwable> errorsFound) {
+ if (!errorsFound.isEmpty()) {
+ StringBuilder builder = new StringBuilder();
+ for (Throwable error : errorsFound) {
+ builder.append(error.getMessage()).append("\n");
+ }
+ Assert.fail(builder.toString());
+ }
+ }
+
+ /**
+ * A delegate that drives the execution of Batch/Flush tests.
+ * It performs several operations in order:
+ * - registration
+ * - for continuous sensors it first ensures that the FIFO is filled
+ * - if events do not arrive on time, an assert will be triggered
+ * - requests flush of sensor data
+ * - waits for {@link SensorEventListener2#onFlushCompleted(Sensor)}
+ * - if the event does not arrive, an assert will be triggered
+ */
+ private class FlushExecutor implements TestSensorOperation.Executor {
+ private final TestSensorEnvironment mEnvironment;
+ private final int mEventCount;
+
+ public FlushExecutor(TestSensorEnvironment environment, int eventCount) {
+ mEnvironment = environment;
+ mEventCount = eventCount;
+ }
+
+ /**
+ * Consider only continuous mode sensors for testing register listener.
+ *
+ * For on-change sensors, we only use
+ * {@link TestSensorManager#registerListener(TestSensorEventListener)} to associate the
+ * listener with the sensor. So that {@link TestSensorManager#requestFlush()} can be
+ * invoked on it.
+ */
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
+ try {
+ CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
+ if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
+ listener.waitForEvents(eventLatch, mEventCount);
+ }
+ CountDownLatch flushLatch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ }
+
+ private class NullTriggerEventListener extends TriggerEventListener {
+ @Override
+ public void onTrigger(TriggerEvent event) {}
+ }
+
+ private class NullSensorEventListener implements SensorEventListener {
+ @Override
+ public void onSensorChanged(SensorEvent event) {}
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 6454678..42b8d33 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -16,17 +16,11 @@
package android.hardware.cts;
-import com.android.cts.util.ReportLog;
-import com.android.cts.util.ResultType;
-import com.android.cts.util.ResultUnit;
-
-import android.app.Instrumentation;
-import android.cts.util.DeviceReportLog;
import android.hardware.Sensor;
-import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -35,25 +29,30 @@
*/
public abstract class SensorTestCase extends AndroidTestCase {
// TODO: consolidate all log tags
- protected final String LOG_TAG = "TestRunner";
+ protected static final String LOG_TAG = "TestRunner";
/**
* By default tests need to run in a {@link TestSensorEnvironment} that assumes each sensor is
* running with a load of several listeners, requesting data at different rates.
*
- * In a better world the component acting as builder of {@link ISensorOperation} would compute
+ * In a better world the component acting as builder of {@link SensorOperation} would compute
* this value based on the tests composed.
*
* Ideally, each {@link Sensor} object would expose this information to clients.
*/
private volatile boolean mEmulateSensorUnderLoad = true;
+ /**
+ * By default the test class is the root of the test hierarchy.
+ */
+ private volatile ISensorTestNode mCurrentTestNode = new TestClassNode(getClass());
+
protected SensorTestCase() {}
@Override
- public void runTest() throws Throwable {
+ public void runBare() throws Throwable {
try {
- super.runTest();
+ super.runBare();
} catch (SensorTestStateNotSupportedException e) {
// the sensor state is not supported in the device, log a warning and skip the test
Log.w(LOG_TAG, e.getMessage());
@@ -68,33 +67,24 @@
return mEmulateSensorUnderLoad;
}
- /**
- * Utility method to log selected stats to a {@link ReportLog} object. The stats must be
- * a number or an array of numbers.
- */
- public static void logSelectedStatsToReportLog(Instrumentation instrumentation, int depth,
- String[] keys, SensorStats stats) {
- DeviceReportLog reportLog = new DeviceReportLog(depth);
+ public void setCurrentTestNode(ISensorTestNode value) {
+ mCurrentTestNode = value;
+ }
- for (String key : keys) {
- Object value = stats.getValue(key);
- if (value instanceof Integer) {
- reportLog.printValue(key, (Integer) value, ResultType.NEUTRAL, ResultUnit.NONE);
- } else if (value instanceof Double) {
- reportLog.printValue(key, (Double) value, ResultType.NEUTRAL, ResultUnit.NONE);
- } else if (value instanceof Float) {
- reportLog.printValue(key, (Float) value, ResultType.NEUTRAL, ResultUnit.NONE);
- } else if (value instanceof double[]) {
- reportLog.printArray(key, (double[]) value, ResultType.NEUTRAL, ResultUnit.NONE);
- } else if (value instanceof float[]) {
- float[] tmpFloat = (float[]) value;
- double[] tmpDouble = new double[tmpFloat.length];
- for (int i = 0; i < tmpDouble.length; i++) tmpDouble[i] = tmpFloat[i];
- reportLog.printArray(key, tmpDouble, ResultType.NEUTRAL, ResultUnit.NONE);
- }
+ protected ISensorTestNode getCurrentTestNode() {
+ return mCurrentTestNode;
+ }
+
+ private class TestClassNode implements ISensorTestNode {
+ private final Class<?> mTestClass;
+
+ public TestClassNode(Class<?> testClass) {
+ mTestClass = testClass;
}
- reportLog.printSummary("summary", 0, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.deliverReportToHost(instrumentation);
+ @Override
+ public String getName() {
+ return mTestClass.getSimpleName();
+ }
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 3bd4a03..0fbd8fa 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -22,6 +22,7 @@
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.content.pm.PackageManager;
import java.util.HashMap;
import java.util.Map;
@@ -105,9 +106,14 @@
*/
public void testSensorProperties() {
// sensor type: [getMinDelay()]
- Map<Integer, Object[]> expectedProperties = new HashMap<Integer, Object[]>(3);
- expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
- expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+ Map<Integer, Object[]> expectedProperties = new HashMap<>(3);
+ if(getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
+ expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{20000});
+ expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{20000});
+ }else {
+ expectedProperties.put(Sensor.TYPE_ACCELEROMETER, new Object[]{10000});
+ expectedProperties.put(Sensor.TYPE_GYROSCOPE, new Object[]{10000});
+ }
expectedProperties.put(Sensor.TYPE_MAGNETIC_FIELD, new Object[]{100000});
SensorManager sensorManager =
@@ -541,25 +547,21 @@
sensorType,
shouldEmulateSensorUnderLoad(),
rateUs);
- TestSensorOperation op = new TestSensorOperation(environment, 5, TimeUnit.SECONDS);
+ TestSensorOperation op =
+ TestSensorOperation.createOperation(environment, 5, TimeUnit.SECONDS);
op.addDefaultVerifications();
- op.setLogEvents(true);
- try {
- op.execute();
- } finally {
- SensorStats.logStats(TAG, op.getStats());
- String sensorRate;
- if (rateUs == SensorManager.SENSOR_DELAY_FASTEST) {
- sensorRate = "fastest";
- } else {
- sensorRate = String.format("%.0fhz", environment.getFrequencyHz());
- }
+ try {
+ op.execute(getCurrentTestNode());
+ } finally {
+ SensorStats stats = op.getStats();
+ stats.log(TAG);
+
String fileName = String.format(
"single_%s_%s.txt",
SensorStats.getSanitizedSensorName(environment.getSensor()),
- sensorRate);
- SensorStats.logStatsToFile(fileName, op.getStats());
+ environment.getFrequencyString());
+ stats.logToFile(fileName);
}
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
deleted file mode 100644
index ca7d133..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link TestSensorEventListener} which collects events to be processed after the test is run.
- * This should only be used for short tests.
- */
-public class CollectingSensorEventListener extends TestSensorEventListener {
- private final ArrayList<TestSensorEvent> mSensorEventsList = new ArrayList<TestSensorEvent>();
-
- /**
- * Constructs a {@link CollectingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public CollectingSensorEventListener(SensorEventListener2 listener) {
- super(listener);
- }
-
- /**
- * Constructs a {@link CollectingSensorEventListener}.
- */
- public CollectingSensorEventListener() {
- this(null);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- super.onSensorChanged(event);
- synchronized (mSensorEventsList) {
- mSensorEventsList.add(new TestSensorEvent(event));
- }
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Clears the event queue before starting.
- * </p>
- */
- @Override
- public void waitForEvents(int eventCount) throws InterruptedException {
- clearEvents();
- super.waitForEvents(eventCount);
- }
-
- /**
- * {@inheritDoc}
- * <p>
- * Clears the event queue before starting.
- * </p>
- */
- @Override
- public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
- clearEvents();
- super.waitForEvents(duration, timeUnit);
- }
-
- /**
- * Get the {@link TestSensorEvent} array from the event queue.
- */
- public List<TestSensorEvent> getEvents() {
- synchronized (mSensorEventsList) {
- return Collections.unmodifiableList(mSensorEventsList);
- }
- }
-
- /**
- * Clear the event queue.
- */
- public void clearEvents() {
- synchronized (mSensorEventsList) {
- mSensorEventsList.clear();
- }
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
index b3b8559..d3dcf37 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
@@ -35,6 +35,8 @@
private final TestSensorManager mCalibratedSensorManager;
private final TestSensorManager mUncalibratedSensorManager;
+ private final TestSensorEventListener mCalibratedTestListener;
+ private final TestSensorEventListener mUncalibratedTestListener;
private final float mThreshold;
public SensorCalibratedUncalibratedVerifier(
@@ -43,6 +45,8 @@
float threshold) {
mCalibratedSensorManager = new TestSensorManager(calibratedEnvironment);
mUncalibratedSensorManager = new TestSensorManager(uncalibratedEnvironment);
+ mCalibratedTestListener = new TestSensorEventListener(calibratedEnvironment);
+ mUncalibratedTestListener = new TestSensorEventListener(uncalibratedEnvironment);
mThreshold = threshold;
}
@@ -50,10 +54,8 @@
* Executes the operation: it collects the data and run verifications on it.
*/
public void execute() throws Throwable {
- CollectingSensorEventListener calibratedTestListener = new CollectingSensorEventListener();
- CollectingSensorEventListener uncalibratedTestListener = new CollectingSensorEventListener();
- mCalibratedSensorManager.registerListener(calibratedTestListener);
- mUncalibratedSensorManager.registerListener(uncalibratedTestListener);
+ mCalibratedSensorManager.registerListener(mCalibratedTestListener);
+ mUncalibratedSensorManager.registerListener(mUncalibratedTestListener);
Thread.sleep(TimeUnit.SECONDS.toMillis(10));
@@ -61,8 +63,8 @@
mUncalibratedSensorManager.unregisterListener();
verifyMeasurements(
- calibratedTestListener.getEvents(),
- uncalibratedTestListener.getEvents(),
+ mCalibratedTestListener.getCollectedEvents(),
+ mUncalibratedTestListener.getCollectedEvents(),
mThreshold);
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index a79e5b1..490e965 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -192,7 +192,7 @@
TestSensorEnvironment environment,
String extras) {
return String.format(
- "%s | sensor='%s', samplingPeriodUs=%d, maxReportLatencyUs=%d | %s",
+ "%s | sensor='%s', samplingPeriod=%dus, maxReportLatency=%dus | %s",
label,
environment.getSensor().getName(),
environment.getRequestedSamplingPeriodUs(),
@@ -219,6 +219,25 @@
}
/**
+ * Sanitizes a string so it can be used in file names.
+ *
+ * @param value The string to sanitize.
+ * @return The sanitized string.
+ *
+ * @throws SensorTestPlatformException If the string cannot be sanitized.
+ */
+ public static String sanitizeStringForFileName(String value)
+ throws SensorTestPlatformException {
+ String sanitizedValue = value.replaceAll("[^a-zA-Z0-9_\\-]", "_");
+ if (sanitizedValue.matches("_*")) {
+ throw new SensorTestPlatformException(
+ "Unable to sanitize string '%s' for file name.",
+ value);
+ }
+ return sanitizedValue;
+ }
+
+ /**
* Ensures that the directory structure represented by the given {@link File} is created.
*/
private static File createDirectoryStructure(File directoryStructure) throws IOException {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 7be6c0c..8067aed 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -17,7 +17,7 @@
package android.hardware.cts.helpers;
import android.hardware.Sensor;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import android.util.Log;
import java.io.BufferedWriter;
@@ -34,28 +34,29 @@
import java.util.Set;
/**
- * Class used to store stats related to {@link ISensorOperation}s. Sensor stats may be linked
+ * Class used to store stats related to {@link SensorOperation}s. Sensor stats may be linked
* together so that they form a tree.
*/
public class SensorStats {
public static final String DELIMITER = "__";
- public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
- public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
public static final String ERROR = "error";
- public static final String EVENT_COUNT_KEY = "event_count";
public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+ public static final String EVENT_TIME_SYNCHRONIZATION_COUNT_KEY =
+ "event_time_synchronization_count";
+ public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
+ "event_time_synchronization_positions";
public static final String FREQUENCY_KEY = "frequency";
public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
public static final String MEAN_KEY = "mean";
public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
public static final String MAGNITUDE_KEY = "magnitude";
- private final Map<String, Object> mValues = new HashMap<String, Object>();
- private final Map<String, SensorStats> mSensorStats = new HashMap<String, SensorStats>();
+ private final Map<String, Object> mValues = new HashMap<>();
+ private final Map<String, SensorStats> mSensorStats = new HashMap<>();
/**
* Add a value.
@@ -72,7 +73,7 @@
/**
* Add a nested {@link SensorStats}. This is useful for keeping track of stats in a
- * {@link ISensorOperation} tree.
+ * {@link SensorOperation} tree.
*
* @param key the key
* @param stats the sub {@link SensorStats} object.
@@ -103,13 +104,13 @@
/**
* Flattens the map and all sub {@link SensorStats} objects. Keys will be flattened using
* {@value #DELIMITER}. For example, if a sub {@link SensorStats} is added with key
- * {@code "key1"} containing the key value pair {@code ("key2", "value")}, the flattened map
- * will contain the entry {@code ("key1__key2", "value")}.
+ * {@code "key1"} containing the key value pair {@code \("key2", "value"\)}, the flattened map
+ * will contain the entry {@code \("key1__key2", "value"\)}.
*
* @return a {@link Map} containing all stats from the value and sub {@link SensorStats}.
*/
public synchronized Map<String, Object> flatten() {
- final Map<String, Object> flattenedMap = new HashMap<String, Object>(mValues);
+ final Map<String, Object> flattenedMap = new HashMap<>(mValues);
for (Entry<String, SensorStats> statsEntry : mSensorStats.entrySet()) {
for (Entry<String, Object> valueEntry : statsEntry.getValue().flatten().entrySet()) {
String key = statsEntry.getKey() + DELIMITER + valueEntry.getKey();
@@ -122,8 +123,8 @@
/**
* Utility method to log the stats to the logcat.
*/
- public static void logStats(String tag, SensorStats stats) {
- final Map<String, Object> flattened = stats.flatten();
+ public void log(String tag) {
+ final Map<String, Object> flattened = flatten();
for (String key : getSortedKeys(flattened)) {
Object value = flattened.get(key);
Log.v(tag, String.format("%s: %s", key, getValueString(value)));
@@ -133,39 +134,29 @@
/**
* Utility method to log the stats to a file. Will overwrite the file if it already exists.
*/
- public static void logStatsToFile(String fileName, SensorStats stats) throws IOException {
+ public void logToFile(String fileName) throws IOException {
File statsDirectory = SensorCtsHelper.getSensorTestDataDirectory("stats/");
File logFile = new File(statsDirectory, fileName);
- final BufferedWriter writer =
- new BufferedWriter(new FileWriter(logFile, false /* append */));
- final Map<String, Object> flattened = stats.flatten();
- try {
+ final Map<String, Object> flattened = flatten();
+ FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+ try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
for (String key : getSortedKeys(flattened)) {
Object value = flattened.get(key);
writer.write(String.format("%s: %s\n", key, getValueString(value)));
}
- } finally {
- writer.flush();
- writer.close();
}
}
/**
* Provides a sanitized sensor name, that can be used in file names.
- * See {@link #logStatsToFile(String, SensorStats)}.
+ * See {@link #logToFile(String)}.
*/
- public static String getSanitizedSensorName(Sensor sensor) throws IOException {
- String sensorType = sensor.getStringType();
- String sanitizedSensorType = sensorType.replaceAll("[^a-zA-Z0-9_\\-]", "_");
- if (sanitizedSensorType.matches("_*")) {
- throw new IOException("Unable to sanitize sensor type (" + sensorType + "). This is a"
- + " 'test framework' issue and the sanitation routine must be fixed.");
- }
- return sanitizedSensorType;
+ public static String getSanitizedSensorName(Sensor sensor) throws SensorTestPlatformException {
+ return SensorCtsHelper.sanitizeStringForFileName(sensor.getStringType());
}
private static List<String> getSortedKeys(Map<String, Object> flattenedStats) {
- List<String> keys = new ArrayList<String>(flattenedStats.keySet());
+ List<String> keys = new ArrayList<>(flattenedStats.keySet());
Collections.sort(keys);
return keys;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java
new file mode 100644
index 0000000..b230749
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorTestPlatformException.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.hardware.cts.helpers;
+
+/**
+ * Exception that indicates an issue in the Sensor Test Platform.
+ * It usually constitutes a bug in the platform that needs to be fixed.
+ */
+public class SensorTestPlatformException extends Exception {
+ public SensorTestPlatformException(String format, Object ... params) {
+ this(String.format(format, params));
+ }
+
+ public SensorTestPlatformException(String message) {
+ super(message + " (This is a 'sensor test platform' issue, please report it so it can be fixed)");
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 8f33f92..143f0a1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -19,15 +19,22 @@
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorManager;
-import android.hardware.cts.helpers.sensoroperations.ISensorOperation;
+import android.hardware.cts.helpers.sensoroperations.SensorOperation;
import java.util.concurrent.TimeUnit;
/**
- * A class that encapsulates base environment information for the {@link ISensorOperation}.
+ * A class that encapsulates base environment information for the {@link SensorOperation}.
* The environment is self contained and carries its state around all the sensor test framework.
*/
public class TestSensorEnvironment {
+
+ /**
+ * It represents the fraction of the expected sampling frequency, at which the sensor can
+ * actually produce events.
+ */
+ private static final float MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER = 0.9f;
+
private final Context mContext;
private final Sensor mSensor;
private final boolean mSensorMightHaveMoreListeners;
@@ -40,7 +47,10 @@
* @param context The context for the test
* @param sensorType The type of the sensor under test
* @param samplingPeriodUs The requested collection period for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(Context context, int sensorType, int samplingPeriodUs) {
this(context, sensorType, false /* sensorMightHaveMoreListeners */, samplingPeriodUs);
}
@@ -52,7 +62,10 @@
* @param sensorType The type of the sensor under test
* @param samplingPeriodUs The requested collection period for the sensor under test
* @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -72,7 +85,10 @@
* @param sensorType The type of the sensor under test
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
* @param samplingPeriodUs The requested collection period for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -93,7 +109,10 @@
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
* @param samplingPeriodUs The requested collection period for the sensor under test
* @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -112,6 +131,26 @@
*
* @param context The context for the test
* @param sensor The sensor under test
+ * @param samplingPeriodUs The requested collection period for the sensor under test
+ * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ */
+ public TestSensorEnvironment(
+ Context context,
+ Sensor sensor,
+ int samplingPeriodUs,
+ int maxReportLatencyUs) {
+ this(context,
+ sensor,
+ false /* sensorMightHaveMoreListeners */,
+ samplingPeriodUs,
+ maxReportLatencyUs);
+ }
+
+ /**
+ * Constructs an environment for sensor testing.
+ *
+ * @param context The context for the test
+ * @param sensor The sensor under test
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load (this
* usually implies that there are several listeners
* requesting different sampling periods)
@@ -160,6 +199,17 @@
}
/**
+ * @return A string representing the frequency equivalent to
+ * {@link #getRequestedSamplingPeriodUs()}.
+ */
+ public String getFrequencyString() {
+ if (mSamplingPeriodUs == SensorManager.SENSOR_DELAY_FASTEST) {
+ return "fastest";
+ }
+ return String.format("%.0fhz", getFrequencyHz());
+ }
+
+ /**
* @return The requested collection max batch report latency in microseconds.
*/
public int getMaxReportLatencyUs() {
@@ -171,7 +221,8 @@
* data at different sampling rates (the rates are unknown); false otherwise.
*/
public boolean isSensorSamplingRateOverloaded() {
- return mSensorMightHaveMoreListeners && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
+ return mSensorMightHaveMoreListeners
+ && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
}
/**
@@ -197,6 +248,24 @@
}
/**
+ * @return The maximum acceptable actual sampling period of this sensor.
+ * For continuous sensors, this is higher than {@link #getExpectedSamplingPeriodUs()}
+ * because sensors are allowed to run up to 10% slower than requested.
+ * For sensors with other reporting modes, this is the maximum integer
+ * {@link Integer#MAX_VALUE} as they can report no events for long
+ * periods of time.
+ */
+ public int getMaximumExpectedSamplingPeriodUs() {
+ int sensorReportingMode = mSensor.getReportingMode();
+ if (sensorReportingMode != Sensor.REPORTING_MODE_CONTINUOUS) {
+ return Integer.MAX_VALUE;
+ }
+
+ int expectedSamplingPeriodUs = getExpectedSamplingPeriodUs();
+ return (int) (expectedSamplingPeriodUs / MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER);
+ }
+
+ /**
* @return The number of axes in the coordinate system of the sensor under test.
*/
public int getSensorAxesCount() {
@@ -266,6 +335,17 @@
return TimeUnit.SECONDS.toNanos(reportLatencySec);
}
+ @Override
+ public String toString() {
+ return String.format(
+ "Sensor='%s', SamplingRateOverloaded=%s, SamplingPeriod=%sus, "
+ + "MaxReportLatency=%sus",
+ mSensor,
+ isSensorSamplingRateOverloaded(),
+ mSamplingPeriodUs,
+ mMaxReportLatencyUs);
+ }
+
/**
* Return true if {@link #getRequestedSamplingPeriodUs()} is not one of
* {@link SensorManager#SENSOR_DELAY_GAME}, {@link SensorManager#SENSOR_DELAY_UI}, or
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
index e8500f1..86b2436 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEvent.java
@@ -21,6 +21,8 @@
import android.hardware.SensorEventListener2;
import android.os.SystemClock;
+import java.util.Arrays;
+
/**
* Class for holding information about individual {@link SensorEvent}s.
*/
@@ -75,4 +77,14 @@
this.accuracy = accuracy;
this.values = values;
}
+
+ @Override
+ public String toString() {
+ return String.format(
+ "Timestamp=%sns, ReceivedTimestamp=%sns, Accuracy=%s, Values=%s",
+ this.timestamp,
+ this.receivedTimestamp,
+ this.accuracy,
+ Arrays.toString(this.values));
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 6567be2..6e25e86 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -21,12 +21,21 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener2;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
-import android.util.Log;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* A {@link SensorEventListener2} which performs operations such as waiting for a specific number of
@@ -36,51 +45,39 @@
*/
public class TestSensorEventListener implements SensorEventListener2 {
public static final String LOG_TAG = "TestSensorEventListener";
- private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
- private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
- private final SensorEventListener2 mListener;
+ private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
+ private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
- private volatile CountDownLatch mEventLatch;
- private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
- private volatile TestSensorEnvironment mEnvironment;
- private volatile boolean mLogEvents;
+ private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<>();
+ private final List<CountDownLatch> mEventLatches = new ArrayList<>();
+ private final List<CountDownLatch> mFlushLatches = new ArrayList<>();
+ private final AtomicInteger mEventsReceivedOutsideHandler = new AtomicInteger();
+
+ private final Handler mHandler;
+ private final TestSensorEnvironment mEnvironment;
+
+ /**
+ * @deprecated Use {@link TestSensorEventListener(TestSensorEnvironment)}.
+ */
+ @Deprecated
+ public TestSensorEventListener() {
+ this(null /* environment */);
+ }
/**
* Construct a {@link TestSensorEventListener}.
*/
- public TestSensorEventListener() {
- this(null);
+ public TestSensorEventListener(TestSensorEnvironment environment) {
+ this(environment, null /* handler */);
}
/**
- * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
+ * Construct a {@link TestSensorEventListener}.
*/
- public TestSensorEventListener(SensorEventListener2 listener) {
- if (listener != null) {
- mListener = listener;
- } else {
- // use a Null Object to simplify handling the listener
- mListener = new SensorEventListener2() {
- public void onFlushCompleted(Sensor sensor) {}
- public void onSensorChanged(SensorEvent sensorEvent) {}
- public void onAccuracyChanged(Sensor sensor, int i) {}
- };
- }
- }
-
- /**
- * Set the sensor, rate, and batch report latency used for the assertions.
- */
- public void setEnvironment(TestSensorEnvironment environment) {
+ public TestSensorEventListener(TestSensorEnvironment environment, Handler handler) {
mEnvironment = environment;
- }
-
- /**
- * Set whether or not to log events
- */
- public void setLogEvents(boolean log) {
- mLogEvents = log;
+ mHandler = handler;
}
/**
@@ -88,19 +85,15 @@
*/
@Override
public void onSensorChanged(SensorEvent event) {
- mListener.onSensorChanged(event);
- if (mLogEvents) {
- Log.v(LOG_TAG, String.format(
- "Sensor %d: sensor_timestamp=%dns, received_timestamp=%dns, values=%s",
- mEnvironment.getSensor().getType(),
- event.timestamp,
- SystemClock.elapsedRealtimeNanos(),
- Arrays.toString(event.values)));
+ long timestampNs = SystemClock.elapsedRealtimeNanos();
+ checkHandler();
+ synchronized (mCollectedEvents) {
+ mCollectedEvents.add(new TestSensorEvent(event, timestampNs));
}
-
- CountDownLatch eventLatch = mEventLatch;
- if(eventLatch != null) {
- eventLatch.countDown();
+ synchronized (mEventLatches) {
+ for (CountDownLatch latch : mEventLatches) {
+ latch.countDown();
+ }
}
}
@@ -109,7 +102,32 @@
*/
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
- mListener.onAccuracyChanged(sensor, accuracy);
+ checkHandler();
+ }
+
+ /**
+ * @param eventCount
+ * @return A CountDownLatch initialzed with eventCount and decremented as sensor events arrive
+ * for this listerner.
+ */
+ public CountDownLatch getLatchForSensorEvents(int eventCount) {
+ CountDownLatch latch = new CountDownLatch(eventCount);
+ synchronized (mEventLatches) {
+ mEventLatches.add(latch);
+ }
+ return latch;
+ }
+
+ /**
+ * @return A CountDownLatch initialzed with 1 and decremented as a flush complete arrives
+ * for this listerner.
+ */
+ public CountDownLatch getLatchForFlushCompleteEvent() {
+ CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mFlushLatches) {
+ mFlushLatches.add(latch);
+ }
+ return latch;
}
/**
@@ -117,12 +135,71 @@
*/
@Override
public void onFlushCompleted(Sensor sensor) {
- CountDownLatch latch = mFlushLatch;
- mFlushLatch = null;
- if(latch != null) {
- latch.countDown();
+ checkHandler();
+ synchronized (mFlushLatches) {
+ for (CountDownLatch latch : mFlushLatches) {
+ latch.countDown();
+ }
}
- mListener.onFlushCompleted(sensor);
+ }
+
+ /**
+ * @return The handler (if any) associated with the instance.
+ */
+ public Handler getHandler() {
+ return mHandler;
+ }
+
+ /**
+ * @return A list of {@link TestSensorEvent}s collected by the listener.
+ */
+ public List<TestSensorEvent> getCollectedEvents() {
+ synchronized (mCollectedEvents){
+ return Collections.unmodifiableList((List<TestSensorEvent>) mCollectedEvents.clone());
+ }
+ }
+
+ /**
+ * Clears the internal list of collected {@link TestSensorEvent}s.
+ */
+ public void clearEvents() {
+ synchronized (mCollectedEvents) {
+ mCollectedEvents.clear();
+ }
+ }
+
+
+ /**
+ * Utility method to log the collected events to a file.
+ * It will overwrite the file if it already exists, the file is created in a relative directory
+ * named 'events' under the sensor test directory (part of external storage).
+ */
+ public void logCollectedEventsToFile(String fileName) throws IOException {
+ StringBuilder builder = new StringBuilder();
+ builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
+ builder.append("SamplingRateOverloaded=")
+ .append(mEnvironment.isSensorSamplingRateOverloaded()).append(", ");
+ builder.append("RequestedSamplingPeriod=")
+ .append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
+ builder.append("MaxReportLatency=")
+ .append(mEnvironment.getMaxReportLatencyUs()).append("us");
+
+ synchronized (mCollectedEvents) {
+ for (TestSensorEvent event : mCollectedEvents) {
+ builder.append("\n");
+ builder.append("Timestamp=").append(event.timestamp).append("ns, ");
+ builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
+ builder.append("Accuracy=").append(event.accuracy).append(", ");
+ builder.append("Values=").append(Arrays.toString(event.values));
+ }
+ }
+
+ File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
+ File logFile = new File(eventsDirectory, fileName);
+ FileWriter fileWriter = new FileWriter(logFile, false /* append */);
+ try (BufferedWriter writer = new BufferedWriter(fileWriter)) {
+ writer.write(builder.toString());
+ }
}
/**
@@ -130,14 +207,20 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForFlushComplete() throws InterruptedException {
- CountDownLatch latch = mFlushLatch;
- if(latch == null) {
- return;
+ public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
+ clearEvents();
+ try {
+ String message = SensorCtsHelper.formatAssertionMessage(
+ "WaitForFlush",
+ mEnvironment,
+ "timeout=%dus",
+ FLUSH_TIMEOUT_US);
+ Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+ } finally {
+ synchronized (mFlushLatches) {
+ mFlushLatches.remove(latch);
+ }
}
- Assert.assertTrue(
- SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
- latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
}
/**
@@ -145,23 +228,32 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForEvents(int eventCount) throws InterruptedException {
- mEventLatch = new CountDownLatch(eventCount);
+ public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
+ clearEvents();
try {
- int rateUs = mEnvironment.getExpectedSamplingPeriodUs();
- // Timeout is 2 * event count * expected period + batch timeout + default wait
- long timeoutUs = (2 * eventCount * rateUs)
+ long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
+ // timeout is 2 * event count * expected period + batch timeout + default wait
+ // we multiply by two as not to raise an error in this function even if the events are
+ // streaming at a lower rate than expected, as long as it's not streaming twice as slow
+ // as expected
+ long timeoutUs = (2 * eventCount * samplingPeriodUs)
+ mEnvironment.getMaxReportLatencyUs()
+ EVENT_TIMEOUT_US;
- String message = SensorCtsHelper.formatAssertionMessage(
- "WaitForEvents",
- mEnvironment,
- "requested:%d, received:%d",
- eventCount,
- eventCount - mEventLatch.getCount());
- Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+ boolean success = latch.await(timeoutUs, TimeUnit.MICROSECONDS);
+ if (!success) {
+ String message = SensorCtsHelper.formatAssertionMessage(
+ "WaitForEvents",
+ mEnvironment,
+ "requested=%d, received=%d, timeout=%dus",
+ eventCount,
+ eventCount - latch.getCount(),
+ timeoutUs);
+ Assert.fail(message);
+ }
} finally {
- mEventLatch = null;
+ synchronized (mEventLatches) {
+ mEventLatches.remove(latch);
+ }
}
}
@@ -171,4 +263,28 @@
public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
SensorCtsHelper.sleep(duration, timeUnit);
}
+
+ /**
+ * Asserts that sensor events arrived in the proper thread if a {@link Handler} was associated
+ * with the current instance.
+ *
+ * If no events were received this assertion will be evaluated to {@code true}.
+ */
+ public void assertEventsReceivedInHandler() {
+ int eventsOutsideHandler = mEventsReceivedOutsideHandler.get();
+ String message = String.format(
+ "Events arrived outside the associated Looper. Expected=0, Found=%d",
+ eventsOutsideHandler);
+ Assert.assertEquals(message, 0 /* expected */, eventsOutsideHandler);
+ }
+
+ /**
+ * Keeps track of the number of events that arrived in a different {@link Looper} than the one
+ * associated with the {@link TestSensorEventListener}.
+ */
+ private void checkHandler() {
+ if (mHandler != null && mHandler.getLooper() != Looper.myLooper()) {
+ mEventsReceivedOutsideHandler.incrementAndGet();
+ }
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index dc40ff4..2468bd1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -19,35 +19,20 @@
import junit.framework.Assert;
import android.content.Context;
-import android.hardware.Sensor;
import android.hardware.SensorEventListener;
-import android.hardware.SensorEventListener2;
import android.hardware.SensorManager;
import android.util.Log;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.CountDownLatch;
/**
- * A test class that performs the actions of {@link SensorManager} on a single sensor. This
- * class allows for a single sensor to be registered and unregistered as well as performing
- * operations such as flushing the sensor events and gathering events. This class also manages
- * performing the test verifications for the sensor manager.
- * <p>
- * This class requires that operations are performed in the following order:
- * <p><ul>
- * <li>{@link #registerListener(TestSensorEventListener)}</li>
- * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
- * <li>{@link #unregisterListener()}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
- * </ul><p>
- * If methods are called outside of this order, they will print a warning to the log and then
- * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
- * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
- * set up and tear down.
- * <p>
+ * A test class that performs the actions of {@link SensorManager} on a single sensor.
+ * This class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events.
+ * This class also manages performing the test verifications for the sensor manager.
+ *
+ * NOTE: this class is expected to mirror {@link SensorManager} operations, and perform the
+ * required test verifications along with them.
*/
public class TestSensorManager {
private static final String LOG_TAG = "TestSensorManager";
@@ -55,7 +40,7 @@
private final SensorManager mSensorManager;
private final TestSensorEnvironment mEnvironment;
- private TestSensorEventListener mTestSensorEventListener;
+ private volatile TestSensorEventListener mTestSensorEventListener;
/**
* @deprecated Use {@link #TestSensorManager(TestSensorEnvironment)} instead.
@@ -90,19 +75,45 @@
return;
}
- mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
- mTestSensorEventListener.setEnvironment(mEnvironment);
-
+ mTestSensorEventListener = listener;
String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
boolean result = mSensorManager.registerListener(
mTestSensorEventListener,
mEnvironment.getSensor(),
mEnvironment.getRequestedSamplingPeriodUs(),
- mEnvironment.getMaxReportLatencyUs());
+ mEnvironment.getMaxReportLatencyUs(),
+ mTestSensorEventListener.getHandler());
Assert.assertTrue(message, result);
}
/**
+ * Register the listener. This method will perform a no-op if the sensor is already registered.
+ *
+ * @return A CountDownLatch initialized with eventCount which is used to wait for sensor
+ * events.
+ * @throws AssertionError if there was an error registering the listener with the
+ * {@link SensorManager}
+ */
+ public CountDownLatch registerListener(TestSensorEventListener listener, int eventCount) {
+ if (mTestSensorEventListener != null) {
+ Log.w(LOG_TAG, "Listener already registered, returning.");
+ return null;
+ }
+
+ CountDownLatch latch = listener.getLatchForSensorEvents(eventCount);
+ mTestSensorEventListener = listener;
+ String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
+ boolean result = mSensorManager.registerListener(
+ mTestSensorEventListener,
+ mEnvironment.getSensor(),
+ mEnvironment.getRequestedSamplingPeriodUs(),
+ mEnvironment.getMaxReportLatencyUs(),
+ mTestSensorEventListener.getHandler());
+ Assert.assertTrue(message, result);
+ return latch;
+ }
+
+ /**
* Unregister the listener. This method will perform a no-op if the sensor is not registered.
*/
public void unregisterListener() {
@@ -110,136 +121,27 @@
Log.w(LOG_TAG, "No listener registered, returning.");
return;
}
-
- mSensorManager.unregisterListener(
- mTestSensorEventListener,
- mEnvironment.getSensor());
+ mSensorManager.unregisterListener(mTestSensorEventListener, mEnvironment.getSensor());
+ mTestSensorEventListener.assertEventsReceivedInHandler();
mTestSensorEventListener = null;
}
/**
- * Wait for a specific number of events.
- */
- public void waitForEvents(int eventCount) throws InterruptedException {
- if (mTestSensorEventListener == null) {
- Log.w(LOG_TAG, "No listener registered, returning.");
- return;
- }
- mTestSensorEventListener.waitForEvents(eventCount);
- }
-
- /**
- * Wait for a specific duration.
- */
- public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
- if (mTestSensorEventListener == null) {
- Log.w(LOG_TAG, "No listener registered, returning.");
- return;
- }
- mTestSensorEventListener.waitForEvents(duration, timeUnit);
- }
-
- /**
* Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
* the sensor is not registered.
*
- * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+ * @return A CountDownLatch which can be used to wait for a flush complete event.
+ * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
*/
- public void startFlush() {
+ public CountDownLatch requestFlush() {
if (mTestSensorEventListener == null) {
- return;
+ Log.w(LOG_TAG, "No listener registered, returning.");
+ return null;
}
-
+ CountDownLatch latch = mTestSensorEventListener.getLatchForFlushCompleteEvent();
Assert.assertTrue(
SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
mSensorManager.flush(mTestSensorEventListener));
- }
-
- /**
- * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
- * perform a no-op if the sensor is not registered.
- *
- * @throws AssertionError if there is a time out
- * @throws InterruptedException if the thread was interrupted
- */
- public void waitForFlushCompleted() throws InterruptedException {
- if (mTestSensorEventListener == null) {
- return;
- }
- mTestSensorEventListener.waitForFlushComplete();
- }
-
- /**
- * Call {@link SensorManager#flush(SensorEventListener)} and wait for
- * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
- * a no-op if the sensor is not registered.
- *
- * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
- * if there is a time out
- * @throws InterruptedException if the thread was interrupted
- */
- public void flush() throws InterruptedException {
- if (mTestSensorEventListener == null) {
- return;
- }
- startFlush();
- waitForFlushCompleted();
- }
-
- /**
- * Register a listener, wait for a specific number of events, and then unregister the listener.
- */
- public void runSensor(TestSensorEventListener listener, int eventCount)
- throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
- try {
- registerListener(listener);
- waitForEvents(eventCount);
- } finally {
- unregisterListener();
- }
- }
-
- /**
- * Register a listener, wait for a specific duration, and then unregister the listener.
- */
- public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit)
- throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
- try {
- registerListener(listener);
- waitForEvents(duration, timeUnit);
- } finally {
- unregisterListener();
- }
- }
-
- /**
- * Registers a listener, waits for a specific duration, calls flush, and waits for flush to
- * complete.
- */
- public void runSensorAndFlush(
- TestSensorEventListener listener,
- long duration,
- TimeUnit timeUnit) throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
-
- try {
- registerListener(listener);
- SensorCtsHelper.sleep(duration, timeUnit);
- startFlush();
- listener.waitForFlushComplete();
- } finally {
- unregisterListener();
- }
+ return latch;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
deleted file mode 100644
index 299f470..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers;
-
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
-import android.hardware.cts.helpers.sensorverification.ISensorVerification;
-
-import java.util.Collection;
-import java.util.LinkedList;
-
-/**
- * A {@link TestSensorEventListener} which performs validations on the received events on the fly.
- * This class is useful for long running tests where it is not practical to store all the events to
- * be processed after.
- */
-public class ValidatingSensorEventListener extends TestSensorEventListener {
-
- private final Collection<ISensorVerification> mVerifications =
- new LinkedList<ISensorVerification>();
-
- /**
- * Construct a {@link ValidatingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public ValidatingSensorEventListener(SensorEventListener2 listener,
- ISensorVerification ... verifications) {
- super(listener);
- for (ISensorVerification verification : verifications) {
- mVerifications.add(verification);
- }
- }
-
- /**
- * Construct a {@link ValidatingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public ValidatingSensorEventListener(SensorEventListener2 listener,
- Collection<ISensorVerification> verifications) {
- this(listener, verifications.toArray(new ISensorVerification[0]));
- }
-
- /**
- * Construct a {@link ValidatingSensorEventListener}.
- */
- public ValidatingSensorEventListener(ISensorVerification ... verifications) {
- this(null, verifications);
- }
-
- /**
- * Construct a {@link ValidatingSensorEventListener}.
- */
- public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
- this(null, verifications);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onSensorChanged(SensorEvent event) {
- TestSensorEvent testEvent = new TestSensorEvent(event);
- for (ISensorVerification verification : mVerifications) {
- verification.addSensorEvent(testEvent);
- }
- super.onSensorChanged(event);
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
new file mode 100644
index 0000000..d34c0af
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/reporting/ISensorTestNode.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.reporting;
+
+import android.hardware.cts.helpers.SensorTestPlatformException;
+
+/**
+ * Interface that represents a node in a hierarchy built by the sensor test platform.
+ */
+// TODO: this is an intermediate state to introduce a full-blown centralized recorder data produced
+// by sensor tests
+public interface ISensorTestNode {
+
+ /**
+ * Provides a name (tag) that can be used to identify the current node.
+ */
+ String getName() throws SensorTestPlatformException;
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
deleted file mode 100644
index 5b969f2..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AbstractSensorOperation.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * A {@link ISensorOperation} which contains a common implementation for gathering
- * {@link SensorStats}.
- */
-public abstract class AbstractSensorOperation implements ISensorOperation {
-
- private final SensorStats mStats = new SensorStats();
-
- /**
- * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
- */
- protected void addSensorStats(String key, SensorStats stats) {
- mStats.addSensorStats(key, stats);
- }
-
- /**
- * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
- * to be added. This is useful for {@link ISensorOperation}s that have many iterations or child
- * operations. The key added is in the form {@code key + "_" + index} where index may be zero
- * padded.
- */
- protected void addSensorStats(String key, int index, SensorStats stats) {
- addSensorStats(String.format("%s_%03d", key, index), stats);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public SensorStats getStats() {
- return mStats;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public abstract ISensorOperation clone();
-
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
index 88e4954..436a7cf 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/AlarmOperation.java
@@ -22,14 +22,14 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import java.util.concurrent.TimeUnit;
/**
- * An {@link ISensorOperation} which performs another {@link ISensorOperation} and then wakes up
+ * An {@link SensorOperation} which performs another {@link SensorOperation} and then wakes up
* after a specified period of time and waits for the child operation to complete.
* <p>
* This operation can be used to allow the device to go to sleep and wake it up after a specified
@@ -40,11 +40,11 @@
* but wake the device one time at the specified period.
* </p>
*/
-public class AlarmOperation extends AbstractSensorOperation {
+public class AlarmOperation extends SensorOperation {
private static final String ACTION = "AlarmOperationAction";
private static final String WAKE_LOCK_TAG = "AlarmOperationWakeLock";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final Context mContext;
private final long mSleepDuration;
private final TimeUnit mTimeUnit;
@@ -55,13 +55,17 @@
/**
* Constructor for {@link DelaySensorOperation}
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the alarm manager
* @param sleepDuration the amount of time to sleep
* @param timeUnit the unit of the duration
*/
- public AlarmOperation(ISensorOperation operation, Context context, long sleepDuration,
+ public AlarmOperation(
+ SensorOperation operation,
+ Context context,
+ long sleepDuration,
TimeUnit timeUnit) {
+ super(operation.getStats());
mOperation = operation;
mContext = context;
mSleepDuration = sleepDuration;
@@ -72,7 +76,7 @@
* {@inheritDoc}
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(ISensorTestNode parent) throws InterruptedException {
// Start alarm
IntentFilter intentFilter = new IntentFilter(ACTION);
BroadcastReceiver receiver = new BroadcastReceiver() {
@@ -92,7 +96,7 @@
// Execute operation
try {
- mOperation.execute();
+ mOperation.execute(asTestNode(parent));
} finally {
releaseWakeLock();
}
@@ -102,14 +106,6 @@
* {@inheritDoc}
*/
@Override
- public SensorStats getStats() {
- return mOperation.getStats();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
public AlarmOperation clone() {
return new AlarmOperation(mOperation, mContext, mSleepDuration, mTimeUnit);
}
@@ -120,7 +116,7 @@
*/
private synchronized void acquireWakeLock() {
// Don't acquire wake lock if the operation has already completed.
- if (mCompleted == true || mWakeLock != null) {
+ if (mCompleted || mWakeLock != null) {
return;
}
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
index b4d1f23..8c52222 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/DelaySensorOperation.java
@@ -17,30 +17,28 @@
package android.hardware.cts.helpers.sensoroperations;
import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import java.util.concurrent.TimeUnit;
/**
- * An {@link ISensorOperation} which delays for a specified period of time before performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which delays for a specified period of time before performing another
+ * {@link SensorOperation}.
*/
-public class DelaySensorOperation implements ISensorOperation {
- private final ISensorOperation mOperation;
+public class DelaySensorOperation extends SensorOperation {
+ private final SensorOperation mOperation;
private final long mDelay;
private final TimeUnit mTimeUnit;
/**
* Constructor for {@link DelaySensorOperation}
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param delay the amount of time to delay
* @param timeUnit the unit of the delay
*/
- public DelaySensorOperation(ISensorOperation operation, long delay, TimeUnit timeUnit) {
- if (operation == null || timeUnit == null) {
- throw new IllegalArgumentException("Arguments cannot be null");
- }
+ public DelaySensorOperation(SensorOperation operation, long delay, TimeUnit timeUnit) {
+ super(operation.getStats());
mOperation = operation;
mDelay = delay;
mTimeUnit = timeUnit;
@@ -50,17 +48,9 @@
* {@inheritDoc}
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(ISensorTestNode parent) throws InterruptedException {
SensorCtsHelper.sleep(mDelay, mTimeUnit);
- mOperation.execute();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public SensorStats getStats() {
- return mOperation.getStats();
+ mOperation.execute(asTestNode(parent));
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
index bb64dfa..238956b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/FakeSensorOperation.java
@@ -17,16 +17,17 @@
package android.hardware.cts.helpers.sensoroperations;
import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import junit.framework.Assert;
import java.util.concurrent.TimeUnit;
/**
- * A fake {@ISensorOperation} that will run for a specified time and then pass or fail. Useful when
- * debugging the framework.
+ * A fake {@link SensorOperation} that will run for a specified time and then pass or fail. Useful
+ * when debugging the framework.
*/
-public class FakeSensorOperation extends AbstractSensorOperation {
+public class FakeSensorOperation extends SensorOperation {
private static final int NANOS_PER_MILLI = 1000000;
private final boolean mFail;
@@ -56,7 +57,7 @@
* {@inheritDoc}
*/
@Override
- public void execute() {
+ public void execute(ISensorTestNode parent) {
long delayNs = TimeUnit.NANOSECONDS.convert(mDelay, mTimeUnit);
try {
Thread.sleep(delayNs / NANOS_PER_MILLI, (int) (delayNs % NANOS_PER_MILLI));
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
deleted file mode 100644
index 62a4e9e..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ISensorOperation.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.SensorStats;
-
-/**
- * Interface used by all sensor operations. This allows for complex operations such as chaining
- * operations together or running operations in parallel.
- * <p>
- * Certain restrictions exist for {@link ISensorOperation}s:
- * <p><ul>
- * <li>{@link #execute()} should only be called once and behavior is undefined for subsequent calls.
- * Once {@link #execute()} is called, the class should not be modified. Generally, there is no
- * synchronization for operations.</li>
- * <li>{@link #getStats()} should only be called after {@link #execute()}. If it is called before,
- * the returned value is undefined.</li>
- * <li>{@link #clone()} may be called any time and should return an operation with the same
- * parameters as the original.</li>
- * </ul>
- */
-public interface ISensorOperation {
-
- /**
- * Executes the sensor operation. This may throw {@link RuntimeException}s such as
- * {@link AssertionError}s.
- *
- * NOTE: the operation is expected to handle interruption by:
- * - cleaning up on {@link InterruptedException}
- * - propagating the exception down the stack
- */
- public void execute() throws InterruptedException;
-
- /**
- * Get the stats for the operation.
- *
- * @return The {@link SensorStats} for the operation.
- */
- public SensorStats getStats();
-
- /**
- * Clones the {@link ISensorOperation}. The implementation should also clone all child
- * operations, so that a cloned operation will run with the exact same parameters as the
- * original. The stats should not be cloned.
- *
- * @return The cloned {@link ISensorOperation}
- */
- public ISensorOperation clone();
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
index 5a4466c..ed70b70 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/ParallelSensorOperation.java
@@ -19,10 +19,10 @@
import junit.framework.Assert;
import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import android.os.SystemClock;
import java.util.ArrayList;
-import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
@@ -33,14 +33,14 @@
import java.util.concurrent.TimeoutException;
/**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in parallel.
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in parallel.
* The children are run in parallel but are given an index label in the order they are added. This
- * class can be combined to compose complex {@link ISensorOperation}s.
+ * class can be combined to compose complex {@link SensorOperation}s.
*/
-public class ParallelSensorOperation extends AbstractSensorOperation {
+public class ParallelSensorOperation extends SensorOperation {
public static final String STATS_TAG = "parallel";
- private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+ private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
private final Long mTimeout;
private final TimeUnit mTimeUnit;
@@ -65,10 +65,10 @@
}
/**
- * Add a set of {@link ISensorOperation}s.
+ * Add a set of {@link SensorOperation}s.
*/
- public void add(ISensorOperation ... operations) {
- for (ISensorOperation operation : operations) {
+ public void add(SensorOperation ... operations) {
+ for (SensorOperation operation : operations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
@@ -77,11 +77,11 @@
}
/**
- * Executes the {@link ISensorOperation}s in parallel. If an exception occurs one or more
+ * Executes the {@link SensorOperation}s in parallel. If an exception occurs one or more
* operations, the first exception will be thrown once all operations are completed.
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(final ISensorTestNode parent) throws InterruptedException {
int operationsCount = mOperations.size();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
operationsCount,
@@ -92,12 +92,13 @@
executor.allowCoreThreadTimeOut(true);
executor.prestartAllCoreThreads();
- ArrayList<Future<ISensorOperation>> futures = new ArrayList<Future<ISensorOperation>>();
- for (final ISensorOperation operation : mOperations) {
- Future<ISensorOperation> future = executor.submit(new Callable<ISensorOperation>() {
+ final ISensorTestNode currentNode = asTestNode(parent);
+ ArrayList<Future<SensorOperation>> futures = new ArrayList<>();
+ for (final SensorOperation operation : mOperations) {
+ Future<SensorOperation> future = executor.submit(new Callable<SensorOperation>() {
@Override
- public ISensorOperation call() throws Exception {
- operation.execute();
+ public SensorOperation call() throws Exception {
+ operation.execute(currentNode);
return operation;
}
});
@@ -111,12 +112,12 @@
}
boolean hasAssertionErrors = false;
- ArrayList<Integer> timeoutIndices = new ArrayList<Integer>();
- ArrayList<Throwable> exceptions = new ArrayList<Throwable>();
+ ArrayList<Integer> timeoutIndices = new ArrayList<>();
+ ArrayList<Throwable> exceptions = new ArrayList<>();
for (int i = 0; i < operationsCount; ++i) {
- Future<ISensorOperation> future = futures.get(i);
+ Future<SensorOperation> future = futures.get(i);
try {
- ISensorOperation operation = getFutureResult(future, executionTimeNs);
+ SensorOperation operation = getFutureResult(future, executionTimeNs);
addSensorStats(STATS_TAG, i, operation.getStats());
} catch (ExecutionException e) {
// extract the exception thrown by the worker thread
@@ -151,7 +152,7 @@
@Override
public ParallelSensorOperation clone() {
ParallelSensorOperation operation = new ParallelSensorOperation();
- for (ISensorOperation subOperation : mOperations) {
+ for (SensorOperation subOperation : mOperations) {
operation.add(subOperation.clone());
}
return operation;
@@ -160,7 +161,7 @@
/**
* Helper method that waits for a {@link Future} to complete, and returns its result.
*/
- private ISensorOperation getFutureResult(Future<ISensorOperation> future, Long timeoutNs)
+ private SensorOperation getFutureResult(Future<SensorOperation> future, Long timeoutNs)
throws ExecutionException, TimeoutException, InterruptedException {
if (timeoutNs == null) {
return future.get();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
index 3d682fe..5b333b8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/RepeatingSensorOperation.java
@@ -17,42 +17,44 @@
package android.hardware.cts.helpers.sensoroperations;
import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
/**
- * A {@link ISensorOperation} that executes a single {@link ISensorOperation} a given number of
- * times. This class can be combined to compose complex {@link ISensorOperation}s.
+ * A {@link SensorOperation} that executes a single {@link SensorOperation} a given number of
+ * times. This class can be combined to compose complex {@link SensorOperation}s.
*/
-public class RepeatingSensorOperation extends AbstractSensorOperation {
+public class RepeatingSensorOperation extends SensorOperation {
public static final String STATS_TAG = "repeating";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final int mIterations;
/**
* Constructor for {@link RepeatingSensorOperation}.
*
- * @param operation the {@link ISensorOperation} to run.
+ * @param operation the {@link SensorOperation} to run.
* @param iterations the number of iterations to run the operation for.
*/
- public RepeatingSensorOperation(ISensorOperation operation, int iterations) {
+ public RepeatingSensorOperation(SensorOperation operation, int iterations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
mOperation = operation;
mIterations = iterations;
-
}
/**
- * Executes the {@link ISensorOperation}s the given number of times. If an exception occurs
- * in one iterations, it is thrown and all subsequent iterations will not run.
+ * Executes the {@link SensorOperation}s the given number of times. If an exception occurs in
+ * one iterations, it is thrown and all subsequent iterations will not run.
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(ISensorTestNode parent) throws InterruptedException {
+ ISensorTestNode currentNode = asTestNode(parent);
for(int i = 0; i < mIterations; ++i) {
- ISensorOperation operation = mOperation.clone();
+ SensorOperation operation = mOperation.clone();
try {
- operation.execute();
+ operation.execute(new TestNode(currentNode, i));
} catch (AssertionError e) {
String msg = String.format("Iteration %d failed: \"%s\"", i, e.getMessage());
getStats().addValue(SensorStats.ERROR, msg);
@@ -70,4 +72,19 @@
public RepeatingSensorOperation clone() {
return new RepeatingSensorOperation(mOperation.clone(), mIterations);
}
+
+ private class TestNode implements ISensorTestNode {
+ private final ISensorTestNode mTestNode;
+ private final int mIteration;
+
+ public TestNode(ISensorTestNode parent, int iteration) {
+ mTestNode = asTestNode(parent);
+ mIteration = iteration;
+ }
+
+ @Override
+ public String getName() throws SensorTestPlatformException {
+ return String.format("%s-iteration%d", mTestNode.getName(), mIteration);
+ }
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
new file mode 100644
index 0000000..66604d3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperation.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.cts.helpers.sensoroperations;
+
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+
+/**
+ * Base class used by all sensor operations. This allows for complex operations such as chaining
+ * operations together or running operations in parallel.
+ * <p>
+ * Certain restrictions exist for {@link SensorOperation}s:
+ * <p><ul>
+ * <li>{@link #execute(ISensorTestNode)} should only be called once and behavior is undefined for
+ * subsequent calls.
+ * Once {@link #execute(ISensorTestNode)} is called, the class should not be modified. Generally,
+ * there is no synchronization for operations.</li>
+ * <li>{@link #getStats()} should only be called after {@link #execute(ISensorTestNode)}. If it
+ * is called before, the returned value is undefined.</li>
+ * <li>{@link #clone()} may be called any time and should return an operation with the same
+ * parameters as the original.</li>
+ * </ul>
+ */
+public abstract class SensorOperation {
+ private final SensorStats mStats;
+
+ protected SensorOperation() {
+ this(new SensorStats());
+ }
+
+ protected SensorOperation(SensorStats stats) {
+ mStats = stats;
+ }
+
+ /**
+ * @return The {@link SensorStats} for the operation.
+ */
+ public SensorStats getStats() {
+ return mStats;
+ }
+
+ /**
+ * Executes the sensor operation.
+ * This may throw {@link RuntimeException}s such as {@link AssertionError}s.
+ *
+ * NOTE: the operation is expected to handle interruption by:
+ * - cleaning up on {@link InterruptedException}
+ * - propagating the exception down the stack
+ */
+ public abstract void execute(ISensorTestNode parent) throws InterruptedException;
+
+ /**
+ * @return The cloned {@link SensorOperation}.
+ *
+ * NOTE: The implementation should also clone all child operations, so that a cloned operation
+ * will run with the exact same parameters as the original. The stats should not be cloned.
+ */
+ public abstract SensorOperation clone();
+
+ /**
+ * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)}
+ */
+ protected void addSensorStats(String key, SensorStats stats) {
+ getStats().addSensorStats(key, stats);
+ }
+
+ /**
+ * Wrapper around {@link SensorStats#addSensorStats(String, SensorStats)} that allows an index
+ * to be added. This is useful for {@link SensorOperation}s that have many iterations or child
+ * operations. The key added is in the form {@code key + "_" + index} where index may be zero
+ * padded.
+ */
+ protected void addSensorStats(String key, int index, SensorStats stats) {
+ addSensorStats(String.format("%s_%03d", key, index), stats);
+ }
+
+ protected ISensorTestNode asTestNode(ISensorTestNode parent) {
+ return new SensorTestNode(parent, this);
+ }
+
+ private class SensorTestNode implements ISensorTestNode {
+ private final ISensorTestNode mParent;
+ private final SensorOperation mOperation;
+
+ public SensorTestNode(ISensorTestNode parent, SensorOperation operation) {
+ mParent = parent;
+ mOperation = operation;
+ }
+
+ @Override
+ public String getName() throws SensorTestPlatformException {
+ return mParent.getName() + "-" + mOperation.getClass().getSimpleName();
+ }
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
index bc48725..30da9a0 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SensorOperationTest.java
@@ -19,18 +19,27 @@
import junit.framework.TestCase;
import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
- * Tests for the primitive {@link ISensorOperation}s including {@link DelaySensorOperation},
+ * Tests for the primitive {@link SensorOperation}s including {@link DelaySensorOperation},
* {@link ParallelSensorOperation}, {@link RepeatingSensorOperation} and
* {@link SequentialSensorOperation}.
*/
public class SensorOperationTest extends TestCase {
private static final long TEST_DURATION_THRESHOLD_MS = TimeUnit.SECONDS.toMillis(5);
+ private final ISensorTestNode mTestNode = new ISensorTestNode() {
+ @Override
+ public String getName() throws SensorTestPlatformException {
+ return "SensorOperationUnitTest";
+ }
+ };
+
/**
* Test that the {@link FakeSensorOperation} functions correctly. Other tests in this class
* rely on this operation.
@@ -38,18 +47,18 @@
public void testFakeSensorOperation() throws InterruptedException {
final int opDurationMs = 100;
- ISensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new FakeSensorOperation(opDurationMs, TimeUnit.MILLISECONDS);
assertFalse(op.getStats().flatten().containsKey("executed"));
long start = System.currentTimeMillis();
- op.execute();
+ op.execute(mTestNode);
long duration = System.currentTimeMillis() - start;
assertTrue(Math.abs(opDurationMs - duration) < TEST_DURATION_THRESHOLD_MS);
assertTrue(op.getStats().flatten().containsKey("executed"));
op = new FakeSensorOperation(true, 0, TimeUnit.MILLISECONDS);
try {
- op.execute();
+ op.execute(mTestNode);
fail("AssertionError expected");
} catch (AssertionError e) {
// Expected
@@ -65,12 +74,12 @@
final int subOpDurationMs = 100;
FakeSensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
- ISensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new DelaySensorOperation(subOp, opDurationMs, TimeUnit.MILLISECONDS);
long startMs = System.currentTimeMillis();
- op.execute();
- long dirationMs = System.currentTimeMillis() - startMs;
- long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - dirationMs);
+ op.execute(mTestNode);
+ long durationMs = System.currentTimeMillis() - startMs;
+ long durationDeltaMs = Math.abs(opDurationMs + subOpDurationMs - durationMs);
assertTrue(durationDeltaMs < TEST_DURATION_THRESHOLD_MS);
}
@@ -83,7 +92,7 @@
ParallelSensorOperation op = new ParallelSensorOperation();
for (int i = 0; i < subOpCount; i++) {
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -92,7 +101,7 @@
assertEquals(0, statsKeys.size());
long start = System.currentTimeMillis();
- op.execute();
+ op.execute(mTestNode);
long durationMs = System.currentTimeMillis() - start;
long durationDeltaMs = Math.abs(subOpDurationMs - durationMs);
String message = String.format(
@@ -124,7 +133,7 @@
ParallelSensorOperation op = new ParallelSensorOperation();
for (int i = 0; i < subOpCount; i++) {
// Trigger failures in the 5th, 55th operations at t=5ms, t=55ms
- ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
+ SensorOperation subOp = new FakeSensorOperation(i % 50 == 5, i, TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -132,7 +141,7 @@
assertEquals(0, statsKeys.size());
try {
- op.execute();
+ op.execute(mTestNode);
fail("AssertionError expected");
} catch (AssertionError e) {
// Expected
@@ -164,7 +173,7 @@
ParallelSensorOperation op = new ParallelSensorOperation(1, TimeUnit.SECONDS);
for (int i = 0; i < subOpCount; i++) {
// Trigger timeouts in the 5th, 55th operations (5 seconds vs 1 seconds)
- ISensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
+ SensorOperation subOp = new FakeSensorOperation(i % 50 == 5 ? 5 : 0, TimeUnit.SECONDS);
op.add(subOp);
}
@@ -172,7 +181,7 @@
assertEquals(0, statsKeys.size());
try {
- op.execute();
+ op.execute(mTestNode);
fail("AssertionError expected");
} catch (AssertionError e) {
// Expected
@@ -196,14 +205,14 @@
final int iterations = 10;
final int subOpDurationMs = 100;
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
- ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs, TimeUnit.MILLISECONDS);
+ SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
Set<String> statsKeys = op.getStats().flatten().keySet();
assertEquals(0, statsKeys.size());
long start = System.currentTimeMillis();
- op.execute();
+ op.execute(mTestNode);
long duration = System.currentTimeMillis() - start;
assertTrue(Math.abs(subOpDurationMs * iterations - duration) < TEST_DURATION_THRESHOLD_MS);
@@ -223,13 +232,13 @@
final int iterations = 100;
final int failCount = 75;
- ISensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
+ SensorOperation subOp = new FakeSensorOperation(0, TimeUnit.MILLISECONDS) {
private int mExecutedCount = 0;
private SensorStats mFakeStats = new SensorStats();
@Override
- public void execute() {
- super.execute();
+ public void execute(ISensorTestNode parent) {
+ super.execute(parent);
mExecutedCount++;
if (failCount == mExecutedCount) {
@@ -249,13 +258,13 @@
return mFakeStats;
}
};
- ISensorOperation op = new RepeatingSensorOperation(subOp, iterations);
+ SensorOperation op = new RepeatingSensorOperation(subOp, iterations);
Set<String> statsKeys = op.getStats().flatten().keySet();
assertEquals(0, statsKeys.size());
try {
- op.execute();
+ op.execute(mTestNode);
fail("AssertionError expected");
} catch (AssertionError e) {
// Expected
@@ -283,7 +292,7 @@
SequentialSensorOperation op = new SequentialSensorOperation();
for (int i = 0; i < subOpCount; i++) {
- ISensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
+ SensorOperation subOp = new FakeSensorOperation(subOpDurationMs,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -292,7 +301,7 @@
assertEquals(0, statsKeys.size());
long start = System.currentTimeMillis();
- op.execute();
+ op.execute(mTestNode);
long duration = System.currentTimeMillis() - start;
assertTrue(Math.abs(subOpDurationMs * subOpCount - duration) < TEST_DURATION_THRESHOLD_MS);
@@ -315,7 +324,7 @@
SequentialSensorOperation op = new SequentialSensorOperation();
for (int i = 0; i < subOpCount; i++) {
// Trigger a failure in the 75th operation only
- ISensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
+ SensorOperation subOp = new FakeSensorOperation(i + 1 == failCount, 0,
TimeUnit.MILLISECONDS);
op.add(subOp);
}
@@ -324,7 +333,7 @@
assertEquals(0, statsKeys.size());
try {
- op.execute();
+ op.execute(mTestNode);
fail("AssertionError expected");
} catch (AssertionError e) {
// Expected
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
index 2ed0ca6..847c0f2 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/SequentialSensorOperation.java
@@ -17,25 +17,25 @@
package android.hardware.cts.helpers.sensoroperations;
import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
-import java.util.LinkedList;
-import java.util.List;
+import java.util.ArrayList;
/**
- * A {@link ISensorOperation} that executes a set of children {@link ISensorOperation}s in a
+ * A {@link SensorOperation} that executes a set of children {@link SensorOperation}s in a
* sequence. The children are executed in the order they are added. This class can be combined to
- * compose complex {@link ISensorOperation}s.
+ * compose complex {@link SensorOperation}s.
*/
-public class SequentialSensorOperation extends AbstractSensorOperation {
+public class SequentialSensorOperation extends SensorOperation {
public static final String STATS_TAG = "sequential";
- private final List<ISensorOperation> mOperations = new LinkedList<ISensorOperation>();
+ private final ArrayList<SensorOperation> mOperations = new ArrayList<>();
/**
- * Add a set of {@link ISensorOperation}s.
+ * Add a set of {@link SensorOperation}s.
*/
- public void add(ISensorOperation ... operations) {
- for (ISensorOperation operation : operations) {
+ public void add(SensorOperation ... operations) {
+ for (SensorOperation operation : operations) {
if (operation == null) {
throw new IllegalArgumentException("Arguments cannot be null");
}
@@ -44,15 +44,16 @@
}
/**
- * Executes the {@link ISensorOperation}s in the order they were added. If an exception occurs
+ * Executes the {@link SensorOperation}s in the order they were added. If an exception occurs
* in one operation, it is thrown and all subsequent operations will not run.
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(ISensorTestNode parent) throws InterruptedException {
+ ISensorTestNode currentNode = asTestNode(parent);
for (int i = 0; i < mOperations.size(); i++) {
- ISensorOperation operation = mOperations.get(i);
+ SensorOperation operation = mOperations.get(i);
try {
- operation.execute();
+ operation.execute(currentNode);
} catch (AssertionError e) {
String msg = String.format("Operation %d failed: \"%s\"", i, e.getMessage());
getStats().addValue(SensorStats.ERROR, msg);
@@ -69,7 +70,7 @@
@Override
public SequentialSensorOperation clone() {
SequentialSensorOperation operation = new SequentialSensorOperation();
- for (ISensorOperation subOperation : mOperations) {
+ for (SensorOperation subOperation : mOperations) {
operation.add(subOperation.clone());
}
return operation;
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
deleted file mode 100644
index d5aa4b9..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
- * events, call flush, and then run all the tests.
- * </p>
- */
-public class TestSensorFlushOperation extends VerifiableSensorOperation {
- private final Long mDuration;
- private final TimeUnit mTimeUnit;
-
- /**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param duration the duration to gather events before calling {@code SensorManager.flush()}
- * @param timeUnit the time unit of the duration
- */
- public TestSensorFlushOperation(
- TestSensorEnvironment environment,
- long duration,
- TimeUnit timeUnit) {
- super(environment);
- mDuration = duration;
- mTimeUnit = timeUnit;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
- mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected VerifiableSensorOperation doClone() {
- return new TestSensorFlushOperation(mEnvironment, mDuration,mTimeUnit);
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 695e1a7..3b90b15 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -16,83 +16,265 @@
package android.hardware.cts.helpers.sensoroperations;
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
+import junit.framework.Assert;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestPlatformException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.hardware.cts.helpers.sensorverification.JitterVerification;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
+import android.os.Handler;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
+ * A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
* <p>
* Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
+ * depending on sensor type. When {{@link #execute(ISensorTestNode)} is called, the sensor will
+ * collect the events and then run all the tests.
* </p>
*/
-public class TestSensorOperation extends VerifiableSensorOperation {
- private final Integer mEventCount;
- private final Long mDuration;
- private final TimeUnit mTimeUnit;
+public class TestSensorOperation extends SensorOperation {
+ private static final String TAG = "TestSensorOperation";
+
+ private final HashSet<ISensorVerification> mVerifications = new HashSet<>();
+
+ private final TestSensorManager mSensorManager;
+ private final TestSensorEnvironment mEnvironment;
+ private final Executor mExecutor;
+ private final Handler mHandler;
/**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param eventCount the number of events to gather
+ * An interface that defines an abstraction for operations to be performed by the
+ * {@link TestSensorOperation}.
*/
- public TestSensorOperation(TestSensorEnvironment environment, int eventCount) {
- this(environment, eventCount, null /* duration */, null /* timeUnit */);
+ public interface Executor {
+ void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException;
}
/**
* Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param duration the duration to gather events for
- * @param timeUnit the time unit of the duration
+ */
+ public TestSensorOperation(TestSensorEnvironment environment, Executor executor) {
+ this(environment, executor, null /* handler */);
+ }
+
+ /**
+ * Create a {@link TestSensorOperation}.
*/
public TestSensorOperation(
TestSensorEnvironment environment,
- long duration,
- TimeUnit timeUnit) {
- this(environment, null /* eventCount */, duration, timeUnit);
+ Executor executor,
+ Handler handler) {
+ mEnvironment = environment;
+ mExecutor = executor;
+ mHandler = handler;
+ mSensorManager = new TestSensorManager(mEnvironment);
}
/**
- * Private helper constructor.
+ * Set all of the default test expectations.
*/
- private TestSensorOperation(
+ public void addDefaultVerifications() {
+ addVerification(EventGapVerification.getDefault(mEnvironment));
+ addVerification(EventOrderingVerification.getDefault(mEnvironment));
+ addVerification(FrequencyVerification.getDefault(mEnvironment));
+ addVerification(JitterVerification.getDefault(mEnvironment));
+ addVerification(MagnitudeVerification.getDefault(mEnvironment));
+ addVerification(MeanVerification.getDefault(mEnvironment));
+ addVerification(StandardDeviationVerification.getDefault(mEnvironment));
+ addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment));
+ }
+
+ public void addVerification(ISensorVerification verification) {
+ if (verification != null) {
+ mVerifications.add(verification);
+ }
+ }
+
+ /**
+ * Collect the specified number of events from the sensor and run all enabled verifications.
+ */
+ @Override
+ public void execute(ISensorTestNode parent) throws InterruptedException {
+ getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
+ TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler);
+ mExecutor.execute(mSensorManager, listener);
+
+ boolean failed = false;
+ StringBuilder sb = new StringBuilder();
+ List<TestSensorEvent> collectedEvents = listener.getCollectedEvents();
+ for (ISensorVerification verification : mVerifications) {
+ failed |= evaluateResults(collectedEvents, verification, sb);
+ }
+
+ if (failed) {
+ trySaveCollectedEvents(parent, listener);
+
+ String msg = SensorCtsHelper
+ .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
+ getStats().addValue(SensorStats.ERROR, msg);
+ Assert.fail(msg);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TestSensorOperation clone() {
+ TestSensorOperation operation = new TestSensorOperation(mEnvironment, mExecutor);
+ for (ISensorVerification verification : mVerifications) {
+ operation.addVerification(verification.clone());
+ }
+ return operation;
+ }
+
+ /**
+ * Evaluate the results of a test, aggregate the stats, and build the error message.
+ */
+ private boolean evaluateResults(
+ List<TestSensorEvent> events,
+ ISensorVerification verification,
+ StringBuilder sb) {
+ try {
+ // this is an intermediate state in refactoring, at some point verifications might
+ // become stateless
+ verification.addSensorEvents(events);
+ verification.verify(mEnvironment, getStats());
+ } catch (AssertionError e) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(e.getMessage());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Tries to save collected {@link TestSensorEvent}s to a file.
+ *
+ * NOTE: it is more important to handle verifications and its results, than failing if the file
+ * cannot be created. So we silently fail if necessary.
+ */
+ private void trySaveCollectedEvents(ISensorTestNode parent, TestSensorEventListener listener) {
+ String sanitizedFileName;
+ try {
+ String fileName = asTestNode(parent).getName();
+ sanitizedFileName = String.format(
+ "%s-%s-%s_%dus.txt",
+ SensorCtsHelper.sanitizeStringForFileName(fileName),
+ SensorStats.getSanitizedSensorName(mEnvironment.getSensor()),
+ mEnvironment.getFrequencyString(),
+ mEnvironment.getMaxReportLatencyUs());
+ } catch (SensorTestPlatformException e) {
+ Log.w(TAG, "Unable to generate file name to save collected events", e);
+ return;
+ }
+
+ try {
+ listener.logCollectedEventsToFile(sanitizedFileName);
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e);
+ }
+ }
+
+ /**
+ * Creates an operation that will wait for a given amount of events to arrive.
+ *
+ * @param environment The test environment.
+ * @param eventCount The number of events to wait for.
+ */
+ public static TestSensorOperation createOperation(
TestSensorEnvironment environment,
- Integer eventCount,
- Long duration,
- TimeUnit timeUnit) {
- super(environment);
- mEventCount = eventCount;
- mDuration = duration;
- mTimeUnit = timeUnit;
+ final int eventCount) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
+ listener.waitForEvents(latch, eventCount);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
/**
- * {@inheritDoc}
+ * Creates an operation that will wait for a given amount of time to collect events.
+ *
+ * @param environment The test environment.
+ * @param duration The duration to wait for events.
+ * @param timeUnit The time unit for {@code duration}.
*/
- @Override
- protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
- if (mEventCount != null) {
- mSensorManager.runSensor(listener, mEventCount);
- } else {
- mSensorManager.runSensor(listener, mDuration, mTimeUnit);
- }
+ public static TestSensorOperation createOperation(
+ TestSensorEnvironment environment,
+ final long duration,
+ final TimeUnit timeUnit) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ sensorManager.registerListener(listener);
+ listener.waitForEvents(duration, timeUnit);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
/**
- * {@inheritDoc}
+ * Creates an operation that will wait for a given amount of time before calling
+ * {@link TestSensorManager#requestFlush()}.
+ *
+ * @param environment The test environment.
+ * @param duration The duration to wait before calling {@link TestSensorManager#requestFlush()}.
+ * @param timeUnit The time unit for {@code duration}.
*/
- @Override
- protected VerifiableSensorOperation doClone() {
- if (mEventCount != null) {
- return new TestSensorOperation(mEnvironment, mEventCount);
- } else {
- return new TestSensorOperation(mEnvironment, mDuration, mTimeUnit);
- }
+ public static TestSensorOperation createFlushOperation(
+ TestSensorEnvironment environment,
+ final long duration,
+ final TimeUnit timeUnit) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ sensorManager.registerListener(listener);
+ SensorCtsHelper.sleep(duration, timeUnit);
+ CountDownLatch latch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(latch);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
deleted file mode 100644
index 57018eb..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import junit.framework.Assert;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-import android.hardware.cts.helpers.TestSensorManager;
-import android.hardware.cts.helpers.ValidatingSensorEventListener;
-import android.hardware.cts.helpers.sensorverification.EventGapVerification;
-import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
-import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
-import android.hardware.cts.helpers.sensorverification.ISensorVerification;
-import android.hardware.cts.helpers.sensorverification.JitterVerification;
-import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
-import android.hardware.cts.helpers.sensorverification.MeanVerification;
-import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
-
-import java.util.Collection;
-import java.util.HashSet;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
- * </p>
- */
-public abstract class VerifiableSensorOperation extends AbstractSensorOperation {
- protected final TestSensorManager mSensorManager;
- protected final TestSensorEnvironment mEnvironment;
-
- private final Collection<ISensorVerification> mVerifications =
- new HashSet<ISensorVerification>();
-
- private boolean mLogEvents = false;
-
- /**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- */
- public VerifiableSensorOperation(TestSensorEnvironment environment) {
- mEnvironment = environment;
- mSensorManager = new TestSensorManager(mEnvironment);
- }
-
- /**
- * Set whether to log events.
- */
- public void setLogEvents(boolean logEvents) {
- mLogEvents = logEvents;
- }
-
- /**
- * Set all of the default test expectations.
- */
- public void addDefaultVerifications() {
- addVerification(EventGapVerification.getDefault(mEnvironment));
- addVerification(EventOrderingVerification.getDefault(mEnvironment));
- addVerification(FrequencyVerification.getDefault(mEnvironment));
- addVerification(JitterVerification.getDefault(mEnvironment));
- addVerification(MagnitudeVerification.getDefault(mEnvironment));
- addVerification(MeanVerification.getDefault(mEnvironment));
- // Skip SigNumVerification since it has no default
- addVerification(StandardDeviationVerification.getDefault(mEnvironment));
- }
-
- public void addVerification(ISensorVerification verification) {
- if (verification != null) {
- mVerifications.add(verification);
- }
- }
-
- /**
- * Collect the specified number of events from the sensor and run all enabled verifications.
- */
- @Override
- public void execute() throws InterruptedException {
- getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
-
- ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
- listener.setLogEvents(mLogEvents);
-
- doExecute(listener);
-
- boolean failed = false;
- StringBuilder sb = new StringBuilder();
- for (ISensorVerification verification : mVerifications) {
- failed |= evaluateResults(verification, sb);
- }
-
- if (failed) {
- String msg = SensorCtsHelper
- .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
- getStats().addValue(SensorStats.ERROR, msg);
- Assert.fail(msg);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public VerifiableSensorOperation clone() {
- VerifiableSensorOperation operation = doClone();
- for (ISensorVerification verification : mVerifications) {
- operation.addVerification(verification.clone());
- }
- return operation;
- }
-
- /**
- * Execute operations in a {@link TestSensorManager}.
- */
- protected abstract void doExecute(TestSensorEventListener listener) throws InterruptedException;
-
- /**
- * Clone the subclass operation.
- */
- protected abstract VerifiableSensorOperation doClone();
-
- /**
- * Evaluate the results of a test, aggregate the stats, and build the error message.
- */
- private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
- try {
- verification.verify(mEnvironment, getStats());
- } catch (AssertionError e) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(e.getMessage());
- return true;
- }
- return false;
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
index b500ea7..9f03f31 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/WakeLockOperation.java
@@ -17,41 +17,42 @@
package android.hardware.cts.helpers.sensoroperations;
import android.content.Context;
-import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.reporting.ISensorTestNode;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
/**
- * An {@link ISensorOperation} which holds a wakelock while performing another
- * {@link ISensorOperation}.
+ * An {@link SensorOperation} which holds a wake-lock while performing another
+ * {@link SensorOperation}.
*/
-public class WakeLockOperation extends AbstractSensorOperation {
+public class WakeLockOperation extends SensorOperation {
private static final String TAG = "WakeLockOperation";
- private final ISensorOperation mOperation;
+ private final SensorOperation mOperation;
private final Context mContext;
- private final int mWakelockFlags;
+ private final int mWakeLockFlags;
/**
* Constructor for {@link WakeLockOperation}.
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the power manager
- * @param wakelockFlags the flags used when acquiring the wakelock
+ * @param wakeLockFlags the flags used when acquiring the wake-lock
*/
- public WakeLockOperation(ISensorOperation operation, Context context, int wakelockFlags) {
+ public WakeLockOperation(SensorOperation operation, Context context, int wakeLockFlags) {
+ super(operation.getStats());
mOperation = operation;
mContext = context;
- mWakelockFlags = wakelockFlags;
+ mWakeLockFlags = wakeLockFlags;
}
/**
* Constructor for {@link WakeLockOperation}.
*
- * @param operation the child {@link ISensorOperation} to perform after the delay
+ * @param operation the child {@link SensorOperation} to perform after the delay
* @param context the context used to access the power manager
*/
- public WakeLockOperation(ISensorOperation operation, Context context) {
+ public WakeLockOperation(SensorOperation operation, Context context) {
this(operation, context, PowerManager.PARTIAL_WAKE_LOCK);
}
@@ -59,13 +60,12 @@
* {@inheritDoc}
*/
@Override
- public void execute() throws InterruptedException {
+ public void execute(ISensorTestNode parent) throws InterruptedException {
PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- WakeLock wakeLock = pm.newWakeLock(mWakelockFlags, TAG);
-
+ WakeLock wakeLock = pm.newWakeLock(mWakeLockFlags, TAG);
wakeLock.acquire();
try {
- mOperation.execute();
+ mOperation.execute(asTestNode(parent));
} finally {
wakeLock.release();
}
@@ -75,15 +75,7 @@
* {@inheritDoc}
*/
@Override
- public SensorStats getStats() {
- return mOperation.getStats();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public ISensorOperation clone() {
- return new WakeLockOperation(mOperation, mContext, mWakelockFlags);
+ public SensorOperation clone() {
+ return new WakeLockOperation(mOperation.clone(), mContext, mWakeLockFlags);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
index 911ae3a..1e775e3 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -18,6 +18,9 @@
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.Collection;
+import java.util.List;
+
/**
* Abstract class that deals with the synchronization of the sensor verifications.
*/
@@ -27,7 +30,7 @@
* {@inheritDoc}
*/
@Override
- public synchronized void addSensorEvents(TestSensorEvent ... events) {
+ public synchronized void addSensorEvents(Collection<TestSensorEvent> events) {
for (TestSensorEvent event : events) {
addSensorEventInternal(event);
}
@@ -37,14 +40,6 @@
* {@inheritDoc}
*/
@Override
- public synchronized void addSensorEvent(TestSensorEvent event) {
- addSensorEventInternal(event);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
public abstract ISensorVerification clone();
/**
@@ -52,18 +47,38 @@
*/
protected abstract void addSensorEventInternal(TestSensorEvent event);
+ protected <TEvent extends IndexedEvent> int[] getIndexArray(List<TEvent> indexedEvents) {
+ int eventsCount = indexedEvents.size();
+ int[] indices = new int[eventsCount];
+ for (int i = 0; i < eventsCount; i++) {
+ indices[i] = indexedEvents.get(i).index;
+ }
+ return indices;
+ }
+
+ /**
+ * Helper class to store the index and current event.
+ * Events are added to the verification in the order they are generated, the index represents
+ * the position of the given event, in the list of added events.
+ */
+ protected class IndexedEvent {
+ public final int index;
+ public final TestSensorEvent event;
+
+ public IndexedEvent(int index, TestSensorEvent event) {
+ this.index = index;
+ this.event = event;
+ }
+ }
+
/**
* Helper class to store the index, previous event, and current event.
*/
- protected class IndexedEventPair {
- public final int index;
- public final TestSensorEvent event;
+ protected class IndexedEventPair extends IndexedEvent {
public final TestSensorEvent previousEvent;
- public IndexedEventPair(int index, TestSensorEvent event,
- TestSensorEvent previousEvent) {
- this.index = index;
- this.event = event;
+ public IndexedEventPair(int index, TestSensorEvent event, TestSensorEvent previousEvent) {
+ super(index, event);
this.previousEvent = previousEvent;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index 156afa8..b692f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -63,19 +63,14 @@
public void verify(TestSensorEnvironment environment, SensorStats stats) {
if (environment.isSensorSamplingRateOverloaded()) {
// the verification is not reliable on environments under load
- stats.addValue(PASSED_KEY, true);
+ stats.addValue(PASSED_KEY, "skipped (under load)");
return;
}
final int count = mEventGaps.size();
stats.addValue(PASSED_KEY, count == 0);
stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
-
- final int[] indices = new int[count];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = mEventGaps.get(i).index;
- }
- stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+ stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
if (count > 0) {
StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
index d01e108..6f17e7b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerificationTest.java
@@ -22,6 +22,9 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
/**
* Tests for {@link EventGapVerification}.
*/
@@ -90,11 +93,13 @@
}
}
- private ISensorVerification getVerification(int expected, long ... timestamps) {
- ISensorVerification verification = new EventGapVerification(expected);
+ private static EventGapVerification getVerification(int expected, long ... timestamps) {
+ Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
for (long timestamp : timestamps) {
- verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+ events.add(new TestSensorEvent(null, timestamp, 0, null));
}
+ EventGapVerification verification = new EventGapVerification(expected);
+ verification.addSensorEvents(events);
return verification;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
index 6598725..d3b317b 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -49,10 +49,8 @@
@SuppressWarnings("deprecation")
public static EventOrderingVerification getDefault(TestSensorEnvironment environment) {
int reportingMode = environment.getSensor().getReportingMode();
- if (reportingMode != Sensor.REPORTING_MODE_CONTINUOUS
- && reportingMode != Sensor.REPORTING_MODE_ON_CHANGE) {
+ if (reportingMode == Sensor.REPORTING_MODE_ONE_SHOT)
return null;
- }
return new EventOrderingVerification();
}
@@ -75,12 +73,9 @@
final int count = mOutOfOrderEvents.size();
stats.addValue(PASSED_KEY, count == 0);
stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
-
- final int[] indices = new int[count];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = mOutOfOrderEvents.get(i).index;
- }
- stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+ stats.addValue(
+ SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY,
+ getIndexArray(mOutOfOrderEvents));
if (count > 0) {
StringBuilder sb = new StringBuilder();
@@ -117,9 +112,9 @@
if (mPreviousEvent == null) {
mMaxTimestamp = event.timestamp;
} else {
- if (event.timestamp < mMaxTimestamp) {
+ if (event.timestamp <= mMaxTimestamp) {
mOutOfOrderEvents.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
- } else if (event.timestamp > mMaxTimestamp) {
+ } else {
mMaxTimestamp = event.timestamp;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
index 88d5c19..b9848fa 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerificationTest.java
@@ -22,6 +22,7 @@
import android.hardware.cts.helpers.TestSensorEvent;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -96,11 +97,13 @@
assertTrue(indices.contains(4));
}
- private EventOrderingVerification getVerification(long ... timestamps) {
- EventOrderingVerification verification = new EventOrderingVerification();
+ private static EventOrderingVerification getVerification(long ... timestamps) {
+ Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
for (long timestamp : timestamps) {
- verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+ events.add(new TestSensorEvent(null, timestamp, 0, null));
}
+ EventOrderingVerification verification = new EventOrderingVerification();
+ verification.addSensorEvents(events);
return verification;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
new file mode 100644
index 0000000..2c1f196
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.SensorEvent;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the timestamp of the {@link SensorEvent} is
+ * synchronized with {@link SystemClock#elapsedRealtimeNanos()}, based on a given threshold.
+ */
+public class EventTimestampSynchronizationVerification extends AbstractSensorVerification {
+ public static final String PASSED_KEY = "timestamp_synchronization_passed";
+
+ // number of indices to print in assertion message before truncating
+ private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+ private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(500);
+
+ private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
+
+ private final long mMaximumSynchronizationErrorNs;
+ private final long mReportLatencyNs;
+
+ /**
+ * Constructs an instance of {@link EventTimestampSynchronizationVerification}.
+ *
+ * @param maximumSynchronizationErrorNs The valid threshold for timestamp synchronization.
+ * @param reportLatencyNs The latency on which batching events are received
+ */
+ public EventTimestampSynchronizationVerification(
+ long maximumSynchronizationErrorNs,
+ long reportLatencyNs) {
+ mMaximumSynchronizationErrorNs = maximumSynchronizationErrorNs;
+ mReportLatencyNs = reportLatencyNs;
+ }
+
+ /**
+ * Gets a default {@link EventTimestampSynchronizationVerification}.
+ *
+ * @param environment The test environment
+ * @return The verification or null if the verification is not supported in the given
+ * environment.
+ */
+ public static EventTimestampSynchronizationVerification getDefault(
+ TestSensorEnvironment environment) {
+ long reportLatencyUs = environment.getMaxReportLatencyUs();
+ long fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+ int maximumExpectedSamplingPeriodUs = environment.getMaximumExpectedSamplingPeriodUs();
+ if (fifoMaxEventCount > 0 && maximumExpectedSamplingPeriodUs != Integer.MAX_VALUE) {
+ long fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+ reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+ }
+ long reportLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
+ return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS, reportLatencyNs);
+ }
+
+ @Override
+ public void verify(TestSensorEnvironment environment, SensorStats stats) {
+ StringBuilder errorMessageBuilder =
+ new StringBuilder(" event timestamp synchronization failures: ");
+ List<IndexedEvent> failures = verifyTimestampSynchronization(errorMessageBuilder);
+
+ int failuresCount = failures.size();
+ stats.addValue(SensorStats.EVENT_TIME_SYNCHRONIZATION_COUNT_KEY, failuresCount);
+ stats.addValue(
+ SensorStats.EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY,
+ getIndexArray(failures));
+
+ boolean success = failures.isEmpty();
+ stats.addValue(PASSED_KEY, success);
+ errorMessageBuilder.insert(0, failuresCount);
+ Assert.assertTrue(errorMessageBuilder.toString(), success);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public EventTimestampSynchronizationVerification clone() {
+ return new EventTimestampSynchronizationVerification(
+ mMaximumSynchronizationErrorNs,
+ mReportLatencyNs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void addSensorEventInternal(TestSensorEvent event) {
+ mCollectedEvents.add(event);
+ }
+
+ /**
+ * Verifies timestamp synchronization for all sensor events.
+ * The verification accounts for a lower and upper threshold, such thresholds are adjusted for
+ * batching cases.
+ *
+ * @param builder A string builder to store error messaged found in the collected sensor events.
+ * @return A list of events tha failed the verification.
+ */
+ private List<IndexedEvent> verifyTimestampSynchronization(StringBuilder builder) {
+ int collectedEventsCount = mCollectedEvents.size();
+ ArrayList<IndexedEvent> failures = new ArrayList<IndexedEvent>();
+
+ for (int i = 0; i < collectedEventsCount; ++i) {
+ TestSensorEvent event = mCollectedEvents.get(i);
+ long eventTimestampNs = event.timestamp;
+ long receivedTimestampNs = event.receivedTimestamp;
+ long upperThresholdNs = receivedTimestampNs;
+ long lowerThresholdNs = receivedTimestampNs - mMaximumSynchronizationErrorNs
+ - mReportLatencyNs;
+
+ if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
+ if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
+ builder.append("position=").append(i);
+ builder.append(", timestamp=").append(eventTimestampNs).append("ns");
+ builder.append(", expected=[").append(lowerThresholdNs);
+ builder.append(", ").append(upperThresholdNs).append("]ns; ");
+ }
+ failures.add(new IndexedEvent(i, event));
+ }
+ }
+ if (failures.size() >= TRUNCATE_MESSAGE_LENGTH) {
+ builder.append("more; ");
+ }
+ return failures;
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
index 24349ce..bbf022a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/FrequencyVerificationTest.java
@@ -22,6 +22,9 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
/**
* Tests for {@link EventOrderingVerification}.
*/
@@ -73,15 +76,17 @@
return new TestSensorEnvironment(getContext(), Sensor.TYPE_ALL, rateUs);
}
- private ISensorVerification getVerification(
+ private static FrequencyVerification getVerification(
double lowerThreshold,
double upperThreshold,
long ... timestamps) {
- ISensorVerification verification =
- new FrequencyVerification(lowerThreshold, upperThreshold);
+ Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
for (long timestamp : timestamps) {
- verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+ events.add(new TestSensorEvent(null, timestamp, 0, null));
}
+ FrequencyVerification verification =
+ new FrequencyVerification(lowerThreshold, upperThreshold);
+ verification.addSensorEvents(events);
return verification;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
index 4f7b65a..2027f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/ISensorVerification.java
@@ -20,24 +20,25 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.Collection;
+
/**
- * Interface describing the sensor verification. This class was designed for to handle streaming
- * events. The methods {@link #addSensorEvent(TestSensorEvent)} and
- * {@link #addSensorEvents(TestSensorEvent...)} should be called in the order that the events are
- * received. The method {@link #verify(TestSensorEnvironment, SensorStats)} should be called after
- * all events are added.
+ * Interface describing the sensor verification.
+ * This class was designed to handle streaming of events.
+ *
+ * The method {@link #addSensorEvents(Collection)} should be called in the order that the events are
+ * received.
+ *
+ * The method {@link #verify(TestSensorEnvironment, SensorStats)} should be called after all events
+ * are added.
*/
public interface ISensorVerification {
/**
- * Add a single {@link TestSensorEvent} to be evaluated.
+ * Add a list of {@link TestSensorEvent}s to be evaluated.
*/
- public void addSensorEvent(TestSensorEvent event);
-
- /**
- * Add multiple {@link TestSensorEvent}s to be evaluated.
- */
- public void addSensorEvents(TestSensorEvent ... events);
+ // TODO: refactor verifications to be stateless, and pass the list of events in verify()
+ void addSensorEvents(Collection<TestSensorEvent> events);
/**
* Evaluate all added {@link TestSensorEvent}s and update stats.
@@ -45,10 +46,10 @@
* @param stats a {@link SensorStats} object used to keep track of the stats.
* @throws AssertionError if the verification fails.
*/
- public void verify(TestSensorEnvironment environment, SensorStats stats);
+ void verify(TestSensorEnvironment environment, SensorStats stats);
/**
* Clones the {@link ISensorVerification}
*/
- public ISensorVerification clone();
+ ISensorVerification clone();
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
index 0c85b63..50e288c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/JitterVerificationTest.java
@@ -22,6 +22,8 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.List;
/**
@@ -98,11 +100,13 @@
assertEquals(3.0, jitterValues.get(3));
}
- private JitterVerification getVerification(int threshold, long ... timestamps) {
- JitterVerification verification = new JitterVerification(threshold);
+ private static JitterVerification getVerification(int threshold, long ... timestamps) {
+ Collection<TestSensorEvent> events = new ArrayList<>(timestamps.length);
for (long timestamp : timestamps) {
- verification.addSensorEvent(new TestSensorEvent(null, timestamp, 0, null));
+ events.add(new TestSensorEvent(null, timestamp, 0, null));
}
+ JitterVerification verification = new JitterVerification(threshold);
+ verification.addSensorEvents(events);
return verification;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
index bb29330..ac873c1 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MagnitudeVerificationTest.java
@@ -22,6 +22,9 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
/**
* Tests for {@link MagnitudeVerification}.
*/
@@ -63,12 +66,14 @@
assertEquals(magnitude, (Float) stats.getValue(SensorStats.MAGNITUDE_KEY), 0.01);
}
- private MagnitudeVerification getVerification(float expected, float threshold,
+ private static MagnitudeVerification getVerification(float expected, float threshold,
float[] ... values) {
- MagnitudeVerification verification = new MagnitudeVerification(expected, threshold);
+ Collection<TestSensorEvent> events = new ArrayList<>(values.length);
for (float[] value : values) {
- verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+ events.add(new TestSensorEvent(null, 0, 0, value));
}
+ MagnitudeVerification verification = new MagnitudeVerification(expected, threshold);
+ verification.addSensorEvents(events);
return verification;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
index b07ea50..d7fcf9f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/MeanVerificationTest.java
@@ -22,6 +22,9 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
/**
* Tests for {@link MeanVerification}.
*/
@@ -89,12 +92,14 @@
verifyStats(stats, false, new float[]{2.0f, 3.0f, 6.0f});
}
- private MeanVerification getVerification(float[] expected, float[] threshold,
+ private static MeanVerification getVerification(float[] expected, float[] threshold,
float[] ... values) {
- MeanVerification verification = new MeanVerification(expected, threshold);
+ Collection<TestSensorEvent> events = new ArrayList<>(values.length);
for (float[] value : values) {
- verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+ events.add(new TestSensorEvent(null, 0, 0, value));
}
+ MeanVerification verification = new MeanVerification(expected, threshold);
+ verification.addSensorEvents(events);
return verification;
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
index 5d958f5..617a438 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/StandardDeviationVerificationTest.java
@@ -22,6 +22,9 @@
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.ArrayList;
+import java.util.Collection;
+
/**
* Tests for {@link StandardDeviationVerification}.
*/
@@ -79,11 +82,15 @@
}
}
- private StandardDeviationVerification getVerification(float[] threshold, float[] ... values) {
- StandardDeviationVerification verification = new StandardDeviationVerification(threshold);
+ private static StandardDeviationVerification getVerification(
+ float[] threshold,
+ float[] ... values) {
+ Collection<TestSensorEvent> events = new ArrayList<>(values.length);
for (float[] value : values) {
- verification.addSensorEvent(new TestSensorEvent(null, 0, 0, value));
+ events.add(new TestSensorEvent(null, 0, 0, value));
}
+ StandardDeviationVerification verification = new StandardDeviationVerification(threshold);
+ verification.addSensorEvents(events);
return verification;
}
}
diff --git a/tests/tests/jni/libjnitest/Android.mk b/tests/tests/jni/libjnitest/Android.mk
index d7d1bad..396e161 100644
--- a/tests/tests/jni/libjnitest/Android.mk
+++ b/tests/tests/jni/libjnitest/Android.mk
@@ -29,6 +29,7 @@
android_jni_cts_InstanceNonce.c \
android_jni_cts_JniCTest.c \
android_jni_cts_JniCppTest.cpp \
+ android_jni_cts_JniStaticTest.cpp \
android_jni_cts_StaticNonce.c \
helper.c \
register.c
diff --git a/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
new file mode 100644
index 0000000..5e8bea0
--- /dev/null
+++ b/tests/tests/jni/libjnitest/android_jni_cts_JniStaticTest.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+/*
+ * Native implementation for the JniStaticTest parts.
+ */
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+extern "C" JNIEXPORT jint JNICALL Java_android_jni_cts_ClassLoaderHelper_nativeGetHashCode(
+ JNIEnv* env,
+ jobject obj __attribute__((unused)),
+ jobject appLoader,
+ jclass appLoaderClass) {
+ jmethodID midFindClass = env->GetMethodID(appLoaderClass, "findClass",
+ "(Ljava/lang/String;)Ljava/lang/Class;");
+ jstring coreClsName = env->NewStringUTF("android.jni.cts.ClassLoaderStaticNonce");
+ jobject coreClass = env->CallObjectMethod(appLoader, midFindClass, coreClsName);
+ jmethodID midHashCode = env->GetMethodID((jclass)coreClass, "hashCode", "()I");
+ jint hash = env->CallIntMethod(coreClass, midHashCode);
+
+ return hash;
+}
diff --git a/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java b/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java
new file mode 100644
index 0000000..c4fe0f0
--- /dev/null
+++ b/tests/tests/jni/src/android/jni/cts/ClassLoaderHelper.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.jni.cts;
+
+class ClassLoaderHelper {
+
+ // Note: To not hava a dependency on other classes, assume that if we do initialize then
+ // it's OK to load the library.
+ static {
+ System.loadLibrary("jnitest");
+ }
+
+ public static boolean run() {
+ ClassLoaderHelper clh = new ClassLoaderHelper();
+
+ int firstHashCode = clh.getHashCodeDirect();
+
+ Runtime.getRuntime().gc();
+
+ int secondHashCode = clh.getHashCodeNative();
+
+ return (firstHashCode == secondHashCode);
+ }
+
+ // Simple helpers to avoid keeping references alive because of dex registers.
+ private int getHashCodeDirect() {
+ return ClassLoaderStaticNonce.class.hashCode();
+ }
+ private int getHashCodeNative() {
+ ClassLoader loader = getClass().getClassLoader();
+ return nativeGetHashCode(loader, loader.getClass());
+ }
+
+ private native int nativeGetHashCode(ClassLoader loader, Class<?> classLoaderClass);
+}
\ No newline at end of file
diff --git a/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java b/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java
new file mode 100644
index 0000000..d425c77
--- /dev/null
+++ b/tests/tests/jni/src/android/jni/cts/ClassLoaderStaticNonce.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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.jni.cts;
+
+import android.util.Log;
+
+/**
+ * Class to help with class-loader check..
+ */
+public class ClassLoaderStaticNonce {
+
+ static Object ctx;
+
+ // Have a static initializer block.
+ static {
+ ctx = new Object();
+ log("Initializing ClassLoaderStaticNonce");
+ }
+
+ private final static A a = new A();
+
+ public static void log(String s) {
+ Log.i("ClassLoaderStaticNone", s);
+ }
+
+ public static class A {
+ // Have a finalizer. This will make the outer class not finalizable when we allocate for
+ // a static field.
+ public void finalize() throws Throwable {
+ super.finalize();
+
+ // Do something so that the finalizer can't be recognized as empty.
+ toNull = null;
+ }
+
+ private Object toNull = new Object();
+ }
+
+
+ public static void setCtx(Object in) {
+ ctx = in;
+ }
+
+ public static Object getCtx() {
+ return ctx;
+ }
+}
diff --git a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
index 3036c71..d4b51b9 100644
--- a/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
+++ b/tests/tests/jni/src/android/jni/cts/JniStaticTest.java
@@ -24,6 +24,20 @@
*/
public class JniStaticTest extends JniTestCase {
+ static {
+ if (!JniTestCase.isCpuAbiNone()) {
+ System.loadLibrary("jnitest");
+ }
+ }
+
+ /**
+ * Test that accessing classes true JNI works as expected. b/19382130
+ */
+ public void test_classload() {
+ // Use an independent class to do this.
+ assertEquals(true, ClassLoaderHelper.run());
+ }
+
/**
* Test a simple no-op and void-returning method call.
*/
diff --git a/tests/tests/location/src/android/location/cts/LocationManagerTest.java b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
index a985aee..118b8f9 100644
--- a/tests/tests/location/src/android/location/cts/LocationManagerTest.java
+++ b/tests/tests/location/src/android/location/cts/LocationManagerTest.java
@@ -49,7 +49,7 @@
* android.permission.ACCESS_LOCATION_EXTRA_COMMANDS to send extra commands to GPS provider
*/
public class LocationManagerTest extends InstrumentationTestCase {
- private static final long TEST_TIME_OUT = 5000;
+ private static final long TEST_TIME_OUT = 10000;
private static final String TEST_MOCK_PROVIDER_NAME = "test_provider";
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 3765809..b298b97 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -26,6 +26,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;
@@ -227,18 +228,32 @@
@UiThreadTest
public void testGpsStatusListener() {
try {
- // .addGpsStatusListener returns true if the listener added successfully
- if (mManager.addGpsStatusListener(new MockGpsStatusListener())) {
- fail("Should have failed to add a gps status listener");
- }
+ mManager.addGpsStatusListener(new MockGpsStatusListener());
+ fail("Should have failed to add a gps status listener");
} catch (SecurityException e) {
// expected
}
try {
- if (mManager.addGpsStatusListener(null)) {
- fail("Should have failed to add null as a gps status listener");
- }
+ mManager.addGpsStatusListener(null);
+ fail("Should have failed to add null as a gps status listener");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @UiThreadTest
+ public void testGpsStatusNmeaListener() {
+ try {
+ mManager.addNmeaListener(new MockGpsStatusNmeaListener());
+ fail("Should have failed to add a gps status nmea listener");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mManager.addNmeaListener(null);
+ fail("Should have failed to add null as a gps status nmea listener");
} catch (SecurityException e) {
// expected
}
@@ -478,4 +493,20 @@
mHasCallOnGpsStatusChanged = true;
}
}
+
+ private static class MockGpsStatusNmeaListener implements NmeaListener {
+ private boolean mHasCallOnNmeaReceived;
+
+ public boolean hasCallOnNmeaReceived() {
+ return mHasCallOnNmeaReceived;
+ }
+
+ public void reset(){
+ mHasCallOnNmeaReceived = false;
+ }
+
+ public void onNmeaReceived(long timestamp, String nmea) {
+ mHasCallOnNmeaReceived = true;
+ }
+ }
}
diff --git a/tests/tests/media/Android.mk b/tests/tests/media/Android.mk
index 15237a8..77d4bb7 100644
--- a/tests/tests/media/Android.mk
+++ b/tests/tests/media/Android.mk
@@ -16,6 +16,20 @@
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ src/android/media/cts/CodecImage.java \
+ src/android/media/cts/CodecUtils.java
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE := ctsmediautil
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
+
+include $(CLEAR_VARS)
+
# don't include this package in any target
LOCAL_MODULE_TAGS := optional
# and when built explicitly put it in the data partition
@@ -24,7 +38,8 @@
# include both the 32 and 64 bit versions
LOCAL_MULTILIB := both
-LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestserver ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ ctsmediautil ctsdeviceutil ctstestserver ctstestrunner
LOCAL_JNI_SHARED_LIBRARIES := libctsmediacodec_jni
diff --git a/tests/tests/media/AndroidManifest.xml b/tests/tests/media/AndroidManifest.xml
index d53b2c6..32df531 100644
--- a/tests/tests/media/AndroidManifest.xml
+++ b/tests/tests/media/AndroidManifest.xml
@@ -64,6 +64,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/assets/noiseandchirps.ogg b/tests/tests/media/assets/noiseandchirps.ogg
index 5c67a88..1acb643 100644
--- a/tests/tests/media/assets/noiseandchirps.ogg
+++ b/tests/tests/media/assets/noiseandchirps.ogg
Binary files differ
diff --git a/tests/tests/media/libmediandkjni/Android.mk b/tests/tests/media/libmediandkjni/Android.mk
index 2d2033f..23f9f5c 100644
--- a/tests/tests/media/libmediandkjni/Android.mk
+++ b/tests/tests/media/libmediandkjni/Android.mk
@@ -20,9 +20,13 @@
LOCAL_MODULE_TAGS := optional
-LOCAL_SRC_FILES := native-media-jni.cpp
+LOCAL_SRC_FILES := \
+ native-media-jni.cpp \
+ codec-utils-jni.cpp
-LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+LOCAL_C_INCLUDES := \
+ $(JNI_H_INCLUDE) \
+ system/core/include
LOCAL_C_INCLUDES += $(call include-path-for, mediandk)
diff --git a/tests/tests/media/libmediandkjni/codec-utils-jni.cpp b/tests/tests/media/libmediandkjni/codec-utils-jni.cpp
new file mode 100644
index 0000000..4f87bb4
--- /dev/null
+++ b/tests/tests/media/libmediandkjni/codec-utils-jni.cpp
@@ -0,0 +1,488 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Original code copied from NDK Native-media sample code */
+
+//#define LOG_NDEBUG 0
+#define TAG "CodecUtilsJNI"
+#include <log/log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <jni.h>
+
+#include <ScopedLocalRef.h>
+#include <JNIHelp.h>
+
+#include <math.h>
+
+typedef ssize_t offs_t;
+
+struct NativeImage {
+ struct crop {
+ int left;
+ int top;
+ int right;
+ int bottom;
+ } crop;
+ struct plane {
+ const uint8_t *buffer;
+ size_t size;
+ ssize_t colInc;
+ ssize_t rowInc;
+ offs_t cropOffs;
+ size_t cropWidth;
+ size_t cropHeight;
+ } plane[3];
+ int width;
+ int height;
+ int format;
+ long timestamp;
+ size_t numPlanes;
+};
+
+struct ChecksumAlg {
+ virtual void init() = 0;
+ virtual void update(uint8_t c) = 0;
+ virtual uint32_t checksum() = 0;
+ virtual size_t length() = 0;
+protected:
+ virtual ~ChecksumAlg() {}
+};
+
+struct Adler32 : ChecksumAlg {
+ Adler32() {
+ init();
+ }
+ void init() {
+ a = 1;
+ len = b = 0;
+ }
+ void update(uint8_t c) {
+ a += c;
+ b += a;
+ ++len;
+ }
+ uint32_t checksum() {
+ return (a % 65521) + ((b % 65521) << 16);
+ }
+ size_t length() {
+ return len;
+ }
+private:
+ uint32_t a, b;
+ size_t len;
+};
+
+static struct ImageFieldsAndMethods {
+ // android.graphics.ImageFormat
+ int YUV_420_888;
+ // android.media.Image
+ jmethodID methodWidth;
+ jmethodID methodHeight;
+ jmethodID methodFormat;
+ jmethodID methodTimestamp;
+ jmethodID methodPlanes;
+ jmethodID methodCrop;
+ // android.media.Image.Plane
+ jmethodID methodBuffer;
+ jmethodID methodPixelStride;
+ jmethodID methodRowStride;
+ // android.graphics.Rect
+ jfieldID fieldLeft;
+ jfieldID fieldTop;
+ jfieldID fieldRight;
+ jfieldID fieldBottom;
+} gFields;
+static bool gFieldsInitialized = false;
+
+void initializeGlobalFields(JNIEnv *env) {
+ if (gFieldsInitialized) {
+ return;
+ }
+ { // ImageFormat
+ jclass imageFormatClazz = env->FindClass("android/graphics/ImageFormat");
+ const jfieldID fieldYUV420888 = env->GetStaticFieldID(imageFormatClazz, "YUV_420_888", "I");
+ gFields.YUV_420_888 = env->GetStaticIntField(imageFormatClazz, fieldYUV420888);
+ env->DeleteLocalRef(imageFormatClazz);
+ imageFormatClazz = NULL;
+ }
+
+ { // Image
+ jclass imageClazz = env->FindClass("android/media/cts/CodecImage");
+ gFields.methodWidth = env->GetMethodID(imageClazz, "getWidth", "()I");
+ gFields.methodHeight = env->GetMethodID(imageClazz, "getHeight", "()I");
+ gFields.methodFormat = env->GetMethodID(imageClazz, "getFormat", "()I");
+ gFields.methodTimestamp = env->GetMethodID(imageClazz, "getTimestamp", "()J");
+ gFields.methodPlanes = env->GetMethodID(
+ imageClazz, "getPlanes", "()[Landroid/media/cts/CodecImage$Plane;");
+ gFields.methodCrop = env->GetMethodID(
+ imageClazz, "getCropRect", "()Landroid/graphics/Rect;");
+ env->DeleteLocalRef(imageClazz);
+ imageClazz = NULL;
+ }
+
+ { // Image.Plane
+ jclass planeClazz = env->FindClass("android/media/cts/CodecImage$Plane");
+ gFields.methodBuffer = env->GetMethodID(planeClazz, "getBuffer", "()Ljava/nio/ByteBuffer;");
+ gFields.methodPixelStride = env->GetMethodID(planeClazz, "getPixelStride", "()I");
+ gFields.methodRowStride = env->GetMethodID(planeClazz, "getRowStride", "()I");
+ env->DeleteLocalRef(planeClazz);
+ planeClazz = NULL;
+ }
+
+ { // Rect
+ jclass rectClazz = env->FindClass("android/graphics/Rect");
+ gFields.fieldLeft = env->GetFieldID(rectClazz, "left", "I");
+ gFields.fieldTop = env->GetFieldID(rectClazz, "top", "I");
+ gFields.fieldRight = env->GetFieldID(rectClazz, "right", "I");
+ gFields.fieldBottom = env->GetFieldID(rectClazz, "bottom", "I");
+ env->DeleteLocalRef(rectClazz);
+ rectClazz = NULL;
+ }
+ gFieldsInitialized = true;
+}
+
+NativeImage *getNativeImage(JNIEnv *env, jobject image, jobject area = NULL) {
+ if (image == NULL) {
+ jniThrowNullPointerException(env, "image is null");
+ return NULL;
+ }
+
+ initializeGlobalFields(env);
+
+ NativeImage *img = new NativeImage;
+ img->format = env->CallIntMethod(image, gFields.methodFormat);
+ img->width = env->CallIntMethod(image, gFields.methodWidth);
+ img->height = env->CallIntMethod(image, gFields.methodHeight);
+ img->timestamp = env->CallLongMethod(image, gFields.methodTimestamp);
+
+ jobject cropRect = NULL;
+ if (area == NULL) {
+ cropRect = env->CallObjectMethod(image, gFields.methodCrop);
+ area = cropRect;
+ }
+
+ img->crop.left = env->GetIntField(area, gFields.fieldLeft);
+ img->crop.top = env->GetIntField(area, gFields.fieldTop);
+ img->crop.right = env->GetIntField(area, gFields.fieldRight);
+ img->crop.bottom = env->GetIntField(area, gFields.fieldBottom);
+ if (img->crop.right == 0 && img->crop.bottom == 0) {
+ img->crop.right = img->width;
+ img->crop.bottom = img->height;
+ }
+
+ if (cropRect != NULL) {
+ env->DeleteLocalRef(cropRect);
+ cropRect = NULL;
+ }
+
+ if (img->format != gFields.YUV_420_888) {
+ jniThrowException(
+ env, "java/lang/UnsupportedOperationException",
+ "only support YUV_420_888 images");
+ delete img;
+ img = NULL;
+ return NULL;
+ }
+ img->numPlanes = 3;
+
+ ScopedLocalRef<jobjectArray> planesArray(
+ env, (jobjectArray)env->CallObjectMethod(image, gFields.methodPlanes));
+ int xDecim = 0;
+ int yDecim = 0;
+ for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+ ScopedLocalRef<jobject> plane(
+ env, env->GetObjectArrayElement(planesArray.get(), (jsize)ix));
+ img->plane[ix].colInc = env->CallIntMethod(plane.get(), gFields.methodPixelStride);
+ img->plane[ix].rowInc = env->CallIntMethod(plane.get(), gFields.methodRowStride);
+ ScopedLocalRef<jobject> buffer(
+ env, env->CallObjectMethod(plane.get(), gFields.methodBuffer));
+
+ img->plane[ix].buffer = (const uint8_t *)env->GetDirectBufferAddress(buffer.get());
+ img->plane[ix].size = env->GetDirectBufferCapacity(buffer.get());
+
+ img->plane[ix].cropOffs =
+ (img->crop.left >> xDecim) * img->plane[ix].colInc
+ + (img->crop.top >> yDecim) * img->plane[ix].rowInc;
+ img->plane[ix].cropHeight =
+ ((img->crop.bottom + (1 << yDecim) - 1) >> yDecim) - (img->crop.top >> yDecim);
+ img->plane[ix].cropWidth =
+ ((img->crop.right + (1 << xDecim) - 1) >> xDecim) - (img->crop.left >> xDecim);
+
+ // sanity check on increments
+ ssize_t widthOffs =
+ (((img->width + (1 << xDecim) - 1) >> xDecim) - 1) * img->plane[ix].colInc;
+ ssize_t heightOffs =
+ (((img->height + (1 << yDecim) - 1) >> yDecim) - 1) * img->plane[ix].rowInc;
+ if (widthOffs < 0 || heightOffs < 0
+ || widthOffs + heightOffs >= (ssize_t)img->plane[ix].size) {
+ jniThrowException(
+ env, "java/lang/IndexOutOfBoundsException", "plane exceeds bytearray");
+ delete img;
+ img = NULL;
+ return NULL;
+ }
+ xDecim = yDecim = 1;
+ }
+ return img;
+}
+
+extern "C" jint Java_android_media_cts_CodecUtils_getImageChecksum(JNIEnv *env,
+ jclass /*clazz*/, jobject image)
+{
+ NativeImage *img = getNativeImage(env, image);
+ if (img == NULL) {
+ return 0;
+ }
+
+ Adler32 adler;
+ for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+ const uint8_t *row = img->plane[ix].buffer + img->plane[ix].cropOffs;
+ for (size_t y = img->plane[ix].cropHeight; y > 0; --y) {
+ const uint8_t *col = row;
+ ssize_t colInc = img->plane[ix].colInc;
+ for (size_t x = img->plane[ix].cropWidth; x > 0; --x) {
+ adler.update(*col);
+ col += colInc;
+ }
+ row += img->plane[ix].rowInc;
+ }
+ }
+ ALOGV("adler %zu/%u", adler.length(), adler.checksum());
+ return adler.checksum();
+}
+
+/* tiled copy that loops around source image boundary */
+extern "C" void Java_android_media_cts_CodecUtils_copyFlexYUVImage(JNIEnv *env,
+ jclass /*clazz*/, jobject target, jobject source)
+{
+ NativeImage *tgt = getNativeImage(env, target);
+ NativeImage *src = getNativeImage(env, source);
+ if (tgt != NULL && src != NULL) {
+ ALOGV("copyFlexYUVImage %dx%d (%d,%d..%d,%d) (%zux%zu) %+zd%+zd %+zd%+zd %+zd%+zd <= "
+ "%dx%d (%d, %d..%d, %d) (%zux%zu) %+zd%+zd %+zd%+zd %+zd%+zd",
+ tgt->width, tgt->height,
+ tgt->crop.left, tgt->crop.top, tgt->crop.right, tgt->crop.bottom,
+ tgt->plane[0].cropWidth, tgt->plane[0].cropHeight,
+ tgt->plane[0].rowInc, tgt->plane[0].colInc,
+ tgt->plane[1].rowInc, tgt->plane[1].colInc,
+ tgt->plane[2].rowInc, tgt->plane[2].colInc,
+ src->width, src->height,
+ src->crop.left, src->crop.top, src->crop.right, src->crop.bottom,
+ src->plane[0].cropWidth, src->plane[0].cropHeight,
+ src->plane[0].rowInc, src->plane[0].colInc,
+ src->plane[1].rowInc, src->plane[1].colInc,
+ src->plane[2].rowInc, src->plane[2].colInc);
+ for (size_t ix = 0; ix < tgt->numPlanes; ++ix) {
+ uint8_t *row = const_cast<uint8_t *>(tgt->plane[ix].buffer) + tgt->plane[ix].cropOffs;
+ for (size_t y = 0; y < tgt->plane[ix].cropHeight; ++y) {
+ uint8_t *col = row;
+ ssize_t colInc = tgt->plane[ix].colInc;
+ const uint8_t *srcRow = (src->plane[ix].buffer + src->plane[ix].cropOffs
+ + src->plane[ix].rowInc * (y % src->plane[ix].cropHeight));
+ for (size_t x = 0; x < tgt->plane[ix].cropWidth; ++x) {
+ *col = srcRow[src->plane[ix].colInc * (x % src->plane[ix].cropWidth)];
+ col += colInc;
+ }
+ row += tgt->plane[ix].rowInc;
+ }
+ }
+ }
+}
+
+extern "C" void Java_android_media_cts_CodecUtils_fillImageRectWithYUV(JNIEnv *env,
+ jclass /*clazz*/, jobject image, jobject area, jint y, jint u, jint v)
+{
+ NativeImage *img = getNativeImage(env, image, area);
+ if (img == NULL) {
+ return;
+ }
+
+ for (size_t ix = 0; ix < img->numPlanes; ++ix) {
+ const uint8_t *row = img->plane[ix].buffer + img->plane[ix].cropOffs;
+ uint8_t val = ix == 0 ? y : ix == 1 ? u : v;
+ for (size_t y = img->plane[ix].cropHeight; y > 0; --y) {
+ uint8_t *col = (uint8_t *)row;
+ ssize_t colInc = img->plane[ix].colInc;
+ for (size_t x = img->plane[ix].cropWidth; x > 0; --x) {
+ *col = val;
+ col += colInc;
+ }
+ row += img->plane[ix].rowInc;
+ }
+ }
+}
+
+void getRawStats(NativeImage *img, jlong rawStats[10])
+{
+ // this works best if crop area is even
+
+ uint64_t sum_x[3] = { 0, 0, 0 }; // Y, U, V
+ uint64_t sum_xx[3] = { 0, 0, 0 }; // YY, UU, VV
+ uint64_t sum_xy[3] = { 0, 0, 0 }; // YU, YV, UV
+
+ const uint8_t *yrow = img->plane[0].buffer + img->plane[0].cropOffs;
+ const uint8_t *urow = img->plane[1].buffer + img->plane[1].cropOffs;
+ const uint8_t *vrow = img->plane[2].buffer + img->plane[2].cropOffs;
+
+ ssize_t ycolInc = img->plane[0].colInc;
+ ssize_t ucolInc = img->plane[1].colInc;
+ ssize_t vcolInc = img->plane[2].colInc;
+
+ ssize_t yrowInc = img->plane[0].rowInc;
+ ssize_t urowInc = img->plane[1].rowInc;
+ ssize_t vrowInc = img->plane[2].rowInc;
+
+ size_t rightOdd = img->crop.right & 1;
+ size_t bottomOdd = img->crop.bottom & 1;
+
+ for (size_t y = img->plane[0].cropHeight; y; --y) {
+ uint8_t *ycol = (uint8_t *)yrow;
+ uint8_t *ucol = (uint8_t *)urow;
+ uint8_t *vcol = (uint8_t *)vrow;
+
+ for (size_t x = img->plane[0].cropWidth; x; --x) {
+ uint64_t Y = *ycol;
+ uint64_t U = *ucol;
+ uint64_t V = *vcol;
+
+ sum_x[0] += Y;
+ sum_x[1] += U;
+ sum_x[2] += V;
+ sum_xx[0] += Y * Y;
+ sum_xx[1] += U * U;
+ sum_xx[2] += V * V;
+ sum_xy[0] += Y * U;
+ sum_xy[1] += Y * V;
+ sum_xy[2] += U * V;
+
+ ycol += ycolInc;
+ if (rightOdd ^ (x & 1)) {
+ ucol += ucolInc;
+ vcol += vcolInc;
+ }
+ }
+
+ yrow += yrowInc;
+ if (bottomOdd ^ (y & 1)) {
+ urow += urowInc;
+ vrow += vrowInc;
+ }
+ }
+
+ rawStats[0] = img->plane[0].cropWidth * (uint64_t)img->plane[0].cropHeight;
+ for (size_t i = 0; i < 3; i++) {
+ rawStats[i + 1] = sum_x[i];
+ rawStats[i + 4] = sum_xx[i];
+ rawStats[i + 7] = sum_xy[i];
+ }
+}
+
+bool Raw2YUVStats(jlong rawStats[10], jfloat stats[9]) {
+ int64_t sum_x[3], sum_xx[3]; // Y, U, V
+ int64_t sum_xy[3]; // YU, YV, UV
+
+ int64_t num = rawStats[0]; // #Y,U,V
+ for (size_t i = 0; i < 3; i++) {
+ sum_x[i] = rawStats[i + 1];
+ sum_xx[i] = rawStats[i + 4];
+ sum_xy[i] = rawStats[i + 7];
+ }
+
+ if (num > 0) {
+ stats[0] = sum_x[0] / (float)num; // y average
+ stats[1] = sum_x[1] / (float)num; // u average
+ stats[2] = sum_x[2] / (float)num; // v average
+
+ // 60 bits for 4Mpixel image
+ // adding 1 to avoid degenerate case when deviation is 0
+ stats[3] = sqrtf((sum_xx[0] + 1) * num - sum_x[0] * sum_x[0]) / num; // y stdev
+ stats[4] = sqrtf((sum_xx[1] + 1) * num - sum_x[1] * sum_x[1]) / num; // u stdev
+ stats[5] = sqrtf((sum_xx[2] + 1) * num - sum_x[2] * sum_x[2]) / num; // v stdev
+
+ // yu covar
+ stats[6] = (float)(sum_xy[0] + 1 - sum_x[0] * sum_x[1] / num) / num / stats[3] / stats[4];
+ // yv covar
+ stats[7] = (float)(sum_xy[1] + 1 - sum_x[0] * sum_x[2] / num) / num / stats[3] / stats[5];
+ // uv covar
+ stats[8] = (float)(sum_xy[2] + 1 - sum_x[1] * sum_x[2] / num) / num / stats[4] / stats[5];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_getRawStats(JNIEnv *env,
+ jclass /*clazz*/, jobject image, jobject area)
+{
+ NativeImage *img = getNativeImage(env, image, area);
+ if (img == NULL) {
+ return NULL;
+ }
+
+ jlong rawStats[10];
+ getRawStats(img, rawStats);
+ jlongArray jstats = env->NewLongArray(10);
+ if (jstats != NULL) {
+ env->SetLongArrayRegion(jstats, 0, 10, rawStats);
+ }
+ return jstats;
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_getYUVStats(JNIEnv *env,
+ jclass /*clazz*/, jobject image, jobject area)
+{
+ NativeImage *img = getNativeImage(env, image, area);
+ if (img == NULL) {
+ return NULL;
+ }
+
+ jlong rawStats[10];
+ getRawStats(img, rawStats);
+ jfloat stats[9];
+ jfloatArray jstats = NULL;
+ if (Raw2YUVStats(rawStats, stats)) {
+ jstats = env->NewFloatArray(9);
+ if (jstats != NULL) {
+ env->SetFloatArrayRegion(jstats, 0, 9, stats);
+ }
+ } else {
+ jniThrowRuntimeException(env, "empty area");
+ }
+
+ return jstats;
+}
+
+extern "C" jobject Java_android_media_cts_CodecUtils_Raw2YUVStats(JNIEnv *env,
+ jclass /*clazz*/, jobject jrawStats)
+{
+ jfloatArray jstats = NULL;
+ jlong rawStats[10];
+ env->GetLongArrayRegion((jlongArray)jrawStats, 0, 10, rawStats);
+ if (!env->ExceptionCheck()) {
+ jfloat stats[9];
+ if (Raw2YUVStats(rawStats, stats)) {
+ jstats = env->NewFloatArray(9);
+ if (jstats != NULL) {
+ env->SetFloatArrayRegion(jstats, 0, 9, stats);
+ }
+ } else {
+ jniThrowRuntimeException(env, "no raw statistics");
+ }
+ }
+ return jstats;
+}
diff --git a/tests/tests/media/libmediandkjni/native-media-jni.cpp b/tests/tests/media/libmediandkjni/native-media-jni.cpp
index 850932f1..9bca242 100644
--- a/tests/tests/media/libmediandkjni/native-media-jni.cpp
+++ b/tests/tests/media/libmediandkjni/native-media-jni.cpp
@@ -16,7 +16,10 @@
/* Original code copied from NDK Native-media sample code */
-#undef NDEBUG
+//#define LOG_NDEBUG 0
+#define TAG "NativeMedia"
+#include <log/log.h>
+
#include <assert.h>
#include <jni.h>
#include <pthread.h>
@@ -27,13 +30,6 @@
#include <android/native_window_jni.h>
-// for __android_log_print(ANDROID_LOG_INFO, "YourApp", "formatted message");
-#include <android/log.h>
-#define TAG "NativeMedia"
-#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
-#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
-#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
-
#include "ndk/NdkMediaExtractor.h"
#include "ndk/NdkMediaCodec.h"
#include "ndk/NdkMediaCrypto.h"
diff --git a/tests/tests/media/res/raw/sine1khzm40db.wav b/tests/tests/media/res/raw/sine1khzm40db.wav
new file mode 100644
index 0000000..ba541c4
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzm40db.wav
Binary files differ
diff --git a/tests/tests/media/res/raw/sine1khzs40dblong.mp3 b/tests/tests/media/res/raw/sine1khzs40dblong.mp3
new file mode 100644
index 0000000..29bc683
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzs40dblong.mp3
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_h264.mp4 b/tests/tests/media/res/raw/swirl_128x128_h264.mp4
new file mode 100644
index 0000000..3ff485a
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_h265.mp4 b/tests/tests/media/res/raw/swirl_128x128_h265.mp4
new file mode 100644
index 0000000..a0b112b
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4
new file mode 100644
index 0000000..694ce95
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_vp8.webm b/tests/tests/media/res/raw/swirl_128x128_vp8.webm
new file mode 100644
index 0000000..7b606a2
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x128_vp9.webm b/tests/tests/media/res/raw/swirl_128x128_vp9.webm
new file mode 100644
index 0000000..7acff11
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x128_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_128x96_h263.3gp b/tests/tests/media/res/raw/swirl_128x96_h263.3gp
new file mode 100644
index 0000000..f0ef242
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_128x96_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_h264.mp4 b/tests/tests/media/res/raw/swirl_130x132_h264.mp4
new file mode 100644
index 0000000..60027fd
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_h265.mp4 b/tests/tests/media/res/raw/swirl_130x132_h265.mp4
new file mode 100644
index 0000000..46fab26
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4
new file mode 100644
index 0000000..ed6b529
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_vp8.webm b/tests/tests/media/res/raw/swirl_130x132_vp8.webm
new file mode 100644
index 0000000..a3f2d21
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_130x132_vp9.webm b/tests/tests/media/res/raw/swirl_130x132_vp9.webm
new file mode 100644
index 0000000..840dc59
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_130x132_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_h264.mp4 b/tests/tests/media/res/raw/swirl_132x130_h264.mp4
new file mode 100644
index 0000000..dc17f8f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_h265.mp4 b/tests/tests/media/res/raw/swirl_132x130_h265.mp4
new file mode 100644
index 0000000..f9a59f5
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4
new file mode 100644
index 0000000..ed975db
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_vp8.webm b/tests/tests/media/res/raw/swirl_132x130_vp8.webm
new file mode 100644
index 0000000..8cd8d4e
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_132x130_vp9.webm b/tests/tests/media/res/raw/swirl_132x130_vp9.webm
new file mode 100644
index 0000000..4a8d79f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_132x130_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_h264.mp4 b/tests/tests/media/res/raw/swirl_136x144_h264.mp4
new file mode 100644
index 0000000..bc5fadf
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_h265.mp4 b/tests/tests/media/res/raw/swirl_136x144_h265.mp4
new file mode 100644
index 0000000..38f1fc8
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4
new file mode 100644
index 0000000..c74bd96
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_vp8.webm b/tests/tests/media/res/raw/swirl_136x144_vp8.webm
new file mode 100644
index 0000000..960d02f
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_136x144_vp9.webm b/tests/tests/media/res/raw/swirl_136x144_vp9.webm
new file mode 100644
index 0000000..5898f07
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_136x144_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_h264.mp4 b/tests/tests/media/res/raw/swirl_144x136_h264.mp4
new file mode 100644
index 0000000..962a218
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_h264.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_h265.mp4 b/tests/tests/media/res/raw/swirl_144x136_h265.mp4
new file mode 100644
index 0000000..8098621
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_h265.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4 b/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4
new file mode 100644
index 0000000..81c1db3
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_mpeg4.mp4
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_vp8.webm b/tests/tests/media/res/raw/swirl_144x136_vp8.webm
new file mode 100644
index 0000000..b050ade
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_vp8.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_144x136_vp9.webm b/tests/tests/media/res/raw/swirl_144x136_vp9.webm
new file mode 100644
index 0000000..9c0539a
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_144x136_vp9.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_176x144_h263.3gp b/tests/tests/media/res/raw/swirl_176x144_h263.3gp
new file mode 100644
index 0000000..ee51660
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_176x144_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/swirl_352x288_h263.3gp b/tests/tests/media/res/raw/swirl_352x288_h263.3gp
new file mode 100644
index 0000000..53830a9
--- /dev/null
+++ b/tests/tests/media/res/raw/swirl_352x288_h263.3gp
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
new file mode 100644
index 0000000..c8b9512
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
deleted file mode 100644
index b0cd94d..0000000
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
similarity index 91%
rename from tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
rename to tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
index 2e769e6..a5eddd3 100644
--- a/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
+++ b/tests/tests/media/res/raw/video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
new file mode 100644
index 0000000..d6f0f3b
--- /dev/null
+++ b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
deleted file mode 100644
index 10a1a5d..0000000
--- a/tests/tests/media/res/raw/video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
similarity index 76%
rename from tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
rename to tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
index b060ca6..15c34b9 100644
--- a/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
+++ b/tests/tests/media/res/raw/video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4 b/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4
new file mode 100644
index 0000000..55a83e7
--- /dev/null
+++ b/tests/tests/media/res/raw/video_480x360_mp4_h264_871kbps_30fps.mp4
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
index 576359a..c347282 100644
--- a/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
+++ b/tests/tests/media/src/android/media/cts/AdaptivePlaybackTest.java
@@ -90,7 +90,7 @@
MediaFormat.MIMETYPE_VIDEO_VP8,
"OMX.google.vp8.decoder",
R.raw.video_480x360_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz,
- R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz);
+ R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
}
public Iterable<Codec> VP9(CodecFactory factory) {
diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
index 7088728..25971e0 100644
--- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java
@@ -1308,36 +1308,163 @@
}
public static byte[] createSoundDataInByteArray(int bufferSamples, final int sampleRate,
- final double frequency) {
+ 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) * Byte.MAX_VALUE) + Byte.MAX_VALUE & 0xFF;
+ 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) {
+ 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) * Short.MAX_VALUE);
+ 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) {
+ 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));
+ 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()) {
+ return;
+ }
+ // constants for test
+ final String TEST_NAME = "testPlayStaticData";
+ final int TEST_FORMAT_ARRAY[] = { // 6 chirps repeated (TEST_LOOPS+1) times, 3 times
+ AudioFormat.ENCODING_PCM_8BIT,
+ AudioFormat.ENCODING_PCM_16BIT,
+ AudioFormat.ENCODING_PCM_FLOAT,
+ };
+ final int TEST_SR_ARRAY[] = {
+ 12055, // Note multichannel tracks will sound very short at low sample rates
+ 48000,
+ };
+ final int TEST_CONF_ARRAY[] = {
+ AudioFormat.CHANNEL_OUT_MONO, // 1.0
+ AudioFormat.CHANNEL_OUT_STEREO, // 2.0
+ AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, // 7.1
+ };
+ final int TEST_MODE = AudioTrack.MODE_STATIC;
+ final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
+ final double TEST_SWEEP = 100;
+ final int TEST_LOOPS = 1;
+
+ for (int TEST_FORMAT : TEST_FORMAT_ARRAY) {
+ double frequency = 400; // frequency changes for each test
+ for (int TEST_SR : TEST_SR_ARRAY) {
+ for (int TEST_CONF : TEST_CONF_ARRAY) {
+ // -------- initialization --------------
+ final int seconds = 1;
+ final int channelCount = Integer.bitCount(TEST_CONF);
+ final int bufferFrames = seconds * TEST_SR;
+ final int bufferSamples = bufferFrames * channelCount;
+ final int bufferSize = bufferSamples
+ * AudioFormat.getBytesPerSample(TEST_FORMAT);
+ final double testFrequency = frequency / channelCount;
+ final long MILLISECONDS_PER_SECOND = 1000;
+ AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR,
+ TEST_CONF, TEST_FORMAT, bufferSize, TEST_MODE);
+ assertEquals(TEST_NAME, track.getState(), AudioTrack.STATE_NO_STATIC_DATA);
+
+ // -------- test --------------
+
+ // test setLoopPoints and setPosition can be called here.
+ assertEquals(TEST_NAME,
+ track.setPlaybackHeadPosition(bufferFrames/2),
+ android.media.AudioTrack.SUCCESS);
+ assertEquals(TEST_NAME,
+ track.setLoopPoints(
+ 0 /*startInFrames*/, bufferFrames, 10 /*loopCount*/),
+ android.media.AudioTrack.SUCCESS);
+ // only need to write once to the static track
+ switch (TEST_FORMAT) {
+ case AudioFormat.ENCODING_PCM_8BIT: {
+ byte data[] = createSoundDataInByteArray(
+ bufferSamples, TEST_SR,
+ testFrequency, TEST_SWEEP);
+ assertEquals(TEST_NAME,
+ track.write(data, 0 /*offsetInBytes*/, data.length),
+ bufferSamples);
+ } break;
+ case AudioFormat.ENCODING_PCM_16BIT: {
+ short data[] = createSoundDataInShortArray(
+ bufferSamples, TEST_SR,
+ testFrequency, TEST_SWEEP);
+ assertEquals(TEST_NAME,
+ track.write(data, 0 /*offsetInBytes*/, data.length),
+ bufferSamples);
+ } break;
+ case AudioFormat.ENCODING_PCM_FLOAT: {
+ float data[] = createSoundDataInFloatArray(
+ bufferSamples, TEST_SR,
+ testFrequency, TEST_SWEEP);
+ assertEquals(TEST_NAME,
+ track.write(data, 0 /*offsetInBytes*/, data.length,
+ AudioTrack.WRITE_BLOCKING),
+ bufferSamples);
+ } break;
+ }
+ assertEquals(TEST_NAME, track.getState(), AudioTrack.STATE_INITIALIZED);
+ // test setLoopPoints and setPosition can be called here.
+ assertEquals(TEST_NAME,
+ track.setPlaybackHeadPosition(0 /*positionInFrames*/),
+ android.media.AudioTrack.SUCCESS);
+ assertEquals(TEST_NAME,
+ track.setLoopPoints(0 /*startInFrames*/, bufferFrames, TEST_LOOPS),
+ android.media.AudioTrack.SUCCESS);
+
+ track.play();
+ Thread.sleep(seconds * MILLISECONDS_PER_SECOND * (TEST_LOOPS + 1));
+ Thread.sleep(WAIT_MSEC);
+
+ // Check position after looping. AudioTrack.getPlaybackHeadPosition() returns
+ // the running count of frames played, not the actual static buffer position.
+ int position = track.getPlaybackHeadPosition();
+ assertEquals(TEST_NAME, position, bufferFrames * (TEST_LOOPS + 1));
+
+ track.stop();
+ Thread.sleep(WAIT_MSEC);
+ // -------- tear down --------------
+ track.release();
+ frequency += 70; // increment test tone frequency
+ }
+ }
+ }
+ }
+
public void testPlayStreamData() throws Exception {
// constants for test
final String TEST_NAME = "testPlayStreamData";
diff --git a/tests/tests/media/src/android/media/cts/CodecImage.java b/tests/tests/media/src/android/media/cts/CodecImage.java
new file mode 100644
index 0000000..60a644a
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CodecImage.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import java.nio.ByteBuffer;
+import java.lang.AutoCloseable;
+
+import android.graphics.Rect;
+
+/**
+ * <p>A single complete image buffer to use with a media source such as a
+ * {@link MediaCodec} or a
+ * {@link android.hardware.camera2.CameraDevice CameraDevice}.</p>
+ *
+ * <p>This class allows for efficient direct application access to the pixel
+ * data of the CodecImage through one or more
+ * {@link java.nio.ByteBuffer ByteBuffers}. Each buffer is encapsulated in a
+ * {@link Plane} that describes the layout of the pixel data in that plane. Due
+ * to this direct access, and unlike the {@link android.graphics.Bitmap Bitmap} class,
+ * Images are not directly usable as UI resources.</p>
+ *
+ * <p>Since Images are often directly produced or consumed by hardware
+ * components, they are a limited resource shared across the system, and should
+ * be closed as soon as they are no longer needed.</p>
+ *
+ * <p>For example, when using the {@link ImageReader} class to read out Images
+ * from various media sources, not closing old CodecImage objects will prevent the
+ * availability of new Images once
+ * {@link ImageReader#getMaxImages the maximum outstanding image count} is
+ * reached. When this happens, the function acquiring new Images will typically
+ * throw an {@link IllegalStateException}.</p>
+ *
+ * @see ImageReader
+ */
+public abstract class CodecImage implements AutoCloseable {
+ /**
+ * Get the format for this image. This format determines the number of
+ * ByteBuffers needed to represent the image, and the general layout of the
+ * pixel data in each in ByteBuffer.
+ *
+ * <p>
+ * The format is one of the values from
+ * {@link android.graphics.ImageFormat ImageFormat}. The mapping between the
+ * formats and the planes is as follows:
+ * </p>
+ *
+ * <table>
+ * <tr>
+ * <th>Format</th>
+ * <th>Plane count</th>
+ * <th>Layout details</th>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#JPEG JPEG}</td>
+ * <td>1</td>
+ * <td>Compressed data, so row and pixel strides are 0. To uncompress, use
+ * {@link android.graphics.BitmapFactory#decodeByteArray BitmapFactory#decodeByteArray}.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#YUV_420_888 YUV_420_888}</td>
+ * <td>3</td>
+ * <td>A luminance plane followed by the Cb and Cr chroma planes.
+ * The chroma planes have half the width and height of the luminance
+ * plane (4:2:0 subsampling). Each pixel sample in each plane has 8 bits.
+ * Each plane has its own row stride and pixel stride.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link android.graphics.ImageFormat#RAW_SENSOR RAW_SENSOR}</td>
+ * <td>1</td>
+ * <td>A single plane of raw sensor image data, with 16 bits per color
+ * sample. The details of the layout need to be queried from the source of
+ * the raw sensor data, such as
+ * {@link android.hardware.camera2.CameraDevice CameraDevice}.
+ * </td>
+ * </tr>
+ * </table>
+ *
+ * @see android.graphics.ImageFormat
+ */
+ public abstract int getFormat();
+
+ /**
+ * The width of the image in pixels. For formats where some color channels
+ * are subsampled, this is the width of the largest-resolution plane.
+ */
+ public abstract int getWidth();
+
+ /**
+ * The height of the image in pixels. For formats where some color channels
+ * are subsampled, this is the height of the largest-resolution plane.
+ */
+ public abstract int getHeight();
+
+ /**
+ * Get the timestamp associated with this frame.
+ * <p>
+ * The timestamp is measured in nanoseconds, and is monotonically
+ * increasing. However, the zero point and whether the timestamp can be
+ * compared against other sources of time or images depend on the source of
+ * this image.
+ * </p>
+ */
+ public abstract long getTimestamp();
+
+ private Rect mCropRect;
+
+ /**
+ * Get the crop rectangle associated with this frame.
+ * <p>
+ * The crop rectangle specifies the region of valid pixels in the image,
+ * using coordinates in the largest-resolution plane.
+ */
+ public Rect getCropRect() {
+ if (mCropRect == null) {
+ return new Rect(0, 0, getWidth(), getHeight());
+ } else {
+ return new Rect(mCropRect); // return a copy
+ }
+ }
+
+ /**
+ * Set the crop rectangle associated with this frame.
+ * <p>
+ * The crop rectangle specifies the region of valid pixels in the image,
+ * using coordinates in the largest-resolution plane.
+ */
+ public void setCropRect(Rect cropRect) {
+ if (cropRect != null) {
+ cropRect = new Rect(cropRect); // make a copy
+ cropRect.intersect(0, 0, getWidth(), getHeight());
+ }
+ mCropRect = cropRect;
+ }
+
+ /**
+ * Get the array of pixel planes for this CodecImage. The number of planes is
+ * determined by the format of the CodecImage.
+ */
+ public abstract Plane[] getPlanes();
+
+ /**
+ * Free up this frame for reuse.
+ * <p>
+ * After calling this method, calling any methods on this {@code CodecImage} will
+ * result in an {@link IllegalStateException}, and attempting to read from
+ * {@link ByteBuffer ByteBuffers} returned by an earlier
+ * {@link Plane#getBuffer} call will have undefined behavior.
+ * </p>
+ */
+ @Override
+ public abstract void close();
+
+ /**
+ * <p>A single color plane of image data.</p>
+ *
+ * <p>The number and meaning of the planes in an CodecImage are determined by the
+ * format of the CodecImage.</p>
+ *
+ * <p>Once the CodecImage has been closed, any access to the the plane's
+ * ByteBuffer will fail.</p>
+ *
+ * @see #getFormat
+ */
+ public static abstract class Plane {
+ /**
+ * <p>The row stride for this color plane, in bytes.</p>
+ *
+ * <p>This is the distance between the start of two consecutive rows of
+ * pixels in the image. The row stride is always greater than 0.</p>
+ */
+ public abstract int getRowStride();
+ /**
+ * <p>The distance between adjacent pixel samples, in bytes.</p>
+ *
+ * <p>This is the distance between two consecutive pixel values in a row
+ * of pixels. It may be larger than the size of a single pixel to
+ * account for interleaved image data or padded formats.
+ * The pixel stride is always greater than 0.</p>
+ */
+ public abstract int getPixelStride();
+ /**
+ * <p>Get a direct {@link java.nio.ByteBuffer ByteBuffer}
+ * containing the frame data.</p>
+ *
+ * <p>In particular, the buffer returned will always have
+ * {@link java.nio.ByteBuffer#isDirect isDirect} return {@code true}, so
+ * the underlying data could be mapped as a pointer in JNI without doing
+ * any copies with {@code GetDirectBufferAddress}.</p>
+ *
+ * @return the byte buffer containing the image data for this plane.
+ */
+ public abstract ByteBuffer getBuffer();
+ }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/CodecState.java b/tests/tests/media/src/android/media/cts/CodecState.java
index cd6b68f..db10cd1 100644
--- a/tests/tests/media/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/src/android/media/cts/CodecState.java
@@ -33,6 +33,9 @@
private boolean mSawInputEOS, mSawOutputEOS;
private boolean mLimitQueueDepth;
+ private boolean mTunneled;
+ private boolean mIsAudio;
+ private int mAudioSessionId;
private ByteBuffer[] mCodecInputBuffers;
private ByteBuffer[] mCodecOutputBuffers;
private int mTrackIndex;
@@ -40,8 +43,9 @@
private LinkedList<Integer> mAvailableOutputBufferIndices;
private LinkedList<MediaCodec.BufferInfo> mAvailableOutputBufferInfos;
private long mPresentationTimeUs;
+ private long mSampleBaseTimeUs;
private MediaCodec mCodec;
- private MediaCodecCencPlayer mMediaCodecPlayer;
+ private MediaTimeProvider mMediaTimeProvider;
private MediaExtractor mExtractor;
private MediaFormat mFormat;
private MediaFormat mOutputFormat;
@@ -51,19 +55,23 @@
* Manages audio and video playback using MediaCodec and AudioTrack.
*/
public CodecState(
- MediaCodecCencPlayer mediaCodecPlayer,
+ MediaTimeProvider mediaTimeProvider,
MediaExtractor extractor,
int trackIndex,
MediaFormat format,
MediaCodec codec,
- boolean limitQueueDepth) {
-
- mMediaCodecPlayer = mediaCodecPlayer;
+ boolean limitQueueDepth,
+ boolean tunneled,
+ int audioSessionId) {
+ mMediaTimeProvider = mediaTimeProvider;
mExtractor = extractor;
mTrackIndex = trackIndex;
mFormat = format;
mSawInputEOS = mSawOutputEOS = false;
mLimitQueueDepth = limitQueueDepth;
+ mTunneled = tunneled;
+ mAudioSessionId = audioSessionId;
+ mSampleBaseTimeUs = -1;
mCodec = codec;
@@ -72,6 +80,10 @@
mAvailableOutputBufferInfos = new LinkedList<MediaCodec.BufferInfo>();
mPresentationTimeUs = 0;
+
+ String mime = mFormat.getString(MediaFormat.KEY_MIME);
+ Log.d(TAG, "CodecState::onOutputFormatChanged " + mime);
+ mIsAudio = mime.startsWith("audio/");
}
public void release() {
@@ -100,7 +112,9 @@
public void start() {
mCodec.start();
mCodecInputBuffers = mCodec.getInputBuffers();
- mCodecOutputBuffers = mCodec.getOutputBuffers();
+ if (!mTunneled || mIsAudio) {
+ mCodecOutputBuffers = mCodec.getOutputBuffers();
+ }
if (mAudioTrack != null) {
mAudioTrack.play();
@@ -119,15 +133,17 @@
public void flush() {
mAvailableInputBufferIndices.clear();
- mAvailableOutputBufferIndices.clear();
- mAvailableOutputBufferInfos.clear();
+ if (!mTunneled || mIsAudio) {
+ mAvailableOutputBufferIndices.clear();
+ mAvailableOutputBufferInfos.clear();
+ }
mSawInputEOS = false;
mSawOutputEOS = false;
if (mAudioTrack != null
- && mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_STOPPED) {
- mAudioTrack.play();
+ && mAudioTrack.getPlayState() != AudioTrack.PLAYSTATE_PLAYING) {
+ mAudioTrack.flush();
}
mCodec.flush();
@@ -153,20 +169,22 @@
while (feedInputBuffer()) {
}
- MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
- int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
+ if (mIsAudio || !mTunneled) {
+ MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
+ int indexOutput = mCodec.dequeueOutputBuffer(info, 0 /* timeoutUs */);
- if (indexOutput == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
- mOutputFormat = mCodec.getOutputFormat();
- onOutputFormatChanged();
- } else if (indexOutput == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
- mCodecOutputBuffers = mCodec.getOutputBuffers();
- } else if (indexOutput != MediaCodec.INFO_TRY_AGAIN_LATER) {
- mAvailableOutputBufferIndices.add(indexOutput);
- mAvailableOutputBufferInfos.add(info);
- }
+ if (indexOutput == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
+ mOutputFormat = mCodec.getOutputFormat();
+ onOutputFormatChanged();
+ } else if (indexOutput == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
+ mCodecOutputBuffers = mCodec.getOutputBuffers();
+ } else if (indexOutput != MediaCodec.INFO_TRY_AGAIN_LATER) {
+ mAvailableOutputBufferIndices.add(indexOutput);
+ mAvailableOutputBufferInfos.add(info);
+ }
- while (drainOutputBuffer()) {
+ while (drainOutputBuffer()) {
+ }
}
}
@@ -200,9 +218,24 @@
Log.d(TAG, "sampleSize: " + sampleSize + " trackIndex:" + trackIndex +
" sampleTime:" + sampleTime + " sampleFlags:" + sampleFlags);
mSawInputEOS = true;
+ // FIX-ME: in tunneled mode we currently use input EOS as output EOS indicator
+ // we should stream duration
+ if (mTunneled && !mIsAudio) {
+ mSawOutputEOS = true;
+ }
return false;
}
+ if (mTunneled && !mIsAudio) {
+ if (mSampleBaseTimeUs == -1) {
+ mSampleBaseTimeUs = sampleTime;
+ }
+ sampleTime -= mSampleBaseTimeUs;
+ // FIX-ME: in tunneled mode we currently use input buffer time
+ // as video presentation time. This is not accurate and should be fixed
+ mPresentationTimeUs = sampleTime;
+ }
+
if ((sampleFlags & MediaExtractor.SAMPLE_FLAG_ENCRYPTED) != 0) {
MediaCodec.CryptoInfo info = new MediaCodec.CryptoInfo();
mExtractor.getSampleCryptoInfo(info);
@@ -222,6 +255,9 @@
Log.d(TAG, "saw input EOS on track " + mTrackIndex);
mSawInputEOS = true;
+ if (mTunneled && !mIsAudio) {
+ mSawOutputEOS = true;
+ }
mCodec.queueInputBuffer(
index, 0 /* offset */, 0 /* sampleSize */,
@@ -255,7 +291,8 @@
sampleRate < 8000 || sampleRate > 128000) {
return;
}
- mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount);
+ mAudioTrack = new NonBlockingAudioTrack(sampleRate, channelCount,
+ mTunneled, mAudioSessionId);
mAudioTrack.play();
}
@@ -285,21 +322,19 @@
}
long realTimeUs =
- mMediaCodecPlayer.getRealTimeUsForMediaTime(info.presentationTimeUs);
+ mMediaTimeProvider.getRealTimeUsForMediaTime(info.presentationTimeUs);
- long nowUs = mMediaCodecPlayer.getNowUs();
+ long nowUs = mMediaTimeProvider.getNowUs();
long lateUs = nowUs - realTimeUs;
if (mAudioTrack != null) {
ByteBuffer buffer = mCodecOutputBuffers[index];
buffer.clear();
- buffer.position(0 /* offset */);
+ ByteBuffer audioBuffer = ByteBuffer.allocate(buffer.remaining());
+ audioBuffer.put(buffer);
- byte[] audioCopy = new byte[info.size];
- buffer.get(audioCopy, 0, info.size);
-
- mAudioTrack.write(audioCopy, info.size);
+ mAudioTrack.write(audioBuffer, info.size, info.presentationTimeUs*1000);
mCodec.releaseOutputBuffer(index, false /* render */);
diff --git a/tests/tests/media/src/android/media/cts/CodecUtils.java b/tests/tests/media/src/android/media/cts/CodecUtils.java
new file mode 100644
index 0000000..ef20e67
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/CodecUtils.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import android.graphics.Rect;
+import android.media.cts.CodecImage;
+import android.media.Image;
+import android.util.Log;
+
+import java.nio.ByteBuffer;
+
+public class CodecUtils {
+ private static final String TAG = "CodecUtils";
+
+ /** Load jni on initialization */
+ static {
+ Log.i(TAG, "before loadlibrary");
+ System.loadLibrary("ctsmediacodec_jni");
+ Log.i(TAG, "after loadlibrary");
+ }
+
+ private static class ImageWrapper extends CodecImage {
+ private final Image mImage;
+ private final Plane[] mPlanes;
+
+ private ImageWrapper(Image image) {
+ mImage = image;
+ Image.Plane[] planes = mImage.getPlanes();
+
+ mPlanes = new Plane[planes.length];
+ for (int i = 0; i < planes.length; i++) {
+ mPlanes[i] = new PlaneWrapper(planes[i]);
+ }
+ }
+
+ public static ImageWrapper createFromImage(Image image) {
+ return new ImageWrapper(image);
+ }
+
+ @Override
+ public int getFormat() {
+ return mImage.getFormat();
+ }
+
+ @Override
+ public int getWidth() {
+ return mImage.getWidth();
+ }
+
+ @Override
+ public int getHeight() {
+ return mImage.getHeight();
+ }
+
+ @Override
+ public long getTimestamp() {
+ return mImage.getTimestamp();
+ }
+
+ @Override
+ public Plane[] getPlanes() {
+ return mPlanes;
+ }
+
+ @Override
+ public void close() {
+ mImage.close();
+ }
+
+ private static class PlaneWrapper extends CodecImage.Plane {
+ private final Image.Plane mPlane;
+
+ PlaneWrapper(Image.Plane plane) {
+ mPlane = plane;
+ }
+
+ @Override
+ public int getRowStride() {
+ return mPlane.getRowStride();
+ }
+
+ @Override
+ public int getPixelStride() {
+ return mPlane.getPixelStride();
+ }
+
+ @Override
+ public ByteBuffer getBuffer() {
+ return mPlane.getBuffer();
+ }
+ }
+ }
+
+
+ public native static int getImageChecksum(CodecImage image);
+ public native static void copyFlexYUVImage(CodecImage target, CodecImage source);
+
+ public static void copyFlexYUVImage(Image target, CodecImage source) {
+ copyFlexYUVImage(ImageWrapper.createFromImage(target), source);
+ }
+ public static void copyFlexYUVImage(Image target, Image source) {
+ copyFlexYUVImage(
+ ImageWrapper.createFromImage(target),
+ ImageWrapper.createFromImage(source));
+ }
+
+ public native static void fillImageRectWithYUV(
+ CodecImage image, Rect area, int y, int u, int v);
+
+ public static void fillImageRectWithYUV(Image image, Rect area, int y, int u, int v) {
+ fillImageRectWithYUV(ImageWrapper.createFromImage(image), area, y, u, v);
+ }
+
+ public native static long[] getRawStats(CodecImage image, Rect area);
+
+ public static long[] getRawStats(Image image, Rect area) {
+ return getRawStats(ImageWrapper.createFromImage(image), area);
+ }
+
+ public native static float[] getYUVStats(CodecImage image, Rect area);
+
+ public static float[] getYUVStats(Image image, Rect area) {
+ return getYUVStats(ImageWrapper.createFromImage(image), area);
+ }
+
+ public native static float[] Raw2YUVStats(long[] rawStats);
+}
+
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
old mode 100644
new mode 100755
index c1855b7..1c9e4b9
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -25,12 +25,16 @@
import android.cts.util.MediaUtils;
import android.graphics.ImageFormat;
import android.media.Image;
+import android.media.AudioManager;
import android.media.MediaCodec;
+import android.media.MediaCodecList;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;
+import android.net.Uri;
import java.io.BufferedInputStream;
import java.io.IOException;
@@ -40,6 +44,7 @@
import java.util.Arrays;
import java.util.List;
import java.util.zip.CRC32;
+import java.util.concurrent.TimeUnit;
public class DecoderTest extends MediaPlayerTestBase {
private static final String TAG = "DecoderTest";
@@ -57,6 +62,24 @@
private Resources mResources;
short[] mMasterBuffer;
+ private MediaCodecTunneledPlayer mMediaCodecPlayer;
+ private static final int SLEEP_TIME_MS = 1000;
+ private static final long PLAY_TIME_MS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES);
+ private static final Uri AUDIO_URL = Uri.parse(
+ "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+ + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ + "&sparams=ip,ipbits,expire,id,itag,source"
+ + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
+ + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
+ + "&key=ik0&user=android-device-test"); // H.264 Base + AAC
+ private static final Uri VIDEO_URL = Uri.parse(
+ "http://redirector.c.youtube.com/videoplayback?id=c80658495af60617"
+ + "&itag=18&source=youtube&ip=0.0.0.0&ipbits=0&expire=19000000000"
+ + "&sparams=ip,ipbits,expire,id,itag,source"
+ + "&signature=46A04ED550CA83B79B60060BA80C79FDA5853D26."
+ + "49582D382B4A9AFAA163DED38D2AE531D85603C0"
+ + "&key=ik0&user=android-device-test"); // H.264 Base + AAC
+
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -907,6 +930,12 @@
}
}
+ public void testH264SecureDecode30fps1280x720Tv() throws Exception {
+ if (checkTv()) {
+ verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30);
+ }
+ }
+
public void testH264Decode30fps1280x720() throws Exception {
testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
@@ -917,6 +946,12 @@
}
}
+ public void testH264SecureDecode60fps1280x720Tv() throws Exception {
+ if (checkTv()) {
+ verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 60);
+ }
+ }
+
public void testH264Decode60fps1280x720() throws Exception {
testDecode(R.raw.video_1280x720_mp4_h264_8192kbps_60fps_aac_stereo_128kbps_44100hz, 596);
}
@@ -927,6 +962,12 @@
}
}
+ public void testH264SecureDecode30fps1920x1080Tv() throws Exception {
+ if (checkTv()) {
+ verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30);
+ }
+ }
+
public void testH264Decode30fps1920x1080() throws Exception {
testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_30fps_aac_stereo_128kbps_44100hz, 299);
}
@@ -937,12 +978,18 @@
}
}
+ public void testH264SecureDecode60fps1920x1080Tv() throws Exception {
+ if (checkTv()) {
+ verifySecureVideoDecodeSupport(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 60);
+ }
+ }
+
public void testH264Decode60fps1920x1080() throws Exception {
testDecode(R.raw.video_1920x1080_mp4_h264_20480kbps_60fps_aac_stereo_128kbps_44100hz, 596);
}
public void testVP8Decode320x240() throws Exception {
- testDecode(R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
+ testDecode(R.raw.video_320x240_webm_vp8_800kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
}
public void testVP8Decode640x360() throws Exception {
@@ -987,7 +1034,7 @@
}
public void testVP8Decode60fps1920x1080() throws Exception {
- testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_48000hz,
+ testDecode(R.raw.video_1920x1080_webm_vp8_20480kbps_60fps_vorbis_stereo_128kbps_44100hz,
249);
}
@@ -1006,7 +1053,7 @@
}
public void testVP9Decode30fps1280x720() throws Exception {
- testDecode(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
+ testDecode(R.raw.video_1280x720_webm_vp9_4096kbps_30fps_vorbis_stereo_128kbps_44100hz, 249);
}
public void testVP9Decode30fps1920x1080() throws Exception {
@@ -1074,7 +1121,7 @@
public void testCodecEarlyEOSHEVC() throws Exception {
testCodecEarlyEOS(
- R.raw.video_1280x720_mp4_hevc_1150kbps_30fps_aac_stereo_128kbps_48000hz,
+ R.raw.video_480x360_mp4_hevc_650kbps_30fps_aac_stereo_128kbps_48000hz,
120 /* eosframe */);
}
@@ -1196,6 +1243,23 @@
assertEquals("different number of frames when using flushed codec", frames1, frames3);
}
+ private static void verifySecureVideoDecodeSupport(String mime, int width, int height, float rate) {
+ MediaFormat baseFormat = new MediaFormat();
+ baseFormat.setString(MediaFormat.KEY_MIME, mime);
+ baseFormat.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
+
+ MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+ format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, true);
+ format.setFloat(MediaFormat.KEY_FRAME_RATE, rate);
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ if (mcl.findDecoderForFormat(baseFormat) == null) {
+ MediaUtils.skipTest("no secure decoder for " + mime);
+ return;
+ }
+ assertNotNull("no decoder for " + format, mcl.findDecoderForFormat(format));
+ }
+
private static MediaCodec createDecoder(String mime) {
try {
if (false) {
@@ -1220,6 +1284,10 @@
}
}
+ private static MediaCodec createDecoder(MediaFormat format) {
+ return MediaUtils.getDecoder(format);
+ }
+
// for video
private int countFrames(int video, int resetMode, int eosframe, Surface s)
throws Exception {
@@ -1332,7 +1400,7 @@
ByteBuffer[] codecInputBuffers;
ByteBuffer[] codecOutputBuffers;
- MediaCodec codec = createDecoder(mime);
+ MediaCodec codec = createDecoder(format);
Log.i("@@@@", "using codec: " + codec.getName());
codec.configure(format, surface, null /* crypto */, 0 /* flags */);
codec.start();
@@ -1590,12 +1658,12 @@
// Log.d(TAG, "color format: " + String.format("0x%08x", colorFormat));
switch (colorFormat) {
// these are the formats we know how to handle for this test
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
- case MediaCodecInfo.CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
- case MediaCodecInfo.CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
+ case CodecCapabilities.COLOR_FormatYUV420Planar:
+ case CodecCapabilities.COLOR_FormatYUV420PackedPlanar:
+ case CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
+ case CodecCapabilities.COLOR_FormatYUV420PackedSemiPlanar:
+ case CodecCapabilities.COLOR_TI_FormatYUV420PackedSemiPlanar:
+ case CodecCapabilities.COLOR_QCOM_FormatYUV420SemiPlanar:
/*
* TODO: Check newer formats or ignore.
* OMX_SEC_COLOR_FormatNV12Tiled = 0x7FC00002
@@ -1850,5 +1918,81 @@
return maxvalue;
}
+ /* return true if a particular video feature is supported for the given mimetype */
+ private boolean isVideoFeatureSupported(String mimeType, String feature) {
+ MediaFormat format = MediaFormat.createVideoFormat( mimeType, 1920, 1080);
+ format.setFeatureEnabled(feature, true);
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ String codecName = mcl.findDecoderForFormat(format);
+ return (codecName == null) ? false : true;
+ }
+
+
+ /**
+ * Test tunneled video playback mode if supported
+ */
+ public void testTunneledVideoPlayback() throws Exception {
+ if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
+ CodecCapabilities.FEATURE_TunneledPlayback)) {
+ MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
+ return;
+ }
+
+ AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+ mMediaCodecPlayer = new MediaCodecTunneledPlayer(
+ getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+ mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
+ mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+ assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+ assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+ // starts video playback
+ mMediaCodecPlayer.startThread();
+
+ long timeOut = System.currentTimeMillis() + 4*PLAY_TIME_MS;
+ while (timeOut > System.currentTimeMillis() && !mMediaCodecPlayer.isEnded()) {
+ Thread.sleep(SLEEP_TIME_MS);
+ if (mMediaCodecPlayer.getCurrentPosition() >= mMediaCodecPlayer.getDuration() ) {
+ Log.d(TAG, "testTunneledVideoPlayback -- current pos = " +
+ mMediaCodecPlayer.getCurrentPosition() +
+ ">= duration = " + mMediaCodecPlayer.getDuration());
+ break;
+ }
+ }
+ assertTrue("Tunneled video playback timeout exceeded!",
+ timeOut > System.currentTimeMillis());
+
+ Log.d(TAG, "playVideo player.reset()");
+ mMediaCodecPlayer.reset();
+ }
+
+ /**
+ * Test tunneled video playback flush if supported
+ */
+ public void testTunneledVideoFlush() throws Exception {
+ if (!isVideoFeatureSupported(MediaFormat.MIMETYPE_VIDEO_AVC,
+ CodecCapabilities.FEATURE_TunneledPlayback)) {
+ MediaUtils.skipTest(TAG, "No tunneled video playback codec found!");
+ return;
+ }
+
+ AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
+ mMediaCodecPlayer = new MediaCodecTunneledPlayer(
+ getActivity().getSurfaceHolder(), true, am.generateAudioSessionId());
+
+ mMediaCodecPlayer.setAudioDataSource(AUDIO_URL, null);
+ mMediaCodecPlayer.setVideoDataSource(VIDEO_URL, null);
+ assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
+ assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+
+ // starts video playback
+ mMediaCodecPlayer.startThread();
+ Thread.sleep(SLEEP_TIME_MS);
+ mMediaCodecPlayer.pause();
+ mMediaCodecPlayer.flush();
+ Thread.sleep(SLEEP_TIME_MS);
+ mMediaCodecPlayer.reset();
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
index 20b3c9c..5a3acd3 100644
--- a/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
+++ b/tests/tests/media/src/android/media/cts/ExtractDecodeEditEncodeMuxTest.java
@@ -20,6 +20,8 @@
import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
import android.media.MediaCodecList;
import android.media.MediaExtractor;
import android.media.MediaFormat;
@@ -29,10 +31,6 @@
import android.util.Log;
import android.view.Surface;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecInfo.CodecCapabilities;
-import android.media.MediaCodecInfo.CodecProfileLevel;
-
import com.android.cts.media.R;
import java.io.File;
@@ -115,28 +113,28 @@
private String mOutputFile;
public void testExtractDecodeEditEncodeMuxQCIF() throws Throwable {
- if(!setSize(176, 144)) return;
+ setSize(176, 144);
setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
setCopyVideo();
TestWrapper.runTest(this);
}
public void testExtractDecodeEditEncodeMuxQVGA() throws Throwable {
- if(!setSize(320, 240)) return;
+ setSize(320, 240);
setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
setCopyVideo();
TestWrapper.runTest(this);
}
public void testExtractDecodeEditEncodeMux720p() throws Throwable {
- if(!setSize(1280, 720)) return;
+ setSize(1280, 720);
setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
setCopyVideo();
TestWrapper.runTest(this);
}
public void testExtractDecodeEditEncodeMuxAudio() throws Throwable {
- if(!setSize(1280, 720)) return;
+ setSize(1280, 720);
setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
setCopyAudio();
setVerifyAudioFormat();
@@ -144,7 +142,7 @@
}
public void testExtractDecodeEditEncodeMuxAudioVideo() throws Throwable {
- if(!setSize(1280, 720)) return;
+ setSize(1280, 720);
setSource(R.raw.video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz);
setCopyAudio();
setCopyVideo();
@@ -207,20 +205,14 @@
}
/**
- * Sets the desired frame size and returns whether the given resolution is
- * supported.
- *
- * <p>If decoding/encoding using AVC as the codec, checks that the resolution
- * is supported. For other codecs, always return {@code true}.
- */
- private boolean setSize(int width, int height) {
+ * Sets the desired frame size.
+ */
+ private void setSize(int width, int height) {
if ((width % 16) != 0 || (height % 16) != 0) {
Log.w(TAG, "WARNING: width or height not multiple of 16");
}
mWidth = width;
mHeight = height;
-
- return isAvcSupportedSize(width, height);
}
/**
diff --git a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
index 5f326ee..cc28b86 100644
--- a/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/ImageReaderDecoderTest.java
@@ -21,14 +21,19 @@
import android.content.Context;
import android.content.res.AssetFileDescriptor;
import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
import android.cts.util.MediaUtils;
+import android.graphics.Rect;
import android.graphics.ImageFormat;
+import android.media.cts.CodecUtils;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
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.MediaExtractor;
import android.media.MediaFormat;
import android.os.Handler;
@@ -38,9 +43,15 @@
import android.util.Log;
import android.view.Surface;
+import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible;
+
+import java.io.File;
import java.io.FileOutputStream;
+import java.io.InputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.ArrayList;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -62,12 +73,16 @@
private static final long WAIT_FOR_IMAGE_TIMEOUT_MS = 1000;
private static final String DEBUG_FILE_NAME_BASE = "/sdcard/";
private static final int NUM_FRAME_DECODED = 100;
- private static final int MAX_NUM_IMAGES = 3;
+ // video decoders only support a single outstanding image with the consumer
+ private static final int MAX_NUM_IMAGES = 1;
+ private static final float COLOR_STDEV_ALLOWANCE = 5f;
+ private static final float COLOR_DELTA_ALLOWANCE = 5f;
+
+ private final static int MODE_IMAGEREADER = 0;
+ private final static int MODE_IMAGE = 1;
private Resources mResources;
private MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo();
- private ByteBuffer[] mInputBuffers;
- private ByteBuffer[] mOutputBuffers;
private ImageReader mReader;
private Surface mReaderSurface;
private HandlerThread mHandlerThread;
@@ -95,31 +110,322 @@
mHandler = null;
}
- /**
- * Test ImageReader with 480x360 hw AVC decoding for flexible yuv format, which is mandatory
- * to be supported by hw decoder.
- */
- public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
- try {
- int format = ImageFormat.YUV_420_888;
- videoDecodeToSurface(
- R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
- /* width */480, /* height */ 360, format, /* useHw */ true);
- } finally {
- closeImageReader();
+ static class MediaAsset {
+ public MediaAsset(int resource, int width, int height) {
+ mResource = resource;
+ mWidth = width;
+ mHeight = height;
+ }
+
+ public int getWidth() {
+ return mWidth;
+ }
+
+ public int getHeight() {
+ return mHeight;
+ }
+
+ public int getResource() {
+ return mResource;
+ }
+
+ private final int mResource;
+ private final int mWidth;
+ private final int mHeight;
+ }
+
+ static class MediaAssets {
+ public MediaAssets(String mime, MediaAsset... assets) {
+ mMime = mime;
+ mAssets = assets;
+ }
+
+ public String getMime() {
+ return mMime;
+ }
+
+ public MediaAsset[] getAssets() {
+ return mAssets;
+ }
+
+ private final String mMime;
+ private final MediaAsset[] mAssets;
+ }
+
+ private static MediaAssets H263_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_H263,
+ new MediaAsset(R.raw.swirl_176x144_h263, 176, 144),
+ new MediaAsset(R.raw.swirl_352x288_h263, 352, 288),
+ new MediaAsset(R.raw.swirl_128x96_h263, 128, 96));
+
+ private static MediaAssets MPEG4_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_MPEG4,
+ new MediaAsset(R.raw.swirl_128x128_mpeg4, 128, 128),
+ new MediaAsset(R.raw.swirl_144x136_mpeg4, 144, 136),
+ new MediaAsset(R.raw.swirl_136x144_mpeg4, 136, 144),
+ new MediaAsset(R.raw.swirl_132x130_mpeg4, 132, 130),
+ new MediaAsset(R.raw.swirl_130x132_mpeg4, 130, 132));
+
+ private static MediaAssets H264_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_AVC,
+ new MediaAsset(R.raw.swirl_128x128_h264, 128, 128),
+ new MediaAsset(R.raw.swirl_144x136_h264, 144, 136),
+ new MediaAsset(R.raw.swirl_136x144_h264, 136, 144),
+ new MediaAsset(R.raw.swirl_132x130_h264, 132, 130),
+ new MediaAsset(R.raw.swirl_130x132_h264, 130, 132));
+
+ private static MediaAssets H265_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_HEVC,
+ new MediaAsset(R.raw.swirl_128x128_h265, 128, 128),
+ new MediaAsset(R.raw.swirl_144x136_h265, 144, 136),
+ new MediaAsset(R.raw.swirl_136x144_h265, 136, 144),
+ new MediaAsset(R.raw.swirl_132x130_h265, 132, 130),
+ new MediaAsset(R.raw.swirl_130x132_h265, 130, 132));
+
+ private static MediaAssets VP8_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_VP8,
+ new MediaAsset(R.raw.swirl_128x128_vp8, 128, 128),
+ new MediaAsset(R.raw.swirl_144x136_vp8, 144, 136),
+ new MediaAsset(R.raw.swirl_136x144_vp8, 136, 144),
+ new MediaAsset(R.raw.swirl_132x130_vp8, 132, 130),
+ new MediaAsset(R.raw.swirl_130x132_vp8, 130, 132));
+
+ private static MediaAssets VP9_ASSETS = new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_VP9,
+ new MediaAsset(R.raw.swirl_128x128_vp9, 128, 128),
+ new MediaAsset(R.raw.swirl_144x136_vp9, 144, 136),
+ new MediaAsset(R.raw.swirl_136x144_vp9, 136, 144),
+ new MediaAsset(R.raw.swirl_132x130_vp9, 132, 130),
+ new MediaAsset(R.raw.swirl_130x132_vp9, 130, 132));
+
+ static final float SWIRL_FPS = 12.f;
+
+ class Decoder {
+ final private String mName;
+ final private String mMime;
+ final private VideoCapabilities mCaps;
+ final private ArrayList<MediaAsset> mAssets;
+
+ boolean isFlexibleFormatSupported(CodecCapabilities caps) {
+ for (int c : caps.colorFormats) {
+ if (c == COLOR_FormatYUV420Flexible) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ Decoder(String name, MediaAssets assets, CodecCapabilities caps) {
+ mName = name;
+ mMime = assets.getMime();
+ mCaps = caps.getVideoCapabilities();
+ mAssets = new ArrayList<MediaAsset>();
+
+ for (MediaAsset asset : assets.getAssets()) {
+ if (mCaps.areSizeAndRateSupported(asset.getWidth(), asset.getHeight(), SWIRL_FPS)
+ && isFlexibleFormatSupported(caps)) {
+ mAssets.add(asset);
+ }
+ }
+ }
+
+ public boolean videoDecode(int mode, boolean checkSwirl) {
+ boolean skipped = true;
+ for (MediaAsset asset: mAssets) {
+ // TODO: loop over all supported image formats
+ int imageFormat = ImageFormat.YUV_420_888;
+ int colorFormat = COLOR_FormatYUV420Flexible;
+ videoDecode(asset, imageFormat, colorFormat, mode, checkSwirl);
+ skipped = false;
+ }
+ return skipped;
+ }
+
+ private void videoDecode(
+ MediaAsset asset, int imageFormat, int colorFormat, int mode, boolean checkSwirl) {
+ int video = asset.getResource();
+ int width = asset.getWidth();
+ int height = asset.getHeight();
+
+ if (DEBUG) Log.d(TAG, "videoDecode " + mName + " " + width + "x" + height);
+
+ MediaCodec decoder = null;
+ AssetFileDescriptor vidFD = null;
+
+ MediaExtractor extractor = null;
+ File tmpFile = null;
+ InputStream is = null;
+ FileOutputStream os = null;
+ MediaFormat mediaFormat = null;
+ try {
+ extractor = new MediaExtractor();
+
+ try {
+ vidFD = mResources.openRawResourceFd(video);
+ extractor.setDataSource(
+ vidFD.getFileDescriptor(), vidFD.getStartOffset(), vidFD.getLength());
+ } catch (NotFoundException e) {
+ // resource is compressed, uncompress locally
+ String tmpName = "tempStream";
+ tmpFile = File.createTempFile(tmpName, null, mContext.getCacheDir());
+ is = mResources.openRawResource(video);
+ os = new FileOutputStream(tmpFile);
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = is.read(buf, 0, buf.length)) > 0) {
+ os.write(buf, 0, len);
+ }
+ os.close();
+ is.close();
+
+ extractor.setDataSource(tmpFile.getAbsolutePath());
+ }
+
+ mediaFormat = extractor.getTrackFormat(0);
+ mediaFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+
+ // Create decoder
+ decoder = MediaCodec.createByCodecName(mName);
+ assertNotNull("couldn't create decoder" + mName, decoder);
+
+ decodeFramesToImage(
+ decoder, extractor, mediaFormat,
+ width, height, imageFormat, mode, checkSwirl);
+
+ decoder.stop();
+ if (vidFD != null) {
+ vidFD.close();
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("while " + mName + " decoding "
+ + mResources.getResourceEntryName(video) + ": " + mediaFormat, e);
+ } finally {
+ if (decoder != null) {
+ decoder.release();
+ }
+ if (extractor != null) {
+ extractor.release();
+ }
+ if (tmpFile != null) {
+ tmpFile.delete();
+ }
+ }
}
}
+ private Decoder[] decoders(MediaAssets assets, boolean goog) {
+ String mime = assets.getMime();
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ ArrayList<Decoder> result = new ArrayList<Decoder>();
+
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder()
+ || info.getName().toLowerCase().startsWith("omx.google.") != goog) {
+ continue;
+ }
+ CodecCapabilities caps = null;
+ try {
+ caps = info.getCapabilitiesForType(mime);
+ } catch (IllegalArgumentException e) { // mime is not supported
+ continue;
+ }
+ assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+ result.add(new Decoder(info.getName(), assets, caps));
+ }
+ return result.toArray(new Decoder[result.size()]);
+ }
+
+ private Decoder[] goog(MediaAssets assets) {
+ return decoders(assets, true /* goog */);
+ }
+
+ private Decoder[] other(MediaAssets assets) {
+ return decoders(assets, false /* goog */);
+ }
+
+ private Decoder[] googH265() { return goog(H265_ASSETS); }
+ private Decoder[] googH264() { return goog(H264_ASSETS); }
+ private Decoder[] googH263() { return goog(H263_ASSETS); }
+ private Decoder[] googMpeg4() { return goog(MPEG4_ASSETS); }
+ private Decoder[] googVP8() { return goog(VP8_ASSETS); }
+ private Decoder[] googVP9() { return goog(VP9_ASSETS); }
+
+ private Decoder[] otherH265() { return other(H265_ASSETS); }
+ private Decoder[] otherH264() { return other(H264_ASSETS); }
+ private Decoder[] otherH263() { return other(H263_ASSETS); }
+ private Decoder[] otherMpeg4() { return other(MPEG4_ASSETS); }
+ private Decoder[] otherVP8() { return other(VP8_ASSETS); }
+ private Decoder[] otherVP9() { return other(VP9_ASSETS); }
+
+ public void testGoogH265Image() { swirlTest(googH265(), MODE_IMAGE); }
+ public void testGoogH264Image() { swirlTest(googH264(), MODE_IMAGE); }
+ public void testGoogH263Image() { swirlTest(googH263(), MODE_IMAGE); }
+ public void testGoogMpeg4Image() { swirlTest(googMpeg4(), MODE_IMAGE); }
+ public void testGoogVP8Image() { swirlTest(googVP8(), MODE_IMAGE); }
+ public void testGoogVP9Image() { swirlTest(googVP9(), MODE_IMAGE); }
+
+ public void testOtherH265Image() { swirlTest(otherH265(), MODE_IMAGE); }
+ public void testOtherH264Image() { swirlTest(otherH264(), MODE_IMAGE); }
+ public void testOtherH263Image() { swirlTest(otherH263(), MODE_IMAGE); }
+ public void testOtherMpeg4Image() { swirlTest(otherMpeg4(), MODE_IMAGE); }
+ public void testOtherVP8Image() { swirlTest(otherVP8(), MODE_IMAGE); }
+ public void testOtherVP9Image() { swirlTest(otherVP9(), MODE_IMAGE); }
+
+ public void testGoogH265ImageReader() { swirlTest(googH265(), MODE_IMAGEREADER); }
+ public void testGoogH264ImageReader() { swirlTest(googH264(), MODE_IMAGEREADER); }
+ public void testGoogH263ImageReader() { swirlTest(googH263(), MODE_IMAGEREADER); }
+ public void testGoogMpeg4ImageReader() { swirlTest(googMpeg4(), MODE_IMAGEREADER); }
+ public void testGoogVP8ImageReader() { swirlTest(googVP8(), MODE_IMAGEREADER); }
+ public void testGoogVP9ImageReader() { swirlTest(googVP9(), MODE_IMAGEREADER); }
+
+ public void testOtherH265ImageReader() { swirlTest(otherH265(), MODE_IMAGEREADER); }
+ public void testOtherH264ImageReader() { swirlTest(otherH264(), MODE_IMAGEREADER); }
+ public void testOtherH263ImageReader() { swirlTest(otherH263(), MODE_IMAGEREADER); }
+ public void testOtherMpeg4ImageReader() { swirlTest(otherMpeg4(), MODE_IMAGEREADER); }
+ public void testOtherVP8ImageReader() { swirlTest(otherVP8(), MODE_IMAGEREADER); }
+ public void testOtherVP9ImageReader() { swirlTest(otherVP9(), MODE_IMAGEREADER); }
+
/**
- * Test ImageReader with 480x360 sw AVC decoding for flexible yuv format, which is mandatory
- * to be supported by sw decoder.
+ * Test ImageReader with 480x360 non-google AVC decoding for flexible yuv format
+ */
+ public void testHwAVCDecode360pForFlexibleYuv() throws Exception {
+ Decoder[] decoders = other(new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_AVC,
+ new MediaAsset(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ 480 /* width */, 360 /* height */)));
+
+ decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
+ }
+
+ /**
+ * Test ImageReader with 480x360 google (SW) AVC decoding for flexible yuv format
*/
public void testSwAVCDecode360pForFlexibleYuv() throws Exception {
+ Decoder[] decoders = goog(new MediaAssets(
+ MediaFormat.MIMETYPE_VIDEO_AVC,
+ new MediaAsset(
+ R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
+ 480 /* width */, 360 /* height */)));
+
+ decodeTest(decoders, MODE_IMAGEREADER, false /* checkSwirl */);
+ }
+
+ private void swirlTest(Decoder[] decoders, int mode) {
+ decodeTest(decoders, mode, true /* checkSwirl */);
+ }
+
+ private void decodeTest(Decoder[] decoders, int mode, boolean checkSwirl) {
try {
- int format = ImageFormat.YUV_420_888;
- videoDecodeToSurface(
- R.raw.video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz,
- /* width */ 480, /* height */ 360, format, /* useHw */ false);
+ boolean skipped = true;
+ for (Decoder codec : decoders) {
+ if (codec.videoDecode(mode, checkSwirl)) {
+ skipped = false;
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("decoder does not any of the input files");
+ }
} finally {
closeImageReader();
}
@@ -152,61 +458,26 @@
}
}
- private void videoDecodeToSurface(int video, int width,
- int height, int imageFormat, boolean useHw) throws Exception {
- MediaCodec decoder = null;
- MediaExtractor extractor;
- int outputBufferIndex;
- ByteBuffer[] decoderInputBuffers;
- ByteBuffer[] decoderOutputBuffers;
-
- if (!MediaUtils.checkCodecForResource(mContext, video, 0 /* track */)) {
- return; // skip
- }
-
- AssetFileDescriptor vidFD = mResources.openRawResourceFd(video);
-
- extractor = new MediaExtractor();
- extractor.setDataSource(vidFD.getFileDescriptor(), vidFD.getStartOffset(),
- vidFD.getLength());
-
- MediaFormat mediaFmt = extractor.getTrackFormat(0);
- mediaFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT,
- CodecCapabilities.COLOR_FormatYUV420Flexible);
- String mime = mediaFmt.getString(MediaFormat.KEY_MIME);
- try {
- // Create decoder
- decoder = createDecoder(mime, useHw);
- assertNotNull("couldn't find decoder", decoder);
- if (VERBOSE) Log.v(TAG, "using decoder: " + decoder.getName());
-
- decodeFramesToImageReader(width, height, imageFormat, decoder,
- extractor, mediaFmt, mime);
-
- decoder.stop();
- } finally {
- decoder.release();
- }
-
- }
-
/**
* Decode video frames to image reader.
*/
- private void decodeFramesToImageReader(int width, int height, int imageFormat,
- MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFmt, String mime)
- throws Exception, InterruptedException {
+ private void decodeFramesToImage(
+ MediaCodec decoder, MediaExtractor extractor, MediaFormat mediaFormat,
+ int width, int height, int imageFormat, int mode, boolean checkSwirl)
+ throws InterruptedException {
ByteBuffer[] decoderInputBuffers;
ByteBuffer[] decoderOutputBuffers;
- if (!imageFormatSupported(decoder, imageFormat, mime)) {
- // TODO: SKIPPING TEST
- return;
- }
- createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
// Configure decoder.
- if (VERBOSE) Log.v(TAG, "stream format: " + mediaFmt);
- decoder.configure(mediaFmt, mReaderSurface, /*crypto*/null, /*flags*/0);
+ if (VERBOSE) Log.v(TAG, "stream format: " + mediaFormat);
+ if (mode == MODE_IMAGEREADER) {
+ createImageReader(width, height, imageFormat, MAX_NUM_IMAGES, mImageListener);
+ decoder.configure(mediaFormat, mReaderSurface, null /* crypto */, 0 /* flags */);
+ } else {
+ assertEquals(mode, MODE_IMAGE);
+ decoder.configure(mediaFormat, null /* surface */, null /* crypto */, 0 /* flags */);
+ }
+
decoder.start();
decoderInputBuffers = decoder.getInputBuffers();
decoderOutputBuffers = decoder.getOutputBuffers();
@@ -271,65 +542,55 @@
// Should be decoding error.
fail("unexpected result from deocder.dequeueOutputBuffer: " + res);
} else {
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ sawOutputEOS = true;
+ }
+
// res >= 0: normal decoding case, copy the output buffer.
// Will use it as reference to valid the ImageReader output
// Some decoders output a 0-sized buffer at the end. Ignore those.
- outputFrameCount++;
boolean doRender = (info.size != 0);
- decoder.releaseOutputBuffer(res, doRender);
if (doRender) {
- // Read image and verify
- Image image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
- Plane[] imagePlanes = image.getPlanes();
+ outputFrameCount++;
+ String fileName = DEBUG_FILE_NAME_BASE + MediaUtils.getTestName()
+ + (mode == MODE_IMAGE ? "_image_" : "_reader_")
+ + width + "x" + height + "_" + outputFrameCount + ".yuv";
- //Verify
- String fileName = DEBUG_FILE_NAME_BASE + width + "x" + height + "_"
- + outputFrameCount + ".yuv";
- validateImage(image, width, height, imageFormat, fileName);
+ Image image = null;
+ try {
+ if (mode == MODE_IMAGE) {
+ image = decoder.getOutputImage(res);
+ } else {
+ decoder.releaseOutputBuffer(res, doRender);
+ res = -1;
+ // Read image and verify
+ image = mImageListener.getImage(WAIT_FOR_IMAGE_TIMEOUT_MS);
+ }
+ validateImage(image, width, height, imageFormat, fileName);
- if (VERBOSE) {
- Log.v(TAG, "Image " + outputFrameCount + " Info:");
- Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
- Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
- Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+ if (checkSwirl) {
+ try {
+ validateSwirl(image);
+ } catch (Throwable e) {
+ dumpFile(fileName, getDataFromImage(image));
+ throw e;
+ }
+ }
+ } finally {
+ if (image != null) {
+ image.close();
+ }
}
- image.close();
+ }
+
+ if (res >= 0) {
+ decoder.releaseOutputBuffer(res, false /* render */);
}
}
}
}
- private boolean imageFormatSupported(MediaCodec decoder, int imageFormat, String mime) {
- MediaCodecInfo codecInfo = decoder.getCodecInfo();
- if (codecInfo == null) {
- return false;
- }
- MediaCodecInfo.CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mime);
- for (int colorFormat : capabilities.colorFormats) {
- if (colorFormat == CodecCapabilities.COLOR_FormatYUV420Flexible
- && imageFormat == ImageFormat.YUV_420_888) {
- return true;
- }
- }
- return false;
- }
-
- private MediaCodec createDecoder(String mime, boolean useHw) throws Exception {
- if (!useHw) {
- if (mime.contains("avc")) {
- return MediaCodec.createByCodecName("OMX.google.h264.decoder");
- } else if (mime.contains("3gpp")) {
- return MediaCodec.createByCodecName("OMX.google.h263.decoder");
- } else if (mime.contains("mp4v")) {
- return MediaCodec.createByCodecName("OMX.google.mpeg4.decoder");
- } else if (mime.contains("vp8")) {
- return MediaCodec.createByCodecName("OMX.google.vpx.decoder");
- }
- }
- return MediaCodec.createDecoderByType(mime);
- }
-
/**
* Validate image based on format and size.
*
@@ -339,22 +600,103 @@
* @param format The image format.
* @param filePath The debug dump file path, null if don't want to dump to file.
*/
- public static void validateImage(Image image, int width, int height, int format,
- String filePath) {
+ public static void validateImage(
+ Image image, int width, int height, int format, String filePath) {
+ if (VERBOSE) {
+ Plane[] imagePlanes = image.getPlanes();
+ Log.v(TAG, "Image " + filePath + " Info:");
+ Log.v(TAG, "first plane pixelstride " + imagePlanes[0].getPixelStride());
+ Log.v(TAG, "first plane rowstride " + imagePlanes[0].getRowStride());
+ Log.v(TAG, "Image timestamp:" + image.getTimestamp());
+ }
+
assertNotNull("Input image is invalid", image);
assertEquals("Format doesn't match", format, image.getFormat());
- assertEquals("Width doesn't match", width, image.getWidth());
- assertEquals("Height doesn't match", height, image.getHeight());
+ assertEquals("Width doesn't match", width, image.getCropRect().width());
+ assertEquals("Height doesn't match", height, image.getCropRect().height());
if(VERBOSE) Log.v(TAG, "validating Image");
byte[] data = getDataFromImage(image);
assertTrue("Invalid image data", data != null && data.length > 0);
- validateYuvData(data, width, height, format, image.getTimestamp(), filePath);
+ validateYuvData(data, width, height, format, image.getTimestamp());
+
+ if (VERBOSE && filePath != null) {
+ dumpFile(filePath, data);
+ }
+ }
+
+ private static void validateSwirl(Image image) {
+ Rect crop = image.getCropRect();
+ final int NUM_SIDES = 4;
+ final int step = 8; // the width of the layers
+ long[][] rawStats = new long[NUM_SIDES][10];
+ int[][] colors = new int[][] {
+ { 111, 96, 204 }, { 178, 27, 174 }, { 100, 192, 92 }, { 106, 117, 62 }
+ };
+
+ // successively accumulate statistics for each layer of the swirl
+ // by using overlapping rectangles, and the observation that
+ // layer_i = rectangle_i - rectangle_(i+1)
+ int lastLayer = 0;
+ int layer = 0;
+ boolean lastLayerValid = false;
+ for (int pos = 0; ; pos += step) {
+ Rect area = new Rect(pos - step, pos, crop.width() / 2, crop.height() + 2 * step - pos);
+ if (area.isEmpty()) {
+ break;
+ }
+ area.offset(crop.left, crop.top);
+ area.intersect(crop);
+ for (int lr = 0; lr < 2; ++lr) {
+ long[] oneStat = CodecUtils.getRawStats(image, area);
+ if (VERBOSE) Log.v(TAG, "area=" + area + ", layer=" + layer + ", last="
+ + lastLayer + ": " + Arrays.toString(oneStat));
+ for (int i = 0; i < oneStat.length; i++) {
+ rawStats[layer][i] += oneStat[i];
+ if (lastLayerValid) {
+ rawStats[lastLayer][i] -= oneStat[i];
+ }
+ }
+ if (VERBOSE && lastLayerValid) {
+ Log.v(TAG, "layer-" + lastLayer + ": " + Arrays.toString(rawStats[lastLayer]));
+ Log.v(TAG, Arrays.toString(CodecUtils.Raw2YUVStats(rawStats[lastLayer])));
+ }
+ // switch to the opposite side
+ layer ^= 2; // NUM_SIDES / 2
+ lastLayer ^= 2; // NUM_SIDES / 2
+ area.offset(crop.centerX() - area.left, 2 * (crop.centerY() - area.centerY()));
+ }
+
+ lastLayer = layer;
+ lastLayerValid = true;
+ layer = (layer + 1) % NUM_SIDES;
+ }
+
+ for (layer = 0; layer < NUM_SIDES; ++layer) {
+ float[] stats = CodecUtils.Raw2YUVStats(rawStats[layer]);
+ if (DEBUG) Log.d(TAG, "layer-" + layer + ": " + Arrays.toString(stats));
+ if (VERBOSE) Log.v(TAG, Arrays.toString(rawStats[layer]));
+
+ // check layer uniformity
+ for (int i = 0; i < 3; i++) {
+ assertTrue("color of layer-" + layer + " is not uniform: "
+ + Arrays.toString(stats),
+ stats[3 + i] < COLOR_STDEV_ALLOWANCE);
+ }
+
+ // check layer color
+ for (int i = 0; i < 3; i++) {
+ assertTrue("color of layer-" + layer + " mismatches target "
+ + Arrays.toString(colors[layer]) + " vs "
+ + Arrays.toString(Arrays.copyOf(stats, 3)),
+ Math.abs(stats[i] - colors[layer][i]) < COLOR_DELTA_ALLOWANCE);
+ }
+ }
}
private static void validateYuvData(byte[] yuvData, int width, int height, int format,
- long ts, String fileName) {
+ long ts) {
assertTrue("YUV format must be one of the YUV_420_888, NV21, or YV12",
format == ImageFormat.YUV_420_888 ||
@@ -364,10 +706,6 @@
if (VERBOSE) Log.v(TAG, "Validating YUV data");
int expectedSize = width * height * ImageFormat.getBitsPerPixel(format) / 8;
assertEquals("Yuv data doesn't match", expectedSize, yuvData.length);
-
- if (DEBUG && fileName != null) {
- dumpFile(fileName, yuvData);
- }
}
private static void checkYuvFormat(int format) {
@@ -412,9 +750,10 @@
*/
private static byte[] getDataFromImage(Image image) {
assertNotNull("Invalid image:", image);
+ Rect crop = image.getCropRect();
int format = image.getFormat();
- int width = image.getWidth();
- int height = image.getHeight();
+ int width = crop.width();
+ int height = crop.height();
int rowStride, pixelStride;
byte[] data = null;
@@ -432,6 +771,7 @@
byte[] rowData = new byte[planes[0].getRowStride()];
if(VERBOSE) Log.v(TAG, "get data from " + planes.length + " planes");
for (int i = 0; i < planes.length; i++) {
+ int shift = (i == 0) ? 0 : 1;
buffer = planes[i].getBuffer();
assertNotNull("Fail to get bytebuffer from plane", buffer);
rowStride = planes[i].getRowStride();
@@ -444,27 +784,32 @@
Log.v(TAG, "height " + height);
}
// For multi-planar yuv images, assuming yuv420 with 2x2 chroma subsampling.
- int w = (i == 0) ? width : width / 2;
- int h = (i == 0) ? height : height / 2;
+ int w = crop.width() >> shift;
+ int h = crop.height() >> shift;
+ buffer.position(rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift));
assertTrue("rowStride " + rowStride + " should be >= width " + w , rowStride >= w);
for (int row = 0; row < h; row++) {
int bytesPerPixel = ImageFormat.getBitsPerPixel(format) / 8;
+ int length;
if (pixelStride == bytesPerPixel) {
// Special case: optimized read of the entire row
- int length = w * bytesPerPixel;
+ length = w * bytesPerPixel;
buffer.get(data, offset, length);
- // Advance buffer the remainder of the row stride
- buffer.position(buffer.position() + rowStride - length);
offset += length;
} else {
// Generic case: should work for any pixelStride but slower.
// Use intermediate buffer to avoid read byte-by-byte from
// DirectByteBuffer, which is very bad for performance
- buffer.get(rowData, 0, rowStride);
+ length = (w - 1) * pixelStride + bytesPerPixel;
+ buffer.get(rowData, 0, length);
for (int col = 0; col < w; col++) {
data[offset++] = rowData[col * pixelStride];
}
}
+ // Advance buffer the remainder of the row stride
+ if (row < h - 1) {
+ buffer.position(buffer.position() + rowStride - length);
+ }
}
if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i);
}
@@ -491,8 +836,9 @@
}
}
- private void createImageReader(int width, int height, int format, int maxNumImages,
- ImageReader.OnImageAvailableListener listener) throws Exception {
+ private void createImageReader(
+ int width, int height, int format, int maxNumImages,
+ ImageReader.OnImageAvailableListener listener) {
closeImageReader();
mReader = ImageReader.newInstance(width, height, format, maxNumImages);
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
new file mode 100644
index 0000000..be5099e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.LoudnessEnhancer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class LoudnessEnhancerTest extends PostProcTestBase {
+
+ private String TAG = "LoudnessEnhancerTest";
+ private LoudnessEnhancer mLE;
+
+ //-----------------------------------------------------------------
+ // LOUDNESS ENHANCER TESTS:
+ //----------------------------------
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ public void test0_0ConstructorAndRelease() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ getLoudnessEnhancer(0);
+ releaseLoudnessEnhancer();
+
+ int session = am.generateAudioSessionId();
+ assertTrue("cannot generate new session", session != AudioManager.ERROR);
+ getLoudnessEnhancer(session);
+ releaseLoudnessEnhancer();
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test set/get target gain
+ public void test1_0TargetGain() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ getLoudnessEnhancer(0);
+ try {
+ mLE.setTargetGain(0);
+ assertEquals("target gain differs from value set", 0.0f, mLE.getTargetGain());
+ mLE.setTargetGain(800);
+ assertEquals("target gain differs from value set", 800.0f, mLE.getTargetGain());
+ } catch (IllegalArgumentException e) {
+ fail("target gain illegal argument");
+ } catch (UnsupportedOperationException e) {
+ fail("target gain unsupported operation");
+ } catch (IllegalStateException e) {
+ fail("target gain operation called in wrong state");
+ } finally {
+ releaseLoudnessEnhancer();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+ private void getLoudnessEnhancer(int session) {
+ releaseLoudnessEnhancer();
+ try {
+ mLE = new LoudnessEnhancer(session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getLoudnessEnhancer() LoudnessEnhancer not found exception: ", e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getLoudnessEnhancer() Effect library not loaded exception: ", e);
+ }
+ assertNotNull("could not create LoudnessEnhancer", mLE);
+ }
+
+ private void releaseLoudnessEnhancer() {
+ if (mLE != null) {
+ mLE.release();
+ mLE = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
new file mode 100644
index 0000000..996ddf7
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserServiceTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.media.browse.MediaBrowser;
+import android.media.browse.MediaBrowser.MediaItem;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+import android.service.media.MediaBrowserService.BrowserRoot;
+import android.test.InstrumentationTestCase;
+
+import java.util.List;
+
+/**
+ * Test {@link android.media.browse.MediaBrowserService}.
+ */
+public class MediaBrowserServiceTest extends InstrumentationTestCase {
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 3000L;
+ private static final long WAIT_TIME_FOR_NO_RESPONSE_MS = 500L;
+ private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+ "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+ private final Object mWaitLock = new Object();
+ private final MediaBrowser.ConnectionCallback mConnectionCallback =
+ new MediaBrowser.ConnectionCallback() {
+ @Override
+ public void onConnected() {
+ synchronized (mWaitLock) {
+ mMediaBrowserService = StubMediaBrowserService.sInstance;
+ mWaitLock.notify();
+ }
+ }
+ };
+
+ private final MediaBrowser.SubscriptionCallback mSubscriptionCallback =
+ new MediaBrowser.SubscriptionCallback() {
+ @Override
+ public void onChildrenLoaded(String parentId, List<MediaItem> children) {
+ synchronized (mWaitLock) {
+ mOnChildrenLoaded = true;
+ mWaitLock.notify();
+ }
+ }
+ };
+
+ private MediaBrowser mMediaBrowser;
+ private StubMediaBrowserService mMediaBrowserService;
+ private boolean mOnChildrenLoaded;
+
+ @Override
+ protected void setUp() throws Exception {
+ getInstrumentation().runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mMediaBrowser = new MediaBrowser(getInstrumentation().getTargetContext(),
+ TEST_BROWSER_SERVICE, mConnectionCallback, null);
+ }
+ });
+ synchronized (mWaitLock) {
+ mMediaBrowser.connect();
+ mWaitLock.wait(TIME_OUT_MS);
+ }
+ assertNotNull(mMediaBrowserService);
+ }
+
+ public void testGetSessionToken() {
+ assertEquals(StubMediaBrowserService.sSession.getSessionToken(),
+ mMediaBrowserService.getSessionToken());
+ }
+
+ public void testNotifyChildrenChanged() throws Exception {
+ synchronized (mWaitLock) {
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mOnChildrenLoaded);
+
+ mOnChildrenLoaded = false;
+ mMediaBrowserService.notifyChildrenChanged(StubMediaBrowserService.MEDIA_ID_ROOT);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mOnChildrenLoaded);
+ }
+ }
+
+ public void testDelayedNotifyChildrenChanged() throws Exception {
+ synchronized (mWaitLock) {
+ mOnChildrenLoaded = false;
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED,
+ mSubscriptionCallback);
+ mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+ assertFalse(mOnChildrenLoaded);
+
+ mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mOnChildrenLoaded);
+
+ mOnChildrenLoaded = false;
+ mMediaBrowserService.notifyChildrenChanged(
+ StubMediaBrowserService.MEDIA_ID_CHILDREN_DELAYED);
+ mWaitLock.wait(WAIT_TIME_FOR_NO_RESPONSE_MS);
+ assertFalse(mOnChildrenLoaded);
+
+ mMediaBrowserService.sendDelayedNotifyChildrenChanged();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mOnChildrenLoaded);
+ }
+ }
+
+ public void testBrowserRoot() {
+ final String id = "test-id";
+ final String key = "test-key";
+ final String val = "test-val";
+ final Bundle extras = new Bundle();
+ extras.putString(key, val);
+
+ MediaBrowserService.BrowserRoot browserRoot = new BrowserRoot(id, extras);
+ assertEquals(id, browserRoot.getRootId());
+ assertEquals(val, browserRoot.getExtras().getString(key));
+ }
+}
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..51d43d9
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaBrowserTest.java
@@ -0,0 +1,197 @@
+/*
+ * 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;
+
+import java.util.List;
+
+/**
+ * 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 = 3000L;
+ private static final ComponentName TEST_BROWSER_SERVICE = new ComponentName(
+ "com.android.cts.media", "android.media.cts.StubMediaBrowserService");
+ private static final ComponentName TEST_INVALID_BROWSER_SERVICE = new ComponentName(
+ "invalid.package", "invalid.ServiceClassName");
+ private final StubConnectionCallback mConnectionCallback = new StubConnectionCallback();
+ private final StubSubscriptionCallback mSubscriptionCallback = new StubSubscriptionCallback();
+
+ private MediaBrowser mMediaBrowser;
+
+ public void testMediaBrowser() {
+ resetCallbacks();
+ 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() {
+ resetCallbacks();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ try {
+ mMediaBrowser.connect();
+ fail();
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ public void testConnectionFailed() {
+ resetCallbacks();
+ createMediaBrowser(TEST_INVALID_BROWSER_SERVICE);
+
+ mMediaBrowser.connect();
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mConnectionCallback.mConnectionFailedCount > 0
+ && mConnectionCallback.mConnectedCount == 0
+ && mConnectionCallback.mConnectionSuspendedCount == 0;
+ }
+ }.run();
+ }
+
+ public void testGetServiceComponentBeforeConnection() {
+ resetCallbacks();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ try {
+ ComponentName serviceComponent = mMediaBrowser.getServiceComponent();
+ fail();
+ } catch (IllegalStateException e) {
+ // expected
+ }
+ }
+
+ public void testSubscribe() {
+ resetCallbacks();
+ createMediaBrowser(TEST_BROWSER_SERVICE);
+ connectMediaBrowserService();
+ mMediaBrowser.subscribe(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback);
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mSubscriptionCallback.mChildrenLoadedCount > 0;
+ }
+ }.run();
+
+ assertEquals(StubMediaBrowserService.MEDIA_ID_ROOT, mSubscriptionCallback.mLastParentId);
+ assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN.length,
+ mSubscriptionCallback.mLastChildMediaItems.size());
+ for (int i = 0; i < StubMediaBrowserService.MEDIA_ID_CHILDREN.length; i++) {
+ assertEquals(StubMediaBrowserService.MEDIA_ID_CHILDREN[i],
+ mSubscriptionCallback.mLastChildMediaItems.get(i).getMediaId());
+ }
+ }
+
+ 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 void resetCallbacks() {
+ mConnectionCallback.reset();
+ mSubscriptionCallback.reset();
+ }
+
+ private static class StubConnectionCallback extends MediaBrowser.ConnectionCallback {
+ volatile int mConnectedCount;
+ volatile int mConnectionFailedCount;
+ volatile int mConnectionSuspendedCount;
+
+ public void reset() {
+ mConnectedCount = 0;
+ mConnectionFailedCount = 0;
+ mConnectionSuspendedCount = 0;
+ }
+
+ @Override
+ public void onConnected() {
+ mConnectedCount++;
+ }
+
+ @Override
+ public void onConnectionFailed() {
+ mConnectionFailedCount++;
+ }
+
+ @Override
+ public void onConnectionSuspended() {
+ mConnectionSuspendedCount++;
+ }
+ }
+
+ private static class StubSubscriptionCallback extends MediaBrowser.SubscriptionCallback {
+ private volatile int mChildrenLoadedCount;
+ private volatile int mErrorCount;
+ private volatile String mLastErrorId;
+ private volatile String mLastParentId;
+ private volatile List<MediaBrowser.MediaItem> mLastChildMediaItems;
+
+ public void reset() {
+ mChildrenLoadedCount = 0;
+ mErrorCount = 0;
+ mLastErrorId = null;
+ mLastParentId = null;
+ mLastChildMediaItems = null;
+ }
+
+ @Override
+ public void onChildrenLoaded(String parentId, List<MediaBrowser.MediaItem> children) {
+ mChildrenLoadedCount++;
+ mLastParentId = parentId;
+ mLastChildMediaItems = children;
+ }
+
+ @Override
+ public void onError(String id) {
+ mLastErrorId = id;
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index 979a5d1..159d13f 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -17,22 +17,29 @@
import android.content.pm.PackageManager;
import android.cts.util.MediaUtils;
+import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecInfo.VideoCapabilities;
import static android.media.MediaCodecInfo.CodecProfileLevel.*;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
import static android.media.MediaFormat.MIMETYPE_VIDEO_AVC;
import static android.media.MediaFormat.MIMETYPE_VIDEO_H263;
import static android.media.MediaFormat.MIMETYPE_VIDEO_HEVC;
import static android.media.MediaFormat.MIMETYPE_VIDEO_MPEG4;
-import android.media.MediaCodecList;
-import android.media.MediaFormat;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_VP8;
+import static android.media.MediaFormat.MIMETYPE_VIDEO_VP9;
import android.media.MediaPlayer;
-
import android.os.Build;
-
import android.util.Log;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Arrays;
+
/**
* Basic sanity test of data returned by MediaCodeCapabilities.
*/
@@ -40,6 +47,17 @@
private static final String TAG = "MediaCodecCapabilitiesTest";
private static final int PLAY_TIME_MS = 30000;
+ private static final int TIMEOUT_US = 1000000; // 1 sec
+ private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+
+ private final MediaCodecList mRegularCodecs =
+ new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ private final MediaCodecList mAllCodecs =
+ new MediaCodecList(MediaCodecList.ALL_CODECS);
+ private final MediaCodecInfo[] mRegularInfos =
+ mRegularCodecs.getCodecInfos();
+ private final MediaCodecInfo[] mAllInfos =
+ mAllCodecs.getCodecInfos();
// Android device implementations with H.264 encoders, MUST support Baseline Profile Level 3.
// SHOULD support Main Profile/ Level 4, if supported the device must also support Main
@@ -131,7 +149,7 @@
hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain, HEVCMainTierLevel41));
}
- if (MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
+ if (isTv() && MediaUtils.canDecodeVideo(MIMETYPE_VIDEO_HEVC, 3840, 2160, 30)) {
assertTrue(
"H.265 must support Main10 Profile Main Tier Level 5 if UHD is supported",
hasDecoder(MIMETYPE_VIDEO_HEVC, HEVCProfileMain10, HEVCMainTierLevel5));
@@ -332,4 +350,182 @@
}
return false;
}
+
+ private boolean isVideoMime(String mime) {
+ return mime.toLowerCase().startsWith("video/");
+ }
+
+ private Set<String> requiredAdaptiveFormats() {
+ Set<String> adaptiveFormats = new HashSet<String>();
+ adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_AVC);
+ adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
+ adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP8);
+ adaptiveFormats.add(MediaFormat.MIMETYPE_VIDEO_VP9);
+ return adaptiveFormats;
+ }
+
+ public void testHaveAdaptiveVideoDecoderForAllSupportedFormats() {
+ Set<String> supportedFormats = new HashSet<String>();
+ boolean skipped = true;
+
+ // gather all supported video formats
+ for (MediaCodecInfo info : mAllInfos) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ for (String mime : info.getSupportedTypes()) {
+ if (isVideoMime(mime)) {
+ supportedFormats.add(mime);
+ }
+ }
+ }
+
+ // limit to CDD-required formats for now
+ supportedFormats.retainAll(requiredAdaptiveFormats());
+
+ // check if there is an adaptive decoder for each
+ for (String mime : supportedFormats) {
+ skipped = false;
+ // implicit assumption that QVGA video is always valid.
+ MediaFormat format = MediaFormat.createVideoFormat(mime, 176, 144);
+ format.setFeatureEnabled(CodecCapabilities.FEATURE_AdaptivePlayback, true);
+ String codec = mAllCodecs.findDecoderForFormat(format);
+ assertTrue(
+ "could not find adaptive decoder for " + mime, codec != null);
+ }
+ if (skipped) {
+ MediaUtils.skipTest("no video decoders that are required to be adaptive found");
+ }
+ }
+
+ public void testAllVideoDecodersAreAdaptive() {
+ Set<String> adaptiveFormats = requiredAdaptiveFormats();
+ boolean skipped = true;
+ for (MediaCodecInfo info : mAllInfos) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ for (String mime : info.getSupportedTypes()) {
+ if (!isVideoMime(mime)
+ // limit to CDD-required formats for now
+ || !adaptiveFormats.contains(mime)) {
+ continue;
+ }
+ skipped = false;
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ assertTrue(
+ info.getName() + " is not adaptive for " + mime,
+ caps.isFeatureSupported(CodecCapabilities.FEATURE_AdaptivePlayback));
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("no video decoders that are required to be adaptive found");
+ }
+ }
+
+ private MediaFormat createReasonableVideoFormat(
+ CodecCapabilities caps, String mime, boolean encoder, int width, int height) {
+ VideoCapabilities vidCaps = caps.getVideoCapabilities();
+ MediaFormat format = MediaFormat.createVideoFormat(mime, width, height);
+ if (encoder) {
+ // bitrate
+ int maxWidth = vidCaps.getSupportedWidths().getUpper();
+ int maxHeight = vidCaps.getSupportedHeightsFor(width).getUpper();
+ int maxRate = vidCaps.getSupportedFrameRatesFor(width, height).getUpper().intValue();
+ int bitrate = vidCaps.getBitrateRange().clamp(
+ (int)(vidCaps.getBitrateRange().getUpper()
+ / Math.sqrt((double)maxWidth * maxHeight / width / height)));
+ Log.i(TAG, "reasonable bitrate for " + width + "x" + height + "@" + maxRate
+ + " " + mime + " = " + bitrate);
+ format.setInteger(format.KEY_BIT_RATE, bitrate);
+ format.setInteger(format.KEY_FRAME_RATE, maxRate);
+ format.setInteger(format.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ }
+ return format;
+ }
+
+ public void testSecureCodecsAdvertiseSecurePlayback() throws IOException {
+ boolean skipped = true;
+ for (MediaCodecInfo info : mAllInfos) {
+ boolean isEncoder = info.isEncoder();
+ if (isEncoder || !info.getName().endsWith(".secure")) {
+ continue;
+ }
+ for (String mime : info.getSupportedTypes()) {
+ if (!isVideoMime(mime)) {
+ continue;
+ }
+ skipped = false;
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ assertTrue(
+ info.getName() + " does not advertise secure playback",
+ caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback));
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("no video decoders found ending in .secure");
+ }
+ }
+
+ public void testAllNonTunneledVideoCodecsSupportFlexibleYUV() throws IOException {
+ boolean skipped = true;
+ for (MediaCodecInfo info : mAllInfos) {
+ boolean isEncoder = info.isEncoder();
+ for (String mime: info.getSupportedTypes()) {
+ if (!isVideoMime(mime)) {
+ continue;
+ }
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ if (caps.isFeatureRequired(CodecCapabilities.FEATURE_TunneledPlayback)
+ || caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback)) {
+ continue;
+ }
+ skipped = false;
+ boolean found = false;
+ for (int c : caps.colorFormats) {
+ if (c == caps.COLOR_FormatYUV420Flexible) {
+ found = true;
+ break;
+ }
+ }
+ assertTrue(
+ info.getName() + " does not advertise COLOR_FormatYUV420Flexible",
+ found);
+
+ MediaCodec codec = null;
+ MediaFormat format = null;
+ try {
+ codec = MediaCodec.createByCodecName(info.getName());
+ // implicit assumption that QVGA video is always valid.
+ format = createReasonableVideoFormat(caps, mime, isEncoder, 176, 144);
+ format.setInteger(
+ MediaFormat.KEY_COLOR_FORMAT,
+ caps.COLOR_FormatYUV420Flexible);
+
+ codec.configure(format, null /* surface */, null /* crypto */,
+ isEncoder ? codec.CONFIGURE_FLAG_ENCODE : 0);
+ MediaFormat configuredFormat =
+ isEncoder ? codec.getInputFormat() : codec.getOutputFormat();
+ Log.d(TAG, "color format is " + configuredFormat.getInteger(
+ MediaFormat.KEY_COLOR_FORMAT));
+ if (isEncoder) {
+ codec.start();
+ int ix = codec.dequeueInputBuffer(TIMEOUT_US);
+ assertNotNull(
+ info.getName() + " encoder has non-flexYUV input buffer #" + ix,
+ codec.getInputImage(ix));
+ } else {
+ // TODO: test these on various decoders (need test streams)
+ }
+ } finally {
+ if (codec != null) {
+ codec.release();
+ }
+ }
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("no non-tunneled/non-secure video decoders found");
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
index 90696ff..b96d38c 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
@@ -15,6 +15,7 @@
*/
package android.media.cts;
+import android.media.AudioManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
@@ -40,7 +41,7 @@
* {@link MediaDrm} can be used to obtain keys for decrypting protected media streams,
* in conjunction with MediaCrypto.
*/
-public class MediaCodecCencPlayer {
+public class MediaCodecCencPlayer implements MediaTimeProvider {
private static final String TAG = MediaCodecCencPlayer.class.getSimpleName();
private static final int STATE_IDLE = 1;
@@ -300,10 +301,14 @@
CodecState state;
if (isVideo) {
- state = new CodecState(this, mVideoExtractor, trackIndex, format, codec, true);
+ state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
+ trackIndex, format, codec, true, false,
+ AudioManager.AUDIO_SESSION_ID_GENERATE);
mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
} else {
- state = new CodecState(this, mAudioExtractor, trackIndex, format, codec, true);
+ state = new CodecState((MediaTimeProvider)this, mAudioExtractor,
+ trackIndex, format, codec, true, false,
+ AudioManager.AUDIO_SESSION_ID_GENERATE);
mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
index e04517d..9442d09 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java
@@ -368,7 +368,7 @@
// Mandatory audio encoders (for non-watch devices with camera)
if (hasMicrophone() && !isWatch()) {
- list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 16000));
+ list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 8000));
list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_AAC, true, 48000));
// flac encoder is not required
// list.add(new AudioCodec(MediaFormat.MIMETYPE_AUDIO_FLAC, true)); // encoder
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
index 937eee6..494e823 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java
@@ -36,7 +36,6 @@
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
-
/**
* General MediaCodec tests.
*
@@ -1090,4 +1089,4 @@
}
return false;
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java
new file mode 100644
index 0000000..411cd14
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -0,0 +1,506 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+/**
+ * JB(API 21) introduces {@link MediaCodec} tunneled mode API. It allows apps
+ * to use MediaCodec to delegate their Audio/Video rendering to a vendor provided
+ * Codec component.
+ */
+public class MediaCodecTunneledPlayer implements MediaTimeProvider {
+ private static final String TAG = MediaCodecTunneledPlayer.class.getSimpleName();
+
+ private static final int STATE_IDLE = 1;
+ private static final int STATE_PREPARING = 2;
+ private static final int STATE_PLAYING = 3;
+ private static final int STATE_PAUSED = 4;
+
+ private Boolean mThreadStarted = false;
+ private byte[] mSessionId;
+ private CodecState mAudioTrackState;
+ private int mMediaFormatHeight;
+ private int mMediaFormatWidth;
+ private Integer mState;
+ private long mDeltaTimeUs;
+ private long mDurationUs;
+ private Map<Integer, CodecState> mAudioCodecStates;
+ private Map<Integer, CodecState> mVideoCodecStates;
+ private Map<String, String> mAudioHeaders;
+ private Map<String, String> mVideoHeaders;
+ private MediaExtractor mAudioExtractor;
+ private MediaExtractor mVideoExtractor;
+ private SurfaceHolder mSurfaceHolder;
+ private Thread mThread;
+ private Uri mAudioUri;
+ private Uri mVideoUri;
+ private boolean mTunneled;
+ private int mAudioSessionId;
+
+ /*
+ * Media player class to playback video using tunneled MediaCodec.
+ */
+ public MediaCodecTunneledPlayer(SurfaceHolder holder, boolean tunneled, int AudioSessionId) {
+ mSurfaceHolder = holder;
+ mTunneled = tunneled;
+ mAudioTrackState = null;
+ mState = STATE_IDLE;
+ mAudioSessionId = AudioSessionId;
+ mThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ while (true) {
+ synchronized (mThreadStarted) {
+ if (mThreadStarted == false) {
+ break;
+ }
+ }
+ synchronized (mState) {
+ if (mState == STATE_PLAYING) {
+ doSomeWork();
+ if (mAudioTrackState != null) {
+ mAudioTrackState.process();
+ }
+ }
+ }
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException ex) {
+ Log.d(TAG, "Thread interrupted");
+ }
+ }
+ }
+ });
+ }
+
+ public void setAudioDataSource(Uri uri, Map<String, String> headers) {
+ mAudioUri = uri;
+ mAudioHeaders = headers;
+ }
+
+ public void setVideoDataSource(Uri uri, Map<String, String> headers) {
+ mVideoUri = uri;
+ mVideoHeaders = headers;
+ }
+
+ public final int getMediaFormatHeight() {
+ return mMediaFormatHeight;
+ }
+
+ public final int getMediaFormatWidth() {
+ return mMediaFormatWidth;
+ }
+
+ private boolean prepareAudio() throws IOException {
+ for (int i = mAudioExtractor.getTrackCount(); i-- > 0;) {
+ MediaFormat format = mAudioExtractor.getTrackFormat(i);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+
+ if (!mime.startsWith("audio/")) {
+ continue;
+ }
+
+ Log.d(TAG, "audio track #" + i + " " + format + " " + mime +
+ " Is ADTS:" + getMediaFormatInteger(format, MediaFormat.KEY_IS_ADTS) +
+ " Sample rate:" + getMediaFormatInteger(format, MediaFormat.KEY_SAMPLE_RATE) +
+ " Channel count:" +
+ getMediaFormatInteger(format, MediaFormat.KEY_CHANNEL_COUNT));
+
+ mAudioExtractor.selectTrack(i);
+ if (!addTrack(i, format)) {
+ Log.e(TAG, "prepareAudio - addTrack() failed!");
+ return false;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_DURATION)) {
+ long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+ if (durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ Log.d(TAG, "audio track format #" + i +
+ " Duration:" + mDurationUs + " microseconds");
+ }
+ }
+ return true;
+ }
+
+ private boolean prepareVideo() throws IOException {
+ for (int i = mVideoExtractor.getTrackCount(); i-- > 0;) {
+ MediaFormat format = mVideoExtractor.getTrackFormat(i);
+ String mime = format.getString(MediaFormat.KEY_MIME);
+
+ if (!mime.startsWith("video/")) {
+ continue;
+ }
+
+ mMediaFormatHeight = getMediaFormatInteger(format, MediaFormat.KEY_HEIGHT);
+ mMediaFormatWidth = getMediaFormatInteger(format, MediaFormat.KEY_WIDTH);
+ Log.d(TAG, "video track #" + i + " " + format + " " + mime +
+ " Width:" + mMediaFormatWidth + ", Height:" + mMediaFormatHeight);
+
+ mVideoExtractor.selectTrack(i);
+ if (!addTrack(i, format)) {
+ Log.e(TAG, "prepareVideo - addTrack() failed!");
+ return false;
+ }
+
+ if (format.containsKey(MediaFormat.KEY_DURATION)) {
+ long durationUs = format.getLong(MediaFormat.KEY_DURATION);
+
+ if (durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ Log.d(TAG, "track format #" + i + " Duration:" +
+ mDurationUs + " microseconds");
+ }
+ }
+ return true;
+ }
+
+ public boolean prepare() throws IOException {
+ if (null == mAudioExtractor) {
+ mAudioExtractor = new MediaExtractor();
+ if (null == mAudioExtractor) {
+ Log.e(TAG, "prepare - Cannot create Audio extractor.");
+ return false;
+ }
+ }
+
+ if (null == mVideoExtractor){
+ mVideoExtractor = new MediaExtractor();
+ if (null == mVideoExtractor) {
+ Log.e(TAG, "prepare - Cannot create Video extractor.");
+ return false;
+ }
+ }
+
+ mAudioExtractor.setDataSource(mAudioUri.toString(), mAudioHeaders);
+ mVideoExtractor.setDataSource(mVideoUri.toString(), mVideoHeaders);
+
+ if (null == mVideoCodecStates) {
+ mVideoCodecStates = new HashMap<Integer, CodecState>();
+ } else {
+ mVideoCodecStates.clear();
+ }
+
+ if (null == mAudioCodecStates) {
+ mAudioCodecStates = new HashMap<Integer, CodecState>();
+ } else {
+ mAudioCodecStates.clear();
+ }
+
+ if (!prepareAudio()) {
+ Log.e(TAG,"prepare - prepareAudio() failed!");
+ return false;
+ }
+ if (!prepareVideo()) {
+ Log.e(TAG,"prepare - prepareVideo() failed!");
+ return false;
+ }
+
+ synchronized (mState) {
+ mState = STATE_PAUSED;
+ }
+ return true;
+ }
+
+ private boolean addTrack(int trackIndex, MediaFormat format) throws IOException {
+ String mime = format.getString(MediaFormat.KEY_MIME);
+ boolean isVideo = mime.startsWith("video/");
+ boolean isAudio = mime.startsWith("audio/");
+ MediaCodec codec;
+
+ // setup tunneled video codec if needed
+ if (isVideo && mTunneled) {
+ format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback,
+ true);
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ String codecName = mcl.findDecoderForFormat(format);
+ if (codecName == null) {
+ Log.e(TAG,"addTrack - Could not find Tunneled playback codec for "+mime+
+ " format!");
+ return false;
+ }
+
+ codec = MediaCodec.createByCodecName(codecName);
+ if (codec == null) {
+ Log.e(TAG, "addTrack - Could not create Tunneled playback codec "+
+ codecName+"!");
+ return false;
+ }
+
+ if (mAudioTrackState != null) {
+ format.setInteger(MediaFormat.KEY_AUDIO_SESSION_ID, mAudioSessionId);
+ }
+ }
+ else {
+ codec = MediaCodec.createDecoderByType(mime);
+ if (codec == null) {
+ Log.e(TAG, "addTrack - Could not create regular playback codec for mime "+
+ mime+"!");
+ return false;
+ }
+ }
+ codec.configure(
+ format,
+ isVideo ? mSurfaceHolder.getSurface() : null, null, 0);
+
+ CodecState state;
+ if (isVideo) {
+ state = new CodecState((MediaTimeProvider)this, mVideoExtractor,
+ trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+ mVideoCodecStates.put(Integer.valueOf(trackIndex), state);
+ } else {
+ state = new CodecState((MediaTimeProvider)this, mAudioExtractor,
+ trackIndex, format, codec, true, mTunneled, mAudioSessionId);
+ mAudioCodecStates.put(Integer.valueOf(trackIndex), state);
+ }
+
+ if (isAudio) {
+ mAudioTrackState = state;
+ }
+
+ return true;
+ }
+
+ protected int getMediaFormatInteger(MediaFormat format, String key) {
+ return format.containsKey(key) ? format.getInteger(key) : 0;
+ }
+
+ public boolean start() {
+ Log.d(TAG, "start");
+
+ synchronized (mState) {
+ if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
+ return true;
+ } else if (mState == STATE_IDLE) {
+ mState = STATE_PREPARING;
+ return true;
+ } else if (mState != STATE_PAUSED) {
+ throw new IllegalStateException();
+ }
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.start();
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.start();
+ }
+
+ mDeltaTimeUs = -1;
+ mState = STATE_PLAYING;
+ }
+ return false;
+ }
+
+ public void startWork() throws IOException, Exception {
+ try {
+ // Just change state from STATE_IDLE to STATE_PREPARING.
+ start();
+ // Extract media information from uri asset, and change state to STATE_PAUSED.
+ prepare();
+ // Start CodecState, and change from STATE_PAUSED to STATE_PLAYING.
+ start();
+ } catch (IOException e) {
+ throw e;
+ }
+
+ synchronized (mThreadStarted) {
+ mThreadStarted = true;
+ mThread.start();
+ }
+ }
+
+ public void startThread() {
+ start();
+ synchronized (mThreadStarted) {
+ mThreadStarted = true;
+ mThread.start();
+ }
+ }
+
+ public void pause() {
+ Log.d(TAG, "pause");
+
+ synchronized (mState) {
+ if (mState == STATE_PAUSED) {
+ return;
+ } else if (mState != STATE_PLAYING) {
+ throw new IllegalStateException();
+ }
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.pause();
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.pause();
+ }
+
+ mState = STATE_PAUSED;
+ }
+ }
+
+ public void flush() {
+ Log.d(TAG, "flush");
+
+ synchronized (mState) {
+ if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
+ return;
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.flush();
+ }
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.flush();
+ }
+ }
+ }
+
+ public void reset() {
+ synchronized (mState) {
+ if (mState == STATE_PLAYING) {
+ pause();
+ }
+ if (mVideoCodecStates != null) {
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.release();
+ }
+ mVideoCodecStates = null;
+ }
+
+ if (mAudioCodecStates != null) {
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.release();
+ }
+ mAudioCodecStates = null;
+ }
+
+ if (mAudioExtractor != null) {
+ mAudioExtractor.release();
+ mAudioExtractor = null;
+ }
+
+ if (mVideoExtractor != null) {
+ mVideoExtractor.release();
+ mVideoExtractor = null;
+ }
+
+ mDurationUs = -1;
+ mState = STATE_IDLE;
+ }
+ synchronized (mThreadStarted) {
+ mThreadStarted = false;
+ }
+ try {
+ mThread.join();
+ } catch (InterruptedException ex) {
+ Log.d(TAG, "mThread.join " + ex);
+ }
+ }
+
+ public boolean isEnded() {
+ for (CodecState state : mVideoCodecStates.values()) {
+ if (!state.isEnded()) {
+ return false;
+ }
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ if (!state.isEnded()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void doSomeWork() {
+ try {
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.doSomeWork();
+ }
+ } catch (IllegalStateException e) {
+ throw new Error("Video CodecState.doSomeWork" + e);
+ }
+
+ try {
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.doSomeWork();
+ }
+ } catch (IllegalStateException e) {
+ throw new Error("Audio CodecState.doSomeWork" + e);
+ }
+
+ }
+
+ public long getNowUs() {
+ if (mAudioTrackState == null) {
+ return System.currentTimeMillis() * 1000;
+ }
+
+ return mAudioTrackState.getAudioTimeUs();
+ }
+
+ public long getRealTimeUsForMediaTime(long mediaTimeUs) {
+ if (mDeltaTimeUs == -1) {
+ long nowUs = getNowUs();
+ mDeltaTimeUs = nowUs - mediaTimeUs;
+ }
+
+ return mDeltaTimeUs + mediaTimeUs;
+ }
+
+ public int getDuration() {
+ return (int)((mDurationUs + 500) / 1000);
+ }
+
+ public int getCurrentPosition() {
+ if (mVideoCodecStates == null) {
+ return 0;
+ }
+
+ long positionUs = 0;
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ long trackPositionUs = state.getCurrentPositionUs();
+
+ if (trackPositionUs > positionUs) {
+ positionUs = trackPositionUs;
+ }
+ }
+ return (int)((positionUs + 500) / 1000);
+ }
+
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaControllerTest.java b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
new file mode 100644
index 0000000..a153c4d
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaControllerTest.java
@@ -0,0 +1,388 @@
+/*
+ * 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.AudioManager;
+import android.media.Rating;
+import android.media.VolumeProvider;
+import android.media.session.MediaController;
+import android.media.session.MediaSession;
+import android.media.session.PlaybackState.CustomAction;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ResultReceiver;
+import android.test.AndroidTestCase;
+
+/**
+ * Test {@link android.media.session.MediaController}.
+ */
+public class MediaControllerTest extends AndroidTestCase {
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 3000L;
+ private static final String SESSION_TAG = "test-session";
+ private static final String EXTRAS_KEY = "test-key";
+ private static final String EXTRAS_VALUE = "test-val";
+
+ private final Object mWaitLock = new Object();
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private MediaSession mSession;
+ private MediaSessionCallback mCallback = new MediaSessionCallback();
+ private MediaController mController;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSession = new MediaSession(getContext(), SESSION_TAG);
+ mSession.setCallback(mCallback, mHandler);
+ mController = mSession.getController();
+ }
+
+ public void testSendCommand() throws Exception {
+ synchronized (mWaitLock) {
+ mCallback.reset();
+ final String command = "test-command";
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+ mController.sendCommand(command, extras, new ResultReceiver(null));
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCommandCalled);
+ assertNotNull(mCallback.mCommandCallback);
+ assertEquals(command, mCallback.mCommand);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+ }
+ }
+
+ public void testVolumeControl() throws Exception {
+ VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_ABSOLUTE, 11, 5) {
+ @Override
+ public void onSetVolumeTo(int volume) {
+ synchronized (mWaitLock) {
+ setCurrentVolume(volume);
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onAdjustVolume(int direction) {
+ synchronized (mWaitLock) {
+ switch (direction) {
+ case AudioManager.ADJUST_LOWER:
+ setCurrentVolume(getCurrentVolume() - 1);
+ break;
+ case AudioManager.ADJUST_RAISE:
+ setCurrentVolume(getCurrentVolume() + 1);
+ break;
+ }
+ mWaitLock.notify();
+ }
+ }
+ };
+ mSession.setPlaybackToRemote(vp);
+
+ synchronized (mWaitLock) {
+ // test setVolumeTo
+ mController.setVolumeTo(7, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(7, vp.getCurrentVolume());
+
+ // test adjustVolume
+ mController.adjustVolume(AudioManager.ADJUST_LOWER, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(6, vp.getCurrentVolume());
+
+ mController.adjustVolume(AudioManager.ADJUST_RAISE, 0);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertEquals(7, vp.getCurrentVolume());
+ }
+ }
+
+ public void testTransportControlsAndMediaSessionCallback() throws Exception {
+ MediaController.TransportControls controls = mController.getTransportControls();
+ synchronized (mWaitLock) {
+ mCallback.reset();
+ controls.play();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayCalled);
+
+ mCallback.reset();
+ controls.pause();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPauseCalled);
+
+ mCallback.reset();
+ controls.stop();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnStopCalled);
+
+ mCallback.reset();
+ controls.fastForward();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnFastForwardCalled);
+
+ mCallback.reset();
+ controls.rewind();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnRewindCalled);
+
+ mCallback.reset();
+ controls.skipToPrevious();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToPreviousCalled);
+
+ mCallback.reset();
+ controls.skipToNext();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToNextCalled);
+
+ mCallback.reset();
+ final long seekPosition = 1000;
+ controls.seekTo(seekPosition);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSeekToCalled);
+ assertEquals(seekPosition, mCallback.mSeekPosition);
+
+ mCallback.reset();
+ final Rating rating = Rating.newStarRating(Rating.RATING_5_STARS, 3f);
+ controls.setRating(rating);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSetRatingCalled);
+ assertEquals(rating.getRatingStyle(), mCallback.mRating.getRatingStyle());
+ assertEquals(rating.getStarRating(), mCallback.mRating.getStarRating());
+
+ mCallback.reset();
+ final String mediaId = "test-media-id";
+ final Bundle extras = new Bundle();
+ extras.putString(EXTRAS_KEY, EXTRAS_VALUE);
+ controls.playFromMediaId(mediaId, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromMediaIdCalled);
+ assertEquals(mediaId, mCallback.mMediaId);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final String query = "test-query";
+ controls.playFromSearch(query, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlayFromSearchCalled);
+ assertEquals(query, mCallback.mQuery);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final String action = "test-action";
+ controls.sendCustomAction(action, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCustomActionCalled);
+ assertEquals(action, mCallback.mAction);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ mCallback.mOnCustomActionCalled = false;
+ final CustomAction customAction =
+ new CustomAction.Builder(action, action, -1).setExtras(extras).build();
+ controls.sendCustomAction(customAction, extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnCustomActionCalled);
+ assertEquals(action, mCallback.mAction);
+ assertEquals(EXTRAS_VALUE, mCallback.mExtras.getString(EXTRAS_KEY));
+
+ mCallback.reset();
+ final long queueItemId = 1000;
+ controls.skipToQueueItem(queueItemId);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSkipToQueueItemCalled);
+ assertEquals(queueItemId, mCallback.mQueueItemId);
+ }
+ }
+
+ private class MediaSessionCallback extends MediaSession.Callback {
+ private volatile long mSeekPosition;
+ private volatile long mQueueItemId;
+ private volatile Rating mRating;
+ private volatile String mMediaId;
+ private volatile String mQuery;
+ private volatile String mAction;
+ private volatile String mCommand;
+ private volatile Bundle mExtras;
+ private volatile ResultReceiver mCommandCallback;
+
+ private volatile boolean mOnPlayCalled;
+ private volatile boolean mOnPauseCalled;
+ private volatile boolean mOnStopCalled;
+ private volatile boolean mOnFastForwardCalled;
+ private volatile boolean mOnRewindCalled;
+ private volatile boolean mOnSkipToPreviousCalled;
+ private volatile boolean mOnSkipToNextCalled;
+ private volatile boolean mOnSeekToCalled;
+ private volatile boolean mOnSkipToQueueItemCalled;
+ private volatile boolean mOnSetRatingCalled;
+ private volatile boolean mOnPlayFromMediaIdCalled;
+ private volatile boolean mOnPlayFromSearchCalled;
+ private volatile boolean mOnCustomActionCalled;
+ private volatile boolean mOnCommandCalled;
+
+ public void reset() {
+ mSeekPosition = -1;
+ mQueueItemId = -1;
+ mRating = null;
+ mMediaId = null;
+ mQuery = null;
+ mAction = null;
+ mExtras = null;
+ mCommand = null;
+ mCommandCallback = null;
+
+ mOnPlayCalled = false;
+ mOnPauseCalled = false;
+ mOnStopCalled = false;
+ mOnFastForwardCalled = false;
+ mOnRewindCalled = false;
+ mOnSkipToPreviousCalled = false;
+ mOnSkipToNextCalled = false;
+ mOnSkipToQueueItemCalled = false;
+ mOnSeekToCalled = false;
+ mOnSetRatingCalled = false;
+ mOnPlayFromMediaIdCalled = false;
+ mOnPlayFromSearchCalled = false;
+ mOnCustomActionCalled = false;
+ mOnCommandCalled = false;
+ }
+
+ @Override
+ public void onPlay() {
+ synchronized (mWaitLock) {
+ mOnPlayCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ synchronized (mWaitLock) {
+ mOnPauseCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ synchronized (mWaitLock) {
+ mOnStopCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onFastForward() {
+ synchronized (mWaitLock) {
+ mOnFastForwardCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onRewind() {
+ synchronized (mWaitLock) {
+ mOnRewindCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ synchronized (mWaitLock) {
+ mOnSkipToPreviousCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToNext() {
+ synchronized (mWaitLock) {
+ mOnSkipToNextCalled = true;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSeekTo(long pos) {
+ synchronized (mWaitLock) {
+ mOnSeekToCalled = true;
+ mSeekPosition = pos;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSetRating(Rating rating) {
+ synchronized (mWaitLock) {
+ mOnSetRatingCalled = true;
+ mRating = rating;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPlayFromMediaIdCalled = true;
+ mMediaId = mediaId;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onPlayFromSearch(String query, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnPlayFromSearchCalled = true;
+ mQuery = query;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnCustomActionCalled= true;
+ mAction = action;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSkipToQueueItem(long id) {
+ synchronized (mWaitLock) {
+ mOnSkipToQueueItemCalled = true;
+ mQueueItemId = id;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onCommand(String command, Bundle extras, ResultReceiver cb) {
+ synchronized (mWaitLock) {
+ mOnCommandCalled = true;
+ mCommand = command;
+ mExtras = extras;
+ mCommandCallback = cb;
+ mWaitLock.notify();
+ }
+ }
+ }
+}
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/MediaRecorderTest.java b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
index 83d5837..216e21b 100644
--- a/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaRecorderTest.java
@@ -38,7 +38,6 @@
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;
@@ -53,8 +52,6 @@
private static final int RECORDED_DUR_TOLERANCE_MS = 1000;
private static final int VIDEO_WIDTH = 176;
private static final int VIDEO_HEIGHT = 144;
- private static int mVideoWidth = VIDEO_WIDTH;
- private static int mVideoHeight = VIDEO_HEIGHT;
private static final int VIDEO_BIT_RATE_IN_BPS = 128000;
private static final double VIDEO_TIMELAPSE_CAPTURE_RATE_FPS = 1.0;
private static final int AUDIO_BIT_RATE_IN_BPS = 12200;
@@ -208,7 +205,6 @@
int durMs = timelapse? RECORD_TIME_LAPSE_MS: RECORD_TIME_MS;
for (int cameraId = 0; cameraId < nCamera; cameraId++) {
mCamera = Camera.open(cameraId);
- setSupportedResolution(mCamera);
recordVideoUsingCamera(mCamera, OUTPUT_PATH, durMs, timelapse);
mCamera.release();
mCamera = null;
@@ -216,21 +212,6 @@
}
}
- private void setSupportedResolution(Camera camera) {
- Camera.Parameters parameters = camera.getParameters();
- List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
- for (Camera.Size size : previewSizes)
- {
- if (size.width == VIDEO_WIDTH && size.height == VIDEO_HEIGHT) {
- mVideoWidth = VIDEO_WIDTH;
- mVideoHeight = VIDEO_HEIGHT;
- return;
- }
- }
- mVideoWidth = previewSizes.get(0).width;
- mVideoHeight = previewSizes.get(0).height;
- }
-
private void recordVideoUsingCamera(
Camera camera, String fileName, int durMs, boolean timelapse) throws Exception {
// FIXME:
@@ -247,7 +228,7 @@
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.DEFAULT);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mMediaRecorder.setVideoFrameRate(frameRate);
- mMediaRecorder.setVideoSize(mVideoWidth, mVideoHeight);
+ mMediaRecorder.setVideoSize(VIDEO_WIDTH, VIDEO_HEIGHT);
mMediaRecorder.setPreviewDisplay(mActivity.getSurfaceHolder().getSurface());
mMediaRecorder.setOutputFile(fileName);
mMediaRecorder.setLocation(LATITUDE, LONGITUDE);
diff --git a/tests/tests/media/src/android/media/cts/MediaSessionTest.java b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
index df68392..f4a30e8 100644
--- a/tests/tests/media/src/android/media/cts/MediaSessionTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSessionTest.java
@@ -26,21 +26,47 @@
import android.media.VolumeProvider;
import android.media.session.MediaController;
import android.media.session.MediaSession;
-import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Parcel;
import android.test.AndroidTestCase;
import java.util.ArrayList;
+import java.util.List;
import java.util.Set;
public class MediaSessionTest extends AndroidTestCase {
+ // The maximum time to wait for an operation.
+ private static final long TIME_OUT_MS = 3000L;
+ private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
+ private static final String TEST_SESSION_TAG = "test-session-tag";
+ private static final String TEST_KEY = "test-key";
+ private static final String TEST_VALUE = "test-val";
+ private static final int TEST_CURRENT_VOLUME = 10;
+ private static final int TEST_MAX_VOLUME = 11;
+ private static final long TEST_QUEUE_ID = 12L;
+ private static final long TEST_ACTION = 55L;
+
private AudioManager mAudioManager;
+ private Handler mHandler = new Handler(Looper.getMainLooper());
+ private Object mWaitLock = new Object();
+ private MediaControllerCallback mCallback = new MediaControllerCallback();
+ private MediaSession mSession;
@Override
protected void setUp() throws Exception {
super.setUp();
mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // It is OK to call release() twice.
+ mSession.release();
+ super.tearDown();
}
/**
@@ -48,15 +74,31 @@
* initialized correctly.
*/
public void testCreateSession() throws Exception {
- String tag = "test session";
- MediaSession session = new MediaSession(getContext(), tag);
- assertNotNull(session.getSessionToken());
- assertFalse("New session should not be active", session.isActive());
+ assertNotNull(mSession.getSessionToken());
+ assertFalse("New session should not be active", mSession.isActive());
// Verify by getting the controller and checking all its fields
- MediaController controller = session.getController();
+ MediaController controller = mSession.getController();
assertNotNull(controller);
- verifyNewSession(controller, tag);
+ verifyNewSession(controller, TEST_SESSION_TAG);
+ }
+
+ /**
+ * Tests MediaSession.Token created in the constructor of MediaSession.
+ */
+ public void testSessionToken() throws Exception {
+ MediaSession.Token sessionToken = mSession.getSessionToken();
+
+ assertNotNull(sessionToken);
+ assertEquals(0, sessionToken.describeContents());
+
+ // Test writeToParcel
+ Parcel p = Parcel.obtain();
+ sessionToken.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ MediaSession.Token token = MediaSession.Token.CREATOR.createFromParcel(p);
+ assertEquals(token, sessionToken);
+ p.recycle();
}
/**
@@ -64,92 +106,179 @@
* controller.
*/
public void testConfigureSession() throws Exception {
- String tag = "test session";
- String key = "test-key";
- String val = "test-val";
- MediaSession session = new MediaSession(getContext(), tag);
- MediaController controller = session.getController();
+ MediaController controller = mSession.getController();
+ controller.registerCallback(mCallback, mHandler);
- // test setExtras
- Bundle extras = new Bundle();
- extras.putString(key, val);
- session.setExtras(extras);
- Bundle extrasOut = controller.getExtras();
- assertNotNull(extrasOut);
- assertEquals(val, extrasOut.get(key));
+ synchronized (mWaitLock) {
+ // test setExtras
+ mCallback.resetLocked();
+ final Bundle extras = new Bundle();
+ extras.putString(TEST_KEY, TEST_VALUE);
+ mSession.setExtras(extras);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnExtraChangedCalled);
- // test setFlags
- session.setFlags(5);
- assertEquals(5, controller.getFlags());
+ Bundle extrasOut = mCallback.mExtras;
+ assertNotNull(extrasOut);
+ assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
- // test setMetadata
- MediaMetadata metadata = new MediaMetadata.Builder().putString(key, val).build();
- session.setMetadata(metadata);
- MediaMetadata metadataOut = controller.getMetadata();
- assertNotNull(metadataOut);
- assertEquals(val, metadataOut.getString(key));
+ extrasOut = controller.getExtras();
+ assertNotNull(extrasOut);
+ assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
- // test setPlaybackState
- PlaybackState state = new PlaybackState.Builder().setActions(55).build();
- session.setPlaybackState(state);
- PlaybackState stateOut = controller.getPlaybackState();
- assertNotNull(stateOut);
- assertEquals(55L, stateOut.getActions());
+ // test setFlags
+ mSession.setFlags(5);
+ assertEquals(5, controller.getFlags());
- // test setPlaybackToRemote, do this before testing setPlaybackToLocal
- // to ensure it switches correctly.
- try {
- session.setPlaybackToRemote(null);
- fail("Expected IAE for setPlaybackToRemote(null)");
- } catch (IllegalArgumentException e) {
- // expected
+ // test setMetadata
+ mCallback.resetLocked();
+ MediaMetadata metadata =
+ new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build();
+ mSession.setMetadata(metadata);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnMetadataChangedCalled);
+
+ MediaMetadata metadataOut = mCallback.mMediaMetadata;
+ assertNotNull(metadataOut);
+ assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+ metadataOut = controller.getMetadata();
+ assertNotNull(metadataOut);
+ assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
+
+ // test setPlaybackState
+ mCallback.resetLocked();
+ PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build();
+ mSession.setPlaybackState(state);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnPlaybackStateChangedCalled);
+
+ PlaybackState stateOut = mCallback.mPlaybackState;
+ assertNotNull(stateOut);
+ assertEquals(TEST_ACTION, stateOut.getActions());
+
+ stateOut = controller.getPlaybackState();
+ assertNotNull(stateOut);
+ assertEquals(TEST_ACTION, stateOut.getActions());
+
+ // test setQueue and setQueueTitle
+ mCallback.resetLocked();
+ List<MediaSession.QueueItem> queue = new ArrayList<MediaSession.QueueItem>();
+ MediaSession.QueueItem item = new MediaSession.QueueItem(new MediaDescription.Builder()
+ .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID);
+ queue.add(item);
+ mSession.setQueue(queue);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueChangedCalled);
+
+ mSession.setQueueTitle(TEST_VALUE);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+ assertEquals(TEST_VALUE, mCallback.mTitle);
+ assertEquals(queue.size(), mCallback.mQueue.size());
+ assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
+ assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
+
+ assertEquals(TEST_VALUE, controller.getQueueTitle());
+ assertEquals(queue.size(), controller.getQueue().size());
+ assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
+ assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
+
+ mCallback.resetLocked();
+ mSession.setQueue(null);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueChangedCalled);
+
+ mSession.setQueueTitle(null);
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnQueueTitleChangedCalled);
+
+ assertNull(mCallback.mTitle);
+ assertNull(mCallback.mQueue);
+ assertNull(controller.getQueueTitle());
+ assertNull(controller.getQueue());
+
+ // test setSessionActivity
+ Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
+ PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
+ mSession.setSessionActivity(pi);
+ assertEquals(pi, controller.getSessionActivity());
+
+ // test setActivity
+ mSession.setActive(true);
+ assertTrue(mSession.isActive());
+
+ // test release
+ mCallback.resetLocked();
+ mSession.release();
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnSessionDestroyedCalled);
}
- VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED, 11, 11) {};
- session.setPlaybackToRemote(vp);
- MediaController.PlaybackInfo info = controller.getPlaybackInfo();
- assertNotNull(info);
- assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
- assertEquals(11, info.getMaxVolume());
- assertEquals(11, info.getCurrentVolume());
- assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
-
- // test setPlaybackToLocal
- AudioAttributes attrs = new AudioAttributes.Builder().addTag(val).build();
- session.setPlaybackToLocal(attrs);
- info = controller.getPlaybackInfo();
- assertNotNull(info);
- assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
- Set<String> tags = info.getAudioAttributes().getTags();
- assertNotNull(tags);
- assertTrue(tags.contains(val));
-
- // test setQueue and setQueueTitle
- ArrayList<MediaSession.QueueItem> queue = new ArrayList<MediaSession.QueueItem>();
- MediaSession.QueueItem item = new MediaSession.QueueItem(new MediaDescription.Builder()
- .setMediaId(val).setTitle("title").build(), 11);
- queue.add(item);
- session.setQueue(queue);
- session.setQueueTitle(val);
-
- assertEquals(val, controller.getQueueTitle());
- assertEquals(1, controller.getQueue().size());
- assertEquals(11, controller.getQueue().get(0).getQueueId());
- assertEquals(val, controller.getQueue().get(0).getDescription().getMediaId());
-
- session.setQueue(null);
- session.setQueueTitle(null);
-
- assertNull(controller.getQueueTitle());
- assertNull(controller.getQueue());
-
- // test setSessionActivity
- Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
- PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0);
- session.setSessionActivity(pi);
- assertEquals(pi, controller.getSessionActivity());
}
/**
+ * Tests for setPlaybackToLocal and setPlaybackToRemote.
+ */
+ public void testPlaybackToLocalAndRemote() throws Exception {
+ MediaController controller = mSession.getController();
+ controller.registerCallback(mCallback, mHandler);
+
+ synchronized (mWaitLock) {
+ // test setPlaybackToRemote, do this before testing setPlaybackToLocal
+ // to ensure it switches correctly.
+ mCallback.resetLocked();
+ try {
+ mSession.setPlaybackToRemote(null);
+ fail("Expected IAE for setPlaybackToRemote(null)");
+ } catch (IllegalArgumentException e) {
+ // expected
+ }
+ VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED,
+ TEST_MAX_VOLUME, TEST_CURRENT_VOLUME) {};
+ mSession.setPlaybackToRemote(vp);
+
+ MediaController.PlaybackInfo info = null;
+ for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
+ mCallback.mOnAudioInfoChangedCalled = false;
+ mWaitLock.wait(TIME_OUT_MS);
+ assertTrue(mCallback.mOnAudioInfoChangedCalled);
+ info = mCallback.mPlaybackInfo;
+ if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
+ && info.getMaxVolume() == TEST_MAX_VOLUME
+ && info.getPlaybackType() == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE
+ && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED) {
+ break;
+ }
+ }
+ assertNotNull(info);
+ assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+ assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+ assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+ assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
+ assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
+ assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
+ assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
+
+ // test setPlaybackToLocal
+ AudioAttributes attrs = new AudioAttributes.Builder().addTag(TEST_VALUE).build();
+ mSession.setPlaybackToLocal(attrs);
+
+ info = controller.getPlaybackInfo();
+ assertNotNull(info);
+ assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
+ Set<String> tags = info.getAudioAttributes().getTags();
+ assertNotNull(tags);
+ assertTrue(tags.contains(TEST_VALUE));
+ }
+ }
+
+
+ /**
* Verifies that a new session hasn't had any configuration bits set yet.
*
* @param controller The controller for the session
@@ -180,4 +309,100 @@
assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
info.getCurrentVolume());
}
+
+ private class MediaControllerCallback extends MediaController.Callback {
+ private volatile boolean mOnPlaybackStateChangedCalled;
+ private volatile boolean mOnMetadataChangedCalled;
+ private volatile boolean mOnQueueChangedCalled;
+ private volatile boolean mOnQueueTitleChangedCalled;
+ private volatile boolean mOnExtraChangedCalled;
+ private volatile boolean mOnAudioInfoChangedCalled;
+ private volatile boolean mOnSessionDestroyedCalled;
+
+ private volatile PlaybackState mPlaybackState;
+ private volatile MediaMetadata mMediaMetadata;
+ private volatile List<MediaSession.QueueItem> mQueue;
+ private volatile CharSequence mTitle;
+ private volatile Bundle mExtras;
+ private volatile MediaController.PlaybackInfo mPlaybackInfo;
+
+ public void resetLocked() {
+ mOnPlaybackStateChangedCalled = false;
+ mOnMetadataChangedCalled = false;
+ mOnQueueChangedCalled = false;
+ mOnQueueTitleChangedCalled = false;
+ mOnExtraChangedCalled = false;
+ mOnAudioInfoChangedCalled = false;
+ mOnSessionDestroyedCalled = false;
+
+ mPlaybackState = null;
+ mMediaMetadata = null;
+ mQueue = null;
+ mTitle = null;
+ mExtras = null;
+ mPlaybackInfo = null;
+ }
+
+ @Override
+ public void onPlaybackStateChanged(PlaybackState state) {
+ synchronized (mWaitLock) {
+ mOnPlaybackStateChangedCalled = true;
+ mPlaybackState = state;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onMetadataChanged(MediaMetadata metadata) {
+ synchronized (mWaitLock) {
+ mOnMetadataChangedCalled = true;
+ mMediaMetadata = metadata;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onQueueChanged(List<MediaSession.QueueItem> queue) {
+ synchronized (mWaitLock) {
+ mOnQueueChangedCalled = true;
+ mQueue = queue;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onQueueTitleChanged(CharSequence title) {
+ synchronized (mWaitLock) {
+ mOnQueueTitleChangedCalled = true;
+ mTitle = title;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onExtrasChanged(Bundle extras) {
+ synchronized (mWaitLock) {
+ mOnExtraChangedCalled = true;
+ mExtras = extras;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onAudioInfoChanged(MediaController.PlaybackInfo info) {
+ synchronized (mWaitLock) {
+ mOnAudioInfoChangedCalled = true;
+ mPlaybackInfo = info;
+ mWaitLock.notify();
+ }
+ }
+
+ @Override
+ public void onSessionDestroyed() {
+ synchronized (mWaitLock) {
+ mOnSessionDestroyedCalled = true;
+ mWaitLock.notify();
+ }
+ }
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaTimeProvider.java b/tests/tests/media/src/android/media/cts/MediaTimeProvider.java
new file mode 100644
index 0000000..4f6837e
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaTimeProvider.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.media.cts;
+
+/*
+ * Interface used by CodecState to retrieve Media timing info from parent Player
+ */
+public interface MediaTimeProvider {
+ public long getNowUs();
+ public long getRealTimeUsForMediaTime(long mediaTimeUs);
+}
diff --git a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
index fea78c9..e7b08e9 100644
--- a/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/NativeDecoderTest.java
@@ -91,7 +91,7 @@
testExtractor(R.raw.sinesweepwav);
testExtractor(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz);
- testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz);
+ testExtractor(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz);
testExtractor(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz);
testExtractor(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz);
testExtractor(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -193,7 +193,7 @@
testDecoder(R.raw.sinesweepwav) +
testDecoder(R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
- testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+ testDecoder(R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
testDecoder(R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
testDecoder(R.raw.video_176x144_3gp_h263_300kbps_12fps_aac_mono_24kbps_11025hz) +
testDecoder(R.raw.video_480x360_mp4_mpeg4_860kbps_25fps_aac_stereo_128kbps_44100hz);
@@ -391,7 +391,7 @@
testVideoPlayback(
R.raw.video_1280x720_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz) +
testVideoPlayback(
- R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_48000hz) +
+ R.raw.video_1280x720_webm_vp8_333kbps_25fps_vorbis_stereo_128kbps_44100hz) +
testVideoPlayback(
R.raw.video_1280x720_webm_vp9_309kbps_25fps_vorbis_stereo_128kbps_48000hz) +
testVideoPlayback(
diff --git a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
index 3ba1ce8..86bab41 100644
--- a/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
+++ b/tests/tests/media/src/android/media/cts/NonBlockingAudioTrack.java
@@ -18,8 +18,11 @@
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
+import android.media.AudioAttributes;
import android.util.Log;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.util.LinkedList;
/**
@@ -31,22 +34,18 @@
public class NonBlockingAudioTrack {
private static final String TAG = NonBlockingAudioTrack.class.getSimpleName();
- class QueueElem {
- byte[] data;
- int offset;
+ class QueueElement {
+ ByteBuffer data;
int size;
}
private AudioTrack mAudioTrack;
- private boolean mWriteMorePending = false;
private int mSampleRate;
- private int mFrameSize;
- private int mBufferSizeInFrames;
- private int mNumFramesSubmitted = 0;
private int mNumBytesQueued = 0;
- private LinkedList<QueueElem> mQueue = new LinkedList<QueueElem>();
+ private LinkedList<QueueElement> mQueue = new LinkedList<QueueElement>();
- public NonBlockingAudioTrack(int sampleRate, int channelCount) {
+ public NonBlockingAudioTrack(int sampleRate, int channelCount, boolean hwAvSync,
+ int audioSessionId) {
int channelConfig;
switch (channelCount) {
case 1:
@@ -70,17 +69,31 @@
int bufferSize = 2 * minBufferSize;
- mAudioTrack = new AudioTrack(
- AudioManager.STREAM_MUSIC,
- sampleRate,
- channelConfig,
- AudioFormat.ENCODING_PCM_16BIT,
- bufferSize,
- AudioTrack.MODE_STREAM);
+ if (!hwAvSync) {
+ mAudioTrack = new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ sampleRate,
+ channelConfig,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize,
+ AudioTrack.MODE_STREAM);
+ }
+ else {
+ // build AudioTrack using Audio Attributes and FLAG_HW_AV_SYNC
+ AudioAttributes audioAttributes = (new AudioAttributes.Builder())
+ .setLegacyStreamType(AudioManager.STREAM_MUSIC)
+ .setFlags(AudioAttributes.FLAG_HW_AV_SYNC)
+ .build();
+ AudioFormat audioFormat = (new AudioFormat.Builder())
+ .setChannelMask(channelConfig)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setSampleRate(sampleRate)
+ .build();
+ mAudioTrack = new AudioTrack(audioAttributes, audioFormat, bufferSize,
+ AudioTrack.MODE_STREAM, audioSessionId);
+ }
mSampleRate = sampleRate;
- mFrameSize = 2 * channelCount;
- mBufferSizeInFrames = bufferSize / mFrameSize;
}
public long getAudioTimeUs() {
@@ -98,105 +111,82 @@
}
public void stop() {
- cancelWriteMore();
-
mAudioTrack.stop();
- mNumFramesSubmitted = 0;
mQueue.clear();
mNumBytesQueued = 0;
}
public void pause() {
- cancelWriteMore();
-
mAudioTrack.pause();
}
- public void release() {
- cancelWriteMore();
+ public void flush() {
+ if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {
+ return;
+ }
+ mAudioTrack.flush();
+ mQueue.clear();
+ mNumBytesQueued = 0;
+ }
+ public void release() {
+ mQueue.clear();
+ mNumBytesQueued = 0;
mAudioTrack.release();
mAudioTrack = null;
}
public void process() {
- mWriteMorePending = false;
- writeMore();
+ while (!mQueue.isEmpty()) {
+ QueueElement element = mQueue.peekFirst();
+ int written = mAudioTrack.write(element.data, element.size,
+ AudioTrack.WRITE_NON_BLOCKING);
+ if (written < 0) {
+ throw new RuntimeException("Audiotrack.write() failed.");
+ }
+
+ mNumBytesQueued -= written;
+ element.size -= written;
+ if (element.size != 0) {
+ break;
+ }
+ mQueue.removeFirst();
+ }
}
public int getPlayState() {
return mAudioTrack.getPlayState();
}
- private void writeMore() {
- if (mQueue.isEmpty()) {
- return;
- }
+ public void write(ByteBuffer data, int size, long pts) {
+ // create timestamp header
+ final int headerSize = 16;
+ ByteBuffer avSyncHeader;
+ avSyncHeader = ByteBuffer.allocate(headerSize);
+ avSyncHeader.order(ByteOrder.BIG_ENDIAN);
+ avSyncHeader.putInt(0x55550001);
+ avSyncHeader.putInt(size);
+ avSyncHeader.putLong(pts);
+ avSyncHeader.position(0);
- int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
- int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
- int numFramesAvailableToWrite = mBufferSizeInFrames - numFramesPending;
- int numBytesAvailableToWrite = numFramesAvailableToWrite * mFrameSize;
+ QueueElement headerElement = new QueueElement();
+ headerElement.data = avSyncHeader;
+ headerElement.size = headerSize;
- while (numBytesAvailableToWrite > 0) {
- QueueElem elem = mQueue.peekFirst();
+ // accumulate size written to queue
+ mNumBytesQueued += headerSize;
+ mQueue.add(headerElement);
- int numBytes = elem.size;
- if (numBytes > numBytesAvailableToWrite) {
- numBytes = numBytesAvailableToWrite;
- }
-
- int written = mAudioTrack.write(elem.data, elem.offset, numBytes);
- assert(written == numBytes);
-
- mNumFramesSubmitted += written / mFrameSize;
-
- elem.size -= numBytes;
- numBytesAvailableToWrite -= numBytes;
- mNumBytesQueued -= numBytes;
-
- if (elem.size == 0) {
- mQueue.removeFirst();
-
- if (mQueue.isEmpty()) {
- break;
- }
- } else {
- elem.offset += numBytes;
- }
- }
-
- if (!mQueue.isEmpty()) {
- scheduleWriteMore();
- }
- }
-
- private void scheduleWriteMore() {
- if (mWriteMorePending) {
- return;
- }
-
- int numFramesPlayed = mAudioTrack.getPlaybackHeadPosition();
- int numFramesPending = mNumFramesSubmitted - numFramesPlayed;
- int pendingDurationMs = 1000 * numFramesPending / mSampleRate;
-
- mWriteMorePending = true;
- }
-
- private void cancelWriteMore() {
- mWriteMorePending = false;
- }
-
- public void write(byte[] data, int size) {
- QueueElem elem = new QueueElem();
- elem.data = data;
- elem.offset = 0;
- elem.size = size;
+ // create payload element
+ QueueElement element = new QueueElement();
+ element.data = data;
+ element.size = size;
+ data.position(0);
// accumulate size written to queue
mNumBytesQueued += size;
- mQueue.add(elem);
+ mQueue.add(element);
}
}
diff --git a/tests/tests/media/src/android/media/cts/OutputSurface.java b/tests/tests/media/src/android/media/cts/OutputSurface.java
index fc8ad9c..c87326d 100644
--- a/tests/tests/media/src/android/media/cts/OutputSurface.java
+++ b/tests/tests/media/src/android/media/cts/OutputSurface.java
@@ -70,7 +70,7 @@
eglSetup(width, height);
makeCurrent();
- setup();
+ setup(this);
}
/**
@@ -78,14 +78,18 @@
* new one). Creates a Surface that can be passed to MediaCodec.configure().
*/
public OutputSurface() {
- setup();
+ setup(this);
+ }
+
+ public OutputSurface(final SurfaceTexture.OnFrameAvailableListener listener) {
+ setup(listener);
}
/**
* Creates instances of TextureRender and SurfaceTexture, and a Surface associated
* with the SurfaceTexture.
*/
- private void setup() {
+ private void setup(SurfaceTexture.OnFrameAvailableListener listener) {
mTextureRender = new TextureRender();
mTextureRender.surfaceCreated();
@@ -107,7 +111,7 @@
//
// Java language note: passing "this" out of a constructor is generally unwise,
// but we should be able to get away with it here.
- mSurfaceTexture.setOnFrameAvailableListener(this);
+ mSurfaceTexture.setOnFrameAvailableListener(listener);
mSurface = new Surface(mSurfaceTexture);
}
@@ -285,6 +289,11 @@
mTextureRender.drawFrame(mSurfaceTexture);
}
+ public void latchImage() {
+ mTextureRender.checkGlError("before updateTexImage");
+ mSurfaceTexture.updateTexImage();
+ }
+
@Override
public void onFrameAvailable(SurfaceTexture st) {
if (VERBOSE) Log.d(TAG, "new frame available");
diff --git a/tests/tests/media/src/android/media/cts/PostProcTestBase.java b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
index b034763..ef87662 100644
--- a/tests/tests/media/src/android/media/cts/PostProcTestBase.java
+++ b/tests/tests/media/src/android/media/cts/PostProcTestBase.java
@@ -16,7 +16,9 @@
package android.media.cts;
+import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.AudioManager;
import android.media.audiofx.AudioEffect;
import android.os.Looper;
import android.test.AndroidTestCase;
@@ -29,6 +31,8 @@
protected Looper mLooper = null;
protected final Object mLock = new Object();
protected int mChangedParameter = -1;
+ protected final static String BUNDLE_VOLUME_EFFECT_UUID =
+ "119341a0-8469-11df-81f9-0002a5d5c51b";
protected boolean hasAudioOutput() {
return getContext().getPackageManager().hasSystemFeature(
@@ -62,4 +66,13 @@
}
return AudioEffect.isEffectTypeAvailable(AudioEffect.EFFECT_TYPE_ENV_REVERB);
}
+
+ protected int getSessionId() {
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("could not get AudioManager", am);
+ int sessionId = am.generateAudioSessionId();
+ assertTrue("Could not generate session id", sessionId>0);
+ return sessionId;
+ }
+
}
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..35e88f0
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/StubMediaBrowserService.java
@@ -0,0 +1,88 @@
+/*
+ * 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.media.session.MediaSession;
+import android.os.Bundle;
+import android.service.media.MediaBrowserService;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import junit.framework.Assert;
+
+/**
+ * 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";
+ static final String MEDIA_ID_CHILDREN_DELAYED = "test_media_id_children_delayed";
+ static final String[] MEDIA_ID_CHILDREN = new String[] {
+ "test_media_id_children_0", "test_media_id_children_1",
+ "test_media_id_children_2", "test_media_id_children_3",
+ MEDIA_ID_CHILDREN_DELAYED
+ };
+
+ static StubMediaBrowserService sInstance;
+
+ /* package private */ static MediaSession sSession;
+ private Bundle mExtras;
+ private Result<List<MediaItem>> mPendingResult;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ sInstance = this;
+ sSession = new MediaSession(this, "StubMediaBrowserService");
+ 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) {
+ List<MediaItem> mediaItems = new ArrayList<>();
+ if (MEDIA_ID_ROOT.equals(parentMediaId)) {
+ for (String id : MEDIA_ID_CHILDREN) {
+ mediaItems.add(new MediaItem(new MediaDescription.Builder()
+ .setMediaId(id).build(), MediaItem.FLAG_BROWSABLE));
+ }
+ result.sendResult(mediaItems);
+ } else if (MEDIA_ID_CHILDREN_DELAYED.equals(parentMediaId)) {
+ Assert.assertNull(mPendingResult);
+ mPendingResult = result;
+ result.detach();
+ }
+ }
+
+ public void sendDelayedNotifyChildrenChanged() {
+ if (mPendingResult != null) {
+ mPendingResult.sendResult(Collections.<MediaItem>emptyList());
+ mPendingResult = null;
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/VideoEncoderTest.java b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
new file mode 100644
index 0000000..858e47c
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/VideoEncoderTest.java
@@ -0,0 +1,1606 @@
+/*
+ * Copyright 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.media.cts.CodecUtils;
+
+import android.cts.util.MediaUtils;
+import android.graphics.ImageFormat;
+import android.graphics.SurfaceTexture;
+import android.media.Image;
+import android.media.MediaCodec;
+import android.media.MediaCodec.BufferInfo;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaExtractor;
+import android.media.MediaFormat;
+import android.net.Uri;
+import android.util.Log;
+import android.util.Pair;
+import android.util.Range;
+import android.util.Size;
+import android.view.Surface;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.Set;
+
+public class VideoEncoderTest extends MediaPlayerTestBase {
+ private static final int MAX_SAMPLE_SIZE = 256 * 1024;
+ private static final String TAG = "VideoEncoderTest";
+ private static final long FRAME_TIMEOUT_MS = 1000;
+
+ private static final String SOURCE_URL =
+ "android.resource://com.android.cts.media/raw/video_480x360_mp4_h264_871kbps_30fps";
+
+ private final boolean DEBUG = false;
+
+ class VideoStorage {
+ private LinkedList<Pair<ByteBuffer, BufferInfo>> mStream;
+ private MediaFormat mFormat;
+ private int mInputBufferSize;
+
+ public VideoStorage() {
+ mStream = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
+ }
+
+ public void setFormat(MediaFormat format) {
+ mFormat = format;
+ }
+
+ public void addBuffer(ByteBuffer buffer, BufferInfo info) {
+ ByteBuffer savedBuffer = ByteBuffer.allocate(info.size);
+ savedBuffer.put(buffer);
+ if (info.size > mInputBufferSize) {
+ mInputBufferSize = info.size;
+ }
+ BufferInfo savedInfo = new BufferInfo();
+ savedInfo.set(0, savedBuffer.position(), info.presentationTimeUs, info.flags);
+ mStream.addLast(Pair.create(savedBuffer, savedInfo));
+ }
+
+ private void play(MediaCodec decoder, Surface surface) {
+ decoder.reset();
+ final Object condition = new Object();
+ final Iterator<Pair<ByteBuffer, BufferInfo>> it = mStream.iterator();
+ decoder.setCallback(new MediaCodec.Callback() {
+ public void onOutputBufferAvailable(MediaCodec codec, int ix, BufferInfo info) {
+ codec.releaseOutputBuffer(ix, info.size > 0);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ synchronized (condition) {
+ condition.notifyAll();
+ }
+ }
+ }
+ public void onInputBufferAvailable(MediaCodec codec, int ix) {
+ if (it.hasNext()) {
+ Pair<ByteBuffer, BufferInfo> el = it.next();
+ el.first.clear();
+ try {
+ codec.getInputBuffer(ix).put(el.first);
+ } catch (java.nio.BufferOverflowException e) {
+ Log.e(TAG, "cannot fit " + el.first.limit()
+ + "-byte encoded buffer into "
+ + codec.getInputBuffer(ix).remaining()
+ + "-byte input buffer of " + codec.getName()
+ + " configured for " + codec.getInputFormat());
+ throw e;
+ }
+ BufferInfo info = el.second;
+ codec.queueInputBuffer(
+ ix, 0, info.size, info.presentationTimeUs, info.flags);
+ }
+ }
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ Log.i(TAG, "got codec exception", e);
+ fail("received codec error during decode" + e);
+ }
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.i(TAG, "got output format " + format);
+ }
+ });
+ mFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, mInputBufferSize);
+ decoder.configure(mFormat, surface, null /* crypto */, 0 /* flags */);
+ decoder.start();
+ synchronized (condition) {
+ try {
+ condition.wait();
+ } catch (InterruptedException e) {
+ fail("playback interrupted");
+ }
+ }
+ decoder.stop();
+ }
+
+ public void playAll(Surface surface) {
+ if (mFormat == null) {
+ Log.i(TAG, "no stream to play");
+ return;
+ }
+ String mime = mFormat.getString(MediaFormat.KEY_MIME);
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder()) {
+ continue;
+ }
+ MediaCodec codec = null;
+ try {
+ CodecCapabilities caps = info.getCapabilitiesForType(mime);
+ if (!caps.isFormatSupported(mFormat)) {
+ continue;
+ }
+ codec = MediaCodec.createByCodecName(info.getName());
+ } catch (IllegalArgumentException | IOException e) {
+ continue;
+ }
+ play(codec, surface);
+ codec.release();
+ }
+ }
+ }
+
+ abstract class VideoProcessorBase extends MediaCodec.Callback {
+ private static final String TAG = "VideoProcessorBase";
+
+ private MediaExtractor mExtractor;
+ private ByteBuffer mBuffer = ByteBuffer.allocate(MAX_SAMPLE_SIZE);
+ private int mTrackIndex = -1;
+ private boolean mSignaledDecoderEOS;
+
+ protected boolean mCompleted;
+ protected final Object mCondition = new Object();
+
+ protected MediaFormat mDecFormat;
+ protected MediaCodec mDecoder, mEncoder;
+
+ private VideoStorage mEncodedStream;
+ protected int mFrameRate = 0;
+ protected int mBitRate = 0;
+
+ protected void open(String path) throws IOException {
+ mExtractor = new MediaExtractor();
+ if (path.startsWith("android.resource://")) {
+ mExtractor.setDataSource(mContext, Uri.parse(path), null);
+ } else {
+ mExtractor.setDataSource(path);
+ }
+
+ for (int i = 0; i < mExtractor.getTrackCount(); i++) {
+ MediaFormat fmt = mExtractor.getTrackFormat(i);
+ String mime = fmt.getString(MediaFormat.KEY_MIME).toLowerCase();
+ if (mime.startsWith("video/")) {
+ mTrackIndex = i;
+ mDecFormat = fmt;
+ mExtractor.selectTrack(i);
+ break;
+ }
+ }
+ mEncodedStream = new VideoStorage();
+ assertTrue("file " + path + " has no video", mTrackIndex >= 0);
+ }
+
+ // returns true if encoder supports the size
+ protected boolean initCodecsAndConfigureEncoder(
+ String videoEncName, String outMime, int width, int height, int colorFormat)
+ throws IOException {
+ mDecFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ String videoDecName = mcl.findDecoderForFormat(mDecFormat);
+ Log.i(TAG, "decoder for " + mDecFormat + " is " + videoDecName);
+ mDecoder = MediaCodec.createByCodecName(videoDecName);
+ mEncoder = MediaCodec.createByCodecName(videoEncName);
+
+ mDecoder.setCallback(this);
+ mEncoder.setCallback(this);
+
+ VideoCapabilities encCaps =
+ mEncoder.getCodecInfo().getCapabilitiesForType(outMime).getVideoCapabilities();
+ if (!encCaps.isSizeSupported(width, height)) {
+ Log.i(TAG, videoEncName + " does not support size: " + width + "x" + height);
+ return false;
+ }
+
+ MediaFormat outFmt = MediaFormat.createVideoFormat(outMime, width, height);
+
+ {
+ int maxWidth = encCaps.getSupportedWidths().getUpper();
+ int maxHeight = encCaps.getSupportedHeightsFor(maxWidth).getUpper();
+ int frameRate = mFrameRate;
+ if (frameRate <= 0) {
+ int maxRate =
+ encCaps.getSupportedFrameRatesFor(maxWidth, maxHeight)
+ .getUpper().intValue();
+ frameRate = Math.min(30, maxRate);
+ }
+ outFmt.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
+
+ int bitRate = mBitRate;
+ if (bitRate <= 0) {
+ bitRate = encCaps.getBitrateRange().clamp(
+ (int)(encCaps.getBitrateRange().getUpper() /
+ Math.sqrt((double)maxWidth * maxHeight / width / height)));
+ }
+ outFmt.setInteger(MediaFormat.KEY_BIT_RATE, bitRate);
+
+ Log.d(TAG, "frame rate = " + frameRate + ", bit rate = " + bitRate);
+ }
+ outFmt.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
+ outFmt.setInteger(MediaFormat.KEY_COLOR_FORMAT, colorFormat);
+ mEncoder.configure(outFmt, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
+ Log.i(TAG, "encoder input format " + mEncoder.getInputFormat() + " from " + outFmt);
+ return true;
+ }
+
+ protected void close() {
+ if (mDecoder != null) {
+ mDecoder.release();
+ mDecoder = null;
+ }
+ if (mEncoder != null) {
+ mEncoder.release();
+ mEncoder = null;
+ }
+ if (mExtractor != null) {
+ mExtractor.release();
+ mExtractor = null;
+ }
+ }
+
+ // returns true if filled buffer
+ protected boolean fillDecoderInputBuffer(int ix) {
+ if (DEBUG) Log.v(TAG, "decoder received input #" + ix);
+ while (!mSignaledDecoderEOS) {
+ int track = mExtractor.getSampleTrackIndex();
+ if (track >= 0 && track != mTrackIndex) {
+ mExtractor.advance();
+ continue;
+ }
+ int size = mExtractor.readSampleData(mBuffer, 0);
+ if (size < 0) {
+ // queue decoder input EOS
+ if (DEBUG) Log.v(TAG, "queuing decoder EOS");
+ mDecoder.queueInputBuffer(
+ ix, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ mSignaledDecoderEOS = true;
+ } else {
+ mBuffer.limit(size);
+ mBuffer.position(0);
+ BufferInfo info = new BufferInfo();
+ info.set(
+ 0, mBuffer.limit(), mExtractor.getSampleTime(),
+ mExtractor.getSampleFlags());
+ mDecoder.getInputBuffer(ix).put(mBuffer);
+ if (DEBUG) Log.v(TAG, "queing input #" + ix + " for decoder with timestamp "
+ + info.presentationTimeUs);
+ mDecoder.queueInputBuffer(
+ ix, 0, mBuffer.limit(), info.presentationTimeUs, 0);
+ }
+ mExtractor.advance();
+ return true;
+ }
+ return false;
+ }
+
+ protected void emptyEncoderOutputBuffer(int ix, BufferInfo info) {
+ if (DEBUG) Log.v(TAG, "encoder received output #" + ix
+ + " (sz=" + info.size + ", f=" + info.flags
+ + ", ts=" + info.presentationTimeUs + ")");
+ mEncodedStream.addBuffer(mEncoder.getOutputBuffer(ix), info);
+ if (!mCompleted) {
+ mEncoder.releaseOutputBuffer(ix, false);
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ Log.d(TAG, "encoder received output EOS");
+ synchronized(mCondition) {
+ mCompleted = true;
+ mCondition.notifyAll(); // condition is always satisfied
+ }
+ }
+ }
+ }
+
+ protected void saveEncoderFormat(MediaFormat format) {
+ mEncodedStream.setFormat(format);
+ }
+
+ public void playBack(Surface surface) {
+ mEncodedStream.playAll(surface);
+ }
+
+ public void setFrameAndBitRates(int frameRate, int bitRate) {
+ mFrameRate = frameRate;
+ mBitRate = bitRate;
+ }
+
+ public abstract boolean processLoop(
+ String path, String outMime, String videoEncName,
+ int width, int height, boolean optional);
+ };
+
+ class VideoProcessor extends VideoProcessorBase {
+ private static final String TAG = "VideoProcessor";
+ private boolean mWorkInProgress;
+ private boolean mGotDecoderEOS;
+ private boolean mSignaledEncoderEOS;
+
+ private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
+ new LinkedList<Pair<Integer, BufferInfo>>();
+ private LinkedList<Integer> mEncInputBuffers = new LinkedList<Integer>();
+
+ private int mEncInputBufferSize = -1;
+
+ @Override
+ public boolean processLoop(
+ String path, String outMime, String videoEncName,
+ int width, int height, boolean optional) {
+ boolean skipped = true;
+ try {
+ open(path);
+ if (!initCodecsAndConfigureEncoder(
+ videoEncName, outMime, width, height,
+ CodecCapabilities.COLOR_FormatYUV420Flexible)) {
+ assertTrue("could not configure encoder for supported size", optional);
+ return !skipped;
+ }
+ skipped = false;
+
+ mDecoder.configure(mDecFormat, null /* surface */, null /* crypto */, 0);
+
+ mDecoder.start();
+ mEncoder.start();
+
+ // main loop - process GL ops as only main thread has GL context
+ while (!mCompleted) {
+ Pair<Integer, BufferInfo> decBuffer = null;
+ int encBuffer = -1;
+ synchronized (mCondition) {
+ try {
+ // wait for an encoder input buffer and a decoder output buffer
+ // Use a timeout to avoid stalling the test if it doesn't arrive.
+ if (!haveBuffers() && !mCompleted) {
+ mCondition.wait(FRAME_TIMEOUT_MS);
+ }
+ } catch (InterruptedException ie) {
+ fail("wait interrupted"); // shouldn't happen
+ }
+ if (mCompleted) {
+ break;
+ }
+ if (!haveBuffers()) {
+ fail("timed out after " + mBuffersToRender.size()
+ + " decoder output and " + mEncInputBuffers.size()
+ + " encoder input buffers");
+ }
+
+ if (DEBUG) Log.v(TAG, "got image");
+ decBuffer = mBuffersToRender.removeFirst();
+ encBuffer = mEncInputBuffers.removeFirst();
+ if (isEOSOnlyBuffer(decBuffer)) {
+ queueEncoderEOS(decBuffer, encBuffer);
+ continue;
+ }
+ mWorkInProgress = true;
+ }
+
+ if (mWorkInProgress) {
+ renderDecodedBuffer(decBuffer, encBuffer);
+ synchronized(mCondition) {
+ mWorkInProgress = false;
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("received exception " + e);
+ } finally {
+ close();
+ }
+ return !skipped;
+ }
+
+ @Override
+ public void onInputBufferAvailable(MediaCodec mediaCodec, int ix) {
+ if (mediaCodec == mDecoder) {
+ // fill input buffer from extractor
+ fillDecoderInputBuffer(ix);
+ } else if (mediaCodec == mEncoder) {
+ synchronized(mCondition) {
+ mEncInputBuffers.addLast(ix);
+ tryToPropagateEOS();
+ if (haveBuffers()) {
+ mCondition.notifyAll();
+ }
+ }
+ } else {
+ fail("received input buffer on " + mediaCodec.getName());
+ }
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ MediaCodec mediaCodec, int ix, BufferInfo info) {
+ if (mediaCodec == mDecoder) {
+ if (DEBUG) Log.v(TAG, "decoder received output #" + ix
+ + " (sz=" + info.size + ", f=" + info.flags
+ + ", ts=" + info.presentationTimeUs + ")");
+ // render output buffer from decoder
+ if (!mGotDecoderEOS) {
+ boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ // can release empty buffers now
+ if (info.size == 0) {
+ mDecoder.releaseOutputBuffer(ix, false /* render */);
+ ix = -1; // dummy index used by render to not render
+ }
+ synchronized(mCondition) {
+ if (ix < 0 && eos && mBuffersToRender.size() > 0) {
+ // move lone EOS flag to last buffer to be rendered
+ mBuffersToRender.peekLast().second.flags |=
+ MediaCodec.BUFFER_FLAG_END_OF_STREAM;
+ } else if (ix >= 0 || eos) {
+ mBuffersToRender.addLast(Pair.create(ix, info));
+ }
+ if (eos) {
+ tryToPropagateEOS();
+ mGotDecoderEOS = true;
+ }
+ if (haveBuffers()) {
+ mCondition.notifyAll();
+ }
+ }
+ }
+ } else if (mediaCodec == mEncoder) {
+ emptyEncoderOutputBuffer(ix, info);
+ } else {
+ fail("received output buffer on " + mediaCodec.getName());
+ }
+ }
+
+ private void renderDecodedBuffer(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
+ // process heavyweight actions under instance lock
+ Image encImage = mEncoder.getInputImage(encBuffer);
+ Image decImage = mDecoder.getOutputImage(decBuffer.first);
+ assertNotNull("could not get encoder image for " + mEncoder.getInputFormat(), encImage);
+ assertNotNull("could not get decoder image for " + mDecoder.getInputFormat(), decImage);
+ assertEquals("incorrect decoder format",decImage.getFormat(), ImageFormat.YUV_420_888);
+ assertEquals("incorrect encoder format", encImage.getFormat(), ImageFormat.YUV_420_888);
+
+ CodecUtils.copyFlexYUVImage(encImage, decImage);
+
+ // TRICKY: need this for queueBuffer
+ if (mEncInputBufferSize < 0) {
+ mEncInputBufferSize = mEncoder.getInputBuffer(encBuffer).capacity();
+ }
+ Log.d(TAG, "queuing output #" + encBuffer + " for encoder (sz="
+ + mEncInputBufferSize + ", f=" + decBuffer.second.flags
+ + ", ts=" + decBuffer.second.presentationTimeUs + ")");
+ mEncoder.queueInputBuffer(
+ encBuffer, 0, mEncInputBufferSize, decBuffer.second.presentationTimeUs,
+ decBuffer.second.flags);
+ if ((decBuffer.second.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSignaledEncoderEOS = true;
+ }
+ mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
+ }
+
+ @Override
+ public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
+ fail("received error on " + mediaCodec.getName() + ": " + e);
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
+ Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+ if (mediaCodec == mEncoder) {
+ saveEncoderFormat(mediaFormat);
+ }
+ }
+
+ // next methods are synchronized on mCondition
+ private boolean haveBuffers() {
+ return mEncInputBuffers.size() > 0 && mBuffersToRender.size() > 0
+ && !mSignaledEncoderEOS;
+ }
+
+ private boolean isEOSOnlyBuffer(Pair<Integer, BufferInfo> decBuffer) {
+ return decBuffer.first < 0 || decBuffer.second.size == 0;
+ }
+
+ protected void tryToPropagateEOS() {
+ if (!mWorkInProgress && haveBuffers() && isEOSOnlyBuffer(mBuffersToRender.getFirst())) {
+ Pair<Integer, BufferInfo> decBuffer = mBuffersToRender.removeFirst();
+ int encBuffer = mEncInputBuffers.removeFirst();
+ queueEncoderEOS(decBuffer, encBuffer);
+ }
+ }
+
+ void queueEncoderEOS(Pair<Integer, BufferInfo> decBuffer, int encBuffer) {
+ Log.d(TAG, "signaling encoder EOS");
+ mEncoder.queueInputBuffer(encBuffer, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
+ mSignaledEncoderEOS = true;
+ if (decBuffer.first >= 0) {
+ mDecoder.releaseOutputBuffer(decBuffer.first, false /* render */);
+ }
+ }
+ }
+
+
+ class SurfaceVideoProcessor extends VideoProcessorBase
+ implements SurfaceTexture.OnFrameAvailableListener {
+ private static final String TAG = "SurfaceVideoProcessor";
+ private boolean mFrameAvailable;
+ private boolean mEncoderIsActive;
+ private boolean mGotDecoderEOS;
+ private boolean mSignaledEncoderEOS;
+
+ private InputSurface mEncSurface;
+ private OutputSurface mDecSurface;
+ private BufferInfo mInfoOnSurface;
+
+ private LinkedList<Pair<Integer, BufferInfo>> mBuffersToRender =
+ new LinkedList<Pair<Integer, BufferInfo>>();
+
+ @Override
+ public boolean processLoop(
+ String path, String outMime, String videoEncName,
+ int width, int height, boolean optional) {
+ boolean skipped = true;
+ try {
+ open(path);
+ if (!initCodecsAndConfigureEncoder(
+ videoEncName, outMime, width, height,
+ CodecCapabilities.COLOR_FormatSurface)) {
+ assertTrue("could not configure encoder for supported size", optional);
+ return !skipped;
+ }
+ skipped = false;
+
+ mEncSurface = new InputSurface(mEncoder.createInputSurface());
+ mEncSurface.makeCurrent();
+
+ mDecSurface = new OutputSurface(this);
+ //mDecSurface.changeFragmentShader(FRAGMENT_SHADER);
+ mDecoder.configure(mDecFormat, mDecSurface.getSurface(), null /* crypto */, 0);
+
+ mDecoder.start();
+ mEncoder.start();
+
+ // main loop - process GL ops as only main thread has GL context
+ while (!mCompleted) {
+ BufferInfo info = null;
+ synchronized (mCondition) {
+ try {
+ // wait for mFrameAvailable, which is set by onFrameAvailable().
+ // Use a timeout to avoid stalling the test if it doesn't arrive.
+ if (!mFrameAvailable && !mCompleted && !mEncoderIsActive) {
+ mCondition.wait(FRAME_TIMEOUT_MS);
+ }
+ } catch (InterruptedException ie) {
+ fail("wait interrupted"); // shouldn't happen
+ }
+ if (mCompleted) {
+ break;
+ }
+ if (mEncoderIsActive) {
+ mEncoderIsActive = false;
+ if (DEBUG) Log.d(TAG, "encoder is still active, continue");
+ continue;
+ }
+ assertTrue("still waiting for image", mFrameAvailable);
+ if (DEBUG) Log.v(TAG, "got image");
+ info = mInfoOnSurface;
+ }
+ if (info == null) {
+ continue;
+ }
+ if (info.size > 0) {
+ mDecSurface.latchImage();
+ if (DEBUG) Log.v(TAG, "latched image");
+ mFrameAvailable = false;
+
+ mDecSurface.drawImage();
+ Log.d(TAG, "encoding frame at " + info.presentationTimeUs * 1000);
+
+ mEncSurface.setPresentationTime(info.presentationTimeUs * 1000);
+ mEncSurface.swapBuffers();
+ }
+ if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
+ mSignaledEncoderEOS = true;
+ Log.d(TAG, "signaling encoder EOS");
+ mEncoder.signalEndOfInputStream();
+ }
+
+ synchronized (mCondition) {
+ mInfoOnSurface = null;
+ if (mBuffersToRender.size() > 0 && mInfoOnSurface == null) {
+ if (DEBUG) Log.v(TAG, "handling postponed frame");
+ Pair<Integer, BufferInfo> nextBuffer = mBuffersToRender.removeFirst();
+ renderDecodedBuffer(nextBuffer.first, nextBuffer.second);
+ }
+ }
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ fail("received exception " + e);
+ } finally {
+ close();
+ if (mEncSurface != null) {
+ mEncSurface.release();
+ mEncSurface = null;
+ }
+ if (mDecSurface != null) {
+ mDecSurface.release();
+ mDecSurface = null;
+ }
+ }
+ return !skipped;
+ }
+
+ @Override
+ public void onFrameAvailable(SurfaceTexture st) {
+ if (DEBUG) Log.v(TAG, "new frame available");
+ synchronized (mCondition) {
+ assertFalse("mFrameAvailable already set, frame could be dropped", mFrameAvailable);
+ mFrameAvailable = true;
+ mCondition.notifyAll();
+ }
+ }
+
+ @Override
+ public void onInputBufferAvailable(MediaCodec mediaCodec, int ix) {
+ if (mediaCodec == mDecoder) {
+ // fill input buffer from extractor
+ fillDecoderInputBuffer(ix);
+ } else {
+ fail("received input buffer on " + mediaCodec.getName());
+ }
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ MediaCodec mediaCodec, int ix, BufferInfo info) {
+ if (mediaCodec == mDecoder) {
+ if (DEBUG) Log.v(TAG, "decoder received output #" + ix
+ + " (sz=" + info.size + ", f=" + info.flags
+ + ", ts=" + info.presentationTimeUs + ")");
+ // render output buffer from decoder
+ if (!mGotDecoderEOS) {
+ boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ if (eos) {
+ mGotDecoderEOS = true;
+ }
+ // can release empty buffers now
+ if (info.size == 0) {
+ mDecoder.releaseOutputBuffer(ix, false /* render */);
+ ix = -1; // dummy index used by render to not render
+ }
+ if (eos || info.size > 0) {
+ synchronized(mCondition) {
+ if (mInfoOnSurface != null || mBuffersToRender.size() > 0) {
+ if (DEBUG) Log.v(TAG, "postponing render, surface busy");
+ mBuffersToRender.addLast(Pair.create(ix, info));
+ } else {
+ renderDecodedBuffer(ix, info);
+ }
+ }
+ }
+ }
+ } else if (mediaCodec == mEncoder) {
+ emptyEncoderOutputBuffer(ix, info);
+ synchronized(mCondition) {
+ if (!mCompleted) {
+ mEncoderIsActive = true;
+ mCondition.notifyAll();
+ }
+ }
+ } else {
+ fail("received output buffer on " + mediaCodec.getName());
+ }
+ }
+
+ private void renderDecodedBuffer(int ix, BufferInfo info) {
+ boolean eos = (info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0;
+ mInfoOnSurface = info;
+ if (info.size > 0) {
+ Log.d(TAG, "rendering frame #" + ix + " at " + info.presentationTimeUs * 1000
+ + (eos ? " with EOS" : ""));
+ mDecoder.releaseOutputBuffer(ix, info.presentationTimeUs * 1000);
+ }
+
+ if (eos && info.size == 0) {
+ if (DEBUG) Log.v(TAG, "decoder output EOS available");
+ mFrameAvailable = true;
+ mCondition.notifyAll();
+ }
+ }
+
+ @Override
+ public void onError(MediaCodec mediaCodec, MediaCodec.CodecException e) {
+ fail("received error on " + mediaCodec.getName() + ": " + e);
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec mediaCodec, MediaFormat mediaFormat) {
+ Log.i(TAG, mediaCodec.getName() + " got new output format " + mediaFormat);
+ if (mediaCodec == mEncoder) {
+ saveEncoderFormat(mediaFormat);
+ }
+ }
+ }
+
+ class Encoder {
+ final private String mName;
+ final private String mMime;
+ final private VideoCapabilities mCaps;
+
+ final private Map<Size, Set<Size>> mMinMax; // extreme sizes
+ final private Map<Size, Set<Size>> mNearMinMax; // sizes near extreme
+ final private Set<Size> mArbitraryW; // arbitrary widths in the middle
+ final private Set<Size> mArbitraryH; // arbitrary heights in the middle
+ final private Set<Size> mSizes; // all non-specifically tested sizes
+
+ final private int xAlign;
+ final private int yAlign;
+
+ Encoder(String name, String mime, CodecCapabilities caps) {
+ mName = name;
+ mMime = mime;
+ mCaps = caps.getVideoCapabilities();
+
+ /* calculate min/max sizes */
+ mMinMax = new HashMap<Size, Set<Size>>();
+ mNearMinMax = new HashMap<Size, Set<Size>>();
+ mArbitraryW = new HashSet<Size>();
+ mArbitraryH = new HashSet<Size>();
+ mSizes = new HashSet<Size>();
+
+ xAlign = mCaps.getWidthAlignment();
+ yAlign = mCaps.getHeightAlignment();
+
+ initializeSizes();
+ }
+
+ private void initializeSizes() {
+ for (int x = 0; x < 2; ++x) {
+ for (int y = 0; y < 2; ++y) {
+ addExtremeSizesFor(x, y);
+ }
+ }
+
+ // initialize arbitrary sizes
+ for (int i = 1; i <= 7; ++i) {
+ int j = ((7 * i) % 11) + 1;
+ int width, height;
+ try {
+ width = alignedPointInRange(i * 0.125, xAlign, mCaps.getSupportedWidths());
+ height = alignedPointInRange(
+ j * 0.077, yAlign, mCaps.getSupportedHeightsFor(width));
+ mArbitraryW.add(new Size(width, height));
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ height = alignedPointInRange(i * 0.125, yAlign, mCaps.getSupportedHeights());
+ width = alignedPointInRange(j * 0.077, xAlign, mCaps.getSupportedWidthsFor(height));
+ mArbitraryH.add(new Size(width, height));
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ mArbitraryW.removeAll(mArbitraryH);
+ mArbitraryW.removeAll(mSizes);
+ mSizes.addAll(mArbitraryW);
+ mArbitraryH.removeAll(mSizes);
+ mSizes.addAll(mArbitraryH);
+ if (DEBUG) Log.i(TAG, "arbitrary=" + mArbitraryW + "/" + mArbitraryH);
+ }
+
+ private void addExtremeSizesFor(int x, int y) {
+ Set<Size> minMax = new HashSet<Size>();
+ Set<Size> nearMinMax = new HashSet<Size>();
+
+ for (int dx = 0; dx <= xAlign; dx += xAlign) {
+ for (int dy = 0; dy <= yAlign; dy += yAlign) {
+ Set<Size> bucket = (dx + dy == 0) ? minMax : nearMinMax;
+ try {
+ int width = getExtreme(mCaps.getSupportedWidths(), x, dx);
+ int height = getExtreme(mCaps.getSupportedHeightsFor(width), y, dy);
+ bucket.add(new Size(width, height));
+
+ // try max max with more reasonable ratio if too skewed
+ if (x + y == 2 && width >= 4 * height) {
+ Size wideScreen = getLargestSizeForRatio(16, 9);
+ width = getExtreme(
+ mCaps.getSupportedWidths()
+ .intersect(0, wideScreen.getWidth()), x, dx);
+ height = getExtreme(mCaps.getSupportedHeightsFor(width), y, 0);
+ bucket.add(new Size(width, height));
+ }
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ int height = getExtreme(mCaps.getSupportedHeights(), y, dy);
+ int width = getExtreme(mCaps.getSupportedWidthsFor(height), x, dx);
+ bucket.add(new Size(width, height));
+
+ // try max max with more reasonable ratio if too skewed
+ if (x + y == 2 && height >= 4 * width) {
+ Size wideScreen = getLargestSizeForRatio(9, 16);
+ height = getExtreme(
+ mCaps.getSupportedHeights()
+ .intersect(0, wideScreen.getHeight()), y, dy);
+ width = getExtreme(mCaps.getSupportedWidthsFor(height), x, dx);
+ bucket.add(new Size(width, height));
+ }
+ } catch (IllegalArgumentException e) {
+ }
+ }
+ }
+
+ // keep unique sizes
+ minMax.removeAll(mSizes);
+ mSizes.addAll(minMax);
+ nearMinMax.removeAll(mSizes);
+ mSizes.addAll(nearMinMax);
+
+ mMinMax.put(new Size(x, y), minMax);
+ mNearMinMax.put(new Size(x, y), nearMinMax);
+ if (DEBUG) Log.i(TAG, x + "x" + y + ": minMax=" + mMinMax + ", near=" + mNearMinMax);
+ }
+
+ private int alignInRange(double value, int align, Range<Integer> range) {
+ return range.clamp(align * (int)Math.round(value / align));
+ }
+
+ /* point should be between 0. and 1. */
+ private int alignedPointInRange(double point, int align, Range<Integer> range) {
+ return alignInRange(
+ range.getLower() + point * (range.getUpper() - range.getLower()), align, range);
+ }
+
+ private int getExtreme(Range<Integer> range, int i, int delta) {
+ int dim = i == 1 ? range.getUpper() - delta : range.getLower() + delta;
+ if (delta == 0
+ || (dim > range.getLower() && dim < range.getUpper())) {
+ return dim;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ private Size getLargestSizeForRatio(int x, int y) {
+ Range<Integer> widthRange = mCaps.getSupportedWidths();
+ Range<Integer> heightRange = mCaps.getSupportedHeightsFor(widthRange.getUpper());
+ final int xAlign = mCaps.getWidthAlignment();
+ final int yAlign = mCaps.getHeightAlignment();
+
+ // scale by alignment
+ int width = alignInRange(
+ Math.sqrt(widthRange.getUpper() * heightRange.getUpper() * (double)x / y),
+ xAlign, widthRange);
+ int height = alignInRange(
+ width * (double)y / x, yAlign, mCaps.getSupportedHeightsFor(width));
+ return new Size(width, height);
+ }
+
+
+ public boolean testExtreme(int x, int y, boolean flexYUV, boolean near) {
+ boolean skipped = true;
+ for (Size s : (near ? mNearMinMax : mMinMax).get(new Size(x, y))) {
+ if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
+ skipped = false;
+ }
+ }
+ return !skipped;
+ }
+
+ public boolean testArbitrary(boolean flexYUV, boolean widths) {
+ boolean skipped = true;
+ for (Size s : (widths ? mArbitraryW : mArbitraryH)) {
+ if (test(s.getWidth(), s.getHeight(), false /* optional */, flexYUV)) {
+ skipped = false;
+ }
+ }
+ return !skipped;
+ }
+
+ public boolean testSpecific(int width, int height, boolean flexYUV) {
+ // already tested by one of the min/max tests
+ if (mSizes.contains(new Size(width, height))) {
+ return false;
+ }
+ return test(width, height, true /* optional */, flexYUV);
+ }
+
+ public boolean testDetailed(
+ int width, int height, int frameRate, int bitRate, boolean flexYUV) {
+ return test(width, height, frameRate, bitRate, true /* optional */, flexYUV);
+ }
+
+ public boolean testSupport(int width, int height, int frameRate, int bitRate) {
+ return mCaps.areSizeAndRateSupported(width, height, frameRate) &&
+ mCaps.getBitrateRange().contains(bitRate);
+ }
+
+ private boolean test(int width, int height, boolean optional, boolean flexYUV) {
+ return test(width, height, 0 /* frameRate */, 0 /* bitRate */, optional, flexYUV);
+ }
+
+ private boolean test(int width, int height, int frameRate, int bitRate,
+ boolean optional, boolean flexYUV) {
+ Log.i(TAG, "testing " + mMime + " on " + mName + " for " + width + "x" + height
+ + (flexYUV ? " flexYUV" : " surface"));
+
+ VideoProcessorBase processor =
+ flexYUV ? new VideoProcessor() : new SurfaceVideoProcessor();
+
+ processor.setFrameAndBitRates(frameRate, bitRate);
+
+ // We are using a resource URL as an example
+ boolean success = processor.processLoop(
+ SOURCE_URL, mMime, mName, width, height, optional);
+ if (success) {
+ processor.playBack(getActivity().getSurfaceHolder().getSurface());
+ }
+ return success;
+ }
+ }
+
+ private Encoder[] googH265() { return goog(MediaFormat.MIMETYPE_VIDEO_HEVC); }
+ private Encoder[] googH264() { return goog(MediaFormat.MIMETYPE_VIDEO_AVC); }
+ private Encoder[] googH263() { return goog(MediaFormat.MIMETYPE_VIDEO_H263); }
+ private Encoder[] googMpeg4() { return goog(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
+ private Encoder[] googVP8() { return goog(MediaFormat.MIMETYPE_VIDEO_VP8); }
+ private Encoder[] googVP9() { return goog(MediaFormat.MIMETYPE_VIDEO_VP9); }
+
+ private Encoder[] otherH265() { return other(MediaFormat.MIMETYPE_VIDEO_HEVC); }
+ private Encoder[] otherH264() { return other(MediaFormat.MIMETYPE_VIDEO_AVC); }
+ private Encoder[] otherH263() { return other(MediaFormat.MIMETYPE_VIDEO_H263); }
+ private Encoder[] otherMpeg4() { return other(MediaFormat.MIMETYPE_VIDEO_MPEG4); }
+ private Encoder[] otherVP8() { return other(MediaFormat.MIMETYPE_VIDEO_VP8); }
+ private Encoder[] otherVP9() { return other(MediaFormat.MIMETYPE_VIDEO_VP9); }
+
+ private Encoder[] goog(String mime) {
+ return encoders(mime, true /* goog */);
+ }
+
+ private Encoder[] other(String mime) {
+ return encoders(mime, false /* goog */);
+ }
+
+ private Encoder[] combineArray(Encoder[] a, Encoder[] b) {
+ Encoder[] all = new Encoder[a.length + b.length];
+ System.arraycopy(a, 0, all, 0, a.length);
+ System.arraycopy(b, 0, all, a.length, b.length);
+ return all;
+ }
+
+ private Encoder[] h264() {
+ return combineArray(googH264(), otherH264());
+ }
+
+ private Encoder[] vp8() {
+ return combineArray(googVP8(), otherVP8());
+ }
+
+ private Encoder[] encoders(String mime, boolean goog) {
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ ArrayList<Encoder> result = new ArrayList<Encoder>();
+
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (!info.isEncoder()
+ || info.getName().toLowerCase().startsWith("omx.google.") != goog) {
+ continue;
+ }
+ CodecCapabilities caps = null;
+ try {
+ caps = info.getCapabilitiesForType(mime);
+ } catch (IllegalArgumentException e) { // mime is not supported
+ continue;
+ }
+ assertNotNull(info.getName() + " capabilties for " + mime + " returned null", caps);
+ result.add(new Encoder(info.getName(), mime, caps));
+ }
+ return result.toArray(new Encoder[result.size()]);
+ }
+
+ public void testGoogH265FlexMinMin() { minmin(googH265(), true /* flex */); }
+ public void testGoogH265SurfMinMin() { minmin(googH265(), false /* flex */); }
+ public void testGoogH264FlexMinMin() { minmin(googH264(), true /* flex */); }
+ public void testGoogH264SurfMinMin() { minmin(googH264(), false /* flex */); }
+ public void testGoogH263FlexMinMin() { minmin(googH263(), true /* flex */); }
+ public void testGoogH263SurfMinMin() { minmin(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexMinMin() { minmin(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfMinMin() { minmin(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexMinMin() { minmin(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfMinMin() { minmin(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexMinMin() { minmin(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfMinMin() { minmin(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexMinMin() { minmin(otherH265(), true /* flex */); }
+ public void testOtherH265SurfMinMin() { minmin(otherH265(), false /* flex */); }
+ public void testOtherH264FlexMinMin() { minmin(otherH264(), true /* flex */); }
+ public void testOtherH264SurfMinMin() { minmin(otherH264(), false /* flex */); }
+ public void testOtherH263FlexMinMin() { minmin(otherH263(), true /* flex */); }
+ public void testOtherH263SurfMinMin() { minmin(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexMinMin() { minmin(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfMinMin() { minmin(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexMinMin() { minmin(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfMinMin() { minmin(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexMinMin() { minmin(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfMinMin() { minmin(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexMinMax() { minmax(googH265(), true /* flex */); }
+ public void testGoogH265SurfMinMax() { minmax(googH265(), false /* flex */); }
+ public void testGoogH264FlexMinMax() { minmax(googH264(), true /* flex */); }
+ public void testGoogH264SurfMinMax() { minmax(googH264(), false /* flex */); }
+ public void testGoogH263FlexMinMax() { minmax(googH263(), true /* flex */); }
+ public void testGoogH263SurfMinMax() { minmax(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexMinMax() { minmax(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfMinMax() { minmax(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexMinMax() { minmax(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfMinMax() { minmax(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexMinMax() { minmax(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfMinMax() { minmax(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexMinMax() { minmax(otherH265(), true /* flex */); }
+ public void testOtherH265SurfMinMax() { minmax(otherH265(), false /* flex */); }
+ public void testOtherH264FlexMinMax() { minmax(otherH264(), true /* flex */); }
+ public void testOtherH264SurfMinMax() { minmax(otherH264(), false /* flex */); }
+ public void testOtherH263FlexMinMax() { minmax(otherH263(), true /* flex */); }
+ public void testOtherH263SurfMinMax() { minmax(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexMinMax() { minmax(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfMinMax() { minmax(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexMinMax() { minmax(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfMinMax() { minmax(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexMinMax() { minmax(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfMinMax() { minmax(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexMaxMin() { maxmin(googH265(), true /* flex */); }
+ public void testGoogH265SurfMaxMin() { maxmin(googH265(), false /* flex */); }
+ public void testGoogH264FlexMaxMin() { maxmin(googH264(), true /* flex */); }
+ public void testGoogH264SurfMaxMin() { maxmin(googH264(), false /* flex */); }
+ public void testGoogH263FlexMaxMin() { maxmin(googH263(), true /* flex */); }
+ public void testGoogH263SurfMaxMin() { maxmin(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexMaxMin() { maxmin(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfMaxMin() { maxmin(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexMaxMin() { maxmin(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfMaxMin() { maxmin(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexMaxMin() { maxmin(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfMaxMin() { maxmin(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexMaxMin() { maxmin(otherH265(), true /* flex */); }
+ public void testOtherH265SurfMaxMin() { maxmin(otherH265(), false /* flex */); }
+ public void testOtherH264FlexMaxMin() { maxmin(otherH264(), true /* flex */); }
+ public void testOtherH264SurfMaxMin() { maxmin(otherH264(), false /* flex */); }
+ public void testOtherH263FlexMaxMin() { maxmin(otherH263(), true /* flex */); }
+ public void testOtherH263SurfMaxMin() { maxmin(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexMaxMin() { maxmin(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfMaxMin() { maxmin(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexMaxMin() { maxmin(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfMaxMin() { maxmin(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexMaxMin() { maxmin(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfMaxMin() { maxmin(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexMaxMax() { maxmax(googH265(), true /* flex */); }
+ public void testGoogH265SurfMaxMax() { maxmax(googH265(), false /* flex */); }
+ public void testGoogH264FlexMaxMax() { maxmax(googH264(), true /* flex */); }
+ public void testGoogH264SurfMaxMax() { maxmax(googH264(), false /* flex */); }
+ public void testGoogH263FlexMaxMax() { maxmax(googH263(), true /* flex */); }
+ public void testGoogH263SurfMaxMax() { maxmax(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexMaxMax() { maxmax(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfMaxMax() { maxmax(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexMaxMax() { maxmax(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfMaxMax() { maxmax(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexMaxMax() { maxmax(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfMaxMax() { maxmax(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexMaxMax() { maxmax(otherH265(), true /* flex */); }
+ public void testOtherH265SurfMaxMax() { maxmax(otherH265(), false /* flex */); }
+ public void testOtherH264FlexMaxMax() { maxmax(otherH264(), true /* flex */); }
+ public void testOtherH264SurfMaxMax() { maxmax(otherH264(), false /* flex */); }
+ public void testOtherH263FlexMaxMax() { maxmax(otherH263(), true /* flex */); }
+ public void testOtherH263SurfMaxMax() { maxmax(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexMaxMax() { maxmax(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfMaxMax() { maxmax(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexMaxMax() { maxmax(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfMaxMax() { maxmax(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexMaxMax() { maxmax(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfMaxMax() { maxmax(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexNearMinMin() { nearminmin(googH265(), true /* flex */); }
+ public void testGoogH265SurfNearMinMin() { nearminmin(googH265(), false /* flex */); }
+ public void testGoogH264FlexNearMinMin() { nearminmin(googH264(), true /* flex */); }
+ public void testGoogH264SurfNearMinMin() { nearminmin(googH264(), false /* flex */); }
+ public void testGoogH263FlexNearMinMin() { nearminmin(googH263(), true /* flex */); }
+ public void testGoogH263SurfNearMinMin() { nearminmin(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexNearMinMin() { nearminmin(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfNearMinMin() { nearminmin(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexNearMinMin() { nearminmin(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfNearMinMin() { nearminmin(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexNearMinMin() { nearminmin(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfNearMinMin() { nearminmin(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexNearMinMin() { nearminmin(otherH265(), true /* flex */); }
+ public void testOtherH265SurfNearMinMin() { nearminmin(otherH265(), false /* flex */); }
+ public void testOtherH264FlexNearMinMin() { nearminmin(otherH264(), true /* flex */); }
+ public void testOtherH264SurfNearMinMin() { nearminmin(otherH264(), false /* flex */); }
+ public void testOtherH263FlexNearMinMin() { nearminmin(otherH263(), true /* flex */); }
+ public void testOtherH263SurfNearMinMin() { nearminmin(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexNearMinMin() { nearminmin(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfNearMinMin() { nearminmin(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexNearMinMin() { nearminmin(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfNearMinMin() { nearminmin(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexNearMinMin() { nearminmin(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfNearMinMin() { nearminmin(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexNearMinMax() { nearminmax(googH265(), true /* flex */); }
+ public void testGoogH265SurfNearMinMax() { nearminmax(googH265(), false /* flex */); }
+ public void testGoogH264FlexNearMinMax() { nearminmax(googH264(), true /* flex */); }
+ public void testGoogH264SurfNearMinMax() { nearminmax(googH264(), false /* flex */); }
+ public void testGoogH263FlexNearMinMax() { nearminmax(googH263(), true /* flex */); }
+ public void testGoogH263SurfNearMinMax() { nearminmax(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexNearMinMax() { nearminmax(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfNearMinMax() { nearminmax(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexNearMinMax() { nearminmax(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfNearMinMax() { nearminmax(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexNearMinMax() { nearminmax(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfNearMinMax() { nearminmax(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexNearMinMax() { nearminmax(otherH265(), true /* flex */); }
+ public void testOtherH265SurfNearMinMax() { nearminmax(otherH265(), false /* flex */); }
+ public void testOtherH264FlexNearMinMax() { nearminmax(otherH264(), true /* flex */); }
+ public void testOtherH264SurfNearMinMax() { nearminmax(otherH264(), false /* flex */); }
+ public void testOtherH263FlexNearMinMax() { nearminmax(otherH263(), true /* flex */); }
+ public void testOtherH263SurfNearMinMax() { nearminmax(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexNearMinMax() { nearminmax(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfNearMinMax() { nearminmax(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexNearMinMax() { nearminmax(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfNearMinMax() { nearminmax(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexNearMinMax() { nearminmax(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfNearMinMax() { nearminmax(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexNearMaxMin() { nearmaxmin(googH265(), true /* flex */); }
+ public void testGoogH265SurfNearMaxMin() { nearmaxmin(googH265(), false /* flex */); }
+ public void testGoogH264FlexNearMaxMin() { nearmaxmin(googH264(), true /* flex */); }
+ public void testGoogH264SurfNearMaxMin() { nearmaxmin(googH264(), false /* flex */); }
+ public void testGoogH263FlexNearMaxMin() { nearmaxmin(googH263(), true /* flex */); }
+ public void testGoogH263SurfNearMaxMin() { nearmaxmin(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexNearMaxMin() { nearmaxmin(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfNearMaxMin() { nearmaxmin(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexNearMaxMin() { nearmaxmin(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfNearMaxMin() { nearmaxmin(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexNearMaxMin() { nearmaxmin(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfNearMaxMin() { nearmaxmin(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexNearMaxMin() { nearmaxmin(otherH265(), true /* flex */); }
+ public void testOtherH265SurfNearMaxMin() { nearmaxmin(otherH265(), false /* flex */); }
+ public void testOtherH264FlexNearMaxMin() { nearmaxmin(otherH264(), true /* flex */); }
+ public void testOtherH264SurfNearMaxMin() { nearmaxmin(otherH264(), false /* flex */); }
+ public void testOtherH263FlexNearMaxMin() { nearmaxmin(otherH263(), true /* flex */); }
+ public void testOtherH263SurfNearMaxMin() { nearmaxmin(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexNearMaxMin() { nearmaxmin(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfNearMaxMin() { nearmaxmin(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexNearMaxMin() { nearmaxmin(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfNearMaxMin() { nearmaxmin(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexNearMaxMin() { nearmaxmin(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfNearMaxMin() { nearmaxmin(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexNearMaxMax() { nearmaxmax(googH265(), true /* flex */); }
+ public void testGoogH265SurfNearMaxMax() { nearmaxmax(googH265(), false /* flex */); }
+ public void testGoogH264FlexNearMaxMax() { nearmaxmax(googH264(), true /* flex */); }
+ public void testGoogH264SurfNearMaxMax() { nearmaxmax(googH264(), false /* flex */); }
+ public void testGoogH263FlexNearMaxMax() { nearmaxmax(googH263(), true /* flex */); }
+ public void testGoogH263SurfNearMaxMax() { nearmaxmax(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexNearMaxMax() { nearmaxmax(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfNearMaxMax() { nearmaxmax(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexNearMaxMax() { nearmaxmax(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfNearMaxMax() { nearmaxmax(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexNearMaxMax() { nearmaxmax(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfNearMaxMax() { nearmaxmax(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexNearMaxMax() { nearmaxmax(otherH265(), true /* flex */); }
+ public void testOtherH265SurfNearMaxMax() { nearmaxmax(otherH265(), false /* flex */); }
+ public void testOtherH264FlexNearMaxMax() { nearmaxmax(otherH264(), true /* flex */); }
+ public void testOtherH264SurfNearMaxMax() { nearmaxmax(otherH264(), false /* flex */); }
+ public void testOtherH263FlexNearMaxMax() { nearmaxmax(otherH263(), true /* flex */); }
+ public void testOtherH263SurfNearMaxMax() { nearmaxmax(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexNearMaxMax() { nearmaxmax(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfNearMaxMax() { nearmaxmax(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexNearMaxMax() { nearmaxmax(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfNearMaxMax() { nearmaxmax(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexNearMaxMax() { nearmaxmax(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfNearMaxMax() { nearmaxmax(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexArbitraryW() { arbitraryw(googH265(), true /* flex */); }
+ public void testGoogH265SurfArbitraryW() { arbitraryw(googH265(), false /* flex */); }
+ public void testGoogH264FlexArbitraryW() { arbitraryw(googH264(), true /* flex */); }
+ public void testGoogH264SurfArbitraryW() { arbitraryw(googH264(), false /* flex */); }
+ public void testGoogH263FlexArbitraryW() { arbitraryw(googH263(), true /* flex */); }
+ public void testGoogH263SurfArbitraryW() { arbitraryw(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexArbitraryW() { arbitraryw(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfArbitraryW() { arbitraryw(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexArbitraryW() { arbitraryw(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfArbitraryW() { arbitraryw(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexArbitraryW() { arbitraryw(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfArbitraryW() { arbitraryw(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexArbitraryW() { arbitraryw(otherH265(), true /* flex */); }
+ public void testOtherH265SurfArbitraryW() { arbitraryw(otherH265(), false /* flex */); }
+ public void testOtherH264FlexArbitraryW() { arbitraryw(otherH264(), true /* flex */); }
+ public void testOtherH264SurfArbitraryW() { arbitraryw(otherH264(), false /* flex */); }
+ public void testOtherH263FlexArbitraryW() { arbitraryw(otherH263(), true /* flex */); }
+ public void testOtherH263SurfArbitraryW() { arbitraryw(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexArbitraryW() { arbitraryw(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfArbitraryW() { arbitraryw(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexArbitraryW() { arbitraryw(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfArbitraryW() { arbitraryw(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexArbitraryW() { arbitraryw(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfArbitraryW() { arbitraryw(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexArbitraryH() { arbitraryh(googH265(), true /* flex */); }
+ public void testGoogH265SurfArbitraryH() { arbitraryh(googH265(), false /* flex */); }
+ public void testGoogH264FlexArbitraryH() { arbitraryh(googH264(), true /* flex */); }
+ public void testGoogH264SurfArbitraryH() { arbitraryh(googH264(), false /* flex */); }
+ public void testGoogH263FlexArbitraryH() { arbitraryh(googH263(), true /* flex */); }
+ public void testGoogH263SurfArbitraryH() { arbitraryh(googH263(), false /* flex */); }
+ public void testGoogMpeg4FlexArbitraryH() { arbitraryh(googMpeg4(), true /* flex */); }
+ public void testGoogMpeg4SurfArbitraryH() { arbitraryh(googMpeg4(), false /* flex */); }
+ public void testGoogVP8FlexArbitraryH() { arbitraryh(googVP8(), true /* flex */); }
+ public void testGoogVP8SurfArbitraryH() { arbitraryh(googVP8(), false /* flex */); }
+ public void testGoogVP9FlexArbitraryH() { arbitraryh(googVP9(), true /* flex */); }
+ public void testGoogVP9SurfArbitraryH() { arbitraryh(googVP9(), false /* flex */); }
+
+ public void testOtherH265FlexArbitraryH() { arbitraryh(otherH265(), true /* flex */); }
+ public void testOtherH265SurfArbitraryH() { arbitraryh(otherH265(), false /* flex */); }
+ public void testOtherH264FlexArbitraryH() { arbitraryh(otherH264(), true /* flex */); }
+ public void testOtherH264SurfArbitraryH() { arbitraryh(otherH264(), false /* flex */); }
+ public void testOtherH263FlexArbitraryH() { arbitraryh(otherH263(), true /* flex */); }
+ public void testOtherH263SurfArbitraryH() { arbitraryh(otherH263(), false /* flex */); }
+ public void testOtherMpeg4FlexArbitraryH() { arbitraryh(otherMpeg4(), true /* flex */); }
+ public void testOtherMpeg4SurfArbitraryH() { arbitraryh(otherMpeg4(), false /* flex */); }
+ public void testOtherVP8FlexArbitraryH() { arbitraryh(otherVP8(), true /* flex */); }
+ public void testOtherVP8SurfArbitraryH() { arbitraryh(otherVP8(), false /* flex */); }
+ public void testOtherVP9FlexArbitraryH() { arbitraryh(otherVP9(), true /* flex */); }
+ public void testOtherVP9SurfArbitraryH() { arbitraryh(otherVP9(), false /* flex */); }
+
+ public void testGoogH265FlexQCIF() { specific(googH265(), 176, 144, true /* flex */); }
+ public void testGoogH265SurfQCIF() { specific(googH265(), 176, 144, false /* flex */); }
+ public void testGoogH264FlexQCIF() { specific(googH264(), 176, 144, true /* flex */); }
+ public void testGoogH264SurfQCIF() { specific(googH264(), 176, 144, false /* flex */); }
+ public void testGoogH263FlexQCIF() { specific(googH263(), 176, 144, true /* flex */); }
+ public void testGoogH263SurfQCIF() { specific(googH263(), 176, 144, false /* flex */); }
+ public void testGoogMpeg4FlexQCIF() { specific(googMpeg4(), 176, 144, true /* flex */); }
+ public void testGoogMpeg4SurfQCIF() { specific(googMpeg4(), 176, 144, false /* flex */); }
+ public void testGoogVP8FlexQCIF() { specific(googVP8(), 176, 144, true /* flex */); }
+ public void testGoogVP8SurfQCIF() { specific(googVP8(), 176, 144, false /* flex */); }
+ public void testGoogVP9FlexQCIF() { specific(googVP9(), 176, 144, true /* flex */); }
+ public void testGoogVP9SurfQCIF() { specific(googVP9(), 176, 144, false /* flex */); }
+
+ public void testOtherH265FlexQCIF() { specific(otherH265(), 176, 144, true /* flex */); }
+ public void testOtherH265SurfQCIF() { specific(otherH265(), 176, 144, false /* flex */); }
+ public void testOtherH264FlexQCIF() { specific(otherH264(), 176, 144, true /* flex */); }
+ public void testOtherH264SurfQCIF() { specific(otherH264(), 176, 144, false /* flex */); }
+ public void testOtherH263FlexQCIF() { specific(otherH263(), 176, 144, true /* flex */); }
+ public void testOtherH263SurfQCIF() { specific(otherH263(), 176, 144, false /* flex */); }
+ public void testOtherMpeg4FlexQCIF() { specific(otherMpeg4(), 176, 144, true /* flex */); }
+ public void testOtherMpeg4SurfQCIF() { specific(otherMpeg4(), 176, 144, false /* flex */); }
+ public void testOtherVP8FlexQCIF() { specific(otherVP8(), 176, 144, true /* flex */); }
+ public void testOtherVP8SurfQCIF() { specific(otherVP8(), 176, 144, false /* flex */); }
+ public void testOtherVP9FlexQCIF() { specific(otherVP9(), 176, 144, true /* flex */); }
+ public void testOtherVP9SurfQCIF() { specific(otherVP9(), 176, 144, false /* flex */); }
+
+ public void testGoogH265Flex480p() { specific(googH265(), 720, 480, true /* flex */); }
+ public void testGoogH265Surf480p() { specific(googH265(), 720, 480, false /* flex */); }
+ public void testGoogH264Flex480p() { specific(googH264(), 720, 480, true /* flex */); }
+ public void testGoogH264Surf480p() { specific(googH264(), 720, 480, false /* flex */); }
+ public void testGoogH263Flex480p() { specific(googH263(), 720, 480, true /* flex */); }
+ public void testGoogH263Surf480p() { specific(googH263(), 720, 480, false /* flex */); }
+ public void testGoogMpeg4Flex480p() { specific(googMpeg4(), 720, 480, true /* flex */); }
+ public void testGoogMpeg4Surf480p() { specific(googMpeg4(), 720, 480, false /* flex */); }
+ public void testGoogVP8Flex480p() { specific(googVP8(), 720, 480, true /* flex */); }
+ public void testGoogVP8Surf480p() { specific(googVP8(), 720, 480, false /* flex */); }
+ public void testGoogVP9Flex480p() { specific(googVP9(), 720, 480, true /* flex */); }
+ public void testGoogVP9Surf480p() { specific(googVP9(), 720, 480, false /* flex */); }
+
+ public void testOtherH265Flex480p() { specific(otherH265(), 720, 480, true /* flex */); }
+ public void testOtherH265Surf480p() { specific(otherH265(), 720, 480, false /* flex */); }
+ public void testOtherH264Flex480p() { specific(otherH264(), 720, 480, true /* flex */); }
+ public void testOtherH264Surf480p() { specific(otherH264(), 720, 480, false /* flex */); }
+ public void testOtherH263Flex480p() { specific(otherH263(), 720, 480, true /* flex */); }
+ public void testOtherH263Surf480p() { specific(otherH263(), 720, 480, false /* flex */); }
+ public void testOtherMpeg4Flex480p() { specific(otherMpeg4(), 720, 480, true /* flex */); }
+ public void testOtherMpeg4Surf480p() { specific(otherMpeg4(), 720, 480, false /* flex */); }
+ public void testOtherVP8Flex480p() { specific(otherVP8(), 720, 480, true /* flex */); }
+ public void testOtherVP8Surf480p() { specific(otherVP8(), 720, 480, false /* flex */); }
+ public void testOtherVP9Flex480p() { specific(otherVP9(), 720, 480, true /* flex */); }
+ public void testOtherVP9Surf480p() { specific(otherVP9(), 720, 480, false /* flex */); }
+
+ // even though H.263 and MPEG-4 are not defined for 720p or 1080p
+ // test for it, in case device claims support for it.
+
+ public void testGoogH265Flex720p() { specific(googH265(), 1280, 720, true /* flex */); }
+ public void testGoogH265Surf720p() { specific(googH265(), 1280, 720, false /* flex */); }
+ public void testGoogH264Flex720p() { specific(googH264(), 1280, 720, true /* flex */); }
+ public void testGoogH264Surf720p() { specific(googH264(), 1280, 720, false /* flex */); }
+ public void testGoogH263Flex720p() { specific(googH263(), 1280, 720, true /* flex */); }
+ public void testGoogH263Surf720p() { specific(googH263(), 1280, 720, false /* flex */); }
+ public void testGoogMpeg4Flex720p() { specific(googMpeg4(), 1280, 720, true /* flex */); }
+ public void testGoogMpeg4Surf720p() { specific(googMpeg4(), 1280, 720, false /* flex */); }
+ public void testGoogVP8Flex720p() { specific(googVP8(), 1280, 720, true /* flex */); }
+ public void testGoogVP8Surf720p() { specific(googVP8(), 1280, 720, false /* flex */); }
+ public void testGoogVP9Flex720p() { specific(googVP9(), 1280, 720, true /* flex */); }
+ public void testGoogVP9Surf720p() { specific(googVP9(), 1280, 720, false /* flex */); }
+
+ public void testOtherH265Flex720p() { specific(otherH265(), 1280, 720, true /* flex */); }
+ public void testOtherH265Surf720p() { specific(otherH265(), 1280, 720, false /* flex */); }
+ public void testOtherH264Flex720p() { specific(otherH264(), 1280, 720, true /* flex */); }
+ public void testOtherH264Surf720p() { specific(otherH264(), 1280, 720, false /* flex */); }
+ public void testOtherH263Flex720p() { specific(otherH263(), 1280, 720, true /* flex */); }
+ public void testOtherH263Surf720p() { specific(otherH263(), 1280, 720, false /* flex */); }
+ public void testOtherMpeg4Flex720p() { specific(otherMpeg4(), 1280, 720, true /* flex */); }
+ public void testOtherMpeg4Surf720p() { specific(otherMpeg4(), 1280, 720, false /* flex */); }
+ public void testOtherVP8Flex720p() { specific(otherVP8(), 1280, 720, true /* flex */); }
+ public void testOtherVP8Surf720p() { specific(otherVP8(), 1280, 720, false /* flex */); }
+ public void testOtherVP9Flex720p() { specific(otherVP9(), 1280, 720, true /* flex */); }
+ public void testOtherVP9Surf720p() { specific(otherVP9(), 1280, 720, false /* flex */); }
+
+ public void testGoogH265Flex1080p() { specific(googH265(), 1920, 1080, true /* flex */); }
+ public void testGoogH265Surf1080p() { specific(googH265(), 1920, 1080, false /* flex */); }
+ public void testGoogH264Flex1080p() { specific(googH264(), 1920, 1080, true /* flex */); }
+ public void testGoogH264Surf1080p() { specific(googH264(), 1920, 1080, false /* flex */); }
+ public void testGoogH263Flex1080p() { specific(googH263(), 1920, 1080, true /* flex */); }
+ public void testGoogH263Surf1080p() { specific(googH263(), 1920, 1080, false /* flex */); }
+ public void testGoogMpeg4Flex1080p() { specific(googMpeg4(), 1920, 1080, true /* flex */); }
+ public void testGoogMpeg4Surf1080p() { specific(googMpeg4(), 1920, 1080, false /* flex */); }
+ public void testGoogVP8Flex1080p() { specific(googVP8(), 1920, 1080, true /* flex */); }
+ public void testGoogVP8Surf1080p() { specific(googVP8(), 1920, 1080, false /* flex */); }
+ public void testGoogVP9Flex1080p() { specific(googVP9(), 1920, 1080, true /* flex */); }
+ public void testGoogVP9Surf1080p() { specific(googVP9(), 1920, 1080, false /* flex */); }
+
+ public void testOtherH265Flex1080p() { specific(otherH265(), 1920, 1080, true /* flex */); }
+ public void testOtherH265Surf1080p() { specific(otherH265(), 1920, 1080, false /* flex */); }
+ public void testOtherH264Flex1080p() { specific(otherH264(), 1920, 1080, true /* flex */); }
+ public void testOtherH264Surf1080p() { specific(otherH264(), 1920, 1080, false /* flex */); }
+ public void testOtherH263Flex1080p() { specific(otherH263(), 1920, 1080, true /* flex */); }
+ public void testOtherH263Surf1080p() { specific(otherH263(), 1920, 1080, false /* flex */); }
+ public void testOtherMpeg4Flex1080p() { specific(otherMpeg4(), 1920, 1080, true /* flex */); }
+ public void testOtherMpeg4Surf1080p() { specific(otherMpeg4(), 1920, 1080, false /* flex */); }
+ public void testOtherVP8Flex1080p() { specific(otherVP8(), 1920, 1080, true /* flex */); }
+ public void testOtherVP8Surf1080p() { specific(otherVP8(), 1920, 1080, false /* flex */); }
+ public void testOtherVP9Flex1080p() { specific(otherVP9(), 1920, 1080, true /* flex */); }
+ public void testOtherVP9Surf1080p() { specific(otherVP9(), 1920, 1080, false /* flex */); }
+
+ // Tests encoder profiles required by CDD.
+ // H264
+ public void testH264LowQualitySDSupport() {
+ support(h264(), 320, 240, 20, 384 * 1000);
+ }
+
+ public void testH264HighQualitySDSupport() {
+ support(h264(), 720, 480, 30, 2 * 1000000);
+ }
+
+ public void testH264FlexQVGA20fps384kbps() {
+ detailed(h264(), 320, 240, 20, 384 * 1000, true /* flex */);
+ }
+
+ public void testH264SurfQVGA20fps384kbps() {
+ detailed(h264(), 320, 240, 20, 384 * 1000, false /* flex */);
+ }
+
+ public void testH264Flex480p30fps2Mbps() {
+ detailed(h264(), 720, 480, 30, 2 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf480p30fps2Mbps() {
+ detailed(h264(), 720, 480, 30, 2 * 1000000, false /* flex */);
+ }
+
+ public void testH264Flex720p30fps4Mbps() {
+ detailed(h264(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf720p30fps4Mbps() {
+ detailed(h264(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+ }
+
+ public void testH264Flex1080p30fps10Mbps() {
+ detailed(h264(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+ }
+
+ public void testH264Surf1080p30fps10Mbps() {
+ detailed(h264(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+ }
+
+ // VP8
+ public void testVP8LowQualitySDSupport() {
+ support(vp8(), 320, 180, 30, 800 * 1000);
+ }
+
+ public void testVP8HighQualitySDSupport() {
+ support(vp8(), 640, 360, 30, 2 * 1000000);
+ }
+
+ public void testVP8Flex180p30fps800kbps() {
+ detailed(vp8(), 320, 180, 30, 800 * 1000, true /* flex */);
+ }
+
+ public void testVP8Surf180p30fps800kbps() {
+ detailed(vp8(), 320, 180, 30, 800 * 1000, false /* flex */);
+ }
+
+ public void testVP8Flex360p30fps2Mbps() {
+ detailed(vp8(), 640, 360, 30, 2 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf360p30fps2Mbps() {
+ detailed(vp8(), 640, 360, 30, 2 * 1000000, false /* flex */);
+ }
+
+ public void testVP8Flex720p30fps4Mbps() {
+ detailed(vp8(), 1280, 720, 30, 4 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf720p30fps4Mbps() {
+ detailed(vp8(), 1280, 720, 30, 4 * 1000000, false /* flex */);
+ }
+
+ public void testVP8Flex1080p30fps10Mbps() {
+ detailed(vp8(), 1920, 1080, 30, 10 * 1000000, true /* flex */);
+ }
+
+ public void testVP8Surf1080p30fps10Mbps() {
+ detailed(vp8(), 1920, 1080, 30, 10 * 1000000, false /* flex */);
+ }
+
+ private void minmin(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, false /* near */);
+ }
+
+ private void minmax(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, false /* near */);
+ }
+
+ private void maxmin(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, false /* near */);
+ }
+
+ private void maxmax(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, false /* near */);
+ }
+
+ private void nearminmin(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 0 /* x */, 0 /* y */, flexYUV, true /* near */);
+ }
+
+ private void nearminmax(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 0 /* x */, 1 /* y */, flexYUV, true /* near */);
+ }
+
+ private void nearmaxmin(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 1 /* x */, 0 /* y */, flexYUV, true /* near */);
+ }
+
+ private void nearmaxmax(Encoder[] encoders, boolean flexYUV) {
+ extreme(encoders, 1 /* x */, 1 /* y */, flexYUV, true /* near */);
+ }
+
+ private void extreme(Encoder[] encoders, int x, int y, boolean flexYUV, boolean near) {
+ boolean skipped = true;
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ for (Encoder encoder: encoders) {
+ if (encoder.testExtreme(x, y, flexYUV, near)) {
+ skipped = false;
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("duplicate resolution extreme");
+ }
+ }
+
+ private void arbitrary(Encoder[] encoders, boolean flexYUV, boolean widths) {
+ boolean skipped = true;
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ for (Encoder encoder: encoders) {
+ if (encoder.testArbitrary(flexYUV, widths)) {
+ skipped = false;
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("duplicate resolution");
+ }
+ }
+
+ private void arbitraryw(Encoder[] encoders, boolean flexYUV) {
+ arbitrary(encoders, flexYUV, true /* widths */);
+ }
+
+ private void arbitraryh(Encoder[] encoders, boolean flexYUV) {
+ arbitrary(encoders, flexYUV, false /* widths */);
+ }
+
+ /* test specific size */
+ private void specific(Encoder[] encoders, int width, int height, boolean flexYUV) {
+ boolean skipped = true;
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ for (Encoder encoder : encoders) {
+ if (encoder.testSpecific(width, height, flexYUV)) {
+ skipped = false;
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("duplicate or unsupported resolution");
+ }
+ }
+
+ /* test size, frame rate and bit rate */
+ private void detailed(
+ Encoder[] encoders, int width, int height, int frameRate, int bitRate,
+ boolean flexYUV) {
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ boolean skipped = true;
+ for (Encoder encoder : encoders) {
+ if (encoder.testSupport(width, height, frameRate, bitRate)) {
+ skipped = false;
+ encoder.testDetailed(width, height, frameRate, bitRate, flexYUV);
+ }
+ }
+ if (skipped) {
+ MediaUtils.skipTest("unsupported resolution and rate");
+ }
+ }
+
+ /* test size and rate are supported */
+ private void support(Encoder[] encoders, int width, int height, int frameRate, int bitRate) {
+ boolean supported = false;
+ if (encoders.length == 0) {
+ MediaUtils.skipTest("no such encoder present");
+ return;
+ }
+ for (Encoder encoder : encoders) {
+ if (encoder.testSupport(width, height, frameRate, bitRate)) {
+ supported = true;
+ break;
+ }
+ }
+ if (!supported) {
+ fail("unsupported format " + width + "x" + height + " " +
+ frameRate + "fps " + bitRate + "bps");
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/VirtualizerTest.java b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
index 51adf1d..ac8eb84 100644
--- a/tests/tests/media/src/android/media/cts/VirtualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VirtualizerTest.java
@@ -16,14 +16,18 @@
package android.media.cts;
+import android.content.Context;
import android.media.audiofx.AudioEffect;
import android.media.AudioFormat;
-import android.media.AudioManager;
import android.media.audiofx.Virtualizer;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.util.Log;
+import com.android.cts.media.R;
+
+import java.util.Arrays;
+
public class VirtualizerTest extends PostProcTestBase {
private String TAG = "VirtualizerTest";
@@ -288,6 +292,256 @@
}
//-----------------------------------------------------------------
+ // 4 virtualizer capabilities
+ //----------------------------------
+
+ //Test case 4.0: test virtualization format / mode query: at least one of the following
+ // combinations must be supported, otherwise the effect doesn't really qualify as
+ // a virtualizer: AudioFormat.CHANNEL_OUT_STEREO or the quad and 5.1 side/back variants,
+ // in VIRTUALIZATION_MODE_BINAURAL or VIRTUALIZATION_MODE_TRANSAURAL
+ public void test4_0FormatModeQuery() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ boolean isAtLeastOneConfigSupported = false;
+ boolean isConfigSupported = false;
+
+ // testing combinations of input channel mask and virtualization mode
+ for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+ for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+ isConfigSupported = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m]);
+ isAtLeastOneConfigSupported |= isConfigSupported;
+ // optional logging
+ String channelMask = Integer.toHexString(CHANNEL_MASKS[i]).toUpperCase();
+ String nativeChannelMask =
+ Integer.toHexString(CHANNEL_MASKS[i] >> 2).toUpperCase();
+ Log.d(TAG, "content channel mask: 0x" + channelMask + " (native 0x"
+ + nativeChannelMask
+ + ") mode: " + VIRTUALIZATION_MODES[m]
+ + " supported=" + isConfigSupported);
+ }
+ }
+
+ assertTrue("no valid configuration supported", isAtLeastOneConfigSupported);
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //Test case 4.1: test that the capabilities reported by Virtualizer.canVirtualize(int,int)
+ // matches those returned by Virtualizer.getSpeakerAngles(int, int, int[])
+ public void test4_1SpeakerAnglesCapaMatchesFormatModeCapa() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+ // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+ int[] angles = new int[3*6];
+ for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+ for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+ Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+ boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m]);
+ boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m], angles);
+ assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+ canVirtualize == canGetAngles);
+ if(canGetAngles) {
+ //check if the number of angles matched the expected number of channels for
+ //each CHANNEL_MASKS
+ int expectedChannelCount = CHANNEL_MASKS_CHANNEL_COUNT[i];
+ for(int k=0; k<expectedChannelCount; k++) {
+ int speakerIdentification = angles[k*3];
+ assertTrue("found unexpected speaker identification or channel count",
+ speakerIdentification !=AudioFormat.CHANNEL_INVALID );
+ }
+ }
+ }
+ }
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //Test case 4.2: test forcing virtualization mode: at least binaural or transaural must be
+ // supported
+ public void test4_2VirtualizationMode() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ mVirtualizer.setEnabled(true);
+ assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+ // testing binaural
+ boolean binauralSupported =
+ mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_BINAURAL);
+ // testing transaural
+ boolean transauralSupported = mVirtualizer
+ .forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL);
+ // testing at least one supported
+ assertTrue("doesn't support binaural nor transaural",
+ binauralSupported || transauralSupported);
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //Test case 4.3: test disabling virtualization maps to VIRTUALIZATION_MODE_OFF
+ public void test4_3DisablingVirtualizationOff() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ mVirtualizer.setEnabled(false);
+ assertFalse("invalid state from getEnabled", mVirtualizer.getEnabled());
+ int virtMode = mVirtualizer.getVirtualizationMode();
+ assertTrue("disabled virtualization isn't reported as OFF",
+ virtMode == Virtualizer.VIRTUALIZATION_MODE_OFF);
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //Test case 4.4: test forcing virtualization mode to AUTO
+ public void test4_4VirtualizationModeAuto() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ mVirtualizer.setEnabled(true);
+ assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+ boolean forceRes =
+ mVirtualizer.forceVirtualizationMode(Virtualizer.VIRTUALIZATION_MODE_AUTO);
+ assertTrue("can't set virtualization to AUTO", forceRes);
+
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //Test case 4.5: test for consistent capabilities if virtualizer is enabled or disabled
+ public void test4_5ConsistentCapabilitiesWithEnabledDisabled() throws Exception {
+ if (!isVirtualizerAvailable()) {
+ return;
+ }
+ getVirtualizer(getSessionId());
+ try {
+ // 3: see size requirement in Virtualizer.getSpeakerAngles(int, int, int[])
+ // 6: for number of channels of 5.1 masks in CHANNEL_MASKS
+ int[] angles = new int[3*6];
+ boolean[][] values = new boolean[VIRTUALIZATION_MODES.length][CHANNEL_MASKS.length];
+ mVirtualizer.setEnabled(true);
+ assertTrue("invalid state from getEnabled", mVirtualizer.getEnabled());
+ for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+ for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+ Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+ boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m]);
+ boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m], angles);
+ assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+ canVirtualize == canGetAngles);
+ values[m][i] = canVirtualize;
+ }
+ }
+
+ mVirtualizer.setEnabled(false);
+ assertTrue("invalid state from getEnabled", !mVirtualizer.getEnabled());
+ for (int m = 0 ; m < VIRTUALIZATION_MODES.length ; m++) {
+ for (int i = 0 ; i < CHANNEL_MASKS.length ; i++) {
+ Arrays.fill(angles,AudioFormat.CHANNEL_INVALID);
+ boolean canVirtualize = mVirtualizer.canVirtualize(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m]);
+ boolean canGetAngles = mVirtualizer.getSpeakerAngles(CHANNEL_MASKS[i],
+ VIRTUALIZATION_MODES[m], angles);
+ assertTrue("mismatch capability between canVirtualize() and getSpeakerAngles()",
+ canVirtualize == canGetAngles);
+ assertTrue("mismatch capability between enabled and disabled virtualizer",
+ canVirtualize == values[m][i]);
+ }
+ }
+
+ } catch (IllegalArgumentException e) {
+ fail("bad parameter value");
+ } catch (UnsupportedOperationException e) {
+ fail("command not supported");
+ } catch (IllegalStateException e) {
+ fail("command called in wrong state");
+ } finally {
+ releaseVirtualizer();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // private data
+ //----------------------------------
+ // channel masks to test at input of virtualizer
+ private static final int[] CHANNEL_MASKS = {
+ AudioFormat.CHANNEL_OUT_STEREO, //2 channels
+ AudioFormat.CHANNEL_OUT_QUAD, //4 channels
+ //AudioFormat.CHANNEL_OUT_QUAD_SIDE (definition is not public)
+ (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+ AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT), //4 channels
+ AudioFormat.CHANNEL_OUT_5POINT1, //6 channels
+ //AudioFormat.CHANNEL_OUT_5POINT1_SIDE (definition is not public)
+ (AudioFormat.CHANNEL_OUT_FRONT_LEFT | AudioFormat.CHANNEL_OUT_FRONT_RIGHT |
+ AudioFormat.CHANNEL_OUT_FRONT_CENTER |
+ AudioFormat.CHANNEL_OUT_LOW_FREQUENCY |
+ AudioFormat.CHANNEL_OUT_SIDE_LEFT | AudioFormat.CHANNEL_OUT_SIDE_RIGHT) //6 channels
+ };
+
+ private static final int[] CHANNEL_MASKS_CHANNEL_COUNT = {
+ 2,
+ 4,
+ 4,
+ 6,
+ 6
+ };
+
+ private static final int[] VIRTUALIZATION_MODES = {
+ Virtualizer.VIRTUALIZATION_MODE_BINAURAL,
+ Virtualizer.VIRTUALIZATION_MODE_TRANSAURAL
+ };
+
+ //-----------------------------------------------------------------
// private methods
//----------------------------------
@@ -362,7 +616,6 @@
mLooper = Looper.myLooper();
mVirtualizer2 = new Virtualizer(0, 0);
- assertNotNull("could not create virtualizer2", mVirtualizer2);
synchronized(mLock) {
if (mControl) {
@@ -436,4 +689,4 @@
}
}
-}
\ No newline at end of file
+}
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 8c91e9b..3c639a0 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -16,12 +16,18 @@
package android.media.cts;
+import com.android.cts.media.R;
+
+import android.content.Context;
import android.media.audiofx.AudioEffect;
import android.media.AudioFormat;
import android.media.AudioManager;
+import android.media.MediaPlayer;
import android.media.audiofx.Visualizer;
+import android.media.audiofx.Visualizer.MeasurementPeakRms;
import android.os.Looper;
import android.test.AndroidTestCase;
+import java.util.UUID;
import android.util.Log;
public class VisualizerTest extends PostProcTestBase {
@@ -126,7 +132,7 @@
// 2 - check capture
//----------------------------------
- //Test case 2.0: test cature in polling mode
+ //Test case 2.0: test capture in polling mode
public void test2_0PollingCapture() throws Exception {
if (!hasAudioOutput()) {
return;
@@ -217,6 +223,215 @@
}
//-----------------------------------------------------------------
+ // 3 - check measurement mode MEASUREMENT_MODE_NONE
+ //----------------------------------
+
+ //Test case 3.0: test setting NONE measurement mode
+ public void test3_0MeasurementModeNone() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ try {
+ getVisualizer(0);
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
+ assertEquals("setMeasurementMode for NONE doesn't report success",
+ Visualizer.SUCCESS, status);
+
+ int mode = mVisualizer.getMeasurementMode();
+ assertEquals("getMeasurementMode reports NONE",
+ Visualizer.MEASUREMENT_MODE_NONE, mode);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ releaseVisualizer();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // 4 - check measurement mode MEASUREMENT_MODE_PEAK_RMS
+ //----------------------------------
+
+ //Test case 4.0: test setting peak / RMS measurement mode
+ public void test4_0MeasurementModePeakRms() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ try {
+ getVisualizer(0);
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ assertEquals("setMeasurementMode for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+
+ int mode = mVisualizer.getMeasurementMode();
+ assertEquals("getMeasurementMode doesn't report PEAK_RMS",
+ Visualizer.MEASUREMENT_MODE_PEAK_RMS, mode);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ releaseVisualizer();
+ }
+ }
+
+ //Test case 4.1: test measurement of peak / RMS
+ public void test4_1MeasurePeakRms() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ AudioEffect vc = null;
+ try {
+ // this test will play a 1kHz sine wave with peaks at -40dB
+ MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
+ final int EXPECTED_PEAK_MB = -4015;
+ final int EXPECTED_RMS_MB = -4300;
+ final int MAX_MEASUREMENT_ERROR_MB = 2000;
+ assertNotNull("null MediaPlayer", mp);
+
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+ 0,
+ mp.getAudioSessionId());
+ vc.setEnabled(true);
+
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+ getVisualizer(mp.getAudioSessionId());
+ mp.setLooping(true);
+ mp.start();
+
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+ // make sure we're playing long enough so the measurement is valid
+ int currentPosition = mp.getCurrentPosition();
+ final int maxTry = 100;
+ int tryCount = 0;
+ while (currentPosition < 200 && tryCount < maxTry) {
+ Thread.sleep(50);
+ currentPosition = mp.getCurrentPosition();
+ tryCount++;
+ }
+ assertTrue("MediaPlayer not ready", tryCount < maxTry);
+
+ MeasurementPeakRms measurement = new MeasurementPeakRms();
+ status = mVisualizer.getMeasurementPeakRms(measurement);
+ mp.stop();
+ mp.release();
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+ assertEquals("getMeasurementPeakRms() reports failure",
+ Visualizer.SUCCESS, status);
+ Log.i("VisTest", "peak="+measurement.mPeak+" rms="+measurement.mRms);
+ int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+ int deltaRms = Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+ assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+ assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ if (vc != null)
+ vc.release();
+ releaseVisualizer();
+ }
+ }
+
+ //Test case 4.2: test measurement of peak / RMS in Long MP3
+ public void test4_2MeasurePeakRmsLongMP3() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ AudioEffect vc = null;
+ try {
+ // this test will play a 1kHz sine wave with peaks at -40dB
+ MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzs40dblong);
+ final int EXPECTED_PEAK_MB = -4015;
+ final int EXPECTED_RMS_MB = -4300;
+ final int MAX_MEASUREMENT_ERROR_MB = 2000;
+ assertNotNull("null MediaPlayer", mp);
+
+ // creating a volume controller on output mix ensures that ro.audio.silent mutes
+ // audio after the effects and not before
+ vc = new AudioEffect(
+ AudioEffect.EFFECT_TYPE_NULL,
+ UUID.fromString(BUNDLE_VOLUME_EFFECT_UUID),
+ 0,
+ mp.getAudioSessionId());
+ vc.setEnabled(true);
+
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+ getVisualizer(mp.getAudioSessionId());
+ mp.start();
+
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+ // make sure we're playing long enough so the measurement is valid
+ int currentPosition = mp.getCurrentPosition();
+ final int maxTry = 100;
+ int tryCount = 0;
+ while (currentPosition < 400 && tryCount < maxTry) {
+ Thread.sleep(50);
+ currentPosition = mp.getCurrentPosition();
+ tryCount++;
+ }
+ assertTrue("MediaPlayer not ready", tryCount < maxTry);
+
+ MeasurementPeakRms measurement = new MeasurementPeakRms();
+ status = mVisualizer.getMeasurementPeakRms(measurement);
+ mp.stop();
+ mp.release();
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+ assertEquals("getMeasurementPeakRms() reports failure",
+ Visualizer.SUCCESS, status);
+ Log.i("VisTest", "peak="+measurement.mPeak+" rms="+measurement.mRms);
+ int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+ int deltaRms = Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+ assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+ assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ if (vc != null)
+ vc.release();
+ releaseVisualizer();
+ }
+ }
+
+ //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java b/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
index 7c65824..6f4ebdd 100644
--- a/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
+++ b/tests/tests/mediastress/src/android/mediastress/cts/MediaPlayerStressTest.java
@@ -105,13 +105,18 @@
* @throws Exception
*/
protected void doTestVideoPlayback(int mediaNumber, int repeatCounter) throws Exception {
+ Instrumentation inst = getInstrumentation();
+ String mediaName = getFullVideoClipName(mediaNumber);
+ if (!MediaUtils.checkCodecsForPath(inst.getTargetContext(), mediaName)) {
+ return; // not supported, message is already logged
+ }
+
File playbackOutput = new File(WorkDir.getTopDir(), "PlaybackTestResult.txt");
Writer output = new BufferedWriter(new FileWriter(playbackOutput, true));
boolean testResult = true;
boolean onCompleteSuccess = false;
- Instrumentation inst = getInstrumentation();
Intent intent = new Intent();
intent.setClass(inst.getTargetContext(), MediaFrameworkTest.class);
@@ -119,10 +124,6 @@
Activity act = inst.startActivitySync(intent);
- String mediaName = getFullVideoClipName(mediaNumber);
- if (!MediaUtils.checkCodecsForPath(inst.getTargetContext(), mediaName)) {
- return; // not supported, message is already logged
- }
for (int i = 0; i < repeatCounter; i++) {
Log.v(TAG, "start playing " + mediaName);
onCompleteSuccess =
diff --git a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
index d12c1ce..9daf3c4 100644
--- a/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
+++ b/tests/tests/net/src/android/net/cts/ConnectivityManagerTest.java
@@ -202,7 +202,11 @@
}
private boolean isSupported(int networkType) {
- return mNetworks.containsKey(networkType);
+ // Change-Id I02eb5f22737720095f646f8db5c87fd66da129d6 added VPN support
+ // to all devices directly in software, independent of any external
+ // configuration.
+ return mNetworks.containsKey(networkType) ||
+ (networkType == ConnectivityManager.TYPE_VPN);
}
// true if only the system can turn it on
diff --git a/tests/tests/net/src/android/net/cts/LocalSocketTest.java b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
index 0a4bc0d..70d69c8 100644
--- a/tests/tests/net/src/android/net/cts/LocalSocketTest.java
+++ b/tests/tests/net/src/android/net/cts/LocalSocketTest.java
@@ -112,6 +112,7 @@
}
public void testAccessors() throws IOException{
+ final int INITIAL_SEND_BUFFER_SIZE = 1998;
LocalSocket socket = new LocalSocket();
LocalSocketAddress addr = new LocalSocketAddress("secondary");
@@ -126,8 +127,14 @@
socket.setReceiveBufferSize(1999);
assertEquals(1999 << 1, socket.getReceiveBufferSize());
- socket.setSendBufferSize(1998);
- assertEquals(1998 << 1, socket.getSendBufferSize());
+ socket.setSendBufferSize(INITIAL_SEND_BUFFER_SIZE);
+ int sendBufferSize = socket.getSendBufferSize();
+ if ((INITIAL_SEND_BUFFER_SIZE << 1) < sendBufferSize) {
+ socket.setSendBufferSize(sendBufferSize / 2);
+ assertEquals(sendBufferSize, socket.getSendBufferSize());
+ } else {
+ assertEquals(INITIAL_SEND_BUFFER_SIZE << 1, sendBufferSize);
+ }
// Timeout is not support at present, so set is ignored
socket.setSoTimeout(1996);
diff --git a/tests/tests/net/src/android/net/cts/TrafficStatsTest.java b/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
index 74cd771..5b93bee 100755
--- a/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
+++ b/tests/tests/net/src/android/net/cts/TrafficStatsTest.java
@@ -34,6 +34,7 @@
public class TrafficStatsTest extends AndroidTestCase {
private static final String LOG_TAG = "TrafficStatsTest";
+
public void testValidMobileStats() {
// We can't assume a mobile network is even present in this test, so
// we simply assert that a valid value is returned.
@@ -217,17 +218,6 @@
assertTrue("too many non-localhost packets on the sam UID", deltaTxOtherPackets + deltaTxOtherPackets < 20);
}
- // Some other tests don't cleanup connections correctly.
- // They have the same UID, so we discount their lingering traffic
- // which happens only on non-localhost, such as TCP FIN retranmission packets
- long deltaTxOtherPackets = (totalTxPacketsAfter - totalTxPacketsBefore) - uidTxDeltaPackets;
- long deltaRxOtherPackets = (totalRxPacketsAfter - totalRxPacketsBefore) - uidRxDeltaPackets;
- if (deltaTxOtherPackets > 0 || deltaRxOtherPackets > 0) {
- Log.i(LOG_TAG, "lingering traffic data: " + deltaTxOtherPackets + "/" + deltaRxOtherPackets);
- // Make sure that not too many non-localhost packets are accounted for
- assertTrue("too many non-localhost packets on the sam UID", deltaTxOtherPackets + deltaTxOtherPackets < 20);
- }
-
assertTrue("uidtxp: " + uidTxPacketsBefore + " -> " + uidTxPacketsAfter + " delta=" + uidTxDeltaPackets +
" Wanted: " + uidTxDeltaPackets + ">=" + packetCount + "+" + minExpectedExtraPackets + " && " +
uidTxDeltaPackets + "<=" + packetCount + "+" + packetCount + "+" + maxExpectedExtraPackets + "+" + deltaTxOtherPackets,
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 582b81a..eddb416 100644
--- a/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
+++ b/tests/tests/net/src/android/net/ipv6/cts/PingTest.java
@@ -133,6 +133,7 @@
// Check the response is an echo reply.
byte[] response = new byte[bytesRead];
+ responseBuffer.flip();
responseBuffer.get(response, 0, bytesRead);
assertEquals((byte) 0x81, response[0]);
diff --git a/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java b/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
index d434728..e132cce 100644
--- a/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/NsdManagerTest.java
@@ -372,8 +372,6 @@
assertTrue(lastEvent != null);
assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
- assertTrue(eventCacheSize() == 2);
-
// Register service again to see if we discover it
checkForAdditionalEvents();
clearEventCache();
@@ -405,7 +403,6 @@
lastEvent.mInfo.getServiceName());
assertTrue(lastEvent.mInfo.getServiceName().equals(registeredName));
- assertTrue(checkCacheSize(2));
checkForAdditionalEvents();
clearEventCache();
diff --git a/tests/tests/os/src/android/os/cts/BuildVersionTest.java b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
index 3002ca3..419f320 100644
--- a/tests/tests/os/src/android/os/cts/BuildVersionTest.java
+++ b/tests/tests/os/src/android/os/cts/BuildVersionTest.java
@@ -29,8 +29,8 @@
private static final String LOG_TAG = "BuildVersionTest";
private static final Set<String> EXPECTED_RELEASES =
- new HashSet<String>(Arrays.asList("5.0.1", "5.0.2"));
- private static final int EXPECTED_SDK = 21;
+ new HashSet<String>(Arrays.asList("5.1", "5.1.1"));
+ private static final int EXPECTED_SDK = 22;
private static final String EXPECTED_BUILD_VARIANT = "user";
private static final String EXPECTED_TAG = "release-keys";
diff --git a/tests/tests/os/src/android/os/cts/StatFsTest.java b/tests/tests/os/src/android/os/cts/StatFsTest.java
index 67afde9..a0653fd 100644
--- a/tests/tests/os/src/android/os/cts/StatFsTest.java
+++ b/tests/tests/os/src/android/os/cts/StatFsTest.java
@@ -46,15 +46,15 @@
assertTrue(stat.getBlockSize() > 0);
assertTrue(stat.getBlockCount() > 0);
assertTrue(stat.getFreeBlocks() >= stat.getAvailableBlocks());
- assertTrue(stat.getAvailableBlocks() > 0);
+ assertTrue(stat.getAvailableBlocks() >= 0);
assertTrue(stat.getBlockSizeLong() > 0);
assertTrue(stat.getBlockCountLong() > 0);
assertTrue(stat.getFreeBlocksLong() >= stat.getAvailableBlocksLong());
- assertTrue(stat.getAvailableBlocksLong() > 0);
+ assertTrue(stat.getAvailableBlocksLong() >= 0);
- assertTrue(stat.getFreeBytes() > 0);
- assertTrue(stat.getAvailableBytes() > 0);
+ assertTrue(stat.getFreeBytes() >= 0);
+ assertTrue(stat.getAvailableBytes() >= 0);
assertTrue(stat.getTotalBytes() > 0);
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 0000000..0c648c5
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,125 @@
+/*
+ * 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.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+ private static final String DUMMY_INPUT_ID = "dummy";
+
+ private boolean mHasTvInputFramework;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHasTvInputFramework = getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private void verifyQuery(Uri uri, final String[] projection,
+ String tableName) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, null, null, null);
+ fail("Accessing " + tableName + " table should require READ_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyInsert(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().insert(uri, values);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyUpdate(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().update(uri, values, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyDelete(Uri uri, String tableName) throws Exception {
+ try {
+ getContext().getContentResolver().delete(uri, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void testQueryChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQuery(TvContract.Channels.CONTENT_URI, projection, "channels");
+ }
+
+ public void testInsertChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testUpdateChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testDeleteChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testQueryPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ final String[] projection = { TvContract.Programs._ID };
+ verifyQuery(TvContract.Programs.CONTENT_URI, projection, "programs");
+ }
+
+ public void testInsertPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ public void testUpdatePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ public void testDeletePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+ }
+}
diff --git a/tests/tests/provider/src/android/provider/cts/BrowserTest.java b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
index 2f0432a..9f96412 100644
--- a/tests/tests/provider/src/android/provider/cts/BrowserTest.java
+++ b/tests/tests/provider/src/android/provider/cts/BrowserTest.java
@@ -84,7 +84,9 @@
value.put(colNames[i], cursor.getFloat(i));
break;
case Cursor.FIELD_TYPE_INTEGER:
- value.put(colNames[i], cursor.getLong(i));
+ if (!"_ID".equalsIgnoreCase(colNames[i])) {
+ value.put(colNames[i], cursor.getLong(i));
+ }
break;
case Cursor.FIELD_TYPE_STRING:
value.put(colNames[i], cursor.getString(i));
diff --git a/tests/tests/provider/src/android/provider/cts/CalendarTest.java b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
index a8f547b..c9e2213 100644
--- a/tests/tests/provider/src/android/provider/cts/CalendarTest.java
+++ b/tests/tests/provider/src/android/provider/cts/CalendarTest.java
@@ -61,8 +61,8 @@
// an arbitrary int used by some tests
private static final int SOME_ARBITRARY_INT = 143234;
- // 10 sec timeout for reminder broadcast (but shouldn't usually take this long).
- private static final int POLLING_TIMEOUT = 10000;
+ // 15 sec timeout for reminder broadcast (but shouldn't usually take this long).
+ private static final int POLLING_TIMEOUT = 15000;
// @formatter:off
private static final String[] TIME_ZONES = new String[] {
@@ -352,6 +352,7 @@
Events.SYNC_DATA2,
Events.SYNC_DATA3,
Events.SYNC_DATA4,
+ Events.MUTATORS,
};
// @formatter:on
@@ -3275,6 +3276,67 @@
CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
}
+ @MediumTest
+ public void testMutatorSetCorrectly() {
+ String account = "ec_account";
+ String packageName = "com.android.cts.provider";
+ int seed = 0;
+
+ // Clean up just in case
+ CalendarHelper.deleteCalendarByAccount(mContentResolver, account);
+
+ String mutator;
+ Cursor cursor;
+ ContentValues values = new ContentValues();
+ final long calendarId = createAndVerifyCalendar(account, seed++, null);
+
+ // Verify mutator is set to the package, via:
+ // Create:
+ final long eventId = createAndVerifyEvent(account, seed, calendarId, false, null);
+ final Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventId);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertEquals(packageName, mutator);
+
+ // Edit:
+ // First clear the mutator column
+ values.clear();
+ values.putNull(Events.MUTATORS);
+ mContentResolver.update(asSyncAdapter(uri, account, CTS_TEST_TYPE), values, null, null);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertNull(mutator);
+ // Now edit the event and verify the mutator column
+ values.clear();
+ values.put(Events.TITLE, "New title");
+ mContentResolver.update(uri, values, null, null);
+ cursor = mContentResolver.query(uri, new String[] {Events.MUTATORS}, null, null, null);
+ cursor.moveToFirst();
+ mutator = cursor.getString(0);
+ cursor.close();
+ assertEquals(packageName, mutator);
+
+ // Clean up the event
+ assertEquals(1, EventHelper.deleteEventAsSyncAdapter(mContentResolver, uri, account));
+
+ // Delete:
+ // First create as sync adapter
+ final long eventId2 = createAndVerifyEvent(account, seed, calendarId, true, null);
+ final Uri uri2 = ContentUris.withAppendedId(Events.CONTENT_URI, eventId2);
+ // Now delete the event and verify
+ values.clear();
+ values.put(Events.MUTATORS, packageName);
+ removeAndVerifyEvent(uri2, values, account);
+
+
+ // delete the calendar
+ removeAndVerifyCalendar(account, calendarId);
+ }
+
/**
* Acquires the set of instances that appear between the specified start and end points.
*
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
index fcb48cf..b515550 100644
--- a/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_FrequentsStrequentsTest.java
@@ -29,6 +29,7 @@
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.DataUsageFeedback;
import android.provider.ContactsContract.RawContacts;
import android.provider.cts.ContactsContract_TestDataBuilder.TestContact;
@@ -174,6 +175,57 @@
assertCursorStoredValuesWithContactsFilter(uri, ids, false, sContentValues[1], sContentValues[0]);
}
+ public void testStrequents_projection() throws Exception {
+ long[] ids = setupTestData();
+
+ // Start contact 0 and mark contact 2 as frequent
+ starContact(ids[0]);
+ markDataAsUsed(mDataIds[2], 1);
+
+ // Construct a uri for phone only favorites.
+ Uri uri = Contacts.CONTENT_STREQUENT_URI;
+
+ DatabaseAsserts.checkProjection(mResolver, uri,
+ new String[]{
+ Contacts._ID,
+ Contacts.HAS_PHONE_NUMBER,
+ Contacts.NAME_RAW_CONTACT_ID,
+ Contacts.IS_USER_PROFILE,
+ Contacts.CUSTOM_RINGTONE,
+ Contacts.DISPLAY_NAME,
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.IN_DEFAULT_DIRECTORY,
+ Contacts.IN_VISIBLE_GROUP,
+ Contacts.LAST_TIME_CONTACTED,
+ Contacts.LOOKUP_KEY,
+ Contacts.PHONETIC_NAME,
+ Contacts.PHONETIC_NAME_STYLE,
+ Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
+ Contacts.PHOTO_URI,
+ Contacts.PHOTO_THUMBNAIL_URI,
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.SORT_KEY_ALTERNATIVE,
+ Contacts.SORT_KEY_PRIMARY,
+ Contacts.STARRED,
+ Contacts.PINNED,
+ Contacts.TIMES_CONTACTED,
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_CHAT_CAPABILITY,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE,
+ Contacts.CONTACT_STATUS_LABEL,
+ Contacts.CONTACT_STATUS_ICON,
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED,
+ },
+ new long[]{ids[0], ids[2]}
+ );
+ }
+
public void testStrequents_phoneOnly() throws Exception {
long[] ids = setupTestData();
@@ -213,6 +265,63 @@
sContentValues[2], sContentValues[0]);
}
+ public void testStrequents_phoneOnly_projection() throws Exception {
+ long[] ids = setupTestData();
+
+ // Start contact 0 and mark contact 2 as frequent
+ starContact(ids[0]);
+ markDataAsUsed(mDataIds[2], 1);
+
+ // Construct a uri for phone only favorites.
+ Uri uri = Contacts.CONTENT_STREQUENT_URI.buildUpon().
+ appendQueryParameter(ContactsContract.STREQUENT_PHONE_ONLY, "true").build();
+
+ DatabaseAsserts.checkProjection(mResolver, uri,
+ new String[] {
+ Data._ID,
+ Contacts.HAS_PHONE_NUMBER,
+ Contacts.NAME_RAW_CONTACT_ID,
+ Contacts.IS_USER_PROFILE,
+ Contacts.CUSTOM_RINGTONE,
+ Contacts.DISPLAY_NAME,
+ Contacts.DISPLAY_NAME_ALTERNATIVE,
+ Contacts.DISPLAY_NAME_SOURCE,
+ Contacts.IN_DEFAULT_DIRECTORY,
+ Contacts.IN_VISIBLE_GROUP,
+ Contacts.LAST_TIME_CONTACTED,
+ Contacts.LOOKUP_KEY,
+ Contacts.PHONETIC_NAME,
+ Contacts.PHONETIC_NAME_STYLE,
+ Contacts.PHOTO_ID,
+ Contacts.PHOTO_FILE_ID,
+ Contacts.PHOTO_URI,
+ Contacts.PHOTO_THUMBNAIL_URI,
+ Contacts.SEND_TO_VOICEMAIL,
+ Contacts.SORT_KEY_ALTERNATIVE,
+ Contacts.SORT_KEY_PRIMARY,
+ Contacts.STARRED,
+ Contacts.PINNED,
+ Contacts.TIMES_CONTACTED,
+ Contacts.CONTACT_LAST_UPDATED_TIMESTAMP,
+ Contacts.CONTACT_PRESENCE,
+ Contacts.CONTACT_CHAT_CAPABILITY,
+ Contacts.CONTACT_STATUS,
+ Contacts.CONTACT_STATUS_TIMESTAMP,
+ Contacts.CONTACT_STATUS_RES_PACKAGE,
+ Contacts.CONTACT_STATUS_LABEL,
+ Contacts.CONTACT_STATUS_ICON,
+ Data.TIMES_USED,
+ Data.LAST_TIME_USED,
+ Phone.NUMBER,
+ Phone.TYPE,
+ Phone.LABEL,
+ Phone.IS_SUPER_PRIMARY,
+ Phone.CONTACT_ID,
+ },
+ new long[] {mDataIds[0], mDataIds[2]} // Note _id from phone_only is data._id
+ );
+ }
+
public void testFrequents_noFrequentsReturnsEmptyCursor() throws Exception {
long[] ids = setupTestData();
assertCursorStoredValuesWithContactsFilter(Contacts.CONTENT_FREQUENT_URI, ids, false);
diff --git a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
index 0eae82b..c1b2229 100644
--- a/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
+++ b/tests/tests/provider/src/android/provider/cts/MediaStore_FilesTest.java
@@ -373,7 +373,7 @@
values.put(MediaStore.Audio.Media.MIME_TYPE, "audio/mp3");
Uri fileUri = mResolver.insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);
// give media provider some time to realize there's no album art
- //SystemClock.sleep(1000);
+ SystemClock.sleep(1000);
// get its album id
Cursor c = mResolver.query(fileUri, new String[] { MediaStore.Audio.Media.ALBUM_ID},
null, null, null);
diff --git a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
index 7546066..7f98284 100644
--- a/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
+++ b/tests/tests/provider/src/android/provider/cts/contacts/DatabaseAsserts.java
@@ -21,6 +21,8 @@
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
+import android.provider.BaseColumns;
+import android.provider.ContactsContract.Contacts;
import android.provider.cts.contacts.account.StaticAccountAuthenticator;
import android.test.MoreAsserts;
@@ -261,4 +263,76 @@
}
return true;
}
+
+ public static String buildIdSelection(long[] ids) {
+ StringBuilder selection = new StringBuilder();
+ selection.append(BaseColumns._ID + " in ");
+ selection.append("(");
+ for (int i = 0; i < ids.length; i++) {
+ if (i != 0) selection.append(",");
+ selection.append(ids[i]);
+ }
+ selection.append(")");
+ return selection.toString();
+ }
+
+ /**
+ * Check if a query accepts all columns in the projection, and a resulting cursor contains
+ * all expected columns. This also checks the number of resulting rows, but don't check
+ * actual content in a returned cursor.
+ */
+ public static void checkProjection(ContentResolver resolver,
+ Uri uri, String[] allColumns, long[] ids) {
+ final String selection = buildIdSelection(ids);
+
+ // First, check the null projection (i.e. all columns).
+ checkProjectionInner(resolver, uri, null, allColumns, selection, ids.length,
+ /* keepPosition =*/ false);
+
+ // All columns.
+ checkProjectionInner(resolver, uri, allColumns, allColumns, selection, ids.length,
+ /* keepPosition =*/ true);
+
+ // Select each column one by one.
+ for (int i = 0; i < allColumns.length; i++) {
+ final String[] columns = new String[] {allColumns[i]};
+ checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+ /* keepPosition =*/ true);
+ }
+
+ // Select two columns.
+ for (int i = 0; i < allColumns.length; i++) {
+ for (int j = 0; j < allColumns.length; j++) {
+ // Requesting the same column multiple times is okay, but it'd make the column
+ // order check harder.
+ if (i == j) continue;
+ final String[] columns = new String[] {allColumns[i], allColumns[j]};
+ checkProjectionInner(resolver, uri, columns, columns, selection, ids.length,
+ /* keepPosition =*/ true);
+ }
+ }
+ }
+
+ private static void checkProjectionInner(ContentResolver resolver,
+ Uri uri, String[] projection, String[] expectedColumns,
+ String selection, int expectedRowCount, boolean keepPosition) {
+ final Cursor c = resolver.query(uri, projection, selection,
+ /* args =*/ null, /* sort =*/ null);
+ Assert.assertNotNull(c);
+ try {
+ Assert.assertEquals("# of rows", expectedRowCount, c.getCount());
+
+ // Make sure expected columns exist.
+ for (int i = 0; i < expectedColumns.length; i++) {
+ final String column = expectedColumns[i];
+ if (keepPosition) {
+ Assert.assertEquals(column, c.getColumnName(i));
+ } else {
+ Assert.assertTrue(column, c.getColumnIndex(column) >= 0);
+ }
+ }
+ } finally {
+ c.close();
+ }
+ }
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
index 1880132..8faeb22 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve3x3.java
@@ -24,11 +24,17 @@
float cf1[] = {0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f};
float cf2[] = {0.f, -1.f, 0.f, -1.f, 5.f, -1.f, 0.f, -1.f, 0.f};
+ float irCoeff1 = 3.1415927f;
+ float irCoeff2 = -irCoeff1;
+ float cf3[] = {0.f, irCoeff1, 0.f, irCoeff2, 1.f, irCoeff2, 0.f, irCoeff1, 0.f};
+
Element e = makeElement(dt, vecSize);
System.gc();
makeBuffers(w, h, e);
+ mVerify.set_gAllowedIntError(1);
+
ScriptIntrinsicConvolve3x3 si = ScriptIntrinsicConvolve3x3.create(mRS, e);
si.setCoefficients(cf1);
si.setInput(mAllocSrc);
@@ -116,6 +122,44 @@
}
//android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
+ si.setCoefficients(cf3);
+ sr.set_gCoeffs(cf3);
+ si.forEach(mAllocRef, sc);
+ if (dt == Element.DataType.UNSIGNED_8) {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_convolve_U4(mAllocDst, sc);
+ break;
+ case 3:
+ sr.forEach_convolve_U3(mAllocDst, sc);
+ break;
+ case 2:
+ sr.forEach_convolve_U2(mAllocDst, sc);
+ break;
+ case 1:
+ sr.forEach_convolve_U1(mAllocDst, sc);
+ break;
+ }
+ } else {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_convolve_F4(mAllocDst, sc);
+ break;
+ case 3:
+ sr.forEach_convolve_F3(mAllocDst, sc);
+ break;
+ case 2:
+ sr.forEach_convolve_F2(mAllocDst, sc);
+ break;
+ case 1:
+ sr.forEach_convolve_F1(mAllocDst, sc);
+ break;
+ }
+ }
+ //android.util.Log.e("RSI test", "test convolve U8_" + vecSize + " 2 " + w + ", " + h);
+ mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+
mRS.finish();
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
index ee92651..0753c62 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicConvolve5x5.java
@@ -85,13 +85,24 @@
-1.f, 0.f, 0.f, 0.f, -1.f,
-1.f, -1.f, -1.f, -1.f, -1.f};
+ float irCoeff1 = 3.1415927f;
+ float irCoeff2 = -irCoeff1;
+ float cf3[] = {irCoeff1, -1.f, -1.f, -1.f, irCoeff2,
+ irCoeff1, 0.f, 0.f, 0.f, irCoeff2,
+ irCoeff1, 0.f, 7.f, 0.f, irCoeff2,
+ irCoeff1, 0.f, 0.f, 0.f, irCoeff2,
+ irCoeff1, -1.f, -1.f, -1.f, irCoeff2};
+
Element e = makeElement(dt, vecSize);
makeBuffers(w, h, e);
+ mVerify.set_gAllowedIntError(1);
+
ScriptIntrinsicConvolve5x5 si = ScriptIntrinsicConvolve5x5.create(mRS, e);
ScriptC_intrinsic_convolve5x5 sr = new ScriptC_intrinsic_convolve5x5(mRS);
test5(sr, si, e, cf1, "test convolve", 1, w, h, sc);
test5(sr, si, e, cf2, "test convolve", 2, w, h, sc);
+ test5(sr, si, e, cf3, "test convolve", 3, w, h, sc);
}
public void test_U8_4() {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
new file mode 100644
index 0000000..d593bff
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/IntrinsicResize.java
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.renderscript.cts;
+
+import android.renderscript.*;
+import android.util.Log;
+
+public class IntrinsicResize extends IntrinsicBase {
+
+ static final int inX = 307;
+ static final int inY = 157;
+
+ private void testReszie(int w, int h, Element.DataType dt, int vecSize, float scaleX, float scaleY) {
+
+ Element e = makeElement(dt, vecSize);
+
+ System.gc();
+ makeSource(w, h, e);
+
+ int outW = (int) (w*scaleX);
+ int outH = (int) (h*scaleY);
+ if (mAllocRef != null) {
+ mAllocRef.destroy();
+ }
+ if (mAllocDst != null) {
+ mAllocDst.destroy();
+ }
+ mAllocRef = makeAllocation(outW, outH, e);
+ mAllocDst = makeAllocation(outW, outH, e);
+
+ ScriptIntrinsicResize si = ScriptIntrinsicResize.create(mRS);
+ si.setInput(mAllocSrc);
+ si.forEach_bicubic(mAllocRef);
+
+ ScriptC_intrinsic_resize sr = new ScriptC_intrinsic_resize(mRS);
+ sr.set_scaleX((float)w/outW);
+ sr.set_scaleY((float)h/outH);
+ sr.set_gIn(mAllocSrc);
+ sr.set_gWidthIn(w);
+ sr.set_gHeightIn(h);
+ if (dt == Element.DataType.UNSIGNED_8) {
+ switch(vecSize) {
+ case 4:
+ sr.forEach_bicubic_U4(mAllocDst);
+ break;
+ case 3:
+ sr.forEach_bicubic_U3(mAllocDst);
+ break;
+ case 2:
+ sr.forEach_bicubic_U2(mAllocDst);
+ break;
+ case 1:
+ sr.forEach_bicubic_U1(mAllocDst);
+ break;
+ }
+ }
+
+ mVerify.invoke_verify(mAllocRef, mAllocDst, mAllocSrc);
+ if (outW == w && outH == h) {
+ //when scale = 1, check with the original.
+ mVerify.invoke_verify(mAllocRef, mAllocSrc, mAllocSrc);
+ mVerify.invoke_verify(mAllocDst, mAllocSrc, mAllocSrc);
+ }
+ mRS.finish();
+ }
+
+
+ public void test_U8_4_SCALE10_10_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE10_10_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE10_10_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE10_10_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE20_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE20_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE20_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE20_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE05_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE05_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE05_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE05_20_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE20_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_3_SCALE20_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_2_SCALE20_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_1_SCALE20_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE05_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_3_SCALE05_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_2_SCALE05_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_1_SCALE05_05_inSqure() {
+ testReszie(inX, inX, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE10_10_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE10_10_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE10_10_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 1.f, 1.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE10_10_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 1.f, 1.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE20_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE20_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE20_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 2.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE20_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 2.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE05_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_3_SCALE05_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_2_SCALE05_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 2.f);
+ checkError();
+ }
+ public void test_U8_1_SCALE05_20_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 2.f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE20_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_3_SCALE20_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_2_SCALE20_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 2.f, 0.5f);
+ checkError();
+ }
+ public void test_U8_1_SCALE20_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 2.f, 0.5f);
+ checkError();
+ }
+
+ public void test_U8_4_SCALE05_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 4, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_3_SCALE05_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 3, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_2_SCALE05_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 2, 0.5f, 0.5f);
+ checkError();
+ }
+ public void test_U8_1_SCALE05_05_inRectangle() {
+ testReszie(inX, inY, Element.DataType.UNSIGNED_8, 1, 0.5f, 0.5f);
+ checkError();
+ }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
new file mode 100644
index 0000000..a2d22d9
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
@@ -0,0 +1,371 @@
+ /*
+ * 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.renderscript.cts;
+
+import android.renderscript.*;
+
+public class TestVLoad extends RSBaseCompute {
+
+ private ScriptC_vload script;
+ private ScriptC_vload_relaxed scriptRelaxed;
+ Allocation walkAlloc;
+ Allocation inAlloc;
+ Allocation outAlloc;
+ private static java.util.Random random = new java.util.Random();
+
+ final int w = 253;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ random.setSeed(10);
+ script = new ScriptC_vload(mRS);
+ scriptRelaxed = new ScriptC_vload_relaxed(mRS);
+ }
+
+
+
+ protected void createWalk() {
+ int tmp[] = new int[w];
+ boolean b[] = new boolean[w];
+ int toCopy = w;
+ int i = 0;
+
+ while (toCopy > 0) {
+ int x = random.nextInt(w);
+
+ //android.util.Log.v("rs", "x " + x + ", y " + y + ", toCopy " + toCopy);
+ while ((x < w) && b[x]) {
+ x++;
+ if (x >= w) {
+ x = 0;
+ }
+ }
+
+ int maxsize = 1;
+ b[x] = true;
+ if ((x+1 < w) && !b[x+1]) {
+ maxsize ++;
+ b[x+1] = true;
+ if ((x+2 < w) && !b[x+2]) {
+ maxsize ++;
+ b[x+2] = true;
+ if ((x+3 < w) && !b[x+3]) {
+ maxsize ++;
+ b[x+3] = true;
+ }
+ }
+ }
+
+ toCopy -= maxsize;
+ tmp[i] = x | (maxsize << 16);
+ android.util.Log.v("rs", "x " + x + ", vec " + maxsize);
+ i++;
+ }
+
+ walkAlloc = Allocation.createSized(mRS, Element.I32(mRS), i);
+ walkAlloc.copy1DRangeFrom(0, i, tmp);
+ }
+
+ private void testSetup(Type t) {
+ createWalk();
+
+ inAlloc = Allocation.createTyped(mRS, t);
+ outAlloc = Allocation.createTyped(mRS, t);
+ script.set_gAllocIn(inAlloc);
+ script.set_gAllocOut(outAlloc);
+ scriptRelaxed.set_gAllocIn(inAlloc);
+ scriptRelaxed.set_gAllocOut(outAlloc);
+ }
+
+ private void verify(byte[] a1, byte[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(short[] a1, short[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(int[] a1, int[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(long[] a1, long[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(float[] a1, float[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(double[] a1, double[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private byte[] randomByteArray(int len) {
+ byte t[] = new byte[len];
+ random.nextBytes(t);
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private short[] randomShortArray(int len) {
+ short t[] = new short[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = (short)(random.nextInt() & 0xffff);
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private int[] randomIntArray(int len) {
+ int t[] = new int[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = random.nextInt();
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private long[] randomLongArray(int len) {
+ long t[] = new long[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = random.nextLong();
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ public void testVload_char() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ script.forEach_copy2d_char(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch char: ");
+ }
+
+ public void testVload_uchar() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ script.forEach_copy2d_uchar(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uchar: ");
+ }
+
+ public void testVload_char_relaxed() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ scriptRelaxed.forEach_copy2d_char(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed char: ");
+ }
+
+ public void testVload_uchar_relaxed() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ scriptRelaxed.forEach_copy2d_uchar(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed uchar: ");
+ }
+
+ public void testVload_short() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ script.forEach_copy2d_short(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch short: ");
+ }
+
+ public void testVload_ushort() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ script.forEach_copy2d_ushort(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ushort: ");
+ }
+
+ public void testVload_short_relaxed() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ scriptRelaxed.forEach_copy2d_short(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed short: ");
+ }
+
+ public void testVload_ushort_relaxed() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ scriptRelaxed.forEach_copy2d_ushort(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ushort: ");
+ }
+
+ public void testVload_int() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ script.forEach_copy2d_int(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch int: ");
+ }
+
+ public void testVload_uint() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ script.forEach_copy2d_uint(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uint: ");
+ }
+
+ public void testVload_int_relaxed() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ scriptRelaxed.forEach_copy2d_int(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed int: ");
+ }
+
+ public void testVload_uint_relaxed() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ scriptRelaxed.forEach_copy2d_uint(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uint: ");
+ }
+
+ public void testVload_long() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ script.forEach_copy2d_long(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch long: ");
+ }
+
+ public void testVload_ulong() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ script.forEach_copy2d_ulong(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ulong: ");
+ }
+ public void testVload_long_relaxed() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ scriptRelaxed.forEach_copy2d_long(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed long: ");
+ }
+ public void testVload_ulong_relaxed() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ scriptRelaxed.forEach_copy2d_ulong(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ulong: ");
+ }
+
+ public void testVload_float() {
+ testSetup(Type.createX(mRS, Element.F32(mRS), w));
+ float tmp[] = new float[w];
+ float tmp2[] = new float[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextFloat();
+ }
+ inAlloc.copyFrom(tmp);
+ script.forEach_copy2d_float(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch float: ");
+ }
+
+ public void testVload_float_relaxed() {
+ testSetup(Type.createX(mRS, Element.F32(mRS), w));
+ float tmp[] = new float[w];
+ float tmp2[] = new float[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextFloat();
+ }
+ inAlloc.copyFrom(tmp);
+ scriptRelaxed.forEach_copy2d_float(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed float: ");
+ }
+
+ public void testVload_double() {
+ testSetup(Type.createX(mRS, Element.F64(mRS), w));
+ double tmp[] = new double[w];
+ double tmp2[] = new double[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextDouble();
+ }
+ inAlloc.copyFrom(tmp);
+ script.forEach_copy2d_double(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch double: ");
+ }
+
+ public void testVload_double_relaxed() {
+ testSetup(Type.createX(mRS, Element.F64(mRS), w));
+ double tmp[] = new double[w];
+ double tmp2[] = new double[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextDouble();
+ }
+ inAlloc.copyFrom(tmp);
+ scriptRelaxed.forEach_copy2d_double(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed double: ");
+ }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
index 9f9aa2b..966dbdd 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_convolve5x5.rs
@@ -66,7 +66,7 @@
+ convert_float4(rsGetElementAt_uchar4(gIn, x3, y4)) * gCoeffs[23]
+ convert_float4(rsGetElementAt_uchar4(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar4(p0);
}
@@ -113,7 +113,7 @@
+ convert_float3(rsGetElementAt_uchar3(gIn, x3, y4)) * gCoeffs[23]
+ convert_float3(rsGetElementAt_uchar3(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar3(p0);
}
@@ -160,7 +160,7 @@
+ convert_float2(rsGetElementAt_uchar2(gIn, x3, y4)) * gCoeffs[23]
+ convert_float2(rsGetElementAt_uchar2(gIn, x4, y4)) * gCoeffs[24];
- p0 = clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ p0 = clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
return convert_uchar2(p0);
}
@@ -207,7 +207,7 @@
+ (float)(rsGetElementAt_uchar(gIn, x3, y4)) * gCoeffs[23]
+ (float)(rsGetElementAt_uchar(gIn, x4, y4)) * gCoeffs[24];
- return clamp(p0 + p1 + p2 + p3 + p4, 0.f, 255.f);
+ return clamp(p0 + p1 + p2 + p3 + p4 + 0.5f, 0.f, 255.f);
}
float4 __attribute__((kernel)) convolve_F4(uint32_t x, uint32_t y) {
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs
new file mode 100644
index 0000000..fa8c8dd
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/intrinsic_resize.rs
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "shared.rsh"
+
+int32_t gWidthIn;
+int32_t gHeightIn;
+rs_allocation gIn;
+float scaleX;
+float scaleY;
+
+static float4 cubicInterpolate_F4 (float4 p0,float4 p1,float4 p2,float4 p3 , float x) {
+ return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+ + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float3 cubicInterpolate_F3 (float3 p0,float3 p1,float3 p2,float3 p3 , float x) {
+ return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+ + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float2 cubicInterpolate_F2 (float2 p0,float2 p1,float2 p2,float2 p3 , float x) {
+ return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+ + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+static float cubicInterpolate_F1 (float p0,float p1,float p2,float p3 , float x) {
+ return p1 + 0.5f * x * (p2 - p0 + x * (2.f * p0 - 5.f * p1 + 4.f * p2 - p3
+ + x * (3.f * (p1 - p2) + p3 - p0)));
+}
+
+uchar4 __attribute__((kernel)) bicubic_U4(uint32_t x, uint32_t y) {
+ float xf = (x + 0.5f) * scaleX - 0.5f;
+ float yf = (y + 0.5f) * scaleY - 0.5f;
+
+ int startx = (int) floor(xf - 1);
+ int starty = (int) floor(yf - 1);
+ xf = xf - floor(xf);
+ yf = yf - floor(yf);
+ int maxx = gWidthIn - 1;
+ int maxy = gHeightIn - 1;
+
+ uint32_t xs0 = (uint32_t) max(0, startx + 0);
+ uint32_t xs1 = (uint32_t) max(0, startx + 1);
+ uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+ uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+ uint32_t ys0 = (uint32_t) max(0, starty + 0);
+ uint32_t ys1 = (uint32_t) max(0, starty + 1);
+ uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+ uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+ float4 p00 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys0));
+ float4 p01 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys0));
+ float4 p02 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys0));
+ float4 p03 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys0));
+ float4 p0 = cubicInterpolate_F4(p00, p01, p02, p03, xf);
+
+ float4 p10 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys1));
+ float4 p11 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys1));
+ float4 p12 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys1));
+ float4 p13 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys1));
+ float4 p1 = cubicInterpolate_F4(p10, p11, p12, p13, xf);
+
+ float4 p20 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys2));
+ float4 p21 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys2));
+ float4 p22 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys2));
+ float4 p23 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys2));
+ float4 p2 = cubicInterpolate_F4(p20, p21, p22, p23, xf);
+
+ float4 p30 = convert_float4(rsGetElementAt_uchar4(gIn, xs0, ys3));
+ float4 p31 = convert_float4(rsGetElementAt_uchar4(gIn, xs1, ys3));
+ float4 p32 = convert_float4(rsGetElementAt_uchar4(gIn, xs2, ys3));
+ float4 p33 = convert_float4(rsGetElementAt_uchar4(gIn, xs3, ys3));
+ float4 p3 = cubicInterpolate_F4(p30, p31, p32, p33, xf);
+
+ float4 p = cubicInterpolate_F4(p0, p1, p2, p3, yf);
+ p = clamp(p + 0.5f, 0.f, 255.f);
+ return convert_uchar4(p);
+}
+
+uchar3 __attribute__((kernel)) bicubic_U3(uint32_t x, uint32_t y) {
+ float xf = (x + 0.5f) * scaleX - 0.5f;
+ float yf = (y + 0.5f) * scaleY - 0.5f;
+
+ int startx = (int) floor(xf - 1);
+ int starty = (int) floor(yf - 1);
+ xf = xf - floor(xf);
+ yf = yf - floor(yf);
+ int maxx = gWidthIn - 1;
+ int maxy = gHeightIn - 1;
+
+ uint32_t xs0 = (uint32_t) max(0, startx + 0);
+ uint32_t xs1 = (uint32_t) max(0, startx + 1);
+ uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+ uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+ uint32_t ys0 = (uint32_t) max(0, starty + 0);
+ uint32_t ys1 = (uint32_t) max(0, starty + 1);
+ uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+ uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+ float3 p00 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys0));
+ float3 p01 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys0));
+ float3 p02 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys0));
+ float3 p03 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys0));
+ float3 p0 = cubicInterpolate_F3(p00, p01, p02, p03, xf);
+
+ float3 p10 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys1));
+ float3 p11 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys1));
+ float3 p12 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys1));
+ float3 p13 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys1));
+ float3 p1 = cubicInterpolate_F3(p10, p11, p12, p13, xf);
+
+ float3 p20 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys2));
+ float3 p21 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys2));
+ float3 p22 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys2));
+ float3 p23 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys2));
+ float3 p2 = cubicInterpolate_F3(p20, p21, p22, p23, xf);
+
+ float3 p30 = convert_float3(rsGetElementAt_uchar3(gIn, xs0, ys3));
+ float3 p31 = convert_float3(rsGetElementAt_uchar3(gIn, xs1, ys3));
+ float3 p32 = convert_float3(rsGetElementAt_uchar3(gIn, xs2, ys3));
+ float3 p33 = convert_float3(rsGetElementAt_uchar3(gIn, xs3, ys3));
+ float3 p3 = cubicInterpolate_F3(p30, p31, p32, p33, xf);
+
+ float3 p = cubicInterpolate_F3(p0, p1, p2, p3, yf);
+ p = clamp(p + 0.5f, 0.f, 255.f);
+ return convert_uchar3(p);
+}
+
+uchar2 __attribute__((kernel)) bicubic_U2(uint32_t x, uint32_t y) {
+ float xf = (x + 0.5f) * scaleX - 0.5f;
+ float yf = (y + 0.5f) * scaleY - 0.5f;
+
+ int startx = (int) floor(xf - 1);
+ int starty = (int) floor(yf - 1);
+ xf = xf - floor(xf);
+ yf = yf - floor(yf);
+ int maxx = gWidthIn - 1;
+ int maxy = gHeightIn - 1;
+
+ uint32_t xs0 = (uint32_t) max(0, startx + 0);
+ uint32_t xs1 = (uint32_t) max(0, startx + 1);
+ uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+ uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+ uint32_t ys0 = (uint32_t) max(0, starty + 0);
+ uint32_t ys1 = (uint32_t) max(0, starty + 1);
+ uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+ uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+ float2 p00 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys0));
+ float2 p01 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys0));
+ float2 p02 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys0));
+ float2 p03 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys0));
+ float2 p0 = cubicInterpolate_F2(p00, p01, p02, p03, xf);
+
+ float2 p10 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys1));
+ float2 p11 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys1));
+ float2 p12 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys1));
+ float2 p13 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys1));
+ float2 p1 = cubicInterpolate_F2(p10, p11, p12, p13, xf);
+
+ float2 p20 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys2));
+ float2 p21 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys2));
+ float2 p22 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys2));
+ float2 p23 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys2));
+ float2 p2 = cubicInterpolate_F2(p20, p21, p22, p23, xf);
+
+ float2 p30 = convert_float2(rsGetElementAt_uchar2(gIn, xs0, ys3));
+ float2 p31 = convert_float2(rsGetElementAt_uchar2(gIn, xs1, ys3));
+ float2 p32 = convert_float2(rsGetElementAt_uchar2(gIn, xs2, ys3));
+ float2 p33 = convert_float2(rsGetElementAt_uchar2(gIn, xs3, ys3));
+ float2 p3 = cubicInterpolate_F2(p30, p31, p32, p33, xf);
+
+ float2 p = cubicInterpolate_F2(p0, p1, p2, p3, yf);
+ p = clamp(p + 0.5f, 0.f, 255.f);
+ return convert_uchar2(p);
+}
+
+uchar __attribute__((kernel)) bicubic_U1(uint32_t x, uint32_t y) {
+ float xf = (x + 0.5f) * scaleX - 0.5f;
+ float yf = (y + 0.5f) * scaleY - 0.5f;
+
+ int startx = (int) floor(xf - 1);
+ int starty = (int) floor(yf - 1);
+ xf = xf - floor(xf);
+ yf = yf - floor(yf);
+ int maxx = gWidthIn - 1;
+ int maxy = gHeightIn - 1;
+
+ uint32_t xs0 = (uint32_t) max(0, startx + 0);
+ uint32_t xs1 = (uint32_t) max(0, startx + 1);
+ uint32_t xs2 = (uint32_t) min(maxx, startx + 2);
+ uint32_t xs3 = (uint32_t) min(maxx, startx + 3);
+
+ uint32_t ys0 = (uint32_t) max(0, starty + 0);
+ uint32_t ys1 = (uint32_t) max(0, starty + 1);
+ uint32_t ys2 = (uint32_t) min(maxy, starty + 2);
+ uint32_t ys3 = (uint32_t) min(maxy, starty + 3);
+
+ float p00 = (float)(rsGetElementAt_uchar(gIn, xs0, ys0));
+ float p01 = (float)(rsGetElementAt_uchar(gIn, xs1, ys0));
+ float p02 = (float)(rsGetElementAt_uchar(gIn, xs2, ys0));
+ float p03 = (float)(rsGetElementAt_uchar(gIn, xs3, ys0));
+ float p0 = cubicInterpolate_F1(p00, p01, p02, p03, xf);
+
+ float p10 = (float)(rsGetElementAt_uchar(gIn, xs0, ys1));
+ float p11 = (float)(rsGetElementAt_uchar(gIn, xs1, ys1));
+ float p12 = (float)(rsGetElementAt_uchar(gIn, xs2, ys1));
+ float p13 = (float)(rsGetElementAt_uchar(gIn, xs3, ys1));
+ float p1 = cubicInterpolate_F1(p10, p11, p12, p13, xf);
+
+ float p20 = (float)(rsGetElementAt_uchar(gIn, xs0, ys2));
+ float p21 = (float)(rsGetElementAt_uchar(gIn, xs1, ys2));
+ float p22 = (float)(rsGetElementAt_uchar(gIn, xs2, ys2));
+ float p23 = (float)(rsGetElementAt_uchar(gIn, xs3, ys2));
+ float p2 = cubicInterpolate_F1(p20, p21, p22, p23, xf);
+
+ float p30 = (float)(rsGetElementAt_uchar(gIn, xs0, ys3));
+ float p31 = (float)(rsGetElementAt_uchar(gIn, xs1, ys3));
+ float p32 = (float)(rsGetElementAt_uchar(gIn, xs2, ys3));
+ float p33 = (float)(rsGetElementAt_uchar(gIn, xs3, ys3));
+ float p3 = cubicInterpolate_F1(p30, p31, p32, p33, xf);
+
+ float p = cubicInterpolate_F1(p0, p1, p2, p3, yf);
+ p = clamp(p + 0.5f, 0.f, 255.f);
+ return (uchar)p;
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
new file mode 100644
index 0000000..cdf5fd1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
@@ -0,0 +1,60 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty) \
+ void __attribute__((kernel)) copy2d_##ty(int xy_v) { \
+ int lx = xy_v & 0xff; \
+ int vecsize = (xy_v & 0xff0000) >> 16; \
+ switch(vecsize) { \
+ case 1: { \
+ ty i = rsGetElementAt_##ty(gAllocIn, lx); \
+ rsSetElementAt_##ty(gAllocOut, i, lx); \
+ } break; \
+ case 2: { \
+ ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##2(gAllocOut, i, lx); \
+ } break; \
+ case 3: { \
+ ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##3(gAllocOut, i, lx); \
+ } break; \
+ case 4: { \
+ ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##4(gAllocOut, i, lx); \
+ } break; \
+ } \
+ }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
new file mode 100644
index 0000000..61940ba
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
@@ -0,0 +1,62 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#pragma rs_fp_relaxed
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty) \
+ void __attribute__((kernel)) copy2d_##ty(int xy_v) { \
+ int lx = xy_v & 0xff; \
+ int vecsize = (xy_v & 0xff0000) >> 16; \
+ switch(vecsize) { \
+ case 1: { \
+ ty i = rsGetElementAt_##ty(gAllocIn, lx); \
+ rsSetElementAt_##ty(gAllocOut, i, lx); \
+ } break; \
+ case 2: { \
+ ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##2(gAllocOut, i, lx); \
+ } break; \
+ case 3: { \
+ ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##3(gAllocOut, i, lx); \
+ } break; \
+ case 4: { \
+ ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##4(gAllocOut, i, lx); \
+ } break; \
+ } \
+ }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
index 9daa2cb..6807523 100644
--- a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -35,10 +35,10 @@
{
aps = 0;
if (output != NULL) {
- *output = 0;
+ *output = AUDIO_IO_HANDLE_NONE;
}
if (session != NULL) {
- *session = 0;
+ *session = AUDIO_UNIQUE_ID_ALLOCATE;
}
int64_t startTime = 0;
@@ -60,19 +60,24 @@
if (output != NULL) {
// get a valid output. Any use case will do.
- for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
+ for (int stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream++) {
*output = AudioSystem::getOutput((audio_stream_type_t)stream);
- if (*output != 0) {
+ if (*output != AUDIO_IO_HANDLE_NONE) {
break;
}
}
- if (*output == 0) {
+ if (*output == AUDIO_IO_HANDLE_NONE) {
ALOGE("cannot get valid audio output");
return false;
}
}
if (session != NULL) {
- *session = 10000;
+ //get a valid session
+ *session = AudioSystem::newAudioUniqueId();
+ if (*session == AUDIO_UNIQUE_ID_ALLOCATE) {
+ ALOGE("cannot get valid audio session");
+ return false;
+ }
}
return true;
}
@@ -92,11 +97,13 @@
return false;
}
- status_t status = aps->startOutput(output, (audio_stream_type_t)(-1), session);
+ status_t status = aps->startOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1),
+ (audio_session_t)session);
if (status == NO_ERROR) {
return false;
}
- status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+ status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT,
+ (audio_session_t)session);
if (status == NO_ERROR) {
return false;
}
@@ -118,11 +125,13 @@
return false;
}
- status_t status = aps->stopOutput(output, (audio_stream_type_t)(-1), session);
+ status_t status = aps->stopOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1),
+ (audio_session_t)session);
if (status == NO_ERROR) {
return false;
}
- status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+ status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT,
+ (audio_session_t)session);
if (status == NO_ERROR) {
return false;
}
@@ -153,6 +162,30 @@
return true;
}
+/*
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ * Test with NUM_RANDOM_TESTS random values for stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActiveRemotely(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+
+ if (!init(aps, NULL, NULL)) {
+ return false;
+ }
+
+ if (aps->isStreamActiveRemotely((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0)) {
+ return false;
+ }
+
+ if (aps->isStreamActiveRemotely((audio_stream_type_t)AUDIO_STREAM_CNT, 0)) {
+ return false;
+ }
+ return true;
+}
+
static JNINativeMethod gMethods[] = {
{ "native_test_startOutput", "()Z",
(void *) android_security_cts_AudioPolicy_test_startOutput },
@@ -160,6 +193,8 @@
(void *) android_security_cts_AudioPolicy_test_stopOutput },
{ "native_test_isStreamActive", "()Z",
(void *) android_security_cts_AudioPolicy_test_isStreamActive },
+ { "native_test_isStreamActiveRemotely", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
};
int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
diff --git a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
index faa6eea..1c45e91 100644
--- a/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
+++ b/tests/tests/security/jni/android_security_cts_NativeCodeTest.cpp
@@ -37,6 +37,7 @@
#include <errno.h>
#include <inttypes.h>
#include <linux/sysctl.h>
+#include <arpa/inet.h>
#define PASSED 0
#define UNKNOWN_ERROR -1
@@ -282,6 +283,57 @@
return (ret == -1 && errno == EINVAL);
}
+static jboolean android_security_cts_NativeCodeTest_doNvmapIocFromIdTest(JNIEnv*, jobject)
+{
+ /*
+ * IOCTL code specified from the original notification.
+ * Also available in:
+ * .../kernel/tegra/drivers/video/tegra/nvmap/nvmap_ioctl.h
+ * #define NVMAP_IOC_MAGIC 'N'
+ * #define NVMAP_IOC_FROM_ID _IOWR(NVMAP_IOC_MAGIC, 2, struct nvmap_create_handle)
+ */
+ const int NVMAP_IOC_FROM_ID = 0xc0084e02;
+ int nvmap = open("/dev/nvmap", O_RDWR | O_CLOEXEC, 0);
+ bool vulnerable = false;
+
+ if (nvmap >= 0) {
+ if (0 == ioctl(nvmap, NVMAP_IOC_FROM_ID)) {
+ /* IOCTL succeeded */
+ vulnerable = true;
+ }
+ else if (errno != ENOTTY) {
+ /* IOCTL failed, but provided the wrong error number */
+ vulnerable = true;
+ }
+
+ close(nvmap);
+ }
+
+ return !vulnerable;
+}
+
+static jboolean android_security_cts_NativeCodeTest_doPingPongRootTest(JNIEnv*, jobject)
+{
+ int icmp_sock;
+ struct sockaddr sock_addr;
+
+ memset(&sock_addr, 0, sizeof(sock_addr));
+ icmp_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+ sock_addr.sa_family = AF_INET;
+
+ /* first connect */
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ /* disconnect */
+ sock_addr.sa_family = AF_UNSPEC;
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ /* second disconnect -> crash */
+ sock_addr.sa_family = AF_UNSPEC;
+ connect(icmp_sock, &sock_addr, sizeof(sock_addr));
+
+ return true;
+}
static JNINativeMethod gMethods[] = {
{ "doPerfEventTest", "()Z",
@@ -296,6 +348,10 @@
(void *) android_security_cts_NativeCodeTest_doCVE20141710Test },
{ "doFutexTest", "()Z",
(void *) android_security_cts_NativeCodeTest_doFutexTest },
+ { "doNvmapIocFromIdTest", "()Z",
+ (void *) android_security_cts_NativeCodeTest_doNvmapIocFromIdTest },
+ { "doPingPongRootTest", "()Z",
+ (void *) android_security_cts_NativeCodeTest_doPingPongRootTest },
};
int register_android_security_cts_NativeCodeTest(JNIEnv* env)
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
index 399d8bb..b307247 100644
--- a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -48,7 +48,16 @@
assertTrue(native_test_isStreamActive());
}
+ /**
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ */
+ public void test_isStreamActiveRemotely() throws Exception {
+ assertTrue(native_test_isStreamActiveRemotely());
+ }
+
private static native boolean native_test_startOutput();
private static native boolean native_test_stopOutput();
private static native boolean native_test_isStreamActive();
+ private static native boolean native_test_isStreamActiveRemotely();
}
diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java
index da098f3..de7c15e 100644
--- a/tests/tests/security/src/android/security/cts/CertificateData.java
+++ b/tests/tests/security/src/android/security/cts/CertificateData.java
@@ -28,9 +28,11 @@
"91:C6:D6:EE:3E:8A:C8:63:84:E5:48:C2:99:29:5C:75:6C:81:7B:81",
"4A:65:D5:F4:1D:EF:39:B8:B8:90:4A:4A:D3:64:81:33:CF:C7:A1:D1",
"16:32:47:8D:89:F9:21:3A:92:00:85:63:F5:A4:A7:D3:12:40:8A:D6",
+ "D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0",
"4D:23:78:EC:91:95:39:B5:00:7F:75:8F:03:3B:21:1E:C5:4D:8B:CF",
"E7:B4:F6:9D:61:EC:90:69:DB:7E:90:A7:40:1A:3C:F4:7D:4F:E8:EE",
"DD:E1:D2:A9:01:80:2E:1D:87:5E:84:B3:80:7E:4B:B1:FD:99:41:34",
+ "69:69:56:2E:40:80:F4:24:A1:E7:19:9F:14:BA:F3:EE:58:AB:6A:BB",
"92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F",
"75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE",
"40:9D:4B:D9:17:B5:5C:27:B6:9B:64:CB:98:22:44:0D:CD:09:B8:89",
@@ -49,6 +51,7 @@
"27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4",
"AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A",
"8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0",
+ "1F:24:C6:30:CD:A4:18:EF:20:69:FF:AD:4F:DD:5F:46:3A:1B:69:AA",
"AE:50:83:ED:7C:F4:5C:BC:8F:61:C6:21:FE:68:5D:79:42:21:15:6E",
"DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57",
"74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE",
@@ -56,6 +59,7 @@
"3E:2B:F7:F2:03:1B:96:F3:8C:E6:C4:D8:A8:5D:3E:2D:58:47:6A:0F",
"A3:F1:33:3F:E2:42:BF:CF:C5:D1:4E:8F:39:42:98:40:68:10:D1:A0",
"5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6",
+ "2B:8F:1B:57:33:0D:BB:A2:D0:7A:6C:51:F7:0E:E9:0D:DA:B9:AD:8E",
"A8:98:5D:3A:65:E5:E5:C4:B2:D7:D6:6D:40:C6:DD:2F:B1:9C:54:36",
"59:22:A1:E1:5A:EA:16:35:21:F8:98:39:6A:46:46:B0:44:1B:0F:A9",
"D4:DE:20:D0:5E:66:FC:53:FE:1A:50:88:2C:78:DB:28:52:CA:E4:74",
@@ -155,6 +159,7 @@
"87:82:C6:C3:04:35:3B:CF:D2:96:92:D2:59:3E:7D:44:D9:34:FF:11",
"59:0D:2D:7D:88:4F:40:2E:61:7E:A5:62:32:17:65:CF:17:D8:94:E9",
"AE:C5:FB:3F:C8:E1:BF:C4:E5:4F:03:07:5A:9A:E8:00:B7:F7:B6:FA",
+ "AF:E5:D2:44:A8:D1:19:42:30:FF:47:9F:E2:F8:97:BB:CD:7A:8C:B4",
"5F:3B:8C:F2:F8:10:B3:7D:78:B4:CE:EC:19:19:C3:73:34:B9:C7:74",
"2A:C8:D5:8B:57:CE:BF:2F:49:AF:F2:FC:76:8F:51:14:62:90:7A:41",
"F1:7F:6F:B6:31:DC:99:E3:A3:C8:7F:FE:1C:F1:81:10:88:D9:60:33",
diff --git a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
index 8ebe6ac..dfefad7 100644
--- a/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
+++ b/tests/tests/security/src/android/security/cts/ClonedSecureRandomTest.java
@@ -120,6 +120,7 @@
*/
int firstPid = -1;
int previousPid = -1;
+ int lastPid = -1;
for (int i = 0; i < MAX_PID; i++) {
byte[] output = new byte[RANDOM_BYTES_PER_PID];
int pid;
@@ -202,7 +203,9 @@
firstPid = pid;
}
- if (i > PRIMING_ITERATIONS) {
+ if (i <= PRIMING_ITERATIONS) {
+ lastPid = pid;
+ } else if (pid > lastPid && (lastPid > firstPid || pid < firstPid)) {
wastePids(firstPid, previousPid);
}
}
diff --git a/tests/tests/security/src/android/security/cts/NativeCodeTest.java b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
index 4be00b6..5ee8f69 100644
--- a/tests/tests/security/src/android/security/cts/NativeCodeTest.java
+++ b/tests/tests/security/src/android/security/cts/NativeCodeTest.java
@@ -56,6 +56,21 @@
doFutexTest());
}
+ public void testNvmapIocFromId() throws Exception {
+ assertTrue("Device is vulnerable to CVE-2014-5332. "
+ + "NVIDIA has released code fixes to upstream repositories and device vendors. "
+ + "For more information, see "
+ + "https://nvidia.custhelp.com/app/answers/detail/a_id/3618",
+ doNvmapIocFromIdTest());
+ }
+
+ public void testPingPongRoot() throws Exception {
+ assertTrue("Device is vulnerable to CVE-2015-3636, a vulnerability in the ping "
+ + "socket implementation. Please apply the security patch at "
+ + "https://github.com/torvalds/linux/commit/a134f083e79f",
+ doPingPongRootTest());
+ }
+
/**
* Returns true iff this device is vulnerable to CVE-2013-2094.
* A patch for CVE-2013-2094 can be found at
@@ -110,8 +125,31 @@
private static native boolean doFutexTest();
/**
+ * ANDROID-17453812 / CVE-2014-5332
+ *
+ * Returns true if the device is patched against the NVMAP_IOC_FROM_ID ioctl call.
+ *
+ * More information on this vulnreability is available at
+ * https://nvidia.custhelp.com/app/answers/detail/a_id/3618
+ */
+ private static native boolean doNvmapIocFromIdTest();
+
+ /**
* Returns true if the device is immune to CVE-2014-1710,
* false if the device is vulnerable.
*/
private static native boolean doCVE20141710Test();
+
+ /**
+ * CVE-2015-3636
+ *
+ * Returns true if the patch is applied, crashes the system otherwise.
+ *
+ * Detects if the following patch is present.
+ * https://github.com/torvalds/linux/commit/a134f083e79f
+ *
+ * Credit: Wen Xu and wushi of KeenTeam.
+ * http://seclists.org/oss-sec/2015/q2/333
+ */
+ private static native boolean doPingPongRootTest();
}
diff --git a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
index 66054f9..c97c8c2 100644
--- a/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
+++ b/tests/tests/security/src/android/security/cts/SELinuxDomainTest.java
@@ -265,15 +265,15 @@
}
/*
- * Their will at least be some kernel thread running and all kthreads should
- * be in kernel context.
+ * All kthreads should be in kernel context.
*/
public void testKernelDomain() throws FileNotFoundException {
String domain = "u:r:kernel:s0";
List<ProcessDetails> procs = ProcessDetails.getProcessMap().get(domain);
- assertNotNull(procs);
- for (ProcessDetails p : procs) {
- assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
+ if (procs != null) {
+ for (ProcessDetails p : procs) {
+ assertTrue("Non Kernel thread \"" + p + "\" found!", p.isKernel());
+ }
}
}
@@ -354,23 +354,47 @@
}
private static ProcessDetails getProcessDetails(int pid, File f) throws FileNotFoundException {
- // Get the context via attr/current
- String context = new Scanner(new File(f, "attr/current")).next();
- context = context.trim();
+ Scanner tmp = null;
+ String context;
+ long vSize;
+ try {
+ tmp = new Scanner(new File(f, "attr/current"));
+ // Get the context via attr/current
+ context = tmp.next();
+ context = context.trim();
+ } finally {
+ if (tmp != null) {
+ tmp.close();
+ tmp = null;
+ }
+ }
- // Get the vSize, item #23 from the stat file
- String x = new Scanner(new File(f, "stat")).nextLine();
- long vSize = getVsizeFromStat(x);
+ try {
+ // Get the vSize, item #23 from the stat file
+ tmp = new Scanner(new File(f, "stat"));
+ String x = tmp.nextLine();
+ vSize = getVsizeFromStat(x);
+ } finally {
+ if (tmp != null) {
+ tmp.close();
+ tmp = null;
+ }
+ }
StringBuilder sb = new StringBuilder();
- Scanner tmp = new Scanner(new File(f, "cmdline"));
+ try {
+ tmp = new Scanner(new File(f, "cmdline"));
- // Java's scanner tends to return oddly when handling
- // long binary blobs. Probably some caching optimization.
- while (tmp.hasNext()) {
- sb.append(tmp.next().replace('\0', ' '));
+ // Java's scanner tends to return oddly when handling
+ // long binary blobs. Probably some caching optimization.
+ while (tmp.hasNext()) {
+ sb.append(tmp.next().replace('\0', ' '));
+ }
+ } finally {
+ if (tmp != null) {
+ tmp.close();
+ }
}
- tmp.close();
// At this point we build up a valid proctitle, then split
// on whitespace to get the left portion. Which is either
diff --git a/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
new file mode 100644
index 0000000..cb9d96c
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/SimRestrictedApisTest.java
@@ -0,0 +1,269 @@
+/*
+ * 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.telephony.cts;
+
+import android.content.Context;
+import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
+import android.test.AndroidTestCase;
+
+public class SimRestrictedApisTest extends AndroidTestCase {
+ private static final byte[] TEST_PDU = {
+ 0, 0 };
+ private TelephonyManager mTelephonyManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTelephonyManager =
+ (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
+ }
+
+ private boolean isSimCardPresent() {
+ return mTelephonyManager.getPhoneType() != TelephonyManager.PHONE_TYPE_NONE &&
+ mTelephonyManager.getSimState() != TelephonyManager.SIM_STATE_ABSENT;
+ }
+
+ /**
+ * Tests the SmsManager.injectSmsPdu() API. This makes a call to injectSmsPdu() API and expects
+ * a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testInjectSmsPdu() {
+ try {
+ if (isSimCardPresent()) {
+ SmsManager.getDefault().injectSmsPdu(TEST_PDU, "3gpp", null);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.setLine1NumberForDisplay() API. This makes a call to
+ * setLine1NumberForDisplay() API and expects a SecurityException since the test apk is not
+ * signed by a certificate on the SIM.
+ */
+ public void testSetLine1NumberForDisplay() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().setLine1NumberForDisplay("", "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.setLine1NumberForDisplay(long, string, string) API. This makes a
+ * call to setLine1NumberForDisplay() API and expects a SecurityException since the test apk is
+ * not signed by the certificate on the SIM.
+ */
+ public void testSetLine1NumberForDisplay2() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().setLine1NumberForDisplayForSubscriber(0, "", "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.iccOpenLogicalChannel() API. This makes a call to
+ * iccOpenLogicalChannel() API and expects a SecurityException since the test apk is not signed
+ * by certificate on the SIM.
+ */
+ public void testIccOpenLogicalChannel() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().iccOpenLogicalChannel("");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.iccCloseLogicalChannel() API. This makes a call to
+ * iccCloseLogicalChannel() API and expects a SecurityException since the test apk is not signed
+ * by certificate on the SIM.
+ */
+ public void testIccCloseLogicalChannel() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().iccCloseLogicalChannel(0);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.iccTransmitApduLogicalChannel() API. This makes a call to
+ * iccTransmitApduLogicalChannel() API and expects a SecurityException since the test apk is not
+ * signed by a certificate on the SIM.
+ */
+ public void testIccTransmitApduLogicalChannel() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().iccTransmitApduLogicalChannel(0, 0, 0, 0, 0, 0, "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.iccTransmitApduBasicChannel() API. This makes a call to
+ * iccTransmitApduBasicChannel() API and expects a SecurityException since the test apk is not
+ * signed by a certificate on the SIM.
+ */
+ public void testIccTransmitApduBasicChannel() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().iccTransmitApduBasicChannel(0, 0, 0, 0, 0, "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.sendEnvelopeWithStatus() API. This makes a call to
+ * sendEnvelopeWithStatus() API and expects a SecurityException since the test apk is not signed
+ * by certificate on the SIM.
+ */
+ public void testSendEnvelopeWithStatus() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().sendEnvelopeWithStatus("");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.nvReadItem() API. This makes a call to nvReadItem() API and
+ * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvReadItem() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvReadItem(0);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.nvWriteItem() API. This makes a call to nvWriteItem() API and
+ * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvWriteItem() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvWriteItem(0, "");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.nvWriteCdmaPrl() API. This makes a call to nvWriteCdmaPrl() API
+ * and expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvWriteCdmaPrl() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvWriteCdmaPrl(null);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.nvResetConfig() API. This makes a call to nvResetConfig() API and
+ * expects a SecurityException since the test apk is not signed by a certificate on the SIM.
+ */
+ public void testNvResetConfig() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().nvResetConfig(0);
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.getPreferredNetworkType() API. This makes a call to
+ * getPreferredNetworkType() API and expects a SecurityException since the test apk is not
+ * signed by certificate on the SIM.
+ */
+ public void testGetPreferredNetworkType() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().getPreferredNetworkType();
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.setPreferredNetworkTypeToGlobal() API. This makes a call to
+ * setPreferredNetworkTypeToGlobal() API and expects a SecurityException since the test apk is not
+ * signed by certificate on the SIM.
+ */
+ public void testSetPreferredNetworkTypeToGlobal() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().setPreferredNetworkTypeToGlobal();
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Tests that the test apk doesn't have carrier previliges.
+ */
+ public void testHasCarrierPrivileges() {
+ if (TelephonyManager.getDefault().hasCarrierPrivileges()) {
+ fail("App unexpectedly has carrier privileges");
+ }
+ }
+
+ /**
+ * Tests the TelephonyManager.setOperatorBrandOverride() API. This makes a call to
+ * setOperatorBrandOverride() API and expects a SecurityException since the test apk is not
+ * signed by certificate on the SIM.
+ */
+ public void testSetOperatorBrandOverride() {
+ try {
+ if (isSimCardPresent()) {
+ TelephonyManager.getDefault().setOperatorBrandOverride("");
+ fail("Expected SecurityException. App doesn't have carrier privileges.");
+ }
+ } catch (SecurityException expected) {
+ }
+ }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
new file mode 100644
index 0000000..9b08397
--- /dev/null
+++ b/tests/tests/telephony/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -0,0 +1,77 @@
+/*
+ * 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.telephony.cts;
+
+import android.content.Context;
+import android.cts.util.TestThread;
+import android.net.ConnectivityManager;
+import android.os.Looper;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+import java.util.List;
+
+public class SubscriptionManagerTest extends AndroidTestCase {
+ private SubscriptionManager mSubscriptionManager;
+ private static ConnectivityManager mCm;
+ private SubscriptionManager.OnSubscriptionsChangedListener mListener;
+ private final Object mLock = new Object();
+ private boolean mOnSubscriptionsChangedCalled = false;
+ private static final int TOLERANCE = 1000;
+ private static final String TAG = "android.telephony.cts.SubscriptionManagerTest";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mSubscriptionManager = new SubscriptionManager(getContext());
+ mCm = (ConnectivityManager)getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ if (mListener != null) {
+ // unregister the listener
+ mSubscriptionManager.removeOnSubscriptionsChangedListener(mListener);
+ }
+ super.tearDown();
+ }
+
+ public void testGetActiveSubscriptionInfoCount() {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+ assertTrue(mSubscriptionManager.getActiveSubscriptionInfoCount() <=
+ mSubscriptionManager.getActiveSubscriptionInfoCountMax());
+ }
+
+ public void testActiveSubscriptions() {
+ if (mCm.getNetworkInfo(ConnectivityManager.TYPE_MOBILE) == null) {
+ Log.d(TAG, "Skipping test that requires ConnectivityManager.TYPE_MOBILE");
+ return;
+ }
+ List<SubscriptionInfo> subList = mSubscriptionManager.getActiveSubscriptionInfoList();
+ // Assert when there is no sim card present or detected
+ assertTrue(subList != null && subList.size() > 0);
+ for (int i = 0; i < subList.size(); i++) {
+ assertTrue(subList.get(i).getSubscriptionId() >= 0);
+ assertTrue(subList.get(i).getSimSlotIndex() >= 0);
+ }
+ }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
index 35141a5..b6ae8ec 100644
--- a/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/TelephonyManagerTest.java
@@ -81,9 +81,11 @@
mListener = new PhoneStateListener() {
@Override
public void onCellLocationChanged(CellLocation location) {
- synchronized (mLock) {
- mOnCellLocationChangedCalled = true;
- mLock.notify();
+ if(!mOnCellLocationChangedCalled) {
+ synchronized (mLock) {
+ mOnCellLocationChangedCalled = true;
+ mLock.notify();
+ }
}
}
};
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index dc5d30a..d2b3ddf 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -75,6 +75,16 @@
android:resource="@xml/stub_tv_input_service" />
</service>
+ <service android:name="android.media.tv.cts.HardwareSessionTest$HardwareProxyTvInputService"
+ android:permission="android.permission.BIND_TV_INPUT">
+ <intent-filter>
+ <action android:name="android.media.tv.TvInputService" />
+ </intent-filter>
+ <meta-data android:name="android.media.tv.input"
+ android:resource="@xml/stub_tv_input_service" />
+ </service>
+
+
<activity android:name="android.media.tv.cts.TvViewStubActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
diff --git a/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
new file mode 100644
index 0000000..75cf28d
--- /dev/null
+++ b/tests/tests/tv/src/android/media/tv/cts/HardwareSessionTest.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.tv.cts;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.Context;
+import android.cts.util.PollingCheck;
+import android.media.tv.TvContract;
+import android.media.tv.TvInputInfo;
+import android.media.tv.TvInputManager;
+import android.media.tv.TvInputService;
+import android.media.tv.TvView;
+import android.media.tv.cts.HardwareSessionTest.HardwareProxyTvInputService.CountingSession;
+import android.net.Uri;
+import android.test.ActivityInstrumentationTestCase2;
+
+import com.android.cts.tv.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Test {@link android.media.tv.TvInputService.HardwareSession}.
+ */
+public class HardwareSessionTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
+ /** The maximum time to wait for an operation. */
+ private static final long TIME_OUT = 15000L;
+
+ private TvView mTvView;
+ private Activity mActivity;
+ private Instrumentation mInstrumentation;
+ private TvInputManager mManager;
+ private TvInputInfo mStubInfo;
+ private final List<TvInputInfo> mPassthroughInputList = new ArrayList<>();
+
+ public HardwareSessionTest() {
+ super(TvViewStubActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ mActivity = getActivity();
+ mInstrumentation = getInstrumentation();
+ mTvView = (TvView) mActivity.findViewById(R.id.tvview);
+ mManager = (TvInputManager) mActivity.getSystemService(Context.TV_INPUT_SERVICE);
+ for (TvInputInfo info : mManager.getTvInputList()) {
+ if (info.getServiceInfo().name.equals(HardwareProxyTvInputService.class.getName())) {
+ mStubInfo = info;
+ }
+ if (info.isPassthroughInput()) {
+ mPassthroughInputList.add(info);
+ }
+ }
+ assertNotNull(mStubInfo);
+ }
+
+ public void testHardwareProxyTvInputService() throws Throwable {
+ if (!Utils.hasTvInputFramework(getActivity())) {
+ return;
+ }
+ for (final TvInputInfo info : mPassthroughInputList) {
+ verifyCommandTuneAndHardwareVideoAvailable(info);
+ }
+ }
+
+ public void verifyCommandTuneAndHardwareVideoAvailable(TvInputInfo passthroughInfo) throws
+ Throwable {
+ HardwareProxyTvInputService.sHardwareInputId = passthroughInfo.getId();
+ Uri fakeChannelUri = TvContract.buildChannelUri(0);
+ mTvView.tune(mStubInfo.getId(), fakeChannelUri);
+ mInstrumentation.waitForIdleSync();
+ new PollingCheck(TIME_OUT) {
+ @Override
+ protected boolean check() {
+ CountingSession session = HardwareProxyTvInputService.sSession;
+ return session != null && session.mTuneCount > 0
+ && session.mHardwareVideoAvailableCount > 0;
+ }
+ }.run();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mTvView.reset();
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ HardwareProxyTvInputService.sSession = null;
+ }
+
+ public static class HardwareProxyTvInputService extends TvInputService {
+ static String sHardwareInputId;
+ static CountingSession sSession;
+
+ @Override
+ public Session onCreateSession(String inputId) {
+ sSession = new CountingSession(this);
+ return sSession;
+ }
+
+ public static class CountingSession extends HardwareSession {
+ public volatile int mTuneCount;
+ public volatile int mHardwareVideoAvailableCount;
+
+ CountingSession(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onRelease() {
+ }
+
+ @Override
+ public void onSetCaptionEnabled(boolean enabled) {
+ }
+
+ @Override
+ public String getHardwareInputId() {
+ return sHardwareInputId;
+ }
+
+ @Override
+ public void onSetStreamVolume(float volume) {
+ }
+
+ @Override
+ public boolean onTune(Uri channelUri) {
+ mTuneCount++;
+ return true;
+ }
+
+ @Override
+ public void onHardwareVideoAvailable() {
+ mHardwareVideoAvailableCount++;
+ }
+ }
+ }
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 651a199..6a50b56 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;
@@ -395,4 +404,185 @@
verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
channelId, channelUri);
}
+
+ private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
+ String sortOrder) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
+ fail("Setting sortOrder should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyQueryWithSelection(Uri uri, final String[] projection,
+ String selection) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, selection, null, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().update(uri, values, selection, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
+ try {
+ getContext().getContentResolver().delete(uri, selection, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
+ TvContract.Channels._ID + " ASC");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Programs._ID };
+ verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
+ TvContract.Programs._ID + " ASC");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
+ TvContract.Programs._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
+ TvContract.Programs._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
+ TvContract.Programs._ID + ">0");
+ }
+
+ public void testDefaultValues() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ ContentValues values = new ContentValues();
+ values.put(TvContract.Channels.COLUMN_INPUT_ID, mInputId);
+ Uri channelUri = mContentResolver.insert(mChannelsUri, values);
+ assertNotNull(channelUri);
+ try (Cursor cursor = mContentResolver.query(
+ channelUri, CHANNELS_PROJECTION, null, null, null)) {
+ cursor.moveToNext();
+ assertEquals(TvContract.Channels.TYPE_OTHER,
+ cursor.getString(cursor.getColumnIndex(TvContract.Channels.COLUMN_TYPE)));
+ assertEquals(TvContract.Channels.SERVICE_TYPE_AUDIO_VIDEO,
+ cursor.getString(cursor.getColumnIndex(
+ TvContract.Channels.COLUMN_SERVICE_TYPE)));
+ }
+ 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..48f1f44 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputManagerTest.java
@@ -17,25 +17,30 @@
package android.media.tv.cts;
import android.content.Context;
+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.List;
/**
* Test for {@link android.media.tv.TvInputManager}.
*/
-public class TvInputManagerTest extends AndroidTestCase {
+public class TvInputManagerTest extends ActivityInstrumentationTestCase2<TvViewStubActivity> {
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 TvInputManager.TvInputCallback mCallabck = new TvInputManager.TvInputCallback() {};
private static TvInputInfo getInfoForClassName(List<TvInputInfo> list, String name) {
for (TvInputInfo info : list) {
@@ -46,25 +51,29 @@
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();
}
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(
@@ -72,7 +81,7 @@
}
public void testGetTvInputList() throws Exception {
- if (!Utils.hasTvInputFramework(getContext())) {
+ if (!Utils.hasTvInputFramework(getActivity())) {
return;
}
List<TvInputInfo> list = mManager.getTvInputList();
@@ -85,4 +94,44 @@
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();
+ }
}
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 f0ee277..7068035 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvInputServiceTest.java
@@ -28,9 +28,13 @@
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.View;
import com.android.cts.tv.R;
@@ -46,8 +50,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;
@@ -62,6 +66,7 @@
private int mVideoUnavailableCount;
private int mTrackSelectedCount;
private int mTrackChangedCount;
+ private int mVideoSizeChanged;
private int mContentAllowedCount;
private int mContentBlockedCount;
@@ -91,6 +96,11 @@
}
@Override
+ public void onVideoSizeChanged(String inputId, int width, int height) {
+ mVideoSizeChanged++;
+ }
+
+ @Override
public void onContentAllowed(String inputId) {
mContentAllowedCount++;
}
@@ -99,6 +109,16 @@
public void onContentBlocked(String inputId, TvContentRating rating) {
mContentBlockedCount++;
}
+
+ public void resetCounts() {
+ mChannelRetunedCount = 0;
+ mVideoAvailableCount = 0;
+ mVideoUnavailableCount = 0;
+ mTrackSelectedCount = 0;
+ mTrackChangedCount = 0;
+ mContentAllowedCount = 0;
+ mContentBlockedCount = 0;
+ }
}
public TvInputServiceTest() {
@@ -135,7 +155,12 @@
verifyCommandSetStreamVolume();
verifyCommandSetCaptionEnabled();
verifyCommandSelectTrack();
- verifyCommandDispatchKeyEvent();
+ verifyCommandDispatchKeyDown();
+ verifyCommandDispatchKeyMultiple();
+ verifyCommandDispatchKeyUp();
+ verifyCommandDispatchTouchEvent();
+ verifyCommandDispatchTrackballEvent();
+ verifyCommandDispatchGenericMotionEvent();
verifyCallbackChannelRetuned();
verifyCallbackVideoAvailable();
verifyCallbackVideoUnavailable();
@@ -161,12 +186,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 +205,7 @@
}
public void verifyCommandSetCaptionEnabled() {
+ resetCounts();
mTvView.setCaptionEnabled(true);
mInstrumentation.waitForIdleSync();
new PollingCheck(TIME_OUT) {
@@ -191,18 +218,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 +244,84 @@
}.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 verifyCallbackChannelRetuned() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
Uri fakeChannelUri = TvContract.buildChannelUri(0);
@@ -228,6 +335,7 @@
}
public void verifyCallbackVideoAvailable() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyVideoAvailable();
@@ -240,6 +348,7 @@
}
public void verifyCallbackVideoUnavailable() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyVideoUnavailable(TvInputManager.VIDEO_UNAVAILABLE_REASON_TUNING);
@@ -252,6 +361,7 @@
}
public void verifyCallbackTracksChanged() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
ArrayList<TvTrackInfo> tracks = new ArrayList<>();
@@ -265,7 +375,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 +404,7 @@
}
public void verifyCallbackContentAllowed() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
session.notifyContentAllowed();
@@ -290,6 +417,7 @@
}
public void verifyCallbackContentBlocked() {
+ resetCounts();
CountingSession session = CountingTvInputService.sSession;
assertNotNull(session);
TvContentRating rating = TvContentRating.createRating("android.media.tv", "US_TVPG",
@@ -303,13 +431,20 @@
}.run();
}
+ private void resetCounts() {
+ if (CountingTvInputService.sSession != null) {
+ CountingTvInputService.sSession.resetCounts();
+ }
+ mCallback.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 +453,34 @@
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;
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;
+ }
+
@Override
public void onRelease() {
}
@@ -356,10 +513,52 @@
}
@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;
+ }
}
}
}
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..6232861 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(
@@ -303,7 +317,7 @@
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 +339,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 +376,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 +396,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/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
old mode 100644
new mode 100755
index d267d63..3c6028f
--- a/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
+++ b/tests/tests/uiautomation/src/android/app/uiautomation/cts/UiAutomationTest.java
@@ -115,6 +115,9 @@
// Wait for things to settle.
getUiDevice().waitForIdle();
+ // Wait for Activity draw finish
+ getInstrumentation().waitForIdleSync();
+
// Find the application window.
final int windowId = findAppWindowId(uiAutomation.getWindows());
assertTrue(windowId >= 0);
diff --git a/tests/tests/uirendering/res/layout/blue_padded_layout.xml b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
index 68c9cd1..2bfd049 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_layout.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_layout.xml
@@ -14,16 +14,15 @@
limitations under the License.
-->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_root"
- android:layout_width="200px"
- android:layout_height="200px"
- android:clipChildren="false">
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
+ android:clipChildren="false">
<android.uirendering.cts.testclasses.view.UnclippedBlueView
- android:id="@+id/child"
- android:layout_width="100px"
- android:layout_height="100px"
- android:paddingLeft="15px"
- android:paddingTop="16px"
- android:paddingRight="17px"
- android:paddingBottom="18px"/>
+ android:id="@+id/child"
+ android:layout_width="80px"
+ android:layout_height="80px"
+ android:paddingLeft="15px"
+ android:paddingTop="16px"
+ android:paddingRight="17px"
+ android:paddingBottom="18px"/>
</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/blue_padded_square.xml b/tests/tests/uirendering/res/layout/blue_padded_square.xml
index 71f4b0c..7d867d9 100644
--- a/tests/tests/uirendering/res/layout/blue_padded_square.xml
+++ b/tests/tests/uirendering/res/layout/blue_padded_square.xml
@@ -14,7 +14,6 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/test_root"
- android:layout_width="100px"
- android:layout_height="100px"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
android:background="@drawable/blue_padded_square"/>
diff --git a/tests/tests/uirendering/res/layout/simple_rect_layout.xml b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
index b570df8..7d6f928 100644
--- a/tests/tests/uirendering/res/layout/simple_rect_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_rect_layout.xml
@@ -13,14 +13,12 @@
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:id="@+id/test_root"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical">
-
- <View android:layout_width="100px"
- android:layout_height="100px"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height">
+ <View android:layout_width="80px"
+ android:layout_height="80px"
+ android:translationX="5px"
+ android:translationY="5px"
android:background="#00f" />
-
-</LinearLayout>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/simple_red_layout.xml b/tests/tests/uirendering/res/layout/simple_red_layout.xml
index 2d2d189..2af8db6 100644
--- a/tests/tests/uirendering/res/layout/simple_red_layout.xml
+++ b/tests/tests/uirendering/res/layout/simple_red_layout.xml
@@ -14,7 +14,7 @@
limitations under the License.
-->
<View xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"
android:id="@+id/test_root"
- android:layout_width="180px"
- android:layout_height="180px"
android:background="#f00" />
diff --git a/tests/tests/uirendering/res/layout/simple_shadow_layout.xml b/tests/tests/uirendering/res/layout/simple_shadow_layout.xml
new file mode 100644
index 0000000..f97974b
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/simple_shadow_layout.xml
@@ -0,0 +1,26 @@
+<?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.
+-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height">
+ <View android:layout_width="40px"
+ android:layout_height="40px"
+ android:translationX="25px"
+ android:translationY="25px"
+ android:elevation="10dp"
+ android:background="#fff" />
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/test_container.xml b/tests/tests/uirendering/res/layout/test_container.xml
new file mode 100644
index 0000000..94a8eab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_container.xml
@@ -0,0 +1,24 @@
+<!-- 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.
+ -->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/test_container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ViewStub
+ android:id="@+id/test_content_stub"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
+</FrameLayout>
diff --git a/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
new file mode 100644
index 0000000..9f8a139
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_canvasclientview.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+ -->
+<android.uirendering.cts.testinfrastructure.CanvasClientView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/layout/test_content_webview.xml b/tests/tests/uirendering/res/layout/test_content_webview.xml
new file mode 100644
index 0000000..8b03cab
--- /dev/null
+++ b/tests/tests/uirendering/res/layout/test_content_webview.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+ -->
+<WebView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="@dimen/test_width"
+ android:layout_height="@dimen/test_height"/>
\ No newline at end of file
diff --git a/tests/tests/uirendering/res/values/dimens.xml b/tests/tests/uirendering/res/values/dimens.xml
new file mode 100644
index 0000000..1c304d3
--- /dev/null
+++ b/tests/tests/uirendering/res/values/dimens.xml
@@ -0,0 +1,18 @@
+<!-- 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.
+ -->
+<resources>
+ <dimen name="test_width">90px</dimen>
+ <dimen name="test_height">90px</dimen>
+</resources>
diff --git a/tests/tests/uirendering/res/values/themes.xml b/tests/tests/uirendering/res/values/themes.xml
index 751b7cb..f08f029 100644
--- a/tests/tests/uirendering/res/values/themes.xml
+++ b/tests/tests/uirendering/res/values/themes.xml
@@ -17,9 +17,9 @@
<style name="WhiteBackgroundTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen">
<item name="android:windowNoTitle">true</item>
<item name="android:windowFullscreen">true</item>
+ <item name="android:windowOverscan">true</item>
<item name="android:fadingEdge">none</item>
<item name="android:windowBackground">@android:color/white</item>
- <!--This shouldn't be necessary currently a hack for an existing bug with transitions-->
<item name="android:windowContentTransitions">false</item>
<item name="android:windowAnimationStyle">@null</item>
</style>
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
index e766f63..56d9cf5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapcomparers/MSSIMComparer.java
@@ -15,7 +15,6 @@
*/
package android.uirendering.cts.bitmapcomparers;
-import com.android.cts.uirendering.R;
import com.android.cts.uirendering.ScriptC_MSSIMComparer;
import android.content.res.Resources;
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
index ea836ea..7f62b3e 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/ColorVerifier.java
@@ -22,11 +22,16 @@
private int mColor;
public ColorVerifier(int color) {
- this(color, 20);
+ this(color, DEFAULT_THRESHOLD);
}
- public ColorVerifier(int color, int threshold) {
- super(threshold);
+ public ColorVerifier(int color, int colorTolerance) {
+ super(colorTolerance);
+ mColor = color;
+ }
+
+ public ColorVerifier(int color, int colorThreshold, float spatialTolerance) {
+ super(colorThreshold, spatialTolerance);
mColor = color;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
index 672b3f6..d4a63de 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/GoldenImageVerifier.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
import android.uirendering.cts.differencevisualizers.PassFailVisualizer;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
public class GoldenImageVerifier extends BitmapVerifier {
private BitmapComparer mBitmapComparer;
@@ -26,7 +27,7 @@
public GoldenImageVerifier(Bitmap goldenBitmap, BitmapComparer bitmapComparer) {
mGoldenBitmapArray = new int[goldenBitmap.getWidth() * goldenBitmap.getHeight()];
goldenBitmap.getPixels(mGoldenBitmapArray, 0, goldenBitmap.getWidth(), 0, 0,
- goldenBitmap.getWidth(), goldenBitmap.getHeight());
+ ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
mBitmapComparer = bitmapComparer;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
index 0bdcc9b..2d00db5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/PerPixelBitmapVerifier.java
@@ -26,38 +26,54 @@
*/
public abstract class PerPixelBitmapVerifier extends BitmapVerifier {
private static final String TAG = "PerPixelBitmapVerifer";
- private int mTolerance;
+ public static final int DEFAULT_THRESHOLD = 48;
- public PerPixelBitmapVerifier(int tolerance) {
- mTolerance = tolerance;
+ // total color difference tolerated without the pixel failing
+ private int mColorTolerance;
+
+ // portion of bitmap allowed to fail pixel check
+ private float mSpatialTolerance;
+
+ public PerPixelBitmapVerifier() {
+ this(DEFAULT_THRESHOLD, 0);
+ }
+
+ public PerPixelBitmapVerifier(int colorTolerance) {
+ this(colorTolerance, 0);
+ }
+
+ public PerPixelBitmapVerifier(int colorTolerance, float spatialTolerance) {
+ mColorTolerance = colorTolerance;
+ mSpatialTolerance = spatialTolerance;
}
protected int getExpectedColor(int x, int y) {
return Color.WHITE;
}
-
public boolean verify(int[] bitmap, int offset, int stride, int width, int height) {
- int failCount = 0;
+ int failures = 0;
int[] differenceMap = new int[bitmap.length];
for (int y = 0 ; y < height ; y++) {
for (int x = 0 ; x < width ; x++) {
int index = indexFromXAndY(x, y, stride, offset);
- int expectedColor = getExpectedColor(x, y);
- if (!verifyPixel(bitmap[index], expectedColor)) {
- if (failCount < 50) {
- Log.d(TAG, "Expected : " + Integer.toHexString(expectedColor)
+ if (!verifyPixel(x, y, bitmap[index])) {
+ if (failures < 50) {
+ Log.d(TAG, "Expected : " + Integer.toHexString(getExpectedColor(x, y))
+ " received : " + Integer.toHexString(bitmap[index])
+ " at position (" + x + "," + y + ")");
}
- failCount++;
+ failures++;
differenceMap[index] = FAIL_COLOR;
} else {
differenceMap[index] = PASS_COLOR;
}
}
}
- boolean success = failCount == 0;
+ int toleratedFailures = (int) (mSpatialTolerance * width * height);
+ boolean success = failures <= toleratedFailures;
+ Log.d(TAG, failures + " failures observed out of "
+ + toleratedFailures + " tolerated failures");
if (!success) {
mDifferenceBitmap = Bitmap.createBitmap(ActivityTestBase.TEST_WIDTH,
ActivityTestBase.TEST_HEIGHT, Bitmap.Config.ARGB_8888);
@@ -67,7 +83,9 @@
return success;
}
- protected boolean verifyPixel(int color, int expectedColor) {
- return CompareUtils.verifyPixelWithThreshold(color, expectedColor, mTolerance);
+
+ protected boolean verifyPixel(int x, int y, int observedColor) {
+ int expectedColor = getExpectedColor(x, y);
+ return CompareUtils.verifyPixelWithThreshold(observedColor, expectedColor, mColorTolerance);
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
index 06a430b..f4bece1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/RectVerifier.java
@@ -26,7 +26,7 @@
private Rect mInnerRect;
public RectVerifier(int outerColor, int innerColor, Rect innerRect) {
- this(outerColor, innerColor, innerRect, 20);
+ this(outerColor, innerColor, innerRect, DEFAULT_THRESHOLD);
}
public RectVerifier(int outerColor, int innerColor, Rect innerRect, int tolerance) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
index cb62694..c97e020 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/bitmapverifiers/SamplePointVerifier.java
@@ -28,13 +28,19 @@
*/
public class SamplePointVerifier extends BitmapVerifier {
private static final String TAG = "SamplePoint";
- private Point[] mTestPoints;
- private int[] mExpectedColors;
- private int mTolerance = 20;
+ private static final int DEFAULT_TOLERANCE = 20;
+ private final Point[] mTestPoints;
+ private final int[] mExpectedColors;
+ private final int mTolerance;
public SamplePointVerifier(Point[] testPoints, int[] expectedColors) {
+ this(testPoints, expectedColors, DEFAULT_TOLERANCE);
+ }
+
+ public SamplePointVerifier(Point[] testPoints, int[] expectedColors, int tolerance) {
mTestPoints = testPoints;
mExpectedColors = expectedColors;
+ mTolerance = tolerance;
}
@Override
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
index ddae100..1d5cd16 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/BitmapFilterTests.java
@@ -28,7 +28,6 @@
import android.uirendering.cts.testinfrastructure.CanvasClient;
public class BitmapFilterTests extends ActivityTestBase {
- private static final int THRESHOLD = 20;
private static final int WHITE_WEIGHT = 255 * 3;
private enum FilterEnum {
// Creates Paint object that will have bitmap filtering
@@ -40,72 +39,77 @@
ADD_FILTER
}
- /**
- * Verifies that a Bitmap only contains white and black, within a certain threshold
- */
- private static BitmapVerifier mBlackWhiteVerifier = new PerPixelBitmapVerifier(THRESHOLD) {
+ private static final BitmapVerifier BLACK_WHITE_ONLY_VERIFIER
+ = new PerPixelBitmapVerifier(PerPixelBitmapVerifier.DEFAULT_THRESHOLD, 0.99f) {
@Override
- protected boolean verifyPixel(int color, int expectedColor) {
+ protected boolean verifyPixel(int x, int y, int color) {
int weight = Color.red(color) + Color.blue(color) + Color.green(color);
- return weight < THRESHOLD // is approx Color.BLACK
- || weight > WHITE_WEIGHT - THRESHOLD; // is approx Color.WHITE
+ return weight < DEFAULT_THRESHOLD // is approx Color.BLACK
+ || weight > WHITE_WEIGHT - DEFAULT_THRESHOLD; // is approx Color.WHITE
}
};
+ private static final BitmapVerifier GREY_ONLY_VERIFIER
+ = new ColorVerifier(Color.argb(255, 127, 127, 127),
+ 150); // content will be entirely grey, for a fairly wide range of grey
+ private static final BitmapVerifier GREY_PARTIAL_VERIFIER
+ = new ColorVerifier(Color.argb(255, 127, 127, 127),
+ 300, 0.8f); // content will be mostly grey, for a wide range of grey
+
private static Bitmap createGridBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
for (int i = 0 ; i < width ; i++) {
for (int j = 0 ; j < height ; j++) {
- bitmap.setPixel(i, j, ((i + j * width)) % 2 == 0 ?
- Color.WHITE : Color.BLACK);
+ boolean isWhite = (i + j * width) % 2 == 0;
+ bitmap.setPixel(i, j, isWhite ? Color.WHITE : Color.BLACK);
}
}
return bitmap;
}
private static final int SMALL_GRID_SIZE = 5;
- private static Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
- private static final int BIG_GRID_SIZE = 360;
- private static Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
- private static final int HALFWAY_COLOR = Color.argb(255, 127, 127, 127);
+ private Bitmap mSmallGridBitmap = createGridBitmap(SMALL_GRID_SIZE, SMALL_GRID_SIZE);
- /* All of these tests seem to be broken.
- * TODO: fix in L MR1
+ // samples will occur in the middle of 4 pixels, 2 white
+ // and 2 black, and thus each be close to x7F7F7F grey
+ private static final int BIG_GRID_SIZE = TEST_WIDTH * 2;
+ private Bitmap mBigGridBitmap = createGridBitmap(BIG_GRID_SIZE, BIG_GRID_SIZE);
+
@SmallTest
public void testPaintFilterScaleUp() {
- runScaleTest(FilterEnum.PAINT_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.PAINT_FILTER, true);
}
@SmallTest
public void testPaintFilterScaleDown() {
- runScaleTest(FilterEnum.PAINT_FILTER, false, new ColorVerifier(HALFWAY_COLOR, 15));
+ runScaleTest(FilterEnum.PAINT_FILTER, false);
}
@SmallTest
public void testDrawFilterRemoveFilterScaleUp() {
- runScaleTest(FilterEnum.REMOVE_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.REMOVE_FILTER, true);
}
@SmallTest
public void testDrawFilterRemoveFilterScaleDown() {
- runScaleTest(FilterEnum.REMOVE_FILTER, false, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.REMOVE_FILTER, false);
}
@SmallTest
public void testDrawFilterScaleUp() {
- runScaleTest(FilterEnum.ADD_FILTER, true, mBlackWhiteVerifier);
+ runScaleTest(FilterEnum.ADD_FILTER, true);
}
@SmallTest
public void testDrawFilterScaleDown() {
- runScaleTest(FilterEnum.ADD_FILTER, false, new ColorVerifier(HALFWAY_COLOR));
+ runScaleTest(FilterEnum.ADD_FILTER, false);
}
-*/
- private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp,
- BitmapVerifier bitmapVerifier) {
+
+ private void runScaleTest(final FilterEnum filterEnum, final boolean scaleUp) {
final int gridWidth = scaleUp ? SMALL_GRID_SIZE : BIG_GRID_SIZE;
final Paint paint = new Paint(filterEnum.equals(FilterEnum.ADD_FILTER) ?
0 : Paint.FILTER_BITMAP_FLAG);
+
CanvasClient canvasClient = new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
@@ -120,6 +124,16 @@
};
createTest()
.addCanvasClient(canvasClient)
- .runWithVerifier(bitmapVerifier);
+ .runWithVerifier(getVerifierForTest(filterEnum, scaleUp));
+ }
+
+ private static BitmapVerifier getVerifierForTest(FilterEnum filterEnum, boolean scaleUp) {
+ if (filterEnum.equals(FilterEnum.REMOVE_FILTER)) {
+ // filtering disabled, so only black and white pixels will come through
+ return BLACK_WHITE_ONLY_VERIFIER;
+ }
+ // if scaling up, output pixels may have single source to sample from,
+ // will only be *mostly* grey.
+ return scaleUp ? GREY_PARTIAL_VERIFIER : GREY_ONLY_VERIFIER;
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
index afbad65..29755d8 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -36,7 +36,7 @@
@SmallTest
public void testBlueRect() {
- final Rect rect = new Rect(10, 10, 100, 100);
+ final Rect rect = new Rect(10, 10, 80, 80);
createTest()
.addCanvasClient(new CanvasClient() {
@Override
@@ -58,8 +58,6 @@
public void draw(Canvas canvas, int width, int height) {
Paint p = new Paint();
p.setAntiAlias(false);
- p.setColor(Color.WHITE);
- canvas.drawRect(0, 0, 100, 100, p);
p.setStrokeWidth(1f);
p.setColor(Color.BLACK);
for (int i = 0; i < 10; i++) {
@@ -81,8 +79,8 @@
canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
ActivityTestBase.TEST_HEIGHT, p);
p.setColor(Color.BLACK);
- p.setStrokeWidth(10);
- canvas.drawRect(10, 10, 20, 20, p);
+ p.setStrokeWidth(5);
+ canvas.drawRect(10, 10, 80, 80, p);
}
})
.runWithComparer(mExactComparer);
@@ -94,10 +92,8 @@
.addCanvasClient(new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
+ canvas.drawColor(Color.GREEN);
Paint p = new Paint();
- p.setColor(Color.GREEN);
- canvas.drawRect(0, 0, ActivityTestBase.TEST_WIDTH,
- ActivityTestBase.TEST_HEIGHT, p);
p.setColor(Color.BLACK);
p.setStrokeWidth(10);
canvas.drawLine(0, 0, 50, 0, p);
@@ -131,7 +127,7 @@
canvas.drawColor(Color.WHITE);
p.setColor(Color.BLACK);
float[] pts = {
- 0, 0, 100, 100, 100, 0, 0, 100, 50, 50, 75, 75
+ 0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
};
canvas.drawLines(pts, p);
}
@@ -185,10 +181,10 @@
public void testBluePaddedSquare() {
final NinePatchDrawable ninePatchDrawable = (NinePatchDrawable)
getActivity().getResources().getDrawable(R.drawable.blue_padded_square);
- ninePatchDrawable.setBounds(0, 0, 100, 100);
+ ninePatchDrawable.setBounds(0, 0, 90, 90);
BitmapVerifier verifier = new RectVerifier(Color.WHITE, Color.BLUE,
- new Rect(10, 10, 90, 90));
+ new Rect(10, 10, 80, 80));
createTest()
.addCanvasClient(new CanvasClient() {
@@ -197,7 +193,7 @@
canvas.drawColor(Color.WHITE);
Paint p = new Paint();
p.setColor(Color.BLUE);
- canvas.drawRect(10, 10, 90, 90, p);
+ canvas.drawRect(10, 10, 80, 80, p);
}
})
.addCanvasClient(new CanvasClient() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
index 6662226..c86ff76 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/InfrastructureTests.java
@@ -15,21 +15,19 @@
*/
package android.uirendering.cts.testclasses;
+import android.graphics.Point;
import com.android.cts.uirendering.R;
-import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Rect;
import android.test.suitebuilder.annotation.SmallTest;
import android.uirendering.cts.bitmapcomparers.BitmapComparer;
-import android.uirendering.cts.bitmapcomparers.ExactComparer;
import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
import android.uirendering.cts.bitmapverifiers.RectVerifier;
import android.uirendering.cts.testinfrastructure.ActivityTestBase;
import android.uirendering.cts.testinfrastructure.CanvasClient;
import android.uirendering.cts.testinfrastructure.ViewInitializer;
-import android.util.Log;
import android.view.View;
public class InfrastructureTests extends ActivityTestBase {
@@ -37,7 +35,7 @@
@SmallTest
public void testScreenshot() {
for (int i = 0 ; i < 500 ; i ++) {
- takeScreenshot();
+ takeScreenshot(new Point());
System.gc();
}
}
@@ -52,16 +50,17 @@
CanvasClient canvasClient = new CanvasClient() {
@Override
public void draw(Canvas canvas, int width, int height) {
- canvas.drawColor(canvas.isHardwareAccelerated() ? Color.WHITE : Color.BLACK);
+ canvas.drawColor(canvas.isHardwareAccelerated() ? Color.BLACK : Color.WHITE);
}
};
- // This is considered a very high threshold and as such, the test should still fail because
- // they are completely different images.
- final float threshold = 0.1f;
BitmapComparer inverseComparer = new BitmapComparer() {
@Override
public boolean verifySame(int[] ideal, int[] given, int offset, int stride, int width,
int height) {
+
+ // Return true if the images aren't even 10% similar. They should be completely
+ // different, since they should both be completely different colors.
+ final float threshold = 0.1f;
return !(new MSSIMComparer(threshold)).verifySame(ideal, given, offset, stride,
width, height);
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
index 4667ee9..ff1e9db 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/LayoutTests.java
@@ -34,7 +34,7 @@
@SmallTest
public void testSimpleRectLayout() {
createTest().addLayout(R.layout.simple_rect_layout, null, false).runWithVerifier(
- new RectVerifier(Color.WHITE, Color.BLUE, new Rect(0, 0, 100, 100)));
+ new RectVerifier(Color.WHITE, Color.BLUE, new Rect(5, 5, 85, 85)));
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
index 8df8057..6911cf0 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/PathClippingTests.java
@@ -17,7 +17,7 @@
import com.android.cts.uirendering.R;
public class PathClippingTests extends ActivityTestBase {
- // draw circle with whole in it, with stroked circle
+ // draw circle with hole in it, with stroked circle
static final CanvasClient sCircleDrawCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
@@ -31,11 +31,11 @@
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(20);
- canvas.drawCircle(50, 50, 40, paint);
+ canvas.drawCircle(30, 30, 40, paint);
}
};
- // draw circle with whole in it, by path operations + path clipping
+ // draw circle with hole in it, by path operations + path clipping
static final CanvasClient sCircleClipCanvasClient = new CanvasClient() {
@Override
public String getDebugString() {
@@ -47,8 +47,8 @@
canvas.save();
Path path = new Path();
- path.addCircle(50, 50, 50, Path.Direction.CW);
- path.addCircle(50, 50, 30, Path.Direction.CCW);
+ path.addCircle(30, 30, 50, Path.Direction.CW);
+ path.addCircle(30, 30, 30, Path.Direction.CCW);
canvas.clipPath(path);
canvas.drawColor(Color.BLUE);
@@ -72,12 +72,12 @@
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of circle
- new Point(50, 50),
+ new Point(30, 50),
// on circle
- new Point(50 + 32, 50 + 32),
+ new Point(30 + 32, 30 + 32),
// outside of circle
- new Point(50 + 38, 50 + 38),
- new Point(100, 100)
+ new Point(30 + 38, 30 + 38),
+ new Point(80, 80)
},
new int[] {
Color.WHITE,
@@ -96,8 +96,8 @@
ViewGroup rootView = (ViewGroup) view;
rootView.setClipChildren(true);
View childView = rootView.getChildAt(0);
- childView.setPivotX(50);
- childView.setPivotY(50);
+ childView.setPivotX(40);
+ childView.setPivotY(40);
childView.setRotation(45f);
}
@@ -105,11 +105,11 @@
.runWithVerifier(new SamplePointVerifier(
new Point[] {
// inside of rotated rect
- new Point(50, 50),
- new Point(50 + 32, 50 + 32),
+ new Point(40, 40),
+ new Point(40 + 25, 40 + 25),
// outside of rotated rect
- new Point(50 + 38, 50 + 38),
- new Point(100, 100)
+ new Point(40 + 31, 40 + 31),
+ new Point(80, 80)
},
new int[] {
Color.BLUE,
@@ -128,15 +128,15 @@
canvas.save();
Path path = new Path();
- path.addCircle(0, 50, 50, Path.Direction.CW);
- path.addCircle(100, 50, 50, Path.Direction.CW);
+ path.addCircle(0, 45, 45, Path.Direction.CW);
+ path.addCircle(90, 45, 45, Path.Direction.CW);
canvas.clipPath(path);
Paint paint = new Paint();
paint.setAntiAlias(true);
- paint.setTextSize(100);
+ paint.setTextSize(90);
paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- canvas.drawText("STRING", 0, 100, paint);
+ canvas.drawText("STRING", 0, 90, paint);
canvas.restore();
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
new file mode 100644
index 0000000..b1d4638
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShadowTests.java
@@ -0,0 +1,56 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.Color;
+import android.graphics.Point;
+import android.uirendering.cts.bitmapverifiers.SamplePointVerifier;
+
+import com.android.cts.uirendering.R;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+
+public class ShadowTests extends ActivityTestBase {
+ @SmallTest
+ public void testShadowLayout() {
+ int shadowColorValue = 0xD7;
+ // Android TV theme overrides shadow opacity to be darker.
+ if (getActivity().getOnTv()) {
+ shadowColorValue = 0xB7;
+ }
+ // Use a higher threshold (30) than default value (20);
+ SamplePointVerifier verifier = new SamplePointVerifier(
+ new Point[] {
+ // view area
+ new Point(25, 64),
+ new Point(64, 64),
+ // shadow area
+ new Point(25, 65),
+ new Point(64, 65)
+ },
+ new int[] {
+ Color.WHITE,
+ Color.WHITE,
+ Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
+ Color.rgb(shadowColorValue, shadowColorValue, shadowColorValue),
+ },
+ 30);
+ createTest()
+ .addLayout(R.layout.simple_shadow_layout, null, true/* HW only */)
+ .runWithVerifier(verifier);
+ }
+}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
index 7947286..32ab0e4 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/SweepTests.java
@@ -22,6 +22,7 @@
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
@@ -50,17 +51,11 @@
public static final int MULTIPLY_COLOR = 0xFF668844;
public static final int SCREEN_COLOR = 0xFFFFEEFF;
- public static final int FILTER_COLOR = 0xFFBB0000;
- public static final int RECT0_COLOR = 0x33808080;
- public static final int RECT1_COLOR = 0x66808080;
- public static final int RECT2_COLOR = 0x99808080;
- public static final int RECT3_COLOR = 0xCC808080;
-
// These points are in pairs, the first being the lower left corner, the second is only in the
// Destination bitmap, the third is the intersection of the two bitmaps, and the fourth is in
// the Source bitmap.
private final static Point[] XFERMODE_TEST_POINTS = new Point[] {
- new Point(1, 160), new Point(50, 50), new Point(70, 70), new Point(140, 140)
+ new Point(1, 80), new Point(25, 25), new Point(35, 35), new Point(70, 70)
};
/**
@@ -128,8 +123,8 @@
};
private final static DisplayModifier XFERMODE_MODIFIER = new DisplayModifier() {
- private final RectF mSrcRect = new RectF(60, 60, 160, 160);
- private final RectF mDstRect = new RectF(20, 20, 120, 120);
+ private final RectF mSrcRect = new RectF(30, 30, 80, 80);
+ private final RectF mDstRect = new RectF(10, 10, 60, 60);
private final Bitmap mSrcBitmap = createSrc();
private final Bitmap mDstBitmap = createDst();
@@ -144,8 +139,7 @@
}
private Bitmap createSrc() {
- Bitmap srcB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap srcB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas srcCanvas = new Canvas(srcB);
Paint srcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
srcPaint.setColor(SRC_COLOR);
@@ -154,8 +148,7 @@
}
private Bitmap createDst() {
- Bitmap dstB = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap dstB = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas dstCanvas = new Canvas(dstB);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(DST_COLOR);
@@ -164,18 +157,22 @@
}
};
-
// We care about one point in each of the four rectangles of different alpha values, as well as
// the area outside the rectangles
private final static Point[] COLOR_FILTER_ALPHA_POINTS = new Point[] {
- new Point(15, 90), new Point(45, 90), new Point(75, 90), new Point(105, 90),
- new Point(135, 90)
+ new Point(9, 45),
+ new Point(27, 45),
+ new Point(45, 45),
+ new Point(63, 45),
+ new Point(81, 45)
};
- private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP = new LinkedHashMap<PorterDuff.Mode, int[]>() {
+ public static final int FILTER_COLOR = 0xFFBB0000;
+ private final Map<PorterDuff.Mode, int[]> COLOR_FILTER_ALPHA_MAP
+ = new LinkedHashMap<PorterDuff.Mode, int[]>() {
{
put(PorterDuff.Mode.SRC, new int[] {
- FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
+ FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR, FILTER_COLOR
});
put(PorterDuff.Mode.DST, new int[] {
@@ -228,10 +225,17 @@
}
};
+ /**
+ * Draws 5 blocks of different color/opacity to be blended against
+ */
private final static DisplayModifier COLOR_FILTER_ALPHA_MODIFIER = new DisplayModifier() {
- private final static int mBlockWidths = 30;
- private final int[] mColorValues = new int[] {RECT0_COLOR, RECT1_COLOR, RECT2_COLOR,
- RECT3_COLOR};
+ private final int[] BLOCK_COLORS = new int[] {
+ 0x33808080,
+ 0x66808080,
+ 0x99808080,
+ 0xCC808080,
+ 0x00000000
+ };
private final Bitmap mBitmap = createQuadRectBitmap();
@@ -240,13 +244,15 @@
}
private Bitmap createQuadRectBitmap() {
- Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint paint = new Paint();
- for (int i = 0 ; i < 4 ; i++) {
- paint.setColor(mColorValues[i]);
- canvas.drawRect(i * mBlockWidths, 0, (i + 1) * mBlockWidths, MODIFIER_HEIGHT, paint);
+ paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
+ final int blockCount = BLOCK_COLORS.length;
+ final int blockWidth = TEST_WIDTH / blockCount;
+ for (int i = 0 ; i < blockCount; i++) {
+ paint.setColor(BLOCK_COLORS[i]);
+ canvas.drawRect(i * blockWidth, 0, (i + 1) * blockWidth, TEST_HEIGHT, paint);
}
return bitmap;
}
@@ -266,10 +272,9 @@
}
private Bitmap createGradient() {
- LinearGradient gradient = new LinearGradient(30, 90, 150, 90, mColors, null,
+ LinearGradient gradient = new LinearGradient(15, 45, 75, 45, mColors, null,
Shader.TileMode.REPEAT);
- Bitmap bitmap = Bitmap.createBitmap(MODIFIER_WIDTH, MODIFIER_HEIGHT,
- Bitmap.Config.ARGB_8888);
+ Bitmap bitmap = Bitmap.createBitmap(TEST_WIDTH, TEST_HEIGHT, Bitmap.Config.ARGB_8888);
Paint p = new Paint();
p.setShader(gradient);
Canvas c = new Canvas(bitmap);
@@ -281,9 +286,7 @@
public static final DisplayModifier mCircleDrawModifier = new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawCircle(ActivityTestBase.TEST_WIDTH / 2,
- ActivityTestBase.TEST_HEIGHT / 2,
- ActivityTestBase.TEST_HEIGHT / 2, paint);
+ canvas.drawCircle(TEST_WIDTH / 2, TEST_HEIGHT / 2, TEST_HEIGHT / 2, paint);
}
};
@@ -343,16 +346,15 @@
}
/*
- * TODO: fix this test for L MR1
@SmallTest
public void testShaderSweeps() {
- int mask = DisplayModifier.Accessor.AA_MASK |
- DisplayModifier.Accessor.SHADER_MASK |
- DisplayModifier.Accessor.XFERMODE_MASK |
- DisplayModifier.Accessor.SHAPES_MASK;
+ int mask = DisplayModifier.Accessor.AA_MASK
+ | DisplayModifier.Accessor.SHADER_MASK
+ | DisplayModifier.Accessor.XFERMODE_MASK
+ | DisplayModifier.Accessor.SHAPES_MASK;
sweepModifiersForMask(mask, null, DEFAULT_MSSIM_COMPARER, null);
}
- */
+ */
protected void sweepModifiersForMask(int mask, final DisplayModifier drawOp,
BitmapComparer[] bitmapComparers, BitmapVerifier[] bitmapVerifiers) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
index da2db48..343228f 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ViewClippingTests.java
@@ -20,11 +20,11 @@
* Since the layout is blue on a white background, this is always done with a RectVerifier.
*/
public class ViewClippingTests extends ActivityTestBase {
- final Rect FULL_RECT = new Rect(0, 0, 200, 200);
- final Rect BOUNDS_RECT = new Rect(0, 0, 100, 100);
- final Rect PADDED_RECT = new Rect(15, 16, 83, 82);
- final Rect OUTLINE_RECT = new Rect(1, 2, 98, 99);
- final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 70, 80);
+ final Rect FULL_RECT = new Rect(0, 0, 90, 90);
+ final Rect BOUNDS_RECT = new Rect(0, 0, 80, 80);
+ final Rect PADDED_RECT = new Rect(15, 16, 63, 62);
+ final Rect OUTLINE_RECT = new Rect(1, 2, 78, 79);
+ final Rect CLIP_BOUNDS_RECT = new Rect(10, 20, 50, 60);
final ViewInitializer BOUNDS_CLIP_INIT = new ViewInitializer() {
@Override
@@ -61,10 +61,9 @@
}
};
- // TODO: attempt to reduce
- static final int TOLERANCE = 16;
static BitmapVerifier makeClipVerifier(Rect blueBoundsRect) {
- return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, TOLERANCE);
+ // very high error tolerance, since all these tests care about is clip alignment
+ return new RectVerifier(Color.WHITE, Color.BLUE, blueBoundsRect, 75);
}
public void testSimpleUnclipped() {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
index 7a16e3c..b8935fb 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/view/UnclippedBlueView.java
@@ -4,7 +4,6 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.util.AttributeSet;
-import android.view.View;
import android.widget.FrameLayout;
public class UnclippedBlueView extends FrameLayout {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
index 783e710..e1c09f5 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/ActivityTestBase.java
@@ -17,6 +17,7 @@
import android.annotation.Nullable;
import android.graphics.Bitmap;
+import android.graphics.Point;
import android.renderscript.Allocation;
import android.renderscript.RenderScript;
import android.test.ActivityInstrumentationTestCase2;
@@ -41,15 +42,16 @@
public static final String TAG = "ActivityTestBase";
public static final boolean DEBUG = false;
public static final boolean USE_RS = false;
- public static final int TEST_WIDTH = 180;
- public static final int TEST_HEIGHT = 180; //The minimum height and width of a device
+
+ //The minimum height and width of a device
+ public static final int TEST_WIDTH = 90;
+ public static final int TEST_HEIGHT = 90;
+
public static final int MAX_SCREEN_SHOTS = 100;
private int[] mHardwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
private int[] mSoftwareArray = new int[TEST_HEIGHT * TEST_WIDTH];
private DifferenceVisualizer mDifferenceVisualizer;
- private Allocation mIdealAllocation;
- private Allocation mGivenAllocation;
private RenderScript mRenderScript;
private TestCaseBuilder mTestCaseBuilder;
@@ -115,16 +117,29 @@
getActivity().runOnUiThread(finishRunnable);
}
- public Bitmap takeScreenshot() {
+ static int[] getBitmapPixels(Bitmap bitmap) {
+ int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
+ bitmap.getPixels(pixels, 0, bitmap.getWidth(),
+ 0, 0, bitmap.getWidth(), bitmap.getHeight());
+ return pixels;
+ }
+
+ private Bitmap takeScreenshotImpl(Point testOffset) {
+ Bitmap source = getInstrumentation().getUiAutomation().takeScreenshot();
+ return Bitmap.createBitmap(source, testOffset.x, testOffset.y, TEST_WIDTH, TEST_HEIGHT);
+ }
+
+ public Bitmap takeScreenshot(Point testOffset) {
getInstrumentation().waitForIdleSync();
- Bitmap bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+ Bitmap bitmap1 = takeScreenshotImpl(testOffset);
Bitmap bitmap2;
int count = 0;
do {
bitmap2 = bitmap1;
- bitmap1 = getInstrumentation().getUiAutomation().takeScreenshot();
+ bitmap1 = takeScreenshotImpl(testOffset);
count++;
- } while (count < MAX_SCREEN_SHOTS && !Arrays.equals(bitmap2.mBuffer, bitmap1.mBuffer));
+ } while (count < MAX_SCREEN_SHOTS &&
+ !Arrays.equals(getBitmapPixels(bitmap2), getBitmapPixels(bitmap1)));
return bitmap1;
}
@@ -139,10 +154,11 @@
* Used to execute a specific part of a test and get the resultant bitmap
*/
protected Bitmap captureRenderSpec(TestCase testCase) {
- getActivity().enqueueRenderSpecAndWait(testCase.layoutID, testCase.canvasClient,
+ Point testOffset = getActivity().enqueueRenderSpecAndWait(
+ testCase.layoutID, testCase.canvasClient,
testCase.webViewUrl, testCase.viewInitializer, testCase.useHardware);
testCase.wasTestRan = true;
- return takeScreenshot();
+ return takeScreenshot(testOffset);
}
/**
@@ -154,12 +170,12 @@
boolean success;
if (USE_RS && comparer.supportsRenderScript()) {
- mIdealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
+ Allocation idealAllocation = Allocation.createFromBitmap(mRenderScript, bitmap1,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- mGivenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
+ Allocation givenAllocation = Allocation.createFromBitmap(mRenderScript, bitmap2,
Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
- success = comparer.verifySameRS(getActivity().getResources(), mIdealAllocation,
- mGivenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
+ success = comparer.verifySameRS(getActivity().getResources(), idealAllocation,
+ givenAllocation, 0, TEST_WIDTH, TEST_WIDTH, TEST_HEIGHT, mRenderScript);
} else {
bitmap1.getPixels(mSoftwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
bitmap2.getPixels(mHardwareArray, 0, TEST_WIDTH, 0, 0, TEST_WIDTH, TEST_HEIGHT);
@@ -213,11 +229,6 @@
* every test case is tested against it.
*/
public void runWithComparer(BitmapComparer bitmapComparer) {
- if (getActivity().getOnWatch()) {
- Log.d(TAG, getName() + "skipped");
- return;
- }
-
if (mTestCases.size() == 0) {
throw new IllegalStateException("Need at least one test to run");
}
@@ -236,11 +247,6 @@
* the verifier given.
*/
public void runWithVerifier(BitmapVerifier bitmapVerifier) {
- if (getActivity().getOnWatch()) {
- Log.d(TAG, getName() + "skipped");
- return;
- }
-
if (mTestCases.size() == 0) {
throw new IllegalStateException("Need at least one test to run");
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
index 92242f0..60127ae 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/CanvasClientView.java
@@ -19,6 +19,7 @@
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
+import android.util.AttributeSet;
import android.view.View;
/**
@@ -26,14 +27,21 @@
*/
public class CanvasClientView extends View {
private CanvasClient mCanvasClient;
- private int mWidth;
- private int mHeight;
- public CanvasClientView(Context context, CanvasClient canvasClient, int width, int height) {
+ public CanvasClientView(Context context) {
super(context);
+ }
+
+ public CanvasClientView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public CanvasClientView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ public void setCanvasClient(CanvasClient canvasClient) {
mCanvasClient = canvasClient;
- mWidth = width;
- mHeight = height;
}
@Override
@@ -45,11 +53,11 @@
paint.setTextSize(20);
canvas.drawText(s, 200, 200, paint);
}
- if (mCanvasClient != null) {
- canvas.save();
- canvas.clipRect(0, 0, mWidth, mHeight);
- mCanvasClient.draw(canvas, mWidth, mHeight);
- canvas.restore();
- }
+ if (mCanvasClient == null) throw new IllegalStateException("Canvas client missing");
+
+ canvas.save();
+ canvas.clipRect(0, 0, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+ mCanvasClient.draw(canvas, ActivityTestBase.TEST_WIDTH, ActivityTestBase.TEST_HEIGHT);
+ canvas.restore();
}
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index 684293d..b42ac88 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -32,17 +32,16 @@
* Modifies the canvas and paint objects when called.
*/
public abstract class DisplayModifier {
- private static final RectF gRect = new RectF(0, 0, 100, 100);
- private static final float[] gPts = new float[]{
- 0, 100, 100, 0, 100, 200, 200, 100
+ private static final RectF RECT = new RectF(0, 0, 100, 100);
+ private static final float[] POINTS = new float[]{
+ 0, 40, 40, 0, 40, 80, 80, 40
};
- private static final float[] gTriPts = new float[]{
- 75, 0, 130, 130, 130, 130, 0, 130, 0, 130, 75, 0
+ private static final float[] TRIANGLE_POINTS = new float[]{
+ 40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0
};
- private static final int NUM_PARALLEL_LINES = 24;
- private static final float[] gLinePts = new float[NUM_PARALLEL_LINES * 8 + gTriPts.length];
- protected static final int MODIFIER_WIDTH = 180;
- protected static final int MODIFIER_HEIGHT = 180;
+ private static final int NUM_PARALLEL_LINES = 10;
+ private static final float[] LINES = new float[NUM_PARALLEL_LINES * 8
+ + TRIANGLE_POINTS.length];
public static final PorterDuff.Mode[] PORTERDUFF_MODES = new PorterDuff.Mode[] {
PorterDuff.Mode.SRC, PorterDuff.Mode.DST, PorterDuff.Mode.SRC_OVER,
@@ -53,25 +52,23 @@
};
static {
- int index;
- for (index = 0; index < gTriPts.length; index++) {
- gLinePts[index] = gTriPts[index];
- }
+ System.arraycopy(TRIANGLE_POINTS, 0, LINES, 0, TRIANGLE_POINTS.length);
+ int index = TRIANGLE_POINTS.length;
float val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = 150;
- gLinePts[index + 1] = val;
- gLinePts[index + 2] = 300;
- gLinePts[index + 3] = val;
+ LINES[index + 0] = 40;
+ LINES[index + 1] = val;
+ LINES[index + 2] = 80;
+ LINES[index + 3] = val;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
val = 0;
for (int i = 0; i < NUM_PARALLEL_LINES; i++) {
- gLinePts[index + 0] = val;
- gLinePts[index + 1] = 150;
- gLinePts[index + 2] = val;
- gLinePts[index + 3] = 300;
+ LINES[index + 0] = val;
+ LINES[index + 1] = 40;
+ LINES[index + 2] = val;
+ LINES[index + 3] = 80;
index += 4;
val += 8 + (2.0f / NUM_PARALLEL_LINES);
}
@@ -81,7 +78,7 @@
// paint object, like anti-aliasing or drawing. Within those LinkedHashMaps are the various
// options for that specific topic, which contains a displaymodifier which will affect the
// given canvas and paint objects.
- public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> sMaps =
+ public static final LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>> MAPS =
new LinkedHashMap<String, LinkedHashMap<String,DisplayModifier>>() {
{
put("aa", new LinkedHashMap<String, DisplayModifier>() {
@@ -225,7 +222,7 @@
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
canvas.rotate(90);
- canvas.translate(0, -200);
+ canvas.translate(0, -100);
}
});
put("scale2x2", new DisplayModifier() {
@@ -347,13 +344,13 @@
put("roundRect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRoundRect(gRect, 20, 20, paint);
+ canvas.drawRoundRect(RECT, 20, 20, paint);
}
});
put("rect", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawRect(gRect, paint);
+ canvas.drawRect(RECT, paint);
}
});
put("circle", new DisplayModifier() {
@@ -365,36 +362,32 @@
put("oval", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawOval(gRect, paint);
+ canvas.drawOval(RECT, paint);
}
});
put("lines", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawLines(gLinePts, paint);
+ canvas.drawLines(LINES, paint);
}
});
- /* drawPoints does not work with zero stroke width,
- * but it isn't a regression
- * TODO: fix hardware canvas so that drawPoints works
put("plusPoints", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawPoints(gPts, paint);
+ canvas.drawPoints(POINTS, paint);
}
});
- */
put("text", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
+ paint.setTextSize(20);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
});
put("shadowtext", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- paint.setTextSize(36);
+ paint.setTextSize(20);
paint.setShadowLayer(3.0f, 0.0f, 3.0f, 0xffff00ff);
canvas.drawText("TEXTTEST", 0, 50, paint);
}
@@ -410,13 +403,13 @@
put("arc", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, false, paint);
+ canvas.drawArc(RECT, 260, 285, false, paint);
}
});
put("arcFromCenter", new DisplayModifier() {
@Override
public void modifyDrawing(Paint paint, Canvas canvas) {
- canvas.drawArc(gRect, 260, 285, true, paint);
+ canvas.drawArc(RECT, 260, 285, true, paint);
}
});
}
@@ -454,9 +447,9 @@
// Create a Display Map of the valid indices
mDisplayMap = new LinkedHashMap<String, LinkedHashMap<String, DisplayModifier>>();
int index = 0;
- for (String key : DisplayModifier.sMaps.keySet()) {
+ for (String key : DisplayModifier.MAPS.keySet()) {
if (validIndex(index)) {
- mDisplayMap.put(key, DisplayModifier.sMaps.get(key));
+ mDisplayMap.put(key, DisplayModifier.MAPS.get(key));
}
index++;
}
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
index 166b6ff..ce4a3be 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DrawActivity.java
@@ -18,10 +18,12 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.content.res.Configuration;
+import android.graphics.Point;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
+import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.webkit.WebView;
@@ -31,26 +33,28 @@
* A generic activity that uses a view specified by the user.
*/
public class DrawActivity extends Activity {
- private final static long TIME_OUT = 10000;
- private final Object mLock = new Object();
+ private final static long TIME_OUT_MS = 10000;
+ private final Point mLock = new Point();
public static final int MIN_NUMBER_OF_DRAWS = 20;
private Handler mHandler;
private View mView;
- private boolean mOnWatch;
+ private boolean mOnTv;
public void onCreate(Bundle bundle){
super.onCreate(bundle);
+ getWindow().getDecorView().setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
mHandler = new RenderSpecHandler();
int uiMode = getResources().getConfiguration().uiMode;
- mOnWatch = (uiMode & Configuration.UI_MODE_TYPE_WATCH) == Configuration.UI_MODE_TYPE_WATCH;
+ mOnTv = (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
}
- public boolean getOnWatch() {
- return mOnWatch;
+ public boolean getOnTv() {
+ return mOnTv;
}
- public void enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
+ public Point enqueueRenderSpecAndWait(int layoutId, CanvasClient canvasClient, String webViewUrl,
@Nullable ViewInitializer viewInitializer, boolean useHardware) {
((RenderSpecHandler) mHandler).setViewInitializer(viewInitializer);
int arg2 = (useHardware ? View.LAYER_TYPE_NONE : View.LAYER_TYPE_SOFTWARE);
@@ -62,13 +66,16 @@
mHandler.obtainMessage(RenderSpecHandler.LAYOUT_MSG, layoutId, arg2).sendToTarget();
}
+ Point point = new Point();
synchronized (mLock) {
try {
- mLock.wait(TIME_OUT);
+ mLock.wait(TIME_OUT_MS);
+ point.set(mLock.x, mLock.y);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
+ return point;
}
private class RenderSpecHandler extends Handler {
@@ -83,39 +90,40 @@
}
public void handleMessage(Message message) {
- int webViewBuffer = 0;
+ int drawCountDelay = 0;
+ setContentView(R.layout.test_container);
+ ViewStub stub = (ViewStub) findViewById(R.id.test_content_stub);
switch (message.what) {
case LAYOUT_MSG: {
- setContentView(message.arg1);
- mView = findViewById(R.id.test_root);
- if (mView == null) {
- throw new IllegalStateException("test_root failed to inflate");
- }
+ stub.setLayoutResource(message.arg1);
+ mView = stub.inflate();
} break;
case CANVAS_MSG: {
- mView = new CanvasClientView(getApplicationContext(),
- (CanvasClient) (message.obj), ActivityTestBase.TEST_WIDTH,
- ActivityTestBase.TEST_HEIGHT);
- setContentView(mView);
+ stub.setLayoutResource(R.layout.test_content_canvasclientview);
+ mView = stub.inflate();
+ ((CanvasClientView) mView).setCanvasClient((CanvasClient) (message.obj));
} break;
case WEB_VIEW_MSG: {
- mView = new WebView(getApplicationContext());
+ stub.setLayoutResource(R.layout.test_content_webview);
+ mView = stub.inflate();
((WebView) mView).loadUrl((String) message.obj);
((WebView) mView).setInitialScale(100);
- setContentView(mView);
- webViewBuffer = 10;
+ drawCountDelay = 10;
} break;
}
+ if (mView == null) {
+ throw new IllegalStateException("failed to inflate test content");
+ }
+
if (mViewInitializer != null) {
mViewInitializer.intializeView(mView);
}
-
mView.setLayerType(message.arg2, null);
- DrawCounterListener onDrawListener = new DrawCounterListener(webViewBuffer);
+ DrawCounterListener onDrawListener = new DrawCounterListener(drawCountDelay);
mView.getViewTreeObserver().addOnPreDrawListener(onDrawListener);
@@ -133,11 +141,13 @@
@Override
public boolean onPreDraw() {
+
mCurrentDraws++;
if (mCurrentDraws < MIN_NUMBER_OF_DRAWS + mExtraDraws) {
mView.postInvalidate();
} else {
synchronized (mLock) {
+ mLock.set(mView.getLeft(), mView.getTop());
mLock.notify();
}
mView.getViewTreeObserver().removeOnPreDrawListener(this);
diff --git a/tests/tests/util/src/android/util/cts/EventLogTest.java b/tests/tests/util/src/android/util/cts/EventLogTest.java
index 12df64f..bbff3bc 100644
--- a/tests/tests/util/src/android/util/cts/EventLogTest.java
+++ b/tests/tests/util/src/android/util/cts/EventLogTest.java
@@ -69,8 +69,6 @@
EventLog.writeEvent(ANSWER_TAG, 12345L, longString.toString());
EventLog.writeEvent(ANSWER_TAG, longString.toString(), longString.toString());
EventLog.writeEvent(ANSWER_TAG, longArray);
- // Give the message some time to show up in the log
- Thread.sleep(10);
List<Event> events = getEventsAfterMarker(markerData, ANSWER_TAG);
assertEquals(6, events.size());
@@ -156,8 +154,11 @@
}
/** Return elements after and the event that has the marker data and matching tag. */
- private List<Event> getEventsAfterMarker(Object marker, int... tags) throws IOException {
+ private List<Event> getEventsAfterMarker(Object marker, int... tags)
+ throws IOException, InterruptedException {
List<Event> events = new ArrayList<Event>();
+ // Give the message some time to show up in the log
+ Thread.sleep(20);
EventLog.readEvents(tags, events);
for (Iterator<Event> itr = events.iterator(); itr.hasNext(); ) {
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index 6806d29..a36e6fd 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -84,7 +84,7 @@
</activity>
<activity android:name="android.view.animation.cts.AnimationTestCtsActivity"
- android:label="AnimationTestCtsActivity">
+ android:label="AnimationTestCtsActivity" android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
diff --git a/tests/tests/view/res/anim-land/changing_reset_state_anim.xml b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
new file mode 100644
index 0000000..0b3279a
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_state_list_animator.xml b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
new file mode 100644
index 0000000..4c1c41e
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="200dp" android:valueType="floatType"/>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_test_animator.xml b/tests/tests/view/res/anim-land/changing_test_animator.xml
new file mode 100644
index 0000000..d5c83cd
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="x" android:duration="100"
+ android:interpolator="@android:anim/bounce_interpolator"
+ android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_reset_state_anim.xml b/tests/tests/view/res/anim/changing_reset_state_anim.xml
new file mode 100644
index 0000000..dd2b233
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_state_list_animator.xml b/tests/tests/view/res/anim/changing_state_list_animator.xml
new file mode 100644
index 0000000..6bcc0d9
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_test_animator.xml b/tests/tests/view/res/anim/changing_test_animator.xml
new file mode 100644
index 0000000..739a71b
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="x" android:duration="100"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/reset_state_anim.xml b/tests/tests/view/res/anim/reset_state_anim.xml
new file mode 100644
index 0000000..4bbbe62
--- /dev/null
+++ b/tests/tests/view/res/anim/reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_animator.xml b/tests/tests/view/res/anim/test_animator.xml
new file mode 100644
index 0000000..94e9ec8
--- /dev/null
+++ b/tests/tests/view/res/anim/test_animator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="@dimen/test_animator_target_x" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="@dimen/test_animator_target_y" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator.xml b/tests/tests/view/res/anim/test_state_list_animator.xml
new file mode 100644
index 0000000..b6a4822
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator.xml
@@ -0,0 +1,33 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <set>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+ </set>
+ </item>
+ <item android:state_enabled="true" android:state_pressed="false">
+ <set>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ </set>
+ </item>
+ <!-- base state-->
+ <item android:animation="@anim/reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator_2.xml b/tests/tests/view/res/anim/test_state_list_animator_2.xml
new file mode 100644
index 0000000..6aeb41f
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator_2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item android:animation="@anim/changing_reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/values-land/dimens.xml b/tests/tests/view/res/values-land/dimens.xml
new file mode 100644
index 0000000..17b5395
--- /dev/null
+++ b/tests/tests/view/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.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.
+-->
+<resources>
+ <dimen name="test_animator_target_y">30dp</dimen>
+ <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced
+ in the XML on purpose-->
+ <dimen name="reset_state_value">100dp</dimen>
+ <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+ <dimen name="changing_state_list_anim_target_x_value">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/res/values/dimens.xml b/tests/tests/view/res/values/dimens.xml
new file mode 100644
index 0000000..16e5084
--- /dev/null
+++ b/tests/tests/view/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.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.
+-->
+<resources>
+ <dimen name="test_animator_target_x">10dp</dimen>
+ <dimen name="test_animator_target_y">20dp</dimen>
+ <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced in the XML on purpose-->
+ <dimen name="reset_state_value">50dp</dimen>
+ <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+ <dimen name="changing_state_list_anim_target_x_value">100dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java b/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
index 48692f1..1ef9e48 100644
--- a/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
+++ b/tests/tests/view/src/android/view/animation/cts/AnimationTestCtsActivity.java
@@ -20,11 +20,41 @@
import android.app.Activity;
import android.os.Bundle;
+import android.util.Log;
+
+import java.util.concurrent.TimeUnit;
public class AnimationTestCtsActivity extends Activity {
+ final static long VISIBLE_TIMEOUT = TimeUnit.SECONDS.toNanos(3);
+ private boolean mIsVisible;
+
+ public boolean isVisible() {
+ return mIsVisible;
+ }
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.anim_layout);
}
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ mIsVisible = true;
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ mIsVisible = false;
+ }
+
+ public boolean waitUntilVisible() throws InterruptedException {
+ long start = System.nanoTime();
+ while (!mIsVisible && (System.nanoTime() - start) < VISIBLE_TIMEOUT) {
+ Thread.sleep(100);
+ }
+ return mIsVisible;
+ }
}
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
new file mode 100644
index 0000000..cc8ada0
--- /dev/null
+++ b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
@@ -0,0 +1,299 @@
+/*
+* 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.view.animation.cts;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.StateListAnimator;
+import android.app.Instrumentation;
+import android.app.UiAutomation;
+import android.content.Context;
+import android.util.Log;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.Display;
+import android.view.Surface;
+import android.view.View;
+import android.view.WindowManager;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.view.R;
+
+public class AnimatorInflaterTest
+ extends ActivityInstrumentationTestCase2<AnimationTestCtsActivity> {
+
+ private static final String TAG = "AnimatorInflaterTest";
+ Set<Integer> identityHashes = new HashSet<Integer>();
+
+ public AnimatorInflaterTest() {
+ super("com.android.cts.view", AnimationTestCtsActivity.class);
+ }
+
+ private void assertUnique(Object object) {
+ assertUnique(object, "");
+ }
+
+ private void assertUnique(Object object, String msg) {
+ final int code = System.identityHashCode(object);
+ assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+ }
+
+ public void testLoadAnimatorWithDifferentInterpolators() throws Throwable {
+ Animator anim1 = AnimatorInflater
+ .loadAnimator(getActivity(), R.anim.changing_test_animator);
+ if (!rotate()) {
+ return;//cancel test
+ }
+ Animator anim2 = AnimatorInflater
+ .loadAnimator(getActivity(), R.anim.changing_test_animator);
+ assertNotSame(anim1, anim2);
+ assertNotSame("interpolater is orientation dependent, should change",
+ anim1.getInterpolator(), anim2.getInterpolator());
+ }
+
+ /**
+ * Tests animators with dimension references.
+ */
+ public void testLoadAnimator() throws Throwable {
+ // to identify objects
+ Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ assertNotSame("a different animation should be returned", anim1, anim2);
+ assertSame("interpolator should be shallow cloned", anim1.getInterpolator(),
+ anim2.getInterpolator());
+ for (int i = 0; i < 2; i++) {
+ float targetX = getActivity().getResources()
+ .getDimension(R.dimen.test_animator_target_x);
+ // y value changes in landscape orientation
+ float targetY = getActivity().getResources()
+ .getDimension(R.dimen.test_animator_target_y);
+ for (Animator anim : new Animator[]{anim1, anim2}) {
+ assertTrue(anim instanceof AnimatorSet);
+ assertUnique(anim);
+ AnimatorSet set = (AnimatorSet) anim;
+ assertEquals("should have 3 sub animations", 3, set.getChildAnimations().size());
+ for (Animator subAnim : set.getChildAnimations()) {
+ assertUnique(subAnim);
+ assertTrue(subAnim instanceof ObjectAnimator);
+ }
+ final ObjectAnimator child1 = (ObjectAnimator) set.getChildAnimations().get(0);
+ final ObjectAnimator child2 = (ObjectAnimator) set.getChildAnimations().get(1);
+ final DummyObject dummyObject = new DummyObject();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (ObjectAnimator animator : new ObjectAnimator[]{child1, child2}) {
+ animator.setTarget(dummyObject);
+ animator.setupStartValues();
+ animator.start();
+ animator.end();
+ }
+ }
+ });
+ assertEquals(targetX, dummyObject.x);
+ assertEquals(targetY, dummyObject.y);
+ }
+ if (i == 0) {
+ if (!rotate()) {
+ return;//cancel test
+ }
+ }
+ anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+
+ }
+ }
+
+ private boolean rotate() throws Throwable {
+ WindowManager mWindowManager = (WindowManager) getActivity()
+ .getSystemService(Context.WINDOW_SERVICE);
+ Display display = mWindowManager.getDefaultDisplay();
+
+ Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+ getActivity().getClass().getName(), null, false);
+ getInstrumentation().addMonitor(monitor);
+ int nextRotation = 0;
+ switch (display.getRotation()) {
+ case Surface.ROTATION_0:
+ case Surface.ROTATION_180:
+ nextRotation = UiAutomation.ROTATION_FREEZE_90;
+ break;
+ case Surface.ROTATION_90:
+ case Surface.ROTATION_270:
+ nextRotation = UiAutomation.ROTATION_FREEZE_0;
+ break;
+ default:
+ Log.e(TAG, "Cannot get rotation, test is canceled");
+ return false;
+ }
+ boolean rotated = getInstrumentation().getUiAutomation().setRotation(nextRotation);
+ Thread.sleep(500);
+ if (!rotated) {
+ Log.e(TAG, "Rotation failed, test is canceled");
+ }
+ getInstrumentation().waitForIdleSync();
+ if (!getActivity().waitUntilVisible()) {
+ Log.e(TAG, "Activity failed to complete rotation, canceling test");
+ return false;
+ }
+ if (getActivity().getWindowManager().getDefaultDisplay().getRotation() != nextRotation) {
+ Log.e(TAG, "New activity orientation does not match. Canceling test");
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Simple state list animator test that checks for cloning
+ */
+ public void testLoadStateListAnimator() {
+ StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator);
+ StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator);
+ assertUnique(sla1);
+ assertUnique(sla2);
+ }
+
+ /**
+ * Tests a state list animator which has an @anim reference that has different xmls per
+ * orientation
+ */
+ public void testLoadStateListAnimatorWithChangingResetState() throws Throwable {
+ loadStateListAnimatorWithChangingResetStateTest();
+ if (!rotate()) {
+ return;//cancel test
+ }
+
+ loadStateListAnimatorWithChangingResetStateTest();
+ }
+
+ private void loadStateListAnimatorWithChangingResetStateTest() throws Throwable {
+ final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator_2);
+ final View testView = getTestView();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ testView.setStateListAnimator(sla);
+ testView.jumpDrawablesToCurrentState();
+ }
+ });
+ float resetValue = getActivity().getResources().getDimension(R.dimen.reset_state_value);
+ getInstrumentation().waitForIdleSync();
+ assertEquals(resetValue, testView.getX());
+ assertEquals(resetValue, testView.getY());
+ assertEquals(resetValue, testView.getZ());
+ }
+
+ /**
+ * Tests a state list animator which has different xml descriptions per orientation.
+ */
+ public void testLoadChangingStateListAnimator() throws Throwable {
+ loadChangingStateListAnimatorTest();
+ if (!rotate()) {
+ return;//cancel test
+ }
+ loadChangingStateListAnimatorTest();
+ }
+
+ private void loadChangingStateListAnimatorTest() throws Throwable {
+ final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.changing_state_list_animator);
+ final View testView = getTestView();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ testView.setStateListAnimator(sla);
+ testView.jumpDrawablesToCurrentState();
+ }
+ });
+ float targetValue = getActivity().getResources()
+ .getDimension(R.dimen.changing_state_list_anim_target_x_value);
+ getInstrumentation().waitForIdleSync();
+ assertEquals(targetValue, testView.getX());
+ }
+
+ /**
+ * Tests that makes sure that reloaded animator is not affected by previous changes
+ */
+ public void testReloadedAnimatorIsNotModified() throws Throwable {
+ final Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ final CountDownLatch mStarted = new CountDownLatch(1);
+ final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mStarted.countDown();
+ }
+ };
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim1.setTarget(getTestView());
+ anim1.addListener(listener);
+ anim1.start();
+ }
+ });
+ Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ assertTrue(anim1.isStarted());
+ assertFalse(anim2.isStarted());
+ assertFalse("anim2 should not include the listener",
+ anim2.getListeners() != null && anim2.getListeners().contains(listener));
+ assertTrue("animator should start", mStarted.await(10, TimeUnit.SECONDS));
+ assertFalse(anim2.isRunning());
+
+ }
+
+ public View getTestView() {
+ return getActivity().findViewById(R.id.anim_window);
+ }
+
+ class DummyObject {
+
+ float x;
+ float y;
+
+ public float getX() {
+ return x;
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+ }
+}
+
diff --git a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
index 40d1d3d..6007730 100644
--- a/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
+++ b/tests/tests/view/src/android/view/cts/MenuInflaterTest.java
@@ -19,7 +19,6 @@
import com.android.cts.view.R;
import com.android.internal.view.menu.MenuBuilder;
-
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
@@ -28,6 +27,7 @@
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.SubMenu;
@@ -48,17 +48,22 @@
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
- mMenuInflater = mActivity.getMenuInflater();
}
+ @UiThreadTest
public void testConstructor() {
new MenuInflater(mActivity);
}
+ @UiThreadTest
public void testInflate() {
Menu menu = new MenuBuilder(mActivity);
assertEquals(0, menu.size());
+ if (mMenuInflater == null) {
+ mMenuInflater = mActivity.getMenuInflater();
+ }
+
mMenuInflater.inflate(com.android.cts.view.R.menu.browser, menu);
assertNotNull(menu);
assertEquals(1, menu.size());
@@ -77,7 +82,12 @@
}
// Check wheher the objects are created correctly from xml files
+ @UiThreadTest
public void testInflateFromXml(){
+ if (mMenuInflater == null) {
+ mMenuInflater = mActivity.getMenuInflater();
+ }
+
// the visibility and shortcut
Menu menu = new MenuBuilder(mActivity);
mMenuInflater.inflate(R.menu.visible_shortcut, menu);
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index e8da40e..ffbec1e 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -22,6 +22,7 @@
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
+import android.os.Bundle;
import com.android.cts.view.R;
import com.android.internal.view.menu.ContextMenuBuilder;
import com.google.android.collect.Lists;
@@ -3667,6 +3668,11 @@
public boolean onNestedPreFling(View target, float velocityX, float velocityY) {
return false;
}
+
+ @Override
+ public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) {
+ return false;
+ }
}
private final class OnCreateContextMenuListenerImpl implements OnCreateContextMenuListener {
diff --git a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
index fb44334..e8f0cab 100644
--- a/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/URLUtilTest.java
@@ -144,7 +144,7 @@
public void testGuessFileName() {
String url = "ftp://example.url/test";
- assertEquals("test.jpeg", URLUtil.guessFileName(url, null, "image/jpeg"));
+ assertEquals("test.jpg", URLUtil.guessFileName(url, null, "image/jpeg"));
assertEquals("test.bin", URLUtil.guessFileName(url, null, "application/octet-stream"));
}
diff --git a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
index 33a9cee..e9ff48f 100644
--- a/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
+++ b/tests/tests/webkit/src/android/webkit/cts/WebSettingsTest.java
@@ -95,7 +95,7 @@
* brackets are optional):
* <p/>
* Mozilla/5.0 (Linux;[ U;] Android <version>;[ <language>-<country>;]
- * [<devicemodel>;] Build/<buildID>) AppleWebKit/<major>.<minor> (KHTML, like Gecko)
+ * [<devicemodel>;] Build/<buildID>[; wv]) AppleWebKit/<major>.<minor> (KHTML, like Gecko)
* Version/<major>.<minor> Chrome/<major>.<minor>.<branch>.<build>[ Mobile]
* Safari/<major>.<minor>
*/
@@ -107,7 +107,8 @@
Log.i(LOG_TAG, String.format("Checking user agent string %s", actualUserAgentString));
final String patternString =
"Mozilla/5\\.0 \\(Linux;( U;)? Android ([^;]+);( (\\w+)-(\\w+);)?" +
- "\\s?(.*)\\sBuild/(.+)\\) AppleWebKit/(\\d+)\\.(\\d+) \\(KHTML, like Gecko\\) " +
+ "\\s?(.*)\\sBuild/(.+?)(; wv)?\\) AppleWebKit/(\\d+)\\.(\\d+) " +
+ "\\(KHTML, like Gecko\\) " +
"Version/\\d+\\.\\d+ Chrome/\\d+\\.\\d+\\.\\d+\\.\\d+( Mobile)? " +
"Safari/(\\d+)\\.(\\d+)";
// Groups used:
@@ -118,11 +119,12 @@
// 5 - language
// 6 - device model (optional)
// 7 - build ID
- // 8 - AppleWebKit major version number
- // 9 - AppleWebKit minor version number
- // 10 - " Mobile" string (optional)
- // 11 - Safari major version number
- // 12 - Safari minor version number
+ // 8 - WebView identifier "; wv" (optional)
+ // 9 - AppleWebKit major version number
+ // 10 - AppleWebKit minor version number
+ // 11 - " Mobile" string (optional)
+ // 12 - Safari major version number
+ // 13 - Safari minor version number
Log.i(LOG_TAG, String.format("Trying to match pattern %s", patternString));
final Pattern userAgentExpr = Pattern.compile(patternString);
Matcher patternMatcher = userAgentExpr.matcher(actualUserAgentString);
diff --git a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
index 41018a9..9589fec 100644
--- a/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
+++ b/tests/tests/widget/src/android/widget/cts/MockPopupWindowCtsActivity.java
@@ -30,28 +30,10 @@
* Stub activity for testing {@link PopupWindow}
*/
public class MockPopupWindowCtsActivity extends Activity {
- private boolean isFirstRun = true;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Window window = getWindow();
- final View decor = window.getDecorView();
- decor.setOnApplyWindowInsetsListener(new OnApplyWindowInsetsListener() {
- @Override
- public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
- if (isFirstRun) {
- if (insets.isRound()) {
- decor.setPadding(decor.getPaddingLeft(), decor.getPaddingTop(),
- decor.getPaddingRight(),
- decor.getPaddingBottom() + insets.getSystemWindowInsetBottom());
- }
- isFirstRun = false;
- setContentView(R.layout.popupwindow);
- }
- return insets.consumeSystemWindowInsets();
- }
- });
+ setContentView(R.layout.popupwindow);
}
}
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index e1742c8..ae12f9c 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -273,18 +273,21 @@
}
public void testShowAtLocation() {
- int[] viewInWindowXY = new int[2];
- int[] viewOnScreenXY = new int[2];
+ int[] popupContentViewInWindowXY = new int[2];
+ int[] popupContentViewOnScreenXY = new int[2];
mPopupWindow = createPopupWindow(createPopupContent());
+ // Do not attach within the decor; we will be measuring location
+ // with regard to screen coordinates.
+ mPopupWindow.setAttachedInDecor(false);
final View upperAnchor = mActivity.findViewById(R.id.anchor_upper);
final int xOff = 10;
final int yOff = 21;
assertFalse(mPopupWindow.isShowing());
- mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
- assertEquals(0, viewInWindowXY[0]);
- assertEquals(0, viewInWindowXY[1]);
+ mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+ assertEquals(0, popupContentViewInWindowXY[0]);
+ assertEquals(0, popupContentViewInWindowXY[1]);
mInstrumentation.runOnMainSync(new Runnable() {
public void run() {
@@ -294,12 +297,12 @@
mInstrumentation.waitForIdleSync();
assertTrue(mPopupWindow.isShowing());
- mPopupWindow.getContentView().getLocationInWindow(viewInWindowXY);
- mPopupWindow.getContentView().getLocationOnScreen(viewOnScreenXY);
- assertTrue(viewInWindowXY[0] >= 0);
- assertTrue(viewInWindowXY[1] >= 0);
- assertEquals(viewInWindowXY[0] + xOff, viewOnScreenXY[0]);
- assertEquals(viewInWindowXY[1] + yOff, viewOnScreenXY[1]);
+ mPopupWindow.getContentView().getLocationInWindow(popupContentViewInWindowXY);
+ mPopupWindow.getContentView().getLocationOnScreen(popupContentViewOnScreenXY);
+ assertTrue(popupContentViewInWindowXY[0] >= 0);
+ assertTrue(popupContentViewInWindowXY[1] >= 0);
+ assertEquals(popupContentViewInWindowXY[0] + xOff, popupContentViewOnScreenXY[0]);
+ assertEquals(popupContentViewInWindowXY[1] + yOff, popupContentViewOnScreenXY[1]);
dismissPopup();
}
@@ -355,7 +358,11 @@
assertTrue(maxAvailableHeightWithOffset <= avaliable);
anchorView = mActivity.findViewById(R.id.anchor_lower);
- avaliable = getDisplay().getHeight() - anchorView.getHeight();
+ // On some devices the view might actually have larger size than the physical display
+ // due to chin and content will be laid out as if outside of the display. We need to use
+ // larger from the display height and the main view height.
+ avaliable = Math.max(getDisplay().getHeight(),
+ mActivity.findViewById(android.R.id.content).getHeight()) - anchorView.getHeight();
maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
assertTrue(maxAvailableHeight > 0);
assertTrue(maxAvailableHeight <= avaliable);
@@ -453,6 +460,9 @@
mInstrumentation.runOnMainSync(new Runnable() {
public void run() {
mPopupWindow = createPopupWindow(createPopupContent());
+ // Do not attach within the decor; we will be measuring location
+ // with regard to screen coordinates.
+ mPopupWindow.setAttachedInDecor(false);
}
});
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index c8211f6..5259736 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -404,8 +404,8 @@
// exceptional
try {
tableLayout.addView(null);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
}
@@ -445,8 +445,8 @@
try {
tableLayout.addView(null, -1);
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
}
@@ -477,8 +477,8 @@
try {
tableLayout.addView(null, new TableLayout.LayoutParams(200, 300));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
try {
@@ -540,8 +540,8 @@
try {
tableLayout.addView(null, -1, new TableLayout.LayoutParams(200, 300));
- fail("Should throw NullPointerException");
- } catch (NullPointerException e) {
+ fail("Should throw IllegalArgumentException");
+ } catch (IllegalArgumentException e) {
}
try {
diff --git a/tools/selinux/SELinuxNeverallowTestFrame.py b/tools/selinux/SELinuxNeverallowTestFrame.py
index 932014a..0bf766d 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/selinux/src/SELinux_CTS.py b/tools/selinux/src/SELinux_CTS.py
deleted file mode 100644
index ec12be0..0000000
--- a/tools/selinux/src/SELinux_CTS.py
+++ /dev/null
@@ -1,542 +0,0 @@
-import pdb
-import re
-from xml.etree.ElementTree import Element, SubElement, tostring
-
-#define equivalents
-TYPE = 0
-ATTRIBUTE = 1
-TYPEATTRIBUTE = 2
-CLASS = 3
-COMMON = 4
-ALLOW_RULE = 5
-NEVERALLOW_RULE = 6
-OTHER = 7
-
-#define helper methods
-# advance_past_whitespace(): helper function to skip whitespace at current
-# position in file.
-# returns: the non-whitespace character at the file's new position
-#TODO: should I deal with comments here as well?
-def advance_past_whitespace(file_obj):
- c = file_obj.read(1)
- while c.isspace():
- c = file_obj.read(1)
- file_obj.seek(-1, 1)
- return c
-
-# advance_until_whitespace(): helper function to grab the string represented
-# by the current position in file until next whitespace.
-# returns: string until next whitespace. overlooks comments.
-def advance_until_whitespace(file_obj):
- ret_string = ""
- c = file_obj.read(1)
- #TODO: make a better way to deal with ':' and ';'
- while not (c.isspace() or c == ':' or c == '' or c == ';'):
- #don't count comments
- if c == '#':
- file_obj.readline()
- return ret_string
- else:
- ret_string+=c
- c = file_obj.read(1)
- if not c == ':':
- file_obj.seek(-1, 1)
- return ret_string
-
-# expand_avc_rule - takes a processed avc rule and converts it into a list of
-# 4-tuples for use in an access check of form:
- # (source_type, target_type, class, permission)
-def expand_avc_rule(policy, avc_rule):
- ret_list = [ ]
-
- #expand source_types
- source_types = avc_rule['source_types']['set']
- source_types = policy.expand_types(source_types)
- if(avc_rule['source_types']['flags']['complement']):
- #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
- source_types = policy.types - source_types #complement these types
- if len(source_types) == 0:
- print "ERROR: source_types empty after expansion"
- print "Before: "
- print avc_rule['source_types']['set']
- return
-
- #expand target_types
- target_types = avc_rule['target_types']['set']
- target_types = policy.expand_types(target_types)
- if(avc_rule['target_types']['flags']['complement']):
- #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
- target_types = policy.types - target_types #complement these types
- if len(target_types) == 0:
- print "ERROR: target_types empty after expansion"
- print "Before: "
- print avc_rule['target_types']['set']
- return
-
- # get classes
- rule_classes = avc_rule['classes']['set']
- if '' in rule_classes:
- print "FOUND EMPTY STRING IN CLASSES"
- print "Total sets:"
- print avc_rule['source_types']['set']
- print avc_rule['target_types']['set']
- print rule_classes
- print avc_rule['permissions']['set']
-
- if len(rule_classes) == 0:
- print "ERROR: empy set of object classes in avc rule"
- return
-
- # get permissions
- permissions = avc_rule['permissions']['set']
- if len(permissions) == 0:
- print "ERROR: empy set of permissions in avc rule\n"
- return
-
- #create the list with collosal nesting, n^4 baby!
- for s in source_types:
- for t in target_types:
- for c in rule_classes:
- if c == '':
- continue
- #expand permissions on a per-class basis
- exp_permissions = policy.expand_permissions(c, permissions)
- if(avc_rule['permissions']['flags']['complement']):
- exp_permissions = policy.classes[c] - exp_permissions
- if len(exp_permissions) == 0:
- print "ERROR: permissions empty after expansion\n"
- print "Before: "
- print avc_rule['permissions']['set']
- return
- for p in exp_permissions:
- source = s
- if t == 'self':
- target = s
- else:
- target = t
- obj_class = c
- permission = p
- ret_list.append((source, target, obj_class, permission))
- return ret_list
-
-# expand_avc_rule - takes a processed avc rule and converts it into an xml
-# representation with the information needed in a checkSELinuxAccess() call.
-# (source_type, target_type, class, permission)
-def expand_avc_rule_to_xml(policy, avc_rule, rule_name, rule_type):
- rule_xml = Element('avc_rule')
- rule_xml.set('name', rule_name)
- rule_xml.set('type', rule_type)
-
- #expand source_types
- source_types = avc_rule['source_types']['set']
- source_types = policy.expand_types(source_types)
- if(avc_rule['source_types']['flags']['complement']):
- #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
- source_types = policy.types - source_types #complement these types
- if len(source_types) == 0:
- print "ERROR: source_types empty after expansion"
- print "Before: "
- print avc_rule['source_types']['set']
- return
- for s in source_types:
- elem = SubElement(rule_xml, 'type')
- elem.set('type', 'source')
- elem.text = s
-
- #expand target_types
- target_types = avc_rule['target_types']['set']
- target_types = policy.expand_types(target_types)
- if(avc_rule['target_types']['flags']['complement']):
- #TODO: deal with negated 'self', not present in current policy.conf, though (I think)
- target_types = policy.types - target_types #complement these types
- if len(target_types) == 0:
- print "ERROR: target_types empty after expansion"
- print "Before: "
- print avc_rule['target_types']['set']
- return
- for t in target_types:
- elem = SubElement(rule_xml, 'type')
- elem.set('type', 'target')
- elem.text = t
-
- # get classes
- rule_classes = avc_rule['classes']['set']
-
- if len(rule_classes) == 0:
- print "ERROR: empy set of object classes in avc rule"
- return
-
- # get permissions
- permissions = avc_rule['permissions']['set']
- if len(permissions) == 0:
- print "ERROR: empy set of permissions in avc rule\n"
- return
-
- # permissions are class-dependent, so bundled together
- for c in rule_classes:
- if c == '':
- print "AH!!! empty class found!\n"
- continue
- c_elem = SubElement(rule_xml, 'obj_class')
- c_elem.set('name', c)
- #expand permissions on a per-class basis
- exp_permissions = policy.expand_permissions(c, permissions)
- if(avc_rule['permissions']['flags']['complement']):
- exp_permissions = policy.classes[c] - exp_permissions
- if len(exp_permissions) == 0:
- print "ERROR: permissions empty after expansion\n"
- print "Before: "
- print avc_rule['permissions']['set']
- return
-
- for p in exp_permissions:
- p_elem = SubElement(c_elem, 'permission')
- p_elem.text = p
-
- return rule_xml
-
-# expand_brackets - helper function which reads a file into a string until '{ }'s
-# are balanced. Brackets are removed from the string. This function is based
-# on the understanding that nested brackets in our policy.conf file occur only due
-# to macro expansion, and we just need to know how much is included in a given
-# policy sub-component.
-def expand_brackets(file_obj):
- ret_string = ""
- c = file_obj.read(1)
- if not c == '{':
- print "Invalid bracket expression: " + c + "\n"
- file_obj.seek(-1, 1)
- return ""
- else:
- bracket_count = 1
- while bracket_count > 0:
- c = file_obj.read(1)
- if c == '{':
- bracket_count+=1
- elif c == '}':
- bracket_count-=1
- elif c == '#':
- #get rid of comment and replace with whitespace
- file_obj.readline()
- ret_string+=' '
- else:
- ret_string+=c
- return ret_string
-
-# get_avc_rule_component - grabs the next component from an avc rule. Basically,
-# just reads the next word or bracketed set of words.
-# returns - a set of the word, or words with metadata
-def get_avc_rule_component(file_obj):
- ret_dict = { 'flags': {}, 'set': set() }
- c = advance_past_whitespace(file_obj)
- if c == '~':
- ret_dict['flags']['complement'] = True
- file_obj.read(1) #move to next char
- c = advance_past_whitespace(file_obj)
- else:
- ret_dict['flags']['complement'] = False
- if not c == '{':
- #TODO: change operations on file to operations on string?
- single_type = advance_until_whitespace(file_obj)
- ret_dict['set'].add(single_type)
- else:
- mult_types = expand_brackets(file_obj)
- mult_types = mult_types.split()
- for t in mult_types:
- ret_dict['set'].add(t)
- return ret_dict
-
-def get_line_type(line):
- if re.search(r'^type\s', line):
- return TYPE
- if re.search(r'^attribute\s', line):
- return ATTRIBUTE
- if re.search(r'^typeattribute\s', line):
- return TYPEATTRIBUTE
- if re.search(r'^class\s', line):
- return CLASS
- if re.search(r'^common\s', line):
- return COMMON
- if re.search(r'^allow\s', line):
- return ALLOW_RULE
- if re.search(r'^neverallow\s', line):
- return NEVERALLOW_RULE
- else:
- return OTHER
-
-def is_multi_line(line_type):
- if line_type == CLASS:
- return True
- elif line_type == COMMON:
- return True
- elif line_type == ALLOW_RULE:
- return True
- elif line_type == NEVERALLOW_RULE:
- return True
- else:
- return False
-
-
-#should only be called with file pointing to the 'i' in 'inherits' segment
-def process_inherits_segment(file_obj):
- inherit_keyword = file_obj.read(8)
- if not inherit_keyword == 'inherits':
- #TODO: handle error, invalid class statement
- print "ERROR: invalid inherits statement"
- return
- else:
- advance_past_whitespace(file_obj)
- ret_inherited_common = advance_until_whitespace(file_obj)
- return ret_inherited_common
-
-class SELinuxPolicy:
-
- def __init__(self):
- self.types = set()
- self.attributes = { }
- self.classes = { }
- self.common_classes = { }
- self.allow_rules = [ ]
- self.neverallow_rules = [ ]
-
- # create policy directly from policy file
- #@classmethod
- def from_file_name(self, policy_file_name):
- self.types = set()
- self.attributes = { }
- self.classes = { }
- self.common_classes = { }
- self.allow_rules = [ ]
- self.neverallow_rules = [ ]
- with open(policy_file_name, 'r') as policy_file:
- line = policy_file.readline()
- while line:
- line_type = get_line_type(line)
- if is_multi_line(line_type):
- self.parse_multi_line(line, line_type, policy_file)
- else:
- self.parse_single_line(line, line_type)
- line = policy_file.readline()
-
- # expand_permissions - generates the actual permission set based on the listed
- # permissions with wildcards and the given class on which they're based.
- def expand_permissions(self, obj_class, permission_set):
- ret_set = set()
- neg_set = set()
- for p in permission_set:
- if p[0] == '-':
- real_p = p[1:]
- if real_p in self.classes[obj_class]:
- neg_set.add(real_p)
- else:
- print "ERROR: invalid permission in avc rule " + real_t + "\n"
- return
- else:
- if p in self.classes[obj_class]:
- ret_set.add(p)
- elif p == '*': #pretty sure this can't be negated? eg -*
- ret_set |= self.classes[obj_class] #All of the permissions
- else:
- print "ERROR: invalid permission in avc rule " + p + "\n"
- return
- return ret_set - neg_set
-
- # expand_types - generates the actual type set based on the listed types,
- # attributes, wildcards and negation. self is left as-is, and is processed
- # specially when generating checkAccess() 4-tuples
- def expand_types(self, type_set):
- ret_set = set()
- neg_set = set()
- for t in type_set:
- if t[0] == '-':
- real_t = t[1:]
- if real_t in self.attributes:
- neg_set |= self.attributes[real_t]
- elif real_t in self.types:
- neg_set.add(real_t)
- elif real_t == 'self':
- ret_set |= real_t
- else:
- print "ERROR: invalid type in avc rule " + real_t + "\nTYPE SET:"
- print type_set
- return
- else:
- if t in self.attributes:
- ret_set |= self.attributes[t]
- elif t in self.types:
- ret_set.add(t)
- elif t == 'self':
- ret_set.add(t)
- elif t == '*': #pretty sure this can't be negated?
- ret_set |= self.types #All of the types
- else:
- print "ERROR: invalid type in avc rule " + t + "\nTYPE SET"
- print type_set
- return
- return ret_set - neg_set
-
- def parse_multi_line(self, line, line_type, file_obj):
- if line_type == CLASS:
- self.process_class_line(line, file_obj)
- elif line_type == COMMON:
- self.process_common_line(line, file_obj)
- elif line_type == ALLOW_RULE:
- self.process_avc_rule_line(line, file_obj)
- elif line_type == NEVERALLOW_RULE:
- self.process_avc_rule_line(line, file_obj)
- else:
- print "Error: This is not a multi-line input"
-
- def parse_single_line(self, line, line_type):
- if line_type == TYPE:
- self.process_type_line(line)
- elif line_type == ATTRIBUTE:
- self.process_attribute_line(line)
- elif line_type == TYPEATTRIBUTE:
- self.process_typeattribute_line(line)
- return
-
- def process_attribute_line(self, line):
- match = re.search(r'^attribute\s+(.+);', line)
- if match:
- declared_attribute = match.group(1)
- self.attributes[declared_attribute] = set()
- else:
- #TODO: handle error? (no state changed)
- return
-
- def process_class_line(self, line, file_obj):
- match = re.search(r'^class\s([^\s]+)\s(.*$)', line)
- if match:
- declared_class = match.group(1)
- #first class declaration has no perms
- if not declared_class in self.classes:
- self.classes[declared_class] = set()
- return
- else:
- #need to parse file from after class name until end of '{ }'s
- file_obj.seek(-(len(match.group(2)) + 1), 1)
- c = advance_past_whitespace(file_obj)
- if not (c == 'i' or c == '{'):
- print "ERROR: invalid class statement"
- return
- elif c == 'i':
- #add inherited permissions
- inherited = process_inherits_segment(file_obj)
- self.classes[declared_class] |= self.common_classes[inherited]
- c = advance_past_whitespace(file_obj)
- if c == '{':
- permissions = expand_brackets(file_obj)
- permissions = re.sub(r'#[^\n]*\n','\n' , permissions) #get rid of all comments
- permissions = permissions.split()
- for p in permissions:
- self.classes[declared_class].add(p)
-
- def process_common_line(self, line, file_obj):
- match = re.search(r'^common\s([^\s]+)(.*$)', line)
- if match:
- declared_common_class = match.group(1)
- #TODO: common classes should only be declared once...
- if not declared_common_class in self.common_classes:
- self.common_classes[declared_common_class] = set()
- #need to parse file from after common_class name until end of '{ }'s
- file_obj.seek(-(len(match.group(2)) + 1), 1)
- c = advance_past_whitespace(file_obj)
- if not c == '{':
- print "ERROR: invalid common statement"
- return
- permissions = expand_brackets(file_obj)
- permissions = permissions.split()
- for p in permissions:
- self.common_classes[declared_common_class].add(p)
- return
-
- def process_avc_rule_line(self, line, file_obj):
- match = re.search(r'^(never)?allow\s(.*$)', line)
- if match:
- if(match.group(1)):
- rule_type = 'neverallow'
- else:
- rule_type = 'allow'
- #need to parse file from after class name until end of '{ }'s
- file_obj.seek(-(len(match.group(2)) + 1), 1)
-
- #grab source type(s)
- source_types = get_avc_rule_component(file_obj)
- if len(source_types['set']) == 0:
- print "ERROR: no source types for avc rule at line: " + line
- return
-
- #grab target type(s)
- target_types = get_avc_rule_component(file_obj)
- if len(target_types['set']) == 0:
- print "ERROR: no target types for avc rule at line: " + line
- return
-
- #skip ':' potentially already handled by advance_until_whitespace
- c = advance_past_whitespace(file_obj)
- if c == ':':
- file_obj.read(1)
-
- #grab class(es)
- classes = get_avc_rule_component(file_obj)
- if len(classes['set']) == 0:
- print "ERROR: no classes for avc rule at line: " + line
- return
-
- #grab permission(s)
- permissions = get_avc_rule_component(file_obj)
- if len(permissions['set']) == 0:
- print "ERROR: no permissions for avc rule at line: " + line
- return
- rule_dict = {
- 'source_types': source_types,
- 'target_types': target_types,
- 'classes': classes,
- 'permissions': permissions }
-
- if rule_type == 'allow':
- self.allow_rules.append(rule_dict)
- elif rule_type == 'neverallow':
- self.neverallow_rules.append(rule_dict)
-
- def process_type_line(self, line):
- #TODO: add support for aliases (not yet in current policy.conf)
- match = re.search(r'^type\s([^,]+),?(.*);', line)
- if match:
- declared_type = match.group(1)
- self.types.add(declared_type)
- if match.group(2):
- declared_attributes = match.group(2)
- declared_attributes = declared_attributes.replace(" ", "") #remove whitespace
- declared_attributes = declared_attributes.split(',') #separate based on delimiter
- for a in declared_attributes:
- if not a in self.attributes:
- #TODO: hanlde error? attribute should already exist
- self.attributes[a] = set()
- self.attributes[a].add(declared_type)
- else:
- #TODO: handle error? (no state changed)
- return
-
- def process_typeattribute_line(self, line):
- match = re.search(r'^typeattribute\s([^\s]+)\s(.*);', line)
- if match:
- declared_type = match.group(1)
- if not declared_type in self.types:
- #TODO: handle error? type should already exist
- self.types.add(declared_type)
- if match.group(2):
- declared_attributes = match.group(2)
- declared_attributes = declared_attributes.replace(" ", "") #remove whitespace
- declared_attributes = declared_attributes.split(',') #separate based on delimiter
- for a in declared_attributes:
- if not a in self.attributes:
- #TODO: hanlde error? attribute should already exist
- self.attributes[a] = set()
- self.attributes[a].add(declared_type)
- else:
- return
- else:
- #TODO: handle error? (no state changed)
- return
diff --git a/tools/selinux/src/example_input_policy.conf b/tools/selinux/src/example_input_policy.conf
deleted file mode 100644
index aeef5f8..0000000
--- a/tools/selinux/src/example_input_policy.conf
+++ /dev/null
@@ -1,9850 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class security
-class process
-class system
-class capability
-
-# file-related classes
-class filesystem
-class file
-class dir
-class fd
-class lnk_file
-class chr_file
-class blk_file
-class sock_file
-class fifo_file
-
-# network-related classes
-class socket
-class tcp_socket
-class udp_socket
-class rawip_socket
-class node
-class netif
-class netlink_socket
-class packet_socket
-class key_socket
-class unix_stream_socket
-class unix_dgram_socket
-
-# sysv-ipc-related classes
-class sem
-class msg
-class msgq
-class shm
-class ipc
-
-#
-# userspace object manager classes
-#
-
-# passwd/chfn/chsh
-class passwd # userspace
-
-# SE-X Windows stuff (more classes below)
-class x_drawable # userspace
-class x_screen # userspace
-class x_gc # userspace
-class x_font # userspace
-class x_colormap # userspace
-class x_property # userspace
-class x_selection # userspace
-class x_cursor # userspace
-class x_client # userspace
-class x_device # userspace
-class x_server # userspace
-class x_extension # userspace
-
-# extended netlink sockets
-class netlink_route_socket
-class netlink_firewall_socket
-class netlink_tcpdiag_socket
-class netlink_nflog_socket
-class netlink_xfrm_socket
-class netlink_selinux_socket
-class netlink_audit_socket
-class netlink_ip6fw_socket
-class netlink_dnrt_socket
-
-class dbus # userspace
-class nscd # userspace
-
-# IPSec association
-class association
-
-# Updated Netlink class for KOBJECT_UEVENT family.
-class netlink_kobject_uevent_socket
-
-class appletalk_socket
-
-class packet
-
-# Kernel access key retention
-class key
-
-class context # userspace
-
-class dccp_socket
-
-class memprotect
-
-class db_database # userspace
-class db_table # userspace
-class db_procedure # userspace
-class db_column # userspace
-class db_tuple # userspace
-class db_blob # userspace
-
-# network peer labels
-class peer
-
-# Capabilities >= 32
-class capability2
-
-# More SE-X Windows stuff
-class x_resource # userspace
-class x_event # userspace
-class x_synthetic_event # userspace
-class x_application_data # userspace
-
-# kernel services that need to override task security, e.g. cachefiles
-class kernel_service
-
-class tun_socket
-
-# Still More SE-X Windows stuff
-class x_pointer # userspace
-class x_keyboard # userspace
-
-# More Database stuff
-class db_schema # userspace
-class db_view # userspace
-class db_sequence # userspace
-class db_language # userspace
-
-class binder
-class zygote
-
-# Property service
-class property_service # userspace
-
-# FLASK
-#line 1 "external/sepolicy/initial_sids"
-# FLASK
-
-#
-# Define initial security identifiers
-#
-
-sid kernel
-sid security
-sid unlabeled
-sid fs
-sid file
-sid file_labels
-sid init
-sid any_socket
-sid port
-sid netif
-sid netmsg
-sid node
-sid igmp_packet
-sid icmp_socket
-sid tcp_socket
-sid sysctl_modprobe
-sid sysctl
-sid sysctl_fs
-sid sysctl_kernel
-sid sysctl_net
-sid sysctl_net_unix
-sid sysctl_vm
-sid sysctl_dev
-sid kmod
-sid policy
-sid scmp_packet
-sid devnull
-
-# FLASK
-#line 1 "external/sepolicy/access_vectors"
-#
-# Define common prefixes for access vectors
-#
-# common common_name { permission_name ... }
-
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
- ioctl
- read
- write
- create
- getattr
- setattr
- lock
- relabelfrom
- relabelto
- append
- unlink
- link
- rename
- execute
- swapon
- quotaon
- mounton
-}
-
-
-#
-# Define a common prefix for socket access vectors.
-#
-
-common socket
-{
-# inherited from file
- ioctl
- read
- write
- create
- getattr
- setattr
- lock
- relabelfrom
- relabelto
- append
-# socket-specific
- bind
- connect
- listen
- accept
- getopt
- setopt
- shutdown
- recvfrom
- sendto
- recv_msg
- send_msg
- name_bind
-}
-
-#
-# Define a common prefix for ipc access vectors.
-#
-
-common ipc
-{
- create
- destroy
- getattr
- setattr
- read
- write
- associate
- unix_read
- unix_write
-}
-
-#
-# Define a common prefix for userspace database object access vectors.
-#
-
-common database
-{
- create
- drop
- getattr
- setattr
- relabelfrom
- relabelto
-}
-
-#
-# Define a common prefix for pointer and keyboard access vectors.
-#
-
-common x_device
-{
- getattr
- setattr
- use
- read
- write
- getfocus
- setfocus
- bell
- force_cursor
- freeze
- grab
- manage
- list_property
- get_property
- set_property
- add
- remove
- create
- destroy
-}
-
-#
-# Define the access vectors.
-#
-# class class_name [ inherits common_name ] { permission_name ... }
-
-
-#
-# Define the access vector interpretation for file-related objects.
-#
-
-class filesystem
-{
- mount
- remount
- unmount
- getattr
- relabelfrom
- relabelto
- transition
- associate
- quotamod
- quotaget
-}
-
-class dir
-inherits file
-{
- add_name
- remove_name
- reparent
- search
- rmdir
- open
- audit_access
- execmod
-}
-
-class file
-inherits file
-{
- execute_no_trans
- entrypoint
- execmod
- open
- audit_access
-}
-
-class lnk_file
-inherits file
-{
- open
- audit_access
- execmod
-}
-
-class chr_file
-inherits file
-{
- execute_no_trans
- entrypoint
- execmod
- open
- audit_access
-}
-
-class blk_file
-inherits file
-{
- open
- audit_access
- execmod
-}
-
-class sock_file
-inherits file
-{
- open
- audit_access
- execmod
-}
-
-class fifo_file
-inherits file
-{
- open
- audit_access
- execmod
-}
-
-class fd
-{
- use
-}
-
-
-#
-# Define the access vector interpretation for network-related objects.
-#
-
-class socket
-inherits socket
-
-class tcp_socket
-inherits socket
-{
- connectto
- newconn
- acceptfrom
- node_bind
- name_connect
-}
-
-class udp_socket
-inherits socket
-{
- node_bind
-}
-
-class rawip_socket
-inherits socket
-{
- node_bind
-}
-
-class node
-{
- tcp_recv
- tcp_send
- udp_recv
- udp_send
- rawip_recv
- rawip_send
- enforce_dest
- dccp_recv
- dccp_send
- recvfrom
- sendto
-}
-
-class netif
-{
- tcp_recv
- tcp_send
- udp_recv
- udp_send
- rawip_recv
- rawip_send
- dccp_recv
- dccp_send
- ingress
- egress
-}
-
-class netlink_socket
-inherits socket
-
-class packet_socket
-inherits socket
-
-class key_socket
-inherits socket
-
-class unix_stream_socket
-inherits socket
-{
- connectto
- newconn
- acceptfrom
-}
-
-class unix_dgram_socket
-inherits socket
-
-#
-# Define the access vector interpretation for process-related objects
-#
-
-class process
-{
- fork
- transition
- sigchld # commonly granted from child to parent
- sigkill # cannot be caught or ignored
- sigstop # cannot be caught or ignored
- signull # for kill(pid, 0)
- signal # all other signals
- ptrace
- getsched
- setsched
- getsession
- getpgid
- setpgid
- getcap
- setcap
- share
- getattr
- setexec
- setfscreate
- noatsecure
- siginh
- setrlimit
- rlimitinh
- dyntransition
- setcurrent
- execmem
- execstack
- execheap
- setkeycreate
- setsockcreate
-}
-
-
-#
-# Define the access vector interpretation for ipc-related objects
-#
-
-class ipc
-inherits ipc
-
-class sem
-inherits ipc
-
-class msgq
-inherits ipc
-{
- enqueue
-}
-
-class msg
-{
- send
- receive
-}
-
-class shm
-inherits ipc
-{
- lock
-}
-
-
-#
-# Define the access vector interpretation for the security server.
-#
-
-class security
-{
- compute_av
- compute_create
- compute_member
- check_context
- load_policy
- compute_relabel
- compute_user
- setenforce # was avc_toggle in system class
- setbool
- setsecparam
- setcheckreqprot
- read_policy
-}
-
-
-#
-# Define the access vector interpretation for system operations.
-#
-
-class system
-{
- ipc_info
- syslog_read
- syslog_mod
- syslog_console
- module_request
-}
-
-#
-# Define the access vector interpretation for controling capabilies
-#
-
-class capability
-{
- # The capabilities are defined in include/linux/capability.h
- # Capabilities >= 32 are defined in the capability2 class.
- # Care should be taken to ensure that these are consistent with
- # those definitions. (Order matters)
-
- chown
- dac_override
- dac_read_search
- fowner
- fsetid
- kill
- setgid
- setuid
- setpcap
- linux_immutable
- net_bind_service
- net_broadcast
- net_admin
- net_raw
- ipc_lock
- ipc_owner
- sys_module
- sys_rawio
- sys_chroot
- sys_ptrace
- sys_pacct
- sys_admin
- sys_boot
- sys_nice
- sys_resource
- sys_time
- sys_tty_config
- mknod
- lease
- audit_write
- audit_control
- setfcap
-}
-
-class capability2
-{
- mac_override # unused by SELinux
- mac_admin # unused by SELinux
- syslog
- wake_alarm
- block_suspend
-}
-
-#
-# Define the access vector interpretation for controlling
-# changes to passwd information.
-#
-class passwd
-{
- passwd # change another user passwd
- chfn # change another user finger info
- chsh # change another user shell
- rootok # pam_rootok check (skip auth)
- crontab # crontab on another user
-}
-
-#
-# SE-X Windows stuff
-#
-class x_drawable
-{
- create
- destroy
- read
- write
- blend
- getattr
- setattr
- list_child
- add_child
- remove_child
- list_property
- get_property
- set_property
- manage
- override
- show
- hide
- send
- receive
-}
-
-class x_screen
-{
- getattr
- setattr
- hide_cursor
- show_cursor
- saver_getattr
- saver_setattr
- saver_hide
- saver_show
-}
-
-class x_gc
-{
- create
- destroy
- getattr
- setattr
- use
-}
-
-class x_font
-{
- create
- destroy
- getattr
- add_glyph
- remove_glyph
- use
-}
-
-class x_colormap
-{
- create
- destroy
- read
- write
- getattr
- add_color
- remove_color
- install
- uninstall
- use
-}
-
-class x_property
-{
- create
- destroy
- read
- write
- append
- getattr
- setattr
-}
-
-class x_selection
-{
- read
- write
- getattr
- setattr
-}
-
-class x_cursor
-{
- create
- destroy
- read
- write
- getattr
- setattr
- use
-}
-
-class x_client
-{
- destroy
- getattr
- setattr
- manage
-}
-
-class x_device
-inherits x_device
-
-class x_server
-{
- getattr
- setattr
- record
- debug
- grab
- manage
-}
-
-class x_extension
-{
- query
- use
-}
-
-class x_resource
-{
- read
- write
-}
-
-class x_event
-{
- send
- receive
-}
-
-class x_synthetic_event
-{
- send
- receive
-}
-
-#
-# Extended Netlink classes
-#
-class netlink_route_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
-}
-
-class netlink_firewall_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
-}
-
-class netlink_tcpdiag_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
-}
-
-class netlink_nflog_socket
-inherits socket
-
-class netlink_xfrm_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
-}
-
-class netlink_selinux_socket
-inherits socket
-
-class netlink_audit_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
- nlmsg_relay
- nlmsg_readpriv
- nlmsg_tty_audit
-}
-
-class netlink_ip6fw_socket
-inherits socket
-{
- nlmsg_read
- nlmsg_write
-}
-
-class netlink_dnrt_socket
-inherits socket
-
-# Define the access vector interpretation for controlling
-# access and communication through the D-BUS messaging
-# system.
-#
-class dbus
-{
- acquire_svc
- send_msg
-}
-
-# Define the access vector interpretation for controlling
-# access through the name service cache daemon (nscd).
-#
-class nscd
-{
- getpwd
- getgrp
- gethost
- getstat
- admin
- shmempwd
- shmemgrp
- shmemhost
- getserv
- shmemserv
-}
-
-# Define the access vector interpretation for controlling
-# access to IPSec network data by association
-#
-class association
-{
- sendto
- recvfrom
- setcontext
- polmatch
-}
-
-# Updated Netlink class for KOBJECT_UEVENT family.
-class netlink_kobject_uevent_socket
-inherits socket
-
-class appletalk_socket
-inherits socket
-
-class packet
-{
- send
- recv
- relabelto
- flow_in # deprecated
- flow_out # deprecated
- forward_in
- forward_out
-}
-
-class key
-{
- view
- read
- write
- search
- link
- setattr
- create
-}
-
-class context
-{
- translate
- contains
-}
-
-class dccp_socket
-inherits socket
-{
- node_bind
- name_connect
-}
-
-class memprotect
-{
- mmap_zero
-}
-
-class db_database
-inherits database
-{
- access
- install_module
- load_module
- get_param # deprecated
- set_param # deprecated
-}
-
-class db_table
-inherits database
-{
- use # deprecated
- select
- update
- insert
- delete
- lock
-}
-
-class db_procedure
-inherits database
-{
- execute
- entrypoint
- install
-}
-
-class db_column
-inherits database
-{
- use # deprecated
- select
- update
- insert
-}
-
-class db_tuple
-{
- relabelfrom
- relabelto
- use # deprecated
- select
- update
- insert
- delete
-}
-
-class db_blob
-inherits database
-{
- read
- write
- import
- export
-}
-
-# network peer labels
-class peer
-{
- recv
-}
-
-class x_application_data
-{
- paste
- paste_after_confirm
- copy
-}
-
-class kernel_service
-{
- use_as_override
- create_files_as
-}
-
-class tun_socket
-inherits socket
-
-class x_pointer
-inherits x_device
-
-class x_keyboard
-inherits x_device
-
-class db_schema
-inherits database
-{
- search
- add_name
- remove_name
-}
-
-class db_view
-inherits database
-{
- expand
-}
-
-class db_sequence
-inherits database
-{
- get_value
- next_value
- set_value
-}
-
-class db_language
-inherits database
-{
- implement
- execute
-}
-
-class binder
-{
- impersonate
- call
- set_context_mgr
- transfer
-}
-
-class zygote
-{
- specifyids
- specifyrlimits
- specifycapabilities
- specifyinvokewith
- specifyseinfo
-}
-
-class property_service
-{
- set
-}
-#line 1 "external/sepolicy/global_macros"
-#####################################
-# Common groupings of object classes.
-#
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#####################################
-# Common groupings of permissions.
-#
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-#####################################
-# Common socket permission sets.
-
-
-#line 1 "external/sepolicy/mls_macros"
-########################################
-#
-# gen_cats(N)
-#
-# declares categores c0 to c(N-1)
-#
-#line 10
-
-
-
-
-########################################
-#
-# gen_sens(N)
-#
-# declares sensitivites s0 to s(N-1) with dominance
-# in increasing numeric order with s0 lowest, s(N-1) highest
-#
-#line 24
-
-
-
-
-#line 34
-
-
-########################################
-#
-# gen_levels(N,M)
-#
-# levels from s0 to (N-1) with categories c0 to (M-1)
-#
-#line 45
-
-
-
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0 }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-
-
-#################################################
-# MLS policy constraints
-#
-
-#
-# Process constraints
-#
-
-# Process transition: Require equivalence unless the subject is trusted.
-mlsconstrain process { transition dyntransition }
- ((h1 eq h2 and l1 eq l2) or t1 == mlstrustedsubject);
-
-# Process read operations: No read up unless trusted.
-mlsconstrain process { getsched getsession getpgid getcap getattr ptrace share }
- (l1 dom l2 or t1 == mlstrustedsubject);
-
-# Process write operations: No write down unless trusted.
-mlsconstrain process { sigkill sigstop signal setsched setpgid setcap setrlimit ptrace share }
- (l1 domby l2 or t1 == mlstrustedsubject);
-
-#
-# Socket constraints
-#
-
-# Create/relabel operations: Subject must be equivalent to object unless
-# the subject is trusted. Sockets inherit the range of their creator.
-mlsconstrain { socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } { create relabelfrom relabelto }
- ((h1 eq h2 and l1 eq l2) or t1 == mlstrustedsubject);
-
-# Datagram send: Sender must be dominated by receiver unless one of them is
-# trusted.
-mlsconstrain unix_dgram_socket { sendto }
- (l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-
-# Stream connect: Client must be equivalent to server unless one of them
-# is trusted.
-mlsconstrain unix_stream_socket { connectto }
- (l1 eq l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-
-#
-# Directory/file constraints
-#
-
-# Create/relabel operations: Subject must be equivalent to object unless
-# the subject is trusted. Also, files should always be single-level.
-# Do NOT exempt mlstrustedobject types from this constraint.
-mlsconstrain { dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { create relabelfrom relabelto }
- (l2 eq h2 and (l1 eq l2 or t1 == mlstrustedsubject));
-
-#
-# Constraints for app data files only.
-#
-
-# Only constrain open, not read/write.
-# Also constrain other forms of manipulation, e.g. chmod/chown, unlink, rename, etc.
-# Subject must be equivalent to object unless the subject is trusted.
-mlsconstrain dir { open search setattr rename add_name remove_name reparent rmdir }
- (t2 != app_data_file or l1 eq l2 or t1 == mlstrustedsubject);
-mlsconstrain { file lnk_file sock_file } { open setattr unlink link rename }
- (t2 != app_data_file or l1 eq l2 or t1 == mlstrustedsubject);
-
-#
-# Constraints for file types other than app data files.
-#
-
-# Read operations: Subject must dominate object unless the subject
-# or the object is trusted.
-mlsconstrain dir { read getattr search }
- (t2 == app_data_file or l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-mlsconstrain { file lnk_file sock_file chr_file blk_file } { read getattr execute }
- (t2 == app_data_file or l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-# Write operations: Subject must be dominated by the object unless the
-# subject or the object is trusted.
-mlsconstrain dir { write setattr rename add_name remove_name reparent rmdir }
- (t2 == app_data_file or l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-mlsconstrain { file lnk_file sock_file chr_file blk_file } { write setattr append unlink link rename }
- (t2 == app_data_file or l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject);
-
-# Special case for FIFOs.
-# These can be unnamed pipes, in which case they will be labeled with the
-# creating process' label. Thus we also have an exemption when the "object"
-# is a MLS trusted subject and can receive data at any level.
-mlsconstrain fifo_file { read getattr }
- (l1 dom l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject or t2 == mlstrustedsubject);
-
-mlsconstrain fifo_file { write setattr append unlink link rename }
- (l1 domby l2 or t1 == mlstrustedsubject or t2 == mlstrustedobject or t2 == mlstrustedsubject);
-
-#
-# IPC constraints
-#
-
-# Create/destroy: equivalence or trusted.
-mlsconstrain { sem msgq shm ipc } { create destroy }
- (l2 eq h2 and (l1 eq l2 or t1 == mlstrustedsubject));
-
-# Read ops: No read up unless trusted.
-mlsconstrain { sem msgq shm ipc } { getattr read associate unix_read }
- (l1 dom l2 or t1 == mlstrustedsubject);
-
-# Write ops: No write down unless trusted.
-mlsconstrain { sem msgq shm ipc } { write unix_write }
- (l1 domby l2 or t1 == mlstrustedsubject);
-
-#
-# Binder IPC constraints
-#
-# Presently commented out, as apps are expected to call one another.
-# This would only make sense if apps were assigned categories
-# based on allowable communications rather than per-app categories.
-#mlsconstrain binder call
-# (l1 eq l2 or t1 == mlstrustedsubject or t2 == mlstrustedsubject);
-#line 1 "external/sepolicy/policy_capabilities"
-# Enable new networking controls.
-policycap network_peer_controls;
-
-# Enable open permission check.
-policycap open_perms;
-#line 1 "external/sepolicy/te_macros"
-#####################################
-# domain_trans(olddomain, type, newdomain)
-# Allow a transition from olddomain to newdomain
-# upon executing a file labeled with type.
-# This only allows the transition; it does not
-# cause it to occur automatically - use domain_auto_trans
-# if that is what you want.
-#
-#line 21
-
-
-#####################################
-# domain_auto_trans(olddomain, type, newdomain)
-# Automatically transition from olddomain to newdomain
-# upon executing a file labeled with type.
-#
-#line 33
-
-
-#####################################
-# file_type_trans(domain, dir_type, file_type)
-# Allow domain to create a file labeled file_type in a
-# directory labeled dir_type.
-# This only allows the transition; it does not
-# cause it to occur automatically - use file_type_auto_trans
-# if that is what you want.
-#
-#line 49
-
-
-#####################################
-# file_type_auto_trans(domain, dir_type, file_type)
-# Automatically label new files with file_type when
-# they are created by domain in directories labeled dir_type.
-#
-#line 62
-
-
-#####################################
-# r_dir_file(domain, type)
-# Allow the specified domain to read directories, files
-# and symbolic links of the specified type.
-#line 71
-
-
-#####################################
-# unconfined_domain(domain)
-# Allow the specified domain to perform more privileged operations
-# than would be typically allowed. Please see the comments at the
-# top of unconfined.te.
-#
-#line 82
-
-
-#####################################
-# tmpfs_domain(domain)
-# Define and allow access to a unique type for
-# this domain when creating tmpfs / shmem / ashmem files.
-#line 92
-
-
-#####################################
-# init_daemon_domain(domain)
-# Set up a transition from init to the daemon domain
-# upon executing its binary.
-#line 101
-
-
-#####################################
-# app_domain(domain)
-# Allow a base set of permissions required for all apps.
-#line 112
-
-
-#####################################
-# relabelto_domain(domain)
-# Allows this domain to use the relabelto permission
-#line 119
-
-
-#####################################
-# platform_app_domain(domain)
-# Allow permissions specific to platform apps.
-#line 127
-
-
-#####################################
-# net_domain(domain)
-# Allow a base set of permissions required for network access.
-#line 134
-
-
-#####################################
-# bluetooth_domain(domain)
-# Allow a base set of permissions required for bluetooth access.
-#line 141
-
-
-#####################################
-# unix_socket_connect(clientdomain, socket, serverdomain)
-# Allow a local socket connection from clientdomain via
-# socket to serverdomain.
-#line 150
-
-
-#####################################
-# unix_socket_send(clientdomain, socket, serverdomain)
-# Allow a local socket send from clientdomain via
-# socket to serverdomain.
-#line 159
-
-
-#####################################
-# binder_use(domain)
-# Allow domain to use Binder IPC.
-#line 169
-
-
-#####################################
-# binder_call(clientdomain, serverdomain)
-# Allow clientdomain to perform binder IPC to serverdomain.
-#line 181
-
-
-#####################################
-# binder_service(domain)
-# Mark a domain as being a Binder service domain.
-# Used to allow binder IPC to the various system services.
-#line 189
-
-
-#####################################
-# selinux_check_access(domain)
-# Allow domain to check SELinux permissions via selinuxfs.
-#line 199
-
-
-#####################################
-# selinux_check_context(domain)
-# Allow domain to check SELinux contexts via selinuxfs.
-#line 208
-
-
-#####################################
-# selinux_getenforce(domain)
-# Allow domain to check whether SELinux is enforcing.
-#line 216
-
-
-#####################################
-# selinux_setenforce(domain)
-# Allow domain to set SELinux to enforcing.
-#line 225
-
-
-#####################################
-# selinux_setbool(domain)
-# Allow domain to set SELinux booleans.
-#line 234
-
-
-#####################################
-# security_access_policy(domain)
-# Read only access to all policy files and
-# selinuxfs
-#line 248
-
-
-#####################################
-# selinux_manage_policy(domain)
-# Ability to manage policy files and
-# trigger runtime reload.
-#line 261
-
-
-#####################################
-# mmac_manage_policy(domain)
-# Ability to manage mmac policy files,
-# trigger runtime reload, change
-# mmac enforcing mode and access logcat.
-#line 274
-
-
-#####################################
-# access_kmsg(domain)
-# Ability to read from kernel logs
-# and execute the klogctl syscall
-# in a non destructive manner. See
-# man 2 klogctl
-#line 284
-
-
-#####################################
-# write_klog(domain)
-# Ability to write to kernel log via
-# klog_write()
-# See system/core/libcutil/klog.c
-#line 295
-
-
-#####################################
-# create_pty(domain)
-# Allow domain to create and use a pty, isolated from any other domain ptys.
-#line 309
-
-
-#####################################
-# Non system_app application set
-#
-
-
-#####################################
-# Userdebug or eng builds
-# SELinux rules which apply only to userdebug or eng builds
-#
-
-
-#####################################
-# permissive_or_unconfined
-# Returns "permissive $1" if FORCE_PERMISSIVE_TO_UNCONFINED is false,
-# and "unconfined($1)" otherwise.
-#
-# This is used for experimental domains, where we want to ensure
-# the domain is unconfined+enforcing once new SELinux policy development
-# has ceased.
-#
-
-
-#####################################
-# write_logd(domain)
-# Ability to write to android log
-# daemon via sockets
-#line 345
-
-
-#####################################
-# read_logd(domain)
-# Ability to read from android
-# log daemon via sockets
-#line 353
-
-
-#####################################
-# control_logd(domain)
-# Ability to control
-# android log daemon via sockets
-#line 363
-
-#line 1 "external/sepolicy/attributes"
-######################################
-# Attribute declarations
-#
-
-# All types used for devices.
-attribute dev_type;
-
-# All types used for processes.
-attribute domain;
-
-# All types used for filesystems.
-attribute fs_type;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-# All types used for /data files.
-attribute data_file_type;
-
-# All types use for sysfs files.
-attribute sysfs_type;
-
-# Attribute used for all sdcards
-attribute sdcard_type;
-
-# All types used for nodes/hosts.
-attribute node_type;
-
-# All types used for network interfaces.
-attribute netif_type;
-
-# All types used for network ports.
-attribute port_type;
-
-# All types used for property service
-attribute property_type;
-
-# All domains that can override MLS restrictions.
-# i.e. processes that can read up and write down.
-attribute mlstrustedsubject;
-
-# All types that can override MLS restrictions.
-# i.e. files that can be read by lower and written by higher
-attribute mlstrustedobject;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for shells.
-attribute shelldomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All domains used for apps with network access.
-attribute netdomain;
-
-# All domains used for apps with bluetooth access.
-attribute bluetoothdomain;
-
-# All domains used for binder service domains.
-attribute binderservicedomain;
-
-# Allow domains used for platform (signed by build key) apps.
-attribute platformappdomain;
-
-# All domains which are allowed the "relabelto" permission
-attribute relabeltodomain;
-#line 1 "external/sepolicy/adbd.te"
-# adbd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type adbd, domain;
-
-#line 7
-
-
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow adbd shell_exec:file { getattr open read execute };
-#line 9
-allow adbd shell:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow shell shell_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow shell adbd:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit adbd shell:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow adbd shell:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition adbd shell_exec:process shell;
-#line 9
-
-# this is an entrypoint
-allow adbd rootfs:file entrypoint;
-
-# Do not sanitize the environment or open fds of the shell.
-allow adbd shell:process noatsecure;
-
-# Set UID and GID to shell. Set supplementary groups.
-allow adbd self:capability { setuid setgid };
-
-# Drop capabilities from bounding set on user builds.
-allow adbd self:capability setpcap;
-
-# Create and use network sockets.
-
-#line 23
-typeattribute adbd netdomain;
-#line 23
-
-
-# Access /dev/android_adb.
-allow adbd adb_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# On emulator, access /dev/qemu*.
-allow adbd qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Use a pseudo tty.
-allow adbd devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# adb push/pull /data/local/tmp.
-allow adbd shell_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow adbd shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# adb push/pull sdcard.
-allow adbd sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow adbd sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Set service.adb.*, sys.powerctl properties.
-
-#line 43
-allow adbd property_socket:sock_file write;
-#line 43
-allow adbd init:unix_stream_socket connectto;
-#line 43
-
-allow adbd shell_prop:property_service set;
-allow adbd powerctl_prop:property_service set;
-
-# XXX Run /system/bin/vdc to connect to vold. Run in a separate domain?
-# Also covers running /system/bin/bu.
-allow adbd system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-#line 50
-allow adbd vold_socket:sock_file write;
-#line 50
-allow adbd vold:unix_stream_socket connectto;
-#line 50
-
-
-# Perform binder IPC to surfaceflinger (screencap)
-# XXX Run screencap in a separate domain?
-
-#line 54
-# Call the servicemanager and transfer references to it.
-#line 54
-allow adbd servicemanager:binder { call transfer };
-#line 54
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 54
-# all domains in domain.te.
-#line 54
-
-
-#line 55
-# Call the server domain and optionally transfer references to it.
-#line 55
-allow adbd surfaceflinger:binder { call transfer };
-#line 55
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 55
-allow surfaceflinger adbd:binder transfer;
-#line 55
-# Receive and use open files from the server.
-#line 55
-allow adbd surfaceflinger:fd use;
-#line 55
-
-
-# Read /data/misc/adb/adb_keys.
-allow adbd adb_keys_file:dir search;
-allow adbd adb_keys_file:file { getattr open read ioctl lock };
-
-# Allow access in case /data/misc/adb still has the old type.
-allow adbd system_data_file:dir search;
-allow adbd system_data_file:file { getattr open read ioctl lock };
-
-# ndk-gdb invokes adb forward to forward the gdbserver socket.
-allow adbd app_data_file:dir search;
-allow adbd app_data_file:sock_file write;
-allow adbd appdomain:unix_stream_socket connectto;
-
-# ndk-gdb invokes adb pull of app_process, linker, and libc.so.
-allow adbd zygote_exec:file { getattr open read ioctl lock };
-allow adbd system_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/app.te"
-###
-### Domain for all zygote spawned apps
-###
-### This file is the base policy for all zygote spawned apps.
-### Other policy files, such as isolated_app.te, untrusted_app.te, etc
-### extend from this policy. Only policies which should apply to ALL
-### zygote spawned apps should be added here.
-###
-
-# Dalvik Compiler JIT Mapping.
-allow appdomain self:process execmem;
-allow appdomain ashmem_device:chr_file execute;
-
-# Allow apps to connect to the keystore
-
-#line 15
-allow appdomain keystore_socket:sock_file write;
-#line 15
-allow appdomain keystore:unix_stream_socket connectto;
-#line 15
-
-
-# Receive and use open file descriptors inherited from zygote.
-allow appdomain zygote:fd use;
-
-# gdbserver for ndk-gdb reads the zygote.
-allow appdomain zygote_exec:file { getattr open read ioctl lock };
-
-# gdbserver for ndk-gdb ptrace attaches to app process.
-allow appdomain self:process ptrace;
-
-# Read system properties managed by zygote.
-allow appdomain zygote_tmpfs:file read;
-
-# Notify zygote of death;
-allow appdomain zygote:process sigchld;
-
-# Notify shell and adbd of death when spawned via runas for ndk-gdb.
-allow appdomain shell:process sigchld;
-allow appdomain adbd:process sigchld;
-
-# child shell or gdbserver pty access for runas.
-allow appdomain devpts:chr_file { getattr read write ioctl };
-
-# Communicate with system_server.
-allow appdomain system_server:fifo_file { { getattr open read ioctl lock } { open append write } };
-allow appdomain system_server:unix_stream_socket { read write setopt };
-
-#line 42
-# Call the server domain and optionally transfer references to it.
-#line 42
-allow appdomain system_server:binder { call transfer };
-#line 42
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 42
-allow system_server appdomain:binder transfer;
-#line 42
-# Receive and use open files from the server.
-#line 42
-allow appdomain system_server:fd use;
-#line 42
-
-
-# Communication with other apps via fifos
-allow appdomain appdomain:fifo_file { { getattr open read ioctl lock } { open append write } };
-
-# Communicate with surfaceflinger.
-allow appdomain surfaceflinger:unix_stream_socket { read write setopt };
-
-#line 49
-# Call the server domain and optionally transfer references to it.
-#line 49
-allow appdomain surfaceflinger:binder { call transfer };
-#line 49
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 49
-allow surfaceflinger appdomain:binder transfer;
-#line 49
-# Receive and use open files from the server.
-#line 49
-allow appdomain surfaceflinger:fd use;
-#line 49
-
-
-# App sandbox file accesses.
-allow appdomain app_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow appdomain app_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read/write data files created by the platform apps if they
-# were passed to the app via binder or local IPC. Do not allow open.
-allow appdomain platform_app_data_file:file { getattr read write };
-
-# lib subdirectory of /data/data dir is system-owned.
-allow appdomain system_data_file:dir { open getattr read search ioctl };
-allow appdomain system_data_file:file { execute execute_no_trans open };
-
-# Execute the shell or other system executables.
-allow appdomain shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow appdomain system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# Read/write wallpaper file (opened by system).
-allow appdomain wallpaper_file:file { getattr read write };
-
-# Write to /data/anr/traces.txt.
-allow appdomain anr_data_file:dir search;
-allow appdomain anr_data_file:file { open append };
-
-# Allow apps to send dump information to dumpstate
-allow appdomain dumpstate:fd use;
-allow appdomain dumpstate:unix_stream_socket { read write getopt getattr };
-allow appdomain shell_data_file:file { write getattr };
-
-# Write to /proc/net/xt_qtaguid/ctrl file.
-allow appdomain qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-# Everybody can read the xt_qtaguid resource tracking misc dev.
-# So allow all apps to read from /dev/xt_qtaguid.
-allow appdomain qtaguid_device:chr_file { getattr open read ioctl lock };
-
-# Grant GPU access to all processes started by Zygote.
-# They need that to render the standard UI.
-allow appdomain gpu_device:chr_file { { { getattr open read ioctl lock } { open append write } } execute };
-
-# Use the Binder.
-
-#line 90
-# Call the servicemanager and transfer references to it.
-#line 90
-allow appdomain servicemanager:binder { call transfer };
-#line 90
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 90
-# all domains in domain.te.
-#line 90
-
-# Perform binder IPC to binder services.
-
-#line 92
-# Call the server domain and optionally transfer references to it.
-#line 92
-allow appdomain binderservicedomain:binder { call transfer };
-#line 92
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 92
-allow binderservicedomain appdomain:binder transfer;
-#line 92
-# Receive and use open files from the server.
-#line 92
-allow appdomain binderservicedomain:fd use;
-#line 92
-
-# Perform binder IPC to other apps.
-
-#line 94
-# Call the server domain and optionally transfer references to it.
-#line 94
-allow appdomain appdomain:binder { call transfer };
-#line 94
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 94
-allow appdomain appdomain:binder transfer;
-#line 94
-# Receive and use open files from the server.
-#line 94
-allow appdomain appdomain:fd use;
-#line 94
-
-
-# Appdomain interaction with isolated apps
-
-#line 97
-allow appdomain isolated_app:dir { open getattr read search ioctl };
-#line 97
-allow appdomain isolated_app:{ file lnk_file } { getattr open read ioctl lock };
-#line 97
-
-
-# Already connected, unnamed sockets being passed over some other IPC
-# hence no sock_file or connectto permission. This appears to be how
-# Chrome works, may need to be updated as more apps using isolated services
-# are examined.
-allow appdomain isolated_app:unix_stream_socket { read write };
-
-# Backup ability for every app. BMS opens and passes the fd
-# to any app that has backup ability. Hence, no open permissions here.
-allow appdomain backup_data_file:file { read write getattr };
-allow appdomain cache_backup_file:file { read write getattr };
-# Backup ability using 'adb backup'
-allow appdomain system_data_file:lnk_file getattr;
-
-# Allow all applications to read downloaded files
-allow appdomain download_file:dir search;
-allow appdomain download_file:file { getattr open read ioctl lock };
-
-# Allow applications to communicate with netd via /dev/socket/dnsproxyd
-# to do DNS resolution
-
-#line 118
-allow appdomain dnsproxyd_socket:sock_file write;
-#line 118
-allow appdomain netd:unix_stream_socket connectto;
-#line 118
-
-
-# Allow applications to communicate with drmserver over binder
-
-#line 121
-# Call the server domain and optionally transfer references to it.
-#line 121
-allow appdomain drmserver:binder { call transfer };
-#line 121
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 121
-allow drmserver appdomain:binder transfer;
-#line 121
-# Receive and use open files from the server.
-#line 121
-allow appdomain drmserver:fd use;
-#line 121
-
-
-# Allow applications to communicate with mediaserver over binder
-
-#line 124
-# Call the server domain and optionally transfer references to it.
-#line 124
-allow appdomain mediaserver:binder { call transfer };
-#line 124
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 124
-allow mediaserver appdomain:binder transfer;
-#line 124
-# Receive and use open files from the server.
-#line 124
-allow appdomain mediaserver:fd use;
-#line 124
-
-
-# Allow applications to make outbound tcp connections to any port
-allow appdomain port_type:tcp_socket name_connect;
-
-# Allow apps to see changes to the routing table.
-allow appdomain self:netlink_route_socket {
- read
- bind
- create
- nlmsg_read
- ioctl
- getattr
- setattr
- getopt
- setopt
- shutdown
-};
-
-# Allow apps to use rawip sockets. This is needed for apps which execute
-# /system/bin/ping, for example.
-allow appdomain self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-# Allow apps to use the USB Accessory interface.
-# http://developer.android.com/guide/topics/connectivity/usb/accessory.html
-#
-# USB devices are first opened by the system server (USBDeviceManagerService)
-# and the file descriptor is passed to the right Activity via binder.
-allow appdomain usb_device:chr_file { read write getattr ioctl };
-allow appdomain usbaccessory_device:chr_file { read write getattr };
-
-# For art.
-allow appdomain dalvikcache_data_file:file execute;
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-allow appdomain unlabeled:file { getattr execute execute_no_trans };
-
-###
-### CTS-specific rules
-###
-
-# For cts/tools/device-setup/TestDeviceSetup/src/android/tests/getinfo/RootProcessScanner.java.
-# Reads /proc/pid/status and statm entries to check that
-# no unexpected root processes are running.
-# Also for cts/tests/tests/security/src/android/security/cts/VoldExploitTest.java
-# Reads /proc/pid/cmdline of vold.
-allow appdomain domain:dir { open read search getattr };
-allow appdomain domain:{ file lnk_file } { open read getattr };
-
-# For cts/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java.
-# testRunAsHasCorrectCapabilities
-allow appdomain runas_exec:file getattr;
-# Others are either allowed elsewhere or not desired.
-
-# For cts/tests/tests/security/src/android/security/cts/SELinuxTest.java
-# Check SELinux policy and contexts.
-
-#line 181
-allow appdomain selinuxfs:dir { open getattr read search ioctl };
-#line 181
-allow appdomain selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 181
-allow appdomain kernel:security compute_av;
-#line 181
-allow appdomain self:netlink_selinux_socket *;
-#line 181
-
-
-#line 182
-allow appdomain selinuxfs:dir { open getattr read search ioctl };
-#line 182
-allow appdomain selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 182
-allow appdomain kernel:security check_context;
-#line 182
-
-# Validate that each process is running in the correct security context.
-allow appdomain domain:process getattr;
-
-# logd access
-
-#line 187
-
-#line 187
-allow appdomain logdr_socket:sock_file write;
-#line 187
-allow appdomain logd:unix_stream_socket connectto;
-#line 187
-
-#line 187
-
-# application inherit logd write socket (urge is to deprecate this long term)
-allow appdomain zygote:unix_dgram_socket write;
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-neverallow { appdomain -unconfineddomain } self:capability2 *;
-
-# Block device access.
-neverallow { appdomain -unconfineddomain } dev_type:blk_file { read write };
-
-# Access to any of the following character devices.
-neverallow { appdomain -unconfineddomain } {
- audio_device
- camera_device
- dm_device
- radio_device
- gps_device
- rpmsg_device
-}:chr_file { read write };
-
-# Note: Try expanding list of app domains in the future.
-neverallow { untrusted_app isolated_app shell -unconfineddomain }
- graphics_device:chr_file { read write };
-
-neverallow { appdomain -nfc -unconfineddomain } nfc_device:chr_file
- { read write };
-neverallow { appdomain -bluetooth -unconfineddomain } hci_attach_dev:chr_file
- { read write };
-neverallow { appdomain -unconfineddomain } tee_device:chr_file { read write };
-
-# Set SELinux enforcing mode, booleans or any other SELinux settings.
-neverallow { appdomain -unconfineddomain } kernel:security
- { setenforce setbool setsecparam setcheckreqprot };
-
-# Load security policy.
-neverallow appdomain kernel:security load_policy;
-
-# Privileged netlink socket interfaces.
-neverallow { appdomain -unconfineddomain }
- self:{
- netlink_socket
- netlink_firewall_socket
- netlink_tcpdiag_socket
- netlink_nflog_socket
- netlink_xfrm_socket
- netlink_audit_socket
- netlink_ip6fw_socket
- netlink_dnrt_socket
- netlink_kobject_uevent_socket
- } *;
-
-# Sockets under /dev/socket that are not specifically typed.
-neverallow { appdomain -unconfineddomain } socket_device:sock_file write;
-
-# Unix domain sockets.
-neverallow { appdomain -unconfineddomain } adbd_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } installd_socket:sock_file write;
-neverallow { appdomain -bluetooth -radio -shell -system_app -unconfineddomain }
- property_socket:sock_file write;
-neverallow { appdomain -radio -unconfineddomain } rild_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } vold_socket:sock_file write;
-neverallow { appdomain -unconfineddomain } zygote_socket:sock_file write;
-
-# ptrace access to non-app domains.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:process ptrace;
-
-# Write access to /proc/pid entries for any non-app domain.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:file write;
-
-# signal access to non-app domains.
-# sigchld allowed for parent death notification.
-# signull allowed for kill(pid, 0) existence test.
-# All others prohibited.
-neverallow { appdomain -unconfineddomain } { domain -appdomain }:process
- { sigkill sigstop signal };
-
-# Transition to a non-app domain.
-# Exception for the shell domain, can transition to runas, etc.
-neverallow { appdomain -shell -unconfineddomain } ~appdomain:process
- { transition dyntransition };
-
-# Map low memory.
-# Note: Take to domain.te and apply to all domains in the future.
-neverallow { appdomain -unconfineddomain } self:memprotect mmap_zero;
-
-# Write to rootfs.
-neverallow { appdomain -unconfineddomain } rootfs:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to /system.
-neverallow { appdomain -unconfineddomain } system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to entrypoint executables.
-neverallow { appdomain -unconfineddomain } exec_type:file
- { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to system-owned parts of /data.
-# This is the default type for anything under /data not otherwise
-# specified in file_contexts. Define a different type for portions
-# that should be writable by apps.
-# Exception for system_app for Settings.
-neverallow { appdomain -unconfineddomain -system_app }
- system_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Write to various other parts of /data.
-neverallow { appdomain -system_app -unconfineddomain }
- security_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain } drm_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain } gps_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
- apk_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
- apk_tmp_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
- apk_private_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -platform_app -unconfineddomain }
- apk_private_tmp_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -shell -unconfineddomain }
- shell_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -bluetooth -unconfineddomain }
- bluetooth_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
- keystore_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
- systemkeys_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
- wifi_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-neverallow { appdomain -unconfineddomain }
- dhcp_data_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } }
- { create write setattr relabelfrom relabelto append unlink link rename };
-
-# Access to factory files.
-neverallow { appdomain -unconfineddomain }
- efs_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { read write };
-
-# Write to various pseudo file systems.
-neverallow { appdomain -bluetooth -nfc -unconfineddomain }
- sysfs:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-neverallow { appdomain -unconfineddomain }
- proc:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Access to syslog(2) or /proc/kmsg.
-neverallow { appdomain -system_app -unconfineddomain }
- kernel:system { syslog_read syslog_mod syslog_console };
-
-# Ability to perform any filesystem operation other than statfs(2).
-# i.e. no mount(2), unmount(2), etc.
-neverallow { appdomain -unconfineddomain } fs_type:filesystem ~getattr;
-
-# Ability to set system properties.
-neverallow { appdomain -system_app -radio -shell -bluetooth -unconfineddomain }
- property_type:property_service set;
-#line 1 "external/sepolicy/binderservicedomain.te"
-# Rules common to all binder service domains
-
-# Allow dumpstate to collect information from binder services
-allow binderservicedomain dumpstate:fd use;
-allow binderservicedomain dumpstate:unix_stream_socket { read write getopt getattr };
-allow binderservicedomain shell_data_file:file { getattr write };
-
-# Allow dumpsys to work from adb shell
-allow binderservicedomain devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-
-#line 3
-typeattribute bluetooth appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type bluetooth_tmpfs, file_type;
-#line 3
-type_transition bluetooth tmpfs:file bluetooth_tmpfs;
-#line 3
-allow bluetooth bluetooth_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow bluetooth bluetooth_tmpfs:file execute;
-#line 3
-
-
-# Data file accesses.
-allow bluetooth bluetooth_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow bluetooth bluetooth_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Socket creation under /data/misc/bluedroid.
-type_transition bluetooth bluetooth_data_file:sock_file bluetooth_socket;
-allow bluetooth bluetooth_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# bluetooth factory file accesses.
-
-#line 14
-allow bluetooth bluetooth_efs_file:dir { open getattr read search ioctl };
-#line 14
-allow bluetooth bluetooth_efs_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 14
-
-
-# Device accesses.
-allow bluetooth { tun_device uhid_device hci_attach_dev }:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Other domains that can create and use bluetooth sockets.
-# SELinux does not presently define a specific socket class for
-# bluetooth sockets, nor does it distinguish among the bluetooth protocols.
-allow bluetoothdomain self:socket *;
-
-# sysfs access.
-allow bluetooth sysfs_bluetooth_writable:file { { getattr open read ioctl lock } { open append write } };
-allow bluetooth self:capability net_admin;
-
-# Allow clients to use a socket provided by the bluetooth app.
-allow bluetoothdomain bluetooth:unix_stream_socket { read write shutdown };
-
-# tethering
-allow bluetooth self:{ tun_socket udp_socket } { ioctl create };
-allow bluetooth efs_file:dir search;
-
-# Talk to init over the property socket.
-
-#line 36
-allow bluetooth property_socket:sock_file write;
-#line 36
-allow bluetooth init:unix_stream_socket connectto;
-#line 36
-
-
-# proc access.
-allow bluetooth proc_bluetooth_writable:file { { getattr open read ioctl lock } { open append write } };
-
-# bluetooth file transfers
-allow bluetooth sdcard_internal:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow bluetooth sdcard_internal:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Allow reading of media_rw_data_file file descriptors
-# passed to bluetooth
-allow bluetooth media_rw_data_file:file { read getattr };
-
-# Allow write access to bluetooth specific properties
-allow bluetooth bluetooth_prop:property_service set;
-
-###
-### Neverallow rules
-###
-### These are things that the bluetooth app should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { bluetooth -unconfineddomain } self:capability ~net_admin;
-#line 1 "external/sepolicy/bootanim.te"
-# bootanimation oneshot service
-type bootanim, domain;
-type bootanim_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init bootanim_exec:file { getattr open read execute };
-#line 5
-allow init bootanim:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow bootanim bootanim_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow bootanim init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init bootanim:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init bootanim:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init bootanim_exec:process bootanim;
-#line 5
-
-#line 5
-
-#line 5
-type bootanim_tmpfs, file_type;
-#line 5
-type_transition bootanim tmpfs:file bootanim_tmpfs;
-#line 5
-allow bootanim bootanim_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow bootanim servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-# Call the server domain and optionally transfer references to it.
-#line 8
-allow bootanim surfaceflinger:binder { call transfer };
-#line 8
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 8
-allow surfaceflinger bootanim:binder transfer;
-#line 8
-# Receive and use open files from the server.
-#line 8
-allow bootanim surfaceflinger:fd use;
-#line 8
-
-
-allow bootanim gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/clatd.te"
-# 464xlat daemon
-type clatd, domain;
-
-#line 3
-typeattribute clatd mlstrustedsubject;
-#line 3
-typeattribute clatd unconfineddomain;
-#line 3
-
-type clatd_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init clatd_exec:file { getattr open read execute };
-#line 6
-allow init clatd:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow clatd clatd_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow clatd init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init clatd:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init clatd:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init clatd_exec:process clatd;
-#line 6
-
-#line 6
-
-#line 6
-type clatd_tmpfs, file_type;
-#line 6
-type_transition clatd tmpfs:file clatd_tmpfs;
-#line 6
-allow clatd clatd_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute clatd netdomain;
-#line 7
-
-#line 1 "external/sepolicy/debuggerd.te"
-# debugger interface
-type debuggerd, domain;
-type debuggerd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init debuggerd_exec:file { getattr open read execute };
-#line 5
-allow init debuggerd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow debuggerd debuggerd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow debuggerd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init debuggerd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init debuggerd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init debuggerd_exec:process debuggerd;
-#line 5
-
-#line 5
-
-#line 5
-type debuggerd_tmpfs, file_type;
-#line 5
-type_transition debuggerd tmpfs:file debuggerd_tmpfs;
-#line 5
-allow debuggerd debuggerd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute debuggerd mlstrustedsubject;
-allow debuggerd self:capability { dac_override sys_ptrace chown kill fowner };
-allow debuggerd self:capability2 { syslog };
-allow debuggerd domain:dir { open getattr read search ioctl };
-allow debuggerd domain:file { getattr open read ioctl lock };
-allow debuggerd { domain -init -ueventd -watchdogd -healthd -adbd }:process ptrace;
-
-#line 12
-allow debuggerd security_file:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd security_file:file { getattr open read ioctl lock };
-#line 12
-allow debuggerd security_file:lnk_file { getattr open read ioctl lock };
-#line 12
-allow debuggerd selinuxfs:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd selinuxfs:file { getattr open read ioctl lock };
-#line 12
-allow debuggerd rootfs:dir { open getattr read search ioctl };
-#line 12
-allow debuggerd rootfs:file { getattr open read ioctl lock };
-#line 12
-
-allow debuggerd system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow debuggerd system_data_file:dir relabelfrom;
-
-#line 15
-typeattribute debuggerd relabeltodomain;
-#line 15
-
-allow debuggerd tombstone_data_file:dir relabelto;
-allow debuggerd tombstone_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow debuggerd tombstone_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow debuggerd domain:process { sigstop signal };
-allow debuggerd exec_type:file { getattr open read ioctl lock };
-# Access app library
-allow debuggerd system_data_file:file open;
-
-# Connect to system_server via /data/system/ndebugsocket.
-
-#line 25
-allow debuggerd system_ndebug_socket:sock_file write;
-#line 25
-allow debuggerd system_server:unix_stream_socket connectto;
-#line 25
-
-
-#line 30
-
-
-# logd access
-
-#line 33
-
-#line 33
-allow debuggerd logdr_socket:sock_file write;
-#line 33
-allow debuggerd logd:unix_stream_socket connectto;
-#line 33
-
-#line 33
-
-#line 1 "external/sepolicy/device.te"
-# Device types
-type device, dev_type, fs_type;
-type alarm_device, dev_type, mlstrustedobject;
-type adb_device, dev_type;
-type ashmem_device, dev_type, mlstrustedobject;
-type audio_device, dev_type;
-type binder_device, dev_type, mlstrustedobject;
-type block_device, dev_type;
-type camera_device, dev_type;
-type dm_device, dev_type;
-type loop_device, dev_type;
-type radio_device, dev_type;
-type ram_device, dev_type;
-type console_device, dev_type;
-type cpuctl_device, dev_type;
-type fscklogs, dev_type;
-type full_device, dev_type;
-# GPU (used by most UI apps)
-type gpu_device, dev_type, mlstrustedobject;
-type graphics_device, dev_type;
-type hw_random_device, dev_type;
-type input_device, dev_type;
-type kmem_device, dev_type;
-type log_device, dev_type, mlstrustedobject;
-type mtd_device, dev_type;
-type mtp_device, dev_type, mlstrustedobject;
-type nfc_device, dev_type;
-type ptmx_device, dev_type, mlstrustedobject;
-type qemu_device, dev_type;
-type kmsg_device, dev_type;
-type null_device, dev_type, mlstrustedobject;
-type random_device, dev_type;
-type sensors_device, dev_type;
-type serial_device, dev_type;
-type socket_device, dev_type;
-type owntty_device, dev_type, mlstrustedobject;
-type tty_device, dev_type;
-type urandom_device, dev_type;
-type video_device, dev_type;
-type vcs_device, dev_type;
-type zero_device, dev_type;
-type fuse_device, dev_type;
-type iio_device, dev_type;
-type ion_device, dev_type, mlstrustedobject;
-type gps_device, dev_type;
-type qtaguid_device, dev_type;
-type watchdog_device, dev_type;
-type uhid_device, dev_type;
-type tun_device, dev_type, mlstrustedobject;
-type usbaccessory_device, dev_type;
-type usb_device, dev_type;
-type klog_device, dev_type;
-type properties_device, dev_type;
-
-# All devices have a uart for the hci
-# attach service. The uart dev node
-# varies per device. This type
-# is used in per device policy
-type hci_attach_dev, dev_type;
-
-# All devices have a rpmsg device for
-# achieving remoteproc and rpmsg modules
-type rpmsg_device, dev_type;
-
-# Partition layout block device
-type root_block_device, dev_type;
-#line 1 "external/sepolicy/dhcp.te"
-type dhcp, domain;
-
-#line 2
-typeattribute dhcp mlstrustedsubject;
-#line 2
-typeattribute dhcp unconfineddomain;
-#line 2
-
-type dhcp_exec, exec_type, file_type;
-type dhcp_data_file, file_type, data_file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init dhcp_exec:file { getattr open read execute };
-#line 6
-allow init dhcp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow dhcp dhcp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow dhcp init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init dhcp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init dhcp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init dhcp_exec:process dhcp;
-#line 6
-
-#line 6
-
-#line 6
-type dhcp_tmpfs, file_type;
-#line 6
-type_transition dhcp tmpfs:file dhcp_tmpfs;
-#line 6
-allow dhcp dhcp_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute dhcp netdomain;
-#line 7
-
-
-allow dhcp cgroup:dir { create write add_name };
-allow dhcp self:capability { setgid setuid net_admin net_raw net_bind_service };
-allow dhcp self:packet_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow dhcp self:netlink_route_socket { { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } } nlmsg_write };
-allow dhcp self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow dhcp shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow dhcp system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-# For /proc/sys/net/ipv4/conf/*/promote_secondaries
-allow dhcp proc_net:file write;
-allow dhcp system_prop:property_service set ;
-
-#line 19
-allow dhcp property_socket:sock_file write;
-#line 19
-allow dhcp init:unix_stream_socket connectto;
-#line 19
-
-allow dhcp owntty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-type_transition dhcp system_data_file:{ dir file } dhcp_data_file;
-allow dhcp dhcp_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow dhcp dhcp_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# PAN connections
-allow dhcp netd:fd use;
-allow dhcp netd:fifo_file { { getattr open read ioctl lock } { open append write } };
-allow dhcp netd:{ { udp_socket unix_dgram_socket } unix_stream_socket } { read write };
-allow dhcp netd:{ netlink_kobject_uevent_socket netlink_route_socket netlink_nflog_socket } { read write };
-#line 1 "external/sepolicy/dnsmasq.te"
-# DNS, DHCP services
-type dnsmasq, domain;
-
-#line 3
-typeattribute dnsmasq mlstrustedsubject;
-#line 3
-typeattribute dnsmasq unconfineddomain;
-#line 3
-
-type dnsmasq_exec, exec_type, file_type;
-
-allow dnsmasq self:capability { net_bind_service setgid setuid };
-allow dnsmasq self:tcp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-allow dnsmasq dhcp_data_file:dir { open search write add_name remove_name };
-allow dnsmasq dhcp_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow dnsmasq port:tcp_socket name_bind;
-allow dnsmasq node:tcp_socket node_bind;
-#line 1 "external/sepolicy/domain.te"
-# Rules for all domains.
-
-# Allow reaping by init.
-allow domain init:process sigchld;
-
-# Read access to properties mapping.
-allow domain kernel:fd use;
-allow domain tmpfs:file { read getattr };
-
-# Search /storage/emulated tmpfs mount.
-allow domain tmpfs:dir { open getattr read search ioctl };
-
-# Intra-domain accesses.
-allow domain self:process ~{ execmem execstack execheap ptrace };
-allow domain self:fd use;
-allow domain self:dir { open getattr read search ioctl };
-allow domain self:lnk_file { getattr open read ioctl lock };
-allow domain self:{ fifo_file file } { { getattr open read ioctl lock } { open append write } };
-allow domain self:{ unix_dgram_socket unix_stream_socket } *;
-
-# Inherit or receive open files from others.
-allow domain init:fd use;
-allow domain system_server:fd use;
-
-# Connect to adbd and use a socket transferred from it.
-# This is used for e.g. adb backup/restore.
-allow domain adbd:unix_stream_socket connectto;
-allow domain adbd:fd use;
-allow domain adbd:unix_stream_socket { getattr getopt read write shutdown };
-
-#line 43
-
-
-###
-### Talk to debuggerd.
-###
-allow domain debuggerd:process sigchld;
-allow domain debuggerd:unix_stream_socket connectto;
-
-# Root fs.
-allow domain rootfs:dir { open getattr read search ioctl };
-allow domain rootfs:file { getattr open read ioctl lock };
-allow domain rootfs:lnk_file { getattr open read ioctl lock };
-
-# Device accesses.
-allow domain device:dir search;
-allow domain dev_type:lnk_file { getattr open read ioctl lock };
-allow domain devpts:dir search;
-allow domain device:file read;
-allow domain socket_device:dir search;
-allow domain owntty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain null_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain zero_device:chr_file { getattr open read ioctl lock };
-allow domain ashmem_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain binder_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain ptmx_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain log_device:dir search;
-allow domain log_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain alarm_device:chr_file { getattr open read ioctl lock };
-allow domain urandom_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain random_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow domain properties_device:file { getattr open read ioctl lock };
-
-# logd access
-
-#line 76
-
-#line 76
-
-#line 76
-allow domain logdw_socket:sock_file write;
-#line 76
-allow domain logd:unix_dgram_socket sendto;
-#line 76
-
-#line 76
-
-
-# Filesystem accesses.
-allow domain fs_type:filesystem getattr;
-allow domain fs_type:dir getattr;
-
-# System file accesses.
-allow domain system_file:dir { open getattr read search ioctl };
-allow domain system_file:file { getattr open read ioctl lock };
-allow domain system_file:file execute;
-allow domain system_file:lnk_file { getattr open read ioctl lock };
-
-# Read files already opened under /data.
-allow domain system_data_file:dir { search getattr };
-allow domain system_data_file:file { getattr read };
-allow domain system_data_file:lnk_file { getattr open read ioctl lock };
-
-# Read apk files under /data/app.
-allow domain apk_data_file:dir { getattr search };
-allow domain apk_data_file:file { getattr open read ioctl lock };
-
-# Read /data/dalvik-cache.
-allow domain dalvikcache_data_file:dir { search getattr };
-allow domain dalvikcache_data_file:file { getattr open read ioctl lock };
-
-# Read already opened /cache files.
-allow domain cache_file:dir { open getattr read search ioctl };
-allow domain cache_file:file { getattr read };
-allow domain cache_file:lnk_file { getattr open read ioctl lock };
-
-# Read timezone related information
-
-#line 107
-allow domain zoneinfo_data_file:dir { open getattr read search ioctl };
-#line 107
-allow domain zoneinfo_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 107
-
-
-# For /acct/uid/*/tasks.
-allow domain cgroup:dir { search write };
-allow domain cgroup:file { open append write };
-
-#Allow access to ion memory allocation device
-allow domain ion_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read access to pseudo filesystems.
-
-#line 117
-allow domain proc:dir { open getattr read search ioctl };
-#line 117
-allow domain proc:{ file lnk_file } { getattr open read ioctl lock };
-#line 117
-
-
-#line 118
-allow domain sysfs:dir { open getattr read search ioctl };
-#line 118
-allow domain sysfs:{ file lnk_file } { getattr open read ioctl lock };
-#line 118
-
-
-#line 119
-allow domain sysfs_devices_system_cpu:dir { open getattr read search ioctl };
-#line 119
-allow domain sysfs_devices_system_cpu:{ file lnk_file } { getattr open read ioctl lock };
-#line 119
-
-
-#line 120
-allow domain inotify:dir { open getattr read search ioctl };
-#line 120
-allow domain inotify:{ file lnk_file } { getattr open read ioctl lock };
-#line 120
-
-
-#line 121
-allow domain cgroup:dir { open getattr read search ioctl };
-#line 121
-allow domain cgroup:{ file lnk_file } { getattr open read ioctl lock };
-#line 121
-
-
-#line 122
-allow domain proc_net:dir { open getattr read search ioctl };
-#line 122
-allow domain proc_net:{ file lnk_file } { getattr open read ioctl lock };
-#line 122
-
-
-# debugfs access
-allow domain debugfs:dir { open getattr read search ioctl };
-allow domain debugfs:file { open append write };
-
-# Get SELinux enforcing status.
-
-#line 129
-allow domain selinuxfs:dir { open getattr read search ioctl };
-#line 129
-allow domain selinuxfs:file { getattr open read ioctl lock };
-#line 129
-
-
-# security files
-allow domain security_file:dir { search getattr };
-allow domain security_file:file getattr;
-
-# World readable asec image contents
-allow domain asec_public_file:file { getattr open read ioctl lock };
-allow domain { asec_public_file asec_apk_file }:dir { open getattr read search ioctl };
-
-######## Backwards compatibility - Unlabeled files ############
-
-# Revert to DAC rules when looking at unlabeled files. Over time, the number
-# of unlabeled files should decrease.
-# TODO: delete these rules in the future.
-#
-# Note on relabelfrom: We allow any app relabelfrom, but without the relabelto
-# capability, it's essentially useless. This is needed to allow an app with
-# relabelto to relabel unlabeled files.
-#
-allow domain unlabeled:{ file lnk_file sock_file fifo_file } { { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } } relabelfrom };
-allow domain unlabeled:dir { { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } relabelfrom };
-neverallow { domain -relabeltodomain } *:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-
-###
-### neverallow rules
-###
-
-# Limit ability to ptrace or read sensitive /proc/pid files of processes
-# with other UIDs to these whitelisted domains.
-neverallow { domain -debuggerd -vold -dumpstate -system_server } self:capability sys_ptrace;
-
-# Limit device node creation and raw I/O to these whitelisted domains.
-neverallow { domain -kernel -init -recovery -ueventd -watchdogd -healthd -vold -uncrypt } self:capability { sys_rawio mknod };
-
-# No domain needs mac_override as it is unused by SELinux.
-neverallow domain self:capability2 mac_override;
-
-# Only recovery needs mac_admin to set contexts not defined in current policy.
-neverallow { domain -recovery } self:capability2 mac_admin;
-
-# Only init should be able to load SELinux policies.
-# The first load technically occurs while still in the kernel domain,
-# but this does not trigger a denial since there is no policy yet.
-# Policy reload requires allowing this to the init domain.
-neverallow { domain -init } kernel:security load_policy;
-
-# Only init prior to switching context should be able to set enforcing mode.
-# init starts in kernel domain and switches to init domain via setcon in
-# the init.rc, so the setenforce occurs while still in kernel. After
-# switching domains, there is never any need to setenforce again by init.
-neverallow { domain -kernel } kernel:security { setenforce setcheckreqprot };
-
-# Only init, ueventd and system_server should be able to access HW RNG
-neverallow { domain -init -system_server -ueventd -unconfineddomain } hw_random_device:chr_file *;
-
-# Ensure that all entrypoint executables are in exec_type.
-neverallow domain { file_type -exec_type }:file entrypoint;
-
-# Ensure that nothing in userspace can access /dev/mem or /dev/kmem
-neverallow { domain -kernel -ueventd -init } kmem_device:chr_file *;
-neverallow domain kmem_device:chr_file ~{ create relabelto unlink setattr };
-
-# Only init should be able to configure kernel usermodehelpers or
-# security-sensitive proc settings.
-neverallow { domain -init } usermodehelper:file { append write };
-neverallow { domain -init } proc_security:file { append write };
-
-# No domain should be allowed to ptrace init.
-neverallow domain init:process ptrace;
-
-# Init can't receive binder calls. If this neverallow rule is being
-# triggered, it's probably due to a service with no SELinux domain.
-neverallow domain init:binder call;
-
-# Don't allow raw read/write/open access to block_device
-# Rather force a relabel to a more specific type
-neverallow { domain -kernel -init -recovery -vold -uncrypt } block_device:blk_file { open read write };
-
-# Don't allow raw read/write/open access to generic devices.
-# Rather force a relabel to a more specific type.
-# ueventd is exempt from this, as its managing these devices.
-neverallow { domain -unconfineddomain -ueventd } device:chr_file { open read write };
-
-# Limit what domains can mount filesystems or change their mount flags.
-# sdcard_type / vfat is exempt as a larger set of domains need
-# this capability, including device-specific domains.
-neverallow { domain -kernel -init -recovery -vold -zygote } { fs_type -sdcard_type }:filesystem { mount remount relabelfrom relabelto };
-#line 1 "external/sepolicy/drmserver.te"
-# drmserver - DRM service
-type drmserver, domain;
-type drmserver_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init drmserver_exec:file { getattr open read execute };
-#line 5
-allow init drmserver:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow drmserver drmserver_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow drmserver init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init drmserver:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init drmserver:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init drmserver_exec:process drmserver;
-#line 5
-
-#line 5
-
-#line 5
-type drmserver_tmpfs, file_type;
-#line 5
-type_transition drmserver tmpfs:file drmserver_tmpfs;
-#line 5
-allow drmserver drmserver_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute drmserver mlstrustedsubject;
-
-# Perform Binder IPC to system server.
-
-#line 9
-# Call the servicemanager and transfer references to it.
-#line 9
-allow drmserver servicemanager:binder { call transfer };
-#line 9
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 9
-# all domains in domain.te.
-#line 9
-
-
-#line 10
-# Call the server domain and optionally transfer references to it.
-#line 10
-allow drmserver system_server:binder { call transfer };
-#line 10
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 10
-allow system_server drmserver:binder transfer;
-#line 10
-# Receive and use open files from the server.
-#line 10
-allow drmserver system_server:fd use;
-#line 10
-
-
-#line 11
-# Call the server domain and optionally transfer references to it.
-#line 11
-allow drmserver appdomain:binder { call transfer };
-#line 11
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 11
-allow appdomain drmserver:binder transfer;
-#line 11
-# Receive and use open files from the server.
-#line 11
-allow drmserver appdomain:fd use;
-#line 11
-
-
-#line 12
-typeattribute drmserver binderservicedomain;
-#line 12
-
-
-# Perform Binder IPC to mediaserver
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow drmserver mediaserver:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow mediaserver drmserver:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow drmserver mediaserver:fd use;
-#line 15
-
-
-allow drmserver sdcard_type:dir search;
-allow drmserver drm_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow drmserver drm_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow drmserver self:{ tcp_socket udp_socket } *;
-allow drmserver port:tcp_socket name_connect;
-allow drmserver tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow drmserver platform_app_data_file:file { read write getattr };
-allow drmserver app_data_file:file { read write getattr };
-allow drmserver sdcard_type:file { read write getattr };
-
-#line 26
-allow drmserver efs_file:dir { open getattr read search ioctl };
-#line 26
-allow drmserver efs_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 26
-
-
-type drmserver_socket, file_type;
-
-# /data/app/tlcd_sock socket file.
-# Clearly, /data/app is the most logical place to create a socket. Not.
-allow drmserver apk_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-type_transition drmserver apk_data_file:sock_file drmserver_socket;
-allow drmserver drmserver_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow drmserver tee:unix_stream_socket connectto;
-# Delete old socket file if present.
-allow drmserver apk_data_file:sock_file unlink;
-
-# After taking a video, drmserver looks at the video file.
-
-#line 40
-allow drmserver media_rw_data_file:dir { open getattr read search ioctl };
-#line 40
-allow drmserver media_rw_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 40
-
-#line 1 "external/sepolicy/dumpstate.te"
-# dumpstate
-type dumpstate, domain;
-
-#line 3
-typeattribute dumpstate mlstrustedsubject;
-#line 3
-typeattribute dumpstate unconfineddomain;
-#line 3
-
-type dumpstate_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init dumpstate_exec:file { getattr open read execute };
-#line 6
-allow init dumpstate:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow dumpstate dumpstate_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow dumpstate init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init dumpstate:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init dumpstate:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init dumpstate_exec:process dumpstate;
-#line 6
-
-#line 6
-
-#line 6
-type dumpstate_tmpfs, file_type;
-#line 6
-type_transition dumpstate tmpfs:file dumpstate_tmpfs;
-#line 6
-allow dumpstate dumpstate_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute dumpstate netdomain;
-#line 7
-
-
-#line 8
-typeattribute dumpstate relabeltodomain;
-#line 8
-
-
-#line 9
-# Call the servicemanager and transfer references to it.
-#line 9
-allow dumpstate servicemanager:binder { call transfer };
-#line 9
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 9
-# all domains in domain.te.
-#line 9
-
-
-# Drop privileges by switching UID / GID
-allow dumpstate self:capability { setuid setgid };
-
-# Allow dumpstate to scan through /proc/pid for all processes
-
-#line 15
-allow dumpstate domain:dir { open getattr read search ioctl };
-#line 15
-allow dumpstate domain:{ file lnk_file } { getattr open read ioctl lock };
-#line 15
-
-
-# Send signals to processes
-allow dumpstate self:capability kill;
-
-# Allow executing files on system, such as:
-# /system/bin/toolbox
-# /system/bin/logcat
-# /system/bin/dumpsys
-allow dumpstate system_file:file execute_no_trans;
-
-# Create and write into /data/anr/
-allow dumpstate self:capability { dac_override chown fowner fsetid };
-allow dumpstate anr_data_file:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } relabelto };
-allow dumpstate anr_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow dumpstate system_data_file:dir { { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } relabelfrom };
-
-# Allow reading /data/system/uiderrors.txt
-# TODO: scope this down.
-allow dumpstate system_data_file:file { getattr open read ioctl lock };
-
-# Read dmesg
-allow dumpstate self:capability2 syslog;
-allow dumpstate kernel:system syslog_read;
-
-# Get process attributes
-allow dumpstate domain:process getattr;
-
-# Signal java processes to dump their stack
-allow dumpstate { appdomain system_server }:process signal;
-
-# Signal native processes to dump their stack.
-# This list comes from native_processes_to_dump in dumpstate/utils.c
-allow dumpstate { drmserver mediaserver sdcardd surfaceflinger }:process signal;
-
-# The /system/bin/ip command needs this for routing table information.
-allow dumpstate self:netlink_route_socket { write getattr setopt };
-
-# The vdc command needs to talk to the vold socket.
-
-#line 54
-allow dumpstate vold_socket:sock_file write;
-#line 54
-allow dumpstate vold:unix_stream_socket connectto;
-#line 54
-
-
-# Vibrate the device after we're done collecting the bugreport
-# /sys/class/timed_output/vibrator/enable
-# TODO: create a new file class, instead of allowing write access to all of /sys
-allow dumpstate sysfs:file { open append write };
-
-# Other random bits of data we want to collect
-allow dumpstate qtaguid_proc:file { getattr open read ioctl lock };
-allow dumpstate debugfs:file { getattr open read ioctl lock };
-
-# Allow dumpstate to make binder calls to any binder service
-
-#line 66
-# Call the server domain and optionally transfer references to it.
-#line 66
-allow dumpstate binderservicedomain:binder { call transfer };
-#line 66
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 66
-allow binderservicedomain dumpstate:binder transfer;
-#line 66
-# Receive and use open files from the server.
-#line 66
-allow dumpstate binderservicedomain:fd use;
-#line 66
-
-
-#line 67
-# Call the server domain and optionally transfer references to it.
-#line 67
-allow dumpstate appdomain:binder { call transfer };
-#line 67
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 67
-allow appdomain dumpstate:binder transfer;
-#line 67
-# Receive and use open files from the server.
-#line 67
-allow dumpstate appdomain:fd use;
-#line 67
-
-
-# Reading /proc/PID/maps of other processes
-allow dumpstate self:capability sys_ptrace;
-
-# Allow the bugreport service to create a file in
-# /data/data/com.android.shell/files/bugreports/bugreport
-allow dumpstate shell_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow dumpstate shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Run a shell.
-allow dumpstate shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# For running am and similar framework commands.
-# Run /system/bin/app_process.
-allow dumpstate zygote_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-# Dalvik Compiler JIT.
-allow dumpstate ashmem_device:chr_file execute;
-allow dumpstate dumpstate_tmpfs:file execute;
-allow dumpstate self:process execmem;
-# For art.
-allow dumpstate dalvikcache_data_file:file execute;
-
-# logd access
-
-#line 91
-
-#line 91
-allow dumpstate logdr_socket:sock_file write;
-#line 91
-allow dumpstate logd:unix_stream_socket connectto;
-#line 91
-
-#line 91
-
-
-#line 92
-# Group AID_LOG checked by filesystem & logd
-#line 92
-# to permit control commands
-#line 92
-
-#line 92
-allow dumpstate logd_socket:sock_file write;
-#line 92
-allow dumpstate logd:unix_stream_socket connectto;
-#line 92
-
-#line 92
-
-#line 1 "external/sepolicy/file.te"
-# Filesystem types
-type labeledfs, fs_type;
-type pipefs, fs_type;
-type sockfs, fs_type;
-type rootfs, fs_type;
-type proc, fs_type;
-# Security-sensitive proc nodes that should not be writable to most.
-type proc_security, fs_type;
-# proc, sysfs, or other nodes that permit configuration of kernel usermodehelpers.
-type usermodehelper, fs_type, sysfs_type;
-type qtaguid_proc, fs_type, mlstrustedobject;
-type proc_bluetooth_writable, fs_type;
-type proc_net, fs_type;
-type selinuxfs, fs_type;
-type cgroup, fs_type, mlstrustedobject;
-type sysfs, fs_type, mlstrustedobject;
-type sysfs_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_bluetooth_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_nfc_power_writable, fs_type, sysfs_type, mlstrustedobject;
-type sysfs_wake_lock, fs_type, sysfs_type;
-# /sys/devices/system/cpu
-type sysfs_devices_system_cpu, fs_type, sysfs_type;
-# /sys/module/lowmemorykiller
-type sysfs_lowmemorykiller, fs_type, sysfs_type;
-type inotify, fs_type, mlstrustedobject;
-type devpts, fs_type, mlstrustedobject;
-type tmpfs, fs_type;
-type shm, fs_type;
-type mqueue, fs_type;
-type sdcard_internal, sdcard_type, fs_type, mlstrustedobject;
-type sdcard_external, sdcard_type, fs_type, mlstrustedobject;
-type debugfs, fs_type, mlstrustedobject;
-
-# File types
-type unlabeled, file_type;
-# Default type for anything under /system.
-type system_file, file_type;
-# Default type for anything under /data.
-type system_data_file, file_type, data_file_type;
-# /data/drm - DRM plugin data
-type drm_data_file, file_type, data_file_type;
-# /data/anr - ANR traces
-type anr_data_file, file_type, data_file_type, mlstrustedobject;
-# /data/tombstones - core dumps
-type tombstone_data_file, file_type, data_file_type;
-# /data/app - user-installed apps
-type apk_data_file, file_type, data_file_type;
-type apk_tmp_file, file_type, data_file_type, mlstrustedobject;
-# /data/app-private - forward-locked apps
-type apk_private_data_file, file_type, data_file_type;
-type apk_private_tmp_file, file_type, data_file_type, mlstrustedobject;
-# /data/dalvik-cache
-type dalvikcache_data_file, file_type, data_file_type;
-# /data/local - writable by shell
-type shell_data_file, file_type, data_file_type;
-# /data/gps
-type gps_data_file, file_type, data_file_type;
-
-# /data/misc subdirectories
-type adb_keys_file, file_type, data_file_type;
-type audio_data_file, file_type, data_file_type;
-type bluetooth_data_file, file_type, data_file_type;
-type camera_data_file, file_type, data_file_type;
-type keystore_data_file, file_type, data_file_type;
-type media_data_file, file_type, data_file_type;
-type media_rw_data_file, file_type, data_file_type;
-type nfc_data_file, file_type, data_file_type;
-type radio_data_file, file_type, data_file_type;
-type systemkeys_data_file, file_type, data_file_type;
-type vpn_data_file, file_type, data_file_type;
-type wifi_data_file, file_type, data_file_type;
-type zoneinfo_data_file, file_type, data_file_type;
-
-# Compatibility with type names used in vanilla Android 4.3 and 4.4.
-typealias audio_data_file alias audio_firmware_file;
-# /data/data subdirectories - app sandboxes
-type app_data_file, file_type, data_file_type;
-type platform_app_data_file, file_type, data_file_type, mlstrustedobject;
-# Default type for anything under /cache
-type cache_file, file_type, mlstrustedobject;
-# Type for /cache/.*\.{data|restore} and default
-# type for anything under /cache/backup
-type cache_backup_file, file_type, mlstrustedobject;
-# Default type for anything under /efs
-type efs_file, file_type;
-# Type for wallpaper file.
-type wallpaper_file, file_type, mlstrustedobject;
-# /mnt/asec
-type asec_apk_file, file_type, data_file_type;
-# Elements of asec files (/mnt/asec) that are world readable
-type asec_public_file, file_type, data_file_type;
-# /data/app-asec
-type asec_image_file, file_type, data_file_type;
-# /data/backup and /data/secure/backup
-type backup_data_file, file_type, data_file_type, mlstrustedobject;
-# For /data/security
-type security_file, file_type;
-# All devices have bluetooth efs files. But they
-# vary per device, so this type is used in per
-# device policy
-type bluetooth_efs_file, file_type;
-# Downloaded files
-type download_file, file_type;
-
-# Socket types
-type adbd_socket, file_type;
-type bluetooth_socket, file_type;
-type dnsproxyd_socket, file_type, mlstrustedobject;
-type dumpstate_socket, file_type;
-type gps_socket, file_type;
-type installd_socket, file_type;
-type keystore_socket, file_type;
-type lmkd_socket, file_type;
-type logd_debug, file_type;
-type logd_socket, file_type;
-type logdr_socket, file_type;
-type logdw_socket, file_type;
-type mdns_socket, file_type;
-type netd_socket, file_type;
-type property_socket, file_type;
-type qemud_socket, file_type;
-type racoon_socket, file_type;
-type rild_socket, file_type;
-type rild_debug_socket, file_type;
-type system_wpa_socket, file_type;
-type system_ndebug_socket, file_type;
-type vold_socket, file_type;
-type wpa_socket, file_type;
-type zygote_socket, file_type;
-
-# UART (for GPS) control proc file
-type gps_control, file_type;
-
-# Allow files to be created in their appropriate filesystems.
-allow fs_type self:filesystem associate;
-allow sysfs_type sysfs:filesystem associate;
-allow file_type labeledfs:filesystem associate;
-allow file_type tmpfs:filesystem associate;
-allow file_type rootfs:filesystem associate;
-allow dev_type tmpfs:filesystem associate;
-#line 1 "external/sepolicy/gpsd.te"
-# gpsd - GPS daemon
-type gpsd, domain;
-
-#line 3
-typeattribute gpsd mlstrustedsubject;
-#line 3
-typeattribute gpsd unconfineddomain;
-#line 3
-
-type gpsd_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init gpsd_exec:file { getattr open read execute };
-#line 6
-allow init gpsd:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow gpsd gpsd_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow gpsd init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init gpsd:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init gpsd:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init gpsd_exec:process gpsd;
-#line 6
-
-#line 6
-
-#line 6
-type gpsd_tmpfs, file_type;
-#line 6
-type_transition gpsd tmpfs:file gpsd_tmpfs;
-#line 6
-allow gpsd gpsd_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute gpsd netdomain;
-#line 7
-
-allow gpsd gps_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow gpsd gps_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Socket is created by the daemon, not by init, and under /data/gps,
-# not under /dev/socket.
-type_transition gpsd gps_data_file:sock_file gps_socket;
-allow gpsd gps_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# XXX Label sysfs files with a specific type?
-allow gpsd sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-allow gpsd gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Execute the shell or system commands.
-allow gpsd shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow gpsd system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-#line 1 "external/sepolicy/hci_attach.te"
-type hci_attach, domain;
-type hci_attach_exec, exec_type, file_type;
-
-
-#line 4
-
-#line 4
-# Allow the necessary permissions.
-#line 4
-
-#line 4
-# Old domain may exec the file and transition to the new domain.
-#line 4
-allow init hci_attach_exec:file { getattr open read execute };
-#line 4
-allow init hci_attach:process transition;
-#line 4
-# New domain is entered by executing the file.
-#line 4
-allow hci_attach hci_attach_exec:file { entrypoint read execute };
-#line 4
-# New domain can send SIGCHLD to its caller.
-#line 4
-allow hci_attach init:process sigchld;
-#line 4
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 4
-dontaudit init hci_attach:process noatsecure;
-#line 4
-# XXX dontaudit candidate but requires further study.
-#line 4
-allow init hci_attach:process { siginh rlimitinh };
-#line 4
-
-#line 4
-# Make the transition occur by default.
-#line 4
-type_transition init hci_attach_exec:process hci_attach;
-#line 4
-
-#line 4
-
-#line 4
-type hci_attach_tmpfs, file_type;
-#line 4
-type_transition hci_attach tmpfs:file hci_attach_tmpfs;
-#line 4
-allow hci_attach hci_attach_tmpfs:file { read write };
-#line 4
-
-#line 4
-
-
-allow hci_attach kernel:system module_request;
-allow hci_attach hci_attach_dev:chr_file { { getattr open read ioctl lock } { open append write } };
-allow hci_attach bluetooth_efs_file:dir { open getattr read search ioctl };
-allow hci_attach bluetooth_efs_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-
-allow healthd rootfs:file { read entrypoint };
-
-#line 6
-type_transition healthd device:chr_file klog_device "__kmsg__";
-#line 6
-allow healthd klog_device:chr_file { create open write unlink };
-#line 6
-allow healthd device:dir { write add_name remove_name };
-#line 6
-
-# /dev/__null__ created by init prior to policy load,
-# open fd inherited by healthd.
-allow healthd tmpfs:chr_file { read write };
-
-allow healthd self:capability { net_admin mknod };
-allow healthd self:capability2 block_suspend;
-allow healthd self:netlink_kobject_uevent_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-
-#line 14
-# Call the servicemanager and transfer references to it.
-#line 14
-allow healthd servicemanager:binder { call transfer };
-#line 14
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 14
-# all domains in domain.te.
-#line 14
-
-
-#line 15
-typeattribute healthd binderservicedomain;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow healthd system_server:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow system_server healthd:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow healthd system_server:fd use;
-#line 16
-
-
-###
-### healthd: charger mode
-###
-
-allow healthd graphics_device:dir { open getattr read search ioctl };
-allow healthd graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow healthd input_device:dir { open getattr read search ioctl };
-allow healthd input_device:chr_file { getattr open read ioctl lock };
-allow healthd ashmem_device:chr_file execute;
-allow healthd self:process execmem;
-#line 1 "external/sepolicy/hostapd.te"
-# userspace wifi access points
-type hostapd, domain;
-
-#line 3
-typeattribute hostapd mlstrustedsubject;
-#line 3
-typeattribute hostapd unconfineddomain;
-#line 3
-
-type hostapd_exec, exec_type, file_type;
-
-allow hostapd self:capability { net_admin net_raw setuid setgid };
-allow hostapd self:netlink_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow hostapd self:packet_socket { create write read };
-allow hostapd self:netlink_route_socket { bind create write nlmsg_write read };
-allow hostapd self:udp_socket { create ioctl };
-
-allow hostapd wifi_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow hostapd wifi_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow hostapd wpa_socket:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow hostapd wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow hostapd netd:fd use;
-allow hostapd netd:udp_socket { read write };
-allow hostapd netd:netlink_kobject_uevent_socket { read write };
-allow hostapd netd:netlink_nflog_socket { read write };
-allow hostapd netd:netlink_route_socket { read write };
-allow hostapd netd:unix_stream_socket { read write };
-allow hostapd netd:fifo_file { read write };
-#line 1 "external/sepolicy/init_shell.te"
-# Restricted domain for shell processes spawned by init
-type init_shell, domain, shelldomain;
-
-#line 3
-# Allow the necessary permissions.
-#line 3
-
-#line 3
-# Old domain may exec the file and transition to the new domain.
-#line 3
-allow init shell_exec:file { getattr open read execute };
-#line 3
-allow init init_shell:process transition;
-#line 3
-# New domain is entered by executing the file.
-#line 3
-allow init_shell shell_exec:file { entrypoint read execute };
-#line 3
-# New domain can send SIGCHLD to its caller.
-#line 3
-allow init_shell init:process sigchld;
-#line 3
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 3
-dontaudit init init_shell:process noatsecure;
-#line 3
-# XXX dontaudit candidate but requires further study.
-#line 3
-allow init init_shell:process { siginh rlimitinh };
-#line 3
-
-#line 3
-# Make the transition occur by default.
-#line 3
-type_transition init shell_exec:process init_shell;
-#line 3
-
-
-#line 4
-typeattribute init_shell mlstrustedsubject;
-#line 4
-typeattribute init_shell unconfineddomain;
-#line 4
-
-
-# inherits from shelldomain.te
-#line 1 "external/sepolicy/init.te"
-# init switches to init domain (via init.rc).
-type init, domain;
-# init is unconfined.
-
-#line 4
-typeattribute init mlstrustedsubject;
-#line 4
-typeattribute init unconfineddomain;
-#line 4
-
-
-#line 5
-type init_tmpfs, file_type;
-#line 5
-type_transition init tmpfs:file init_tmpfs;
-#line 5
-allow init init_tmpfs:file { read write };
-#line 5
-
-
-#line 6
-typeattribute init relabeltodomain;
-#line 6
-
-# add a rule to handle unlabelled mounts
-allow init unlabeled:filesystem mount;
-
-allow init self:capability { sys_rawio mknod };
-
-allow init dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-allow init fs_type:filesystem *;
-allow init {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow init kernel:security load_policy;
-allow init usermodehelper:file { { getattr open read ioctl lock } { open append write } };
-allow init proc_security:file { { getattr open read ioctl lock } { open append write } };
-
-# Transitions to seclabel processes in init.rc
-allow init adbd:process transition;
-allow init healthd:process transition;
-allow init recovery:process transition;
-allow init shell:process transition;
-allow init ueventd:process transition;
-allow init watchdogd:process transition;
-#line 1 "external/sepolicy/inputflinger.te"
-# inputflinger
-type inputflinger, domain;
-
-#line 3
-typeattribute inputflinger mlstrustedsubject;
-#line 3
-typeattribute inputflinger unconfineddomain;
-#line 3
-
-type inputflinger_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init inputflinger_exec:file { getattr open read execute };
-#line 6
-allow init inputflinger:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow inputflinger inputflinger_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow inputflinger init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init inputflinger:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init inputflinger:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init inputflinger_exec:process inputflinger;
-#line 6
-
-#line 6
-
-#line 6
-type inputflinger_tmpfs, file_type;
-#line 6
-type_transition inputflinger tmpfs:file inputflinger_tmpfs;
-#line 6
-allow inputflinger inputflinger_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow inputflinger servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-typeattribute inputflinger binderservicedomain;
-#line 8
-
-#line 1 "external/sepolicy/installd.te"
-# installer daemon
-type installd, domain;
-type installd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init installd_exec:file { getattr open read execute };
-#line 5
-allow init installd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow installd installd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow installd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init installd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init installd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init installd_exec:process installd;
-#line 5
-
-#line 5
-
-#line 5
-type installd_tmpfs, file_type;
-#line 5
-type_transition installd tmpfs:file installd_tmpfs;
-#line 5
-allow installd installd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute installd relabeltodomain;
-#line 6
-
-typeattribute installd mlstrustedsubject;
-allow installd self:capability { chown dac_override fowner fsetid setgid setuid };
-allow installd system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow installd system_data_file:lnk_file create;
-allow installd dalvikcache_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow installd data_file_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow installd data_file_type:dir { relabelfrom relabelto };
-allow installd data_file_type:{ { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } { getattr unlink };
-allow installd apk_data_file:file { getattr open read ioctl lock };
-allow installd apk_tmp_file:file { getattr open read ioctl lock };
-allow installd system_file:file { getattr execute execute_no_trans };
-allow installd cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow installd download_file:dir { { open getattr read search ioctl } write remove_name };
-allow installd download_file:file { { getattr open read ioctl lock } unlink };
-dontaudit installd self:capability sys_admin;
-# Check validity of SELinux context before use.
-
-#line 23
-allow installd selinuxfs:dir { open getattr read search ioctl };
-#line 23
-allow installd selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 23
-allow installd kernel:security check_context;
-#line 23
-
-# Read /seapp_contexts and /data/security/seapp_contexts
-
-#line 25
-allow installd security_file:dir { open getattr read search ioctl };
-#line 25
-allow installd security_file:file { getattr open read ioctl lock };
-#line 25
-allow installd security_file:lnk_file { getattr open read ioctl lock };
-#line 25
-allow installd selinuxfs:dir { open getattr read search ioctl };
-#line 25
-allow installd selinuxfs:file { getattr open read ioctl lock };
-#line 25
-allow installd rootfs:dir { open getattr read search ioctl };
-#line 25
-allow installd rootfs:file { getattr open read ioctl lock };
-#line 25
-
-# ASEC
-allow installd platform_app_data_file:lnk_file { create setattr };
-allow installd app_data_file:lnk_file { create setattr };
-allow installd asec_apk_file:file { getattr open read ioctl lock };
-allow installd bluetooth_data_file:lnk_file { create setattr };
-allow installd nfc_data_file:lnk_file { create setattr };
-allow installd radio_data_file:lnk_file { create setattr };
-allow installd shell_data_file:lnk_file { create setattr };
-#line 1 "external/sepolicy/isolated_app.te"
-###
-### Services with isolatedProcess=true in their manifest.
-###
-### This file defines the rules for isolated apps. An "isolated
-### app" is an APP with UID between AID_ISOLATED_START (99000)
-### and AID_ISOLATED_END (99999).
-###
-### isolated_app includes all the appdomain rules, plus the
-### additional following rules:
-###
-
-type isolated_app, domain;
-
-#line 13
-typeattribute isolated_app appdomain;
-#line 13
-# Label ashmem objects with our own unique type.
-#line 13
-
-#line 13
-type isolated_app_tmpfs, file_type;
-#line 13
-type_transition isolated_app tmpfs:file isolated_app_tmpfs;
-#line 13
-allow isolated_app isolated_app_tmpfs:file { read write };
-#line 13
-
-#line 13
-# Map with PROT_EXEC.
-#line 13
-allow isolated_app isolated_app_tmpfs:file execute;
-#line 13
-
-
-# Already connected, unnamed sockets being passed over some other IPC
-# hence no sock_file or connectto permission. This appears to be how
-# Chrome works, may need to be updated as more apps using isolated services
-# are examined.
-allow isolated_app appdomain:unix_stream_socket { read write };
-
-allow isolated_app dalvikcache_data_file:file execute;
-allow isolated_app apk_data_file:dir getattr;
-#line 1 "external/sepolicy/kernel.te"
-# Life begins with the kernel.
-type kernel, domain;
-
-allow kernel init:process dyntransition;
-
-# The kernel is unconfined.
-
-#line 7
-typeattribute kernel mlstrustedsubject;
-#line 7
-typeattribute kernel unconfineddomain;
-#line 7
-
-
-#line 8
-typeattribute kernel relabeltodomain;
-#line 8
-
-
-allow kernel {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow kernel unlabeled:filesystem mount;
-allow kernel fs_type:filesystem *;
-
-# Initial setenforce by init prior to switching to init domain.
-allow kernel self:security setenforce;
-
-# Set checkreqprot by init.rc prior to switching to init domain.
-allow kernel self:security setcheckreqprot;
-
-# For operations performed by kernel or init prior to switching to init domain.
-## TODO: Investigate whether it is safe to remove these
-allow kernel self:capability { sys_rawio mknod };
-auditallow kernel self:capability { sys_rawio mknod };
-allow kernel dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-auditallow kernel dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/keystore.te"
-type keystore, domain;
-type keystore_exec, exec_type, file_type;
-
-# keystore daemon
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init keystore_exec:file { getattr open read execute };
-#line 5
-allow init keystore:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow keystore keystore_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow keystore init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init keystore:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init keystore:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init keystore_exec:process keystore;
-#line 5
-
-#line 5
-
-#line 5
-type keystore_tmpfs, file_type;
-#line 5
-type_transition keystore tmpfs:file keystore_tmpfs;
-#line 5
-allow keystore keystore_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute keystore mlstrustedsubject;
-
-#line 7
-# Call the servicemanager and transfer references to it.
-#line 7
-allow keystore servicemanager:binder { call transfer };
-#line 7
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 7
-# all domains in domain.te.
-#line 7
-
-
-#line 8
-typeattribute keystore binderservicedomain;
-#line 8
-
-allow keystore keystore_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow keystore keystore_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow keystore keystore_exec:file { getattr };
-allow keystore tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow keystore tee:unix_stream_socket connectto;
-#line 1 "external/sepolicy/lmkd.te"
-# lmkd low memory killer daemon
-type lmkd, domain;
-type lmkd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init lmkd_exec:file { getattr open read execute };
-#line 5
-allow init lmkd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow lmkd lmkd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow lmkd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init lmkd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init lmkd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init lmkd_exec:process lmkd;
-#line 5
-
-#line 5
-
-#line 5
-type lmkd_tmpfs, file_type;
-#line 5
-type_transition lmkd tmpfs:file lmkd_tmpfs;
-#line 5
-allow lmkd lmkd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-allow lmkd self:capability { dac_override sys_resource };
-
-## Open and write to /proc/PID/oom_score_adj
-## TODO: maybe scope this down?
-
-#line 11
-allow lmkd appdomain:dir { open getattr read search ioctl };
-#line 11
-allow lmkd appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 11
-
-allow lmkd appdomain:file write;
-
-#line 13
-allow lmkd system_server:dir { open getattr read search ioctl };
-#line 13
-allow lmkd system_server:{ file lnk_file } { getattr open read ioctl lock };
-#line 13
-
-allow lmkd system_server:file write;
-
-## Writes to /sys/module/lowmemorykiller/parameters/minfree
-allow lmkd sysfs_lowmemorykiller:file { open append write };
-#line 1 "external/sepolicy/logd.te"
-# android user-space log manager
-type logd, domain;
-type logd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init logd_exec:file { getattr open read execute };
-#line 5
-allow init logd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow logd logd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow logd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init logd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init logd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init logd_exec:process logd;
-#line 5
-
-#line 5
-
-#line 5
-type logd_tmpfs, file_type;
-#line 5
-type_transition logd tmpfs:file logd_tmpfs;
-#line 5
-allow logd logd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-allow logd self:unix_stream_socket *;
-
-allow logd self:capability { setuid setgid sys_nice };
-
-
-#line 10
-allow logd domain:dir { open getattr read search ioctl };
-#line 10
-allow logd domain:{ file lnk_file } { getattr open read ioctl lock };
-#line 10
-
-
-#line 17
-
-
-###
-### Neverallow rules
-###
-### logd should NEVER do any of this
-
-# Block device access.
-neverallow logd dev_type:blk_file { read write };
-
-# ptrace any other app
-neverallow logd domain:process ptrace;
-
-# Write to /system.
-neverallow logd system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Write to files in /data/data or system files on /data
-neverallow logd { app_data_file system_data_file }:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-#line 1 "external/sepolicy/media_app.te"
-###
-### Apps signed with the media key.
-###
-
-type media_app, domain;
-
-#line 6
-typeattribute media_app appdomain;
-#line 6
-# Label ashmem objects with our own unique type.
-#line 6
-
-#line 6
-type media_app_tmpfs, file_type;
-#line 6
-type_transition media_app tmpfs:file media_app_tmpfs;
-#line 6
-allow media_app media_app_tmpfs:file { read write };
-#line 6
-
-#line 6
-# Map with PROT_EXEC.
-#line 6
-allow media_app media_app_tmpfs:file execute;
-#line 6
-
-
-#line 7
-typeattribute media_app platformappdomain;
-#line 7
-typeattribute media_app mlstrustedsubject;
-#line 7
-
-
-#line 8
-typeattribute media_app binderservicedomain;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute media_app netdomain;
-#line 10
-
-# Access /dev/mtp_usb.
-allow media_app mtp_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# Write to /cache.
-allow media_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow media_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Stat /cache/lost+found
-allow media_app unlabeled:file getattr;
-allow media_app unlabeled:dir getattr;
-# Stat /cache/backup
-allow media_app cache_backup_file:file getattr;
-allow media_app cache_backup_file:dir getattr;
-# Read files in the rootdir (in particular, file_contexts for restorecon).
-allow media_app rootfs:file { getattr open read ioctl lock };
-allow media_app download_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow media_app download_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Allow platform apps to mark platform app data files as download files
-
-#line 27
-typeattribute media_app relabeltodomain;
-#line 27
-
-allow media_app platform_app_data_file:dir relabelfrom;
-allow media_app download_file:dir relabelto;
-#line 1 "external/sepolicy/mediaserver.te"
-# mediaserver - multimedia daemon
-type mediaserver, domain;
-
-#line 3
-typeattribute mediaserver mlstrustedsubject;
-#line 3
-typeattribute mediaserver unconfineddomain;
-#line 3
-
-type mediaserver_exec, exec_type, file_type;
-
-typeattribute mediaserver mlstrustedsubject;
-
-
-#line 8
-typeattribute mediaserver netdomain;
-#line 8
-
-
-#line 9
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow init mediaserver_exec:file { getattr open read execute };
-#line 9
-allow init mediaserver:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow mediaserver mediaserver_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow mediaserver init:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit init mediaserver:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow init mediaserver:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition init mediaserver_exec:process mediaserver;
-#line 9
-
-#line 9
-
-#line 9
-type mediaserver_tmpfs, file_type;
-#line 9
-type_transition mediaserver tmpfs:file mediaserver_tmpfs;
-#line 9
-allow mediaserver mediaserver_tmpfs:file { read write };
-#line 9
-
-#line 9
-
-
-#line 10
-allow mediaserver property_socket:sock_file write;
-#line 10
-allow mediaserver init:unix_stream_socket connectto;
-#line 10
-
-
-
-#line 12
-allow mediaserver sdcard_type:dir { open getattr read search ioctl };
-#line 12
-allow mediaserver sdcard_type:{ file lnk_file } { getattr open read ioctl lock };
-#line 12
-
-
-
-#line 14
-# Call the servicemanager and transfer references to it.
-#line 14
-allow mediaserver servicemanager:binder { call transfer };
-#line 14
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 14
-# all domains in domain.te.
-#line 14
-
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow mediaserver binderservicedomain:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow binderservicedomain mediaserver:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow mediaserver binderservicedomain:fd use;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow mediaserver appdomain:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow appdomain mediaserver:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow mediaserver appdomain:fd use;
-#line 16
-
-
-#line 17
-typeattribute mediaserver binderservicedomain;
-#line 17
-
-
-allow mediaserver self:process execmem;
-allow mediaserver kernel:system module_request;
-allow mediaserver media_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow mediaserver media_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow mediaserver app_data_file:dir search;
-allow mediaserver app_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver platform_app_data_file:file { getattr read };
-allow mediaserver sdcard_type:file write;
-allow mediaserver { gpu_device graphics_device }:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver video_device:dir { open getattr read search ioctl };
-allow mediaserver video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver audio_device:dir { open getattr read search ioctl };
-allow mediaserver qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver audio_prop:property_service set;
-
-# Access audio devices at all.
-allow mediaserver audio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# XXX Label with a specific type?
-allow mediaserver sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-# XXX Why?
-allow mediaserver apk_data_file:file { read getattr };
-
-# Access camera device.
-allow mediaserver camera_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver rpmsg_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Inter System processes communicate over named pipe (FIFO)
-allow mediaserver system_server:fifo_file { getattr open read ioctl lock };
-
-# Camera data
-
-#line 52
-allow mediaserver camera_data_file:dir { open getattr read search ioctl };
-#line 52
-allow mediaserver camera_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 52
-
-
-#line 53
-allow mediaserver media_rw_data_file:dir { open getattr read search ioctl };
-#line 53
-allow mediaserver media_rw_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 53
-
-
-# Grant access to audio files to mediaserver
-allow mediaserver audio_data_file:dir { { open getattr read search ioctl } add_name write };
-allow mediaserver audio_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read/[write] to /proc/net/xt_qtaguid/ctrl and /dev/xt_qtaguid
-allow mediaserver qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-allow mediaserver qtaguid_device:chr_file { getattr open read ioctl lock };
-
-# Allow abstract socket connection
-allow mediaserver rild:unix_stream_socket { connectto read write setopt };
-
-# Needed on some devices for playing DRM protected content,
-# but seems expected and appropriate for all devices.
-
-#line 68
-allow mediaserver drmserver_socket:sock_file write;
-#line 68
-allow mediaserver drmserver:unix_stream_socket connectto;
-#line 68
-
-
-# Needed on some devices for playing audio on paired BT device,
-# but seems appropriate for all devices.
-
-#line 72
-allow mediaserver bluetooth_socket:sock_file write;
-#line 72
-allow mediaserver bluetooth:unix_stream_socket connectto;
-#line 72
-
-#line 1 "external/sepolicy/mtp.te"
-# vpn tunneling protocol manager
-type mtp, domain;
-
-#line 3
-typeattribute mtp mlstrustedsubject;
-#line 3
-typeattribute mtp unconfineddomain;
-#line 3
-
-type mtp_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init mtp_exec:file { getattr open read execute };
-#line 6
-allow init mtp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow mtp mtp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow mtp init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init mtp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init mtp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init mtp_exec:process mtp;
-#line 6
-
-#line 6
-
-#line 6
-type mtp_tmpfs, file_type;
-#line 6
-type_transition mtp tmpfs:file mtp_tmpfs;
-#line 6
-allow mtp mtp_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute mtp netdomain;
-#line 7
-
-
-# pptp policy
-allow mtp self:tcp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:rawip_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow mtp self:capability net_raw;
-allow mtp ppp:process signal;
-allow mtp port:tcp_socket name_connect;
-allow mtp vpn_data_file:dir search;
-#line 1 "external/sepolicy/netd.te"
-# network manager
-type netd, domain;
-type netd_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init netd_exec:file { getattr open read execute };
-#line 5
-allow init netd:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow netd netd_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow netd init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init netd:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init netd:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init netd_exec:process netd;
-#line 5
-
-#line 5
-
-#line 5
-type netd_tmpfs, file_type;
-#line 5
-type_transition netd tmpfs:file netd_tmpfs;
-#line 5
-allow netd netd_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute netd netdomain;
-#line 6
-
-
-allow netd self:capability { net_admin net_raw kill fsetid };
-allow netd self:netlink_kobject_uevent_socket *;
-allow netd self:netlink_route_socket *;
-allow netd self:netlink_nflog_socket *;
-allow netd self:rawip_socket *;
-allow netd self:unix_stream_socket *;
-allow netd shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow netd system_file:file { getattr execute execute_no_trans };
-allow netd devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# For /proc/sys/net/ipv[46]/route/flush.
-allow netd proc_net:file write;
-
-# For /sys/modules/bcmdhd/parameters/firmware_path
-# XXX Split into its own type.
-allow netd sysfs:file write;
-
-# Set dhcp lease for PAN connection
-
-#line 26
-allow netd property_socket:sock_file write;
-#line 26
-allow netd init:unix_stream_socket connectto;
-#line 26
-
-allow netd system_prop:property_service set;
-
-# Connect to PAN
-
-#line 30
-# Allow the necessary permissions.
-#line 30
-
-#line 30
-# Old domain may exec the file and transition to the new domain.
-#line 30
-allow netd dhcp_exec:file { getattr open read execute };
-#line 30
-allow netd dhcp:process transition;
-#line 30
-# New domain is entered by executing the file.
-#line 30
-allow dhcp dhcp_exec:file { entrypoint read execute };
-#line 30
-# New domain can send SIGCHLD to its caller.
-#line 30
-allow dhcp netd:process sigchld;
-#line 30
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 30
-dontaudit netd dhcp:process noatsecure;
-#line 30
-# XXX dontaudit candidate but requires further study.
-#line 30
-allow netd dhcp:process { siginh rlimitinh };
-#line 30
-
-#line 30
-# Make the transition occur by default.
-#line 30
-type_transition netd dhcp_exec:process dhcp;
-#line 30
-
-allow netd dhcp:process signal;
-
-# Needed to update /data/misc/wifi/hostapd.conf
-# TODO: See what we can do to reduce the need for
-# these capabilities
-allow netd self:capability { dac_override chown fowner };
-allow netd wifi_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow netd wifi_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-
-# Allow netd to spawn hostapd in it's own domain
-
-#line 41
-# Allow the necessary permissions.
-#line 41
-
-#line 41
-# Old domain may exec the file and transition to the new domain.
-#line 41
-allow netd hostapd_exec:file { getattr open read execute };
-#line 41
-allow netd hostapd:process transition;
-#line 41
-# New domain is entered by executing the file.
-#line 41
-allow hostapd hostapd_exec:file { entrypoint read execute };
-#line 41
-# New domain can send SIGCHLD to its caller.
-#line 41
-allow hostapd netd:process sigchld;
-#line 41
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 41
-dontaudit netd hostapd:process noatsecure;
-#line 41
-# XXX dontaudit candidate but requires further study.
-#line 41
-allow netd hostapd:process { siginh rlimitinh };
-#line 41
-
-#line 41
-# Make the transition occur by default.
-#line 41
-type_transition netd hostapd_exec:process hostapd;
-#line 41
-
-allow netd hostapd:process signal;
-
-# Allow netd to spawn dnsmasq in it's own domain
-
-#line 45
-# Allow the necessary permissions.
-#line 45
-
-#line 45
-# Old domain may exec the file and transition to the new domain.
-#line 45
-allow netd dnsmasq_exec:file { getattr open read execute };
-#line 45
-allow netd dnsmasq:process transition;
-#line 45
-# New domain is entered by executing the file.
-#line 45
-allow dnsmasq dnsmasq_exec:file { entrypoint read execute };
-#line 45
-# New domain can send SIGCHLD to its caller.
-#line 45
-allow dnsmasq netd:process sigchld;
-#line 45
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 45
-dontaudit netd dnsmasq:process noatsecure;
-#line 45
-# XXX dontaudit candidate but requires further study.
-#line 45
-allow netd dnsmasq:process { siginh rlimitinh };
-#line 45
-
-#line 45
-# Make the transition occur by default.
-#line 45
-type_transition netd dnsmasq_exec:process dnsmasq;
-#line 45
-
-allow netd dnsmasq:process signal;
-
-# Allow netd to start clatd in its own domain
-
-#line 49
-# Allow the necessary permissions.
-#line 49
-
-#line 49
-# Old domain may exec the file and transition to the new domain.
-#line 49
-allow netd clatd_exec:file { getattr open read execute };
-#line 49
-allow netd clatd:process transition;
-#line 49
-# New domain is entered by executing the file.
-#line 49
-allow clatd clatd_exec:file { entrypoint read execute };
-#line 49
-# New domain can send SIGCHLD to its caller.
-#line 49
-allow clatd netd:process sigchld;
-#line 49
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 49
-dontaudit netd clatd:process noatsecure;
-#line 49
-# XXX dontaudit candidate but requires further study.
-#line 49
-allow netd clatd:process { siginh rlimitinh };
-#line 49
-
-#line 49
-# Make the transition occur by default.
-#line 49
-type_transition netd clatd_exec:process clatd;
-#line 49
-
-allow netd clatd:process signal;
-
-# Support netd running mdnsd
-# TODO: prune this back further
-allow netd ctl_default_prop:property_service set;
-allow netd device:sock_file write;
-
-###
-### Neverallow rules
-###
-### netd should NEVER do any of this
-
-# Block device access.
-neverallow netd dev_type:blk_file { read write };
-
-# Setting SELinux enforcing status or booleans.
-neverallow netd kernel:security { setenforce setbool };
-
-# Load security policy.
-neverallow netd kernel:security load_policy;
-
-# ptrace any other app
-neverallow netd { domain }:process ptrace;
-
-# Write to /system.
-neverallow netd system_file:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-
-# Write to files in /data/data or system files on /data
-neverallow netd { app_data_file system_data_file }:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } write;
-#line 1 "external/sepolicy/net.te"
-# Network types
-type node, node_type;
-type netif, netif_type;
-type port, port_type;
-
-# Use network sockets.
-allow netdomain self:{ tcp_socket udp_socket } *;
-# Connect to ports.
-allow netdomain port_type:tcp_socket name_connect;
-# Bind to ports.
-allow netdomain node_type:{ tcp_socket udp_socket } node_bind;
-allow netdomain port_type:udp_socket name_bind;
-allow netdomain port_type:tcp_socket name_bind;
-# Get route information.
-allow netdomain self:netlink_route_socket { create bind read nlmsg_read };
-
-# Talks to netd via dnsproxyd socket.
-
-#line 18
-allow netdomain dnsproxyd_socket:sock_file write;
-#line 18
-allow netdomain netd:unix_stream_socket connectto;
-#line 18
-
-#line 1 "external/sepolicy/nfc.te"
-# nfc subsystem
-type nfc, domain;
-
-#line 3
-typeattribute nfc appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type nfc_tmpfs, file_type;
-#line 3
-type_transition nfc tmpfs:file nfc_tmpfs;
-#line 3
-allow nfc nfc_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow nfc nfc_tmpfs:file execute;
-#line 3
-
-
-#line 4
-typeattribute nfc binderservicedomain;
-#line 4
-
-
-# NFC device access.
-allow nfc nfc_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Data file accesses.
-allow nfc nfc_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow nfc nfc_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-allow nfc sysfs_nfc_power_writable:file { { getattr open read ioctl lock } { open append write } };
-allow nfc sysfs:file write;
-
-allow nfc sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow nfc sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/platform_app.te"
-###
-### Apps signed with the platform key.
-###
-
-type platform_app, domain;
-
-#line 6
-typeattribute platform_app mlstrustedsubject;
-#line 6
-typeattribute platform_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute platform_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type platform_app_tmpfs, file_type;
-#line 7
-type_transition platform_app tmpfs:file platform_app_tmpfs;
-#line 7
-allow platform_app platform_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow platform_app platform_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute platform_app platformappdomain;
-#line 8
-typeattribute platform_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute platform_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute platform_app bluetoothdomain;
-#line 12
-
-# Write to /cache.
-allow platform_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow platform_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Read from /data/local.
-allow platform_app shell_data_file:dir search;
-allow platform_app shell_data_file:file { open getattr read };
-allow platform_app shell_data_file:lnk_file read;
-# Populate /data/app/vmdl*.tmp, /data/app-private/vmdl*.tmp files
-# created by system server.
-allow platform_app { apk_tmp_file apk_private_tmp_file }:file { { getattr open read ioctl lock } { open append write } };
-allow platform_app apk_private_data_file:dir search;
-# ASEC
-allow platform_app asec_apk_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platform_app asec_apk_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Access download files.
-allow platform_app download_file:file { { getattr open read ioctl lock } { open append write } };
-# Allow BackupManagerService to backup all app domains
-allow platform_app appdomain:fifo_file write;
-
-#
-# Rules for all platform app domains.
-#
-
-# App sandbox file accesses.
-allow platformappdomain platform_app_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain platform_app_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow platformappdomain platform_app_data_file:file execute;
-# App sdcard file accesses
-allow platformappdomain sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Access to /data/media.
-allow platformappdomain media_rw_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow platformappdomain media_rw_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/ppp.te"
-# Point to Point Protocol daemon
-type ppp, domain;
-
-#line 3
-typeattribute ppp mlstrustedsubject;
-#line 3
-typeattribute ppp unconfineddomain;
-#line 3
-
-type ppp_device, dev_type;
-type ppp_exec, exec_type, file_type;
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow mtp ppp_exec:file { getattr open read execute };
-#line 6
-allow mtp ppp:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow ppp ppp_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow ppp mtp:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit mtp ppp:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow mtp ppp:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition mtp ppp_exec:process ppp;
-#line 6
-
-
-allow ppp mtp:socket { ioctl read getattr write setattr append bind connect getopt setopt shutdown };
-allow ppp ppp_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ppp self:capability net_admin;
-allow ppp self:udp_socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow ppp system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow ppp vpn_data_file:dir { open search write add_name remove_name };
-allow ppp vpn_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow ppp mtp:fd use;
-#line 1 "external/sepolicy/property.te"
-type default_prop, property_type;
-type shell_prop, property_type;
-type debug_prop, property_type;
-type debuggerd_prop, property_type;
-type radio_prop, property_type;
-type system_prop, property_type;
-type vold_prop, property_type;
-type rild_prop, property_type;
-type ctl_default_prop, property_type;
-type ctl_dumpstate_prop, property_type;
-type ctl_rildaemon_prop, property_type;
-type audio_prop, property_type;
-type security_prop, property_type;
-type bluetooth_prop, property_type;
-type powerctl_prop, property_type;
-#line 1 "external/sepolicy/qemud.te"
-# qemu support daemon
-type qemud, domain;
-type qemud_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init qemud_exec:file { getattr open read execute };
-#line 5
-allow init qemud:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow qemud qemud_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow qemud init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init qemud:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init qemud:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init qemud_exec:process qemud;
-#line 5
-
-#line 5
-
-#line 5
-type qemud_tmpfs, file_type;
-#line 5
-type_transition qemud tmpfs:file qemud_tmpfs;
-#line 5
-allow qemud qemud_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute qemud mlstrustedsubject;
-#line 6
-typeattribute qemud unconfineddomain;
-#line 1 "external/sepolicy/racoon.te"
-# IKE key management daemon
-type racoon, domain;
-
-#line 3
-typeattribute racoon mlstrustedsubject;
-#line 3
-typeattribute racoon unconfineddomain;
-#line 3
-
-type racoon_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init racoon_exec:file { getattr open read execute };
-#line 6
-allow init racoon:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow racoon racoon_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow racoon init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init racoon:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init racoon:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init racoon_exec:process racoon;
-#line 6
-
-#line 6
-
-#line 6
-type racoon_tmpfs, file_type;
-#line 6
-type_transition racoon tmpfs:file racoon_tmpfs;
-#line 6
-allow racoon racoon_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-typeattribute racoon mlstrustedsubject;
-
-
-#line 9
-# Call the server domain and optionally transfer references to it.
-#line 9
-allow racoon servicemanager:binder { call transfer };
-#line 9
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 9
-allow servicemanager racoon:binder transfer;
-#line 9
-# Receive and use open files from the server.
-#line 9
-allow racoon servicemanager:fd use;
-#line 9
-
-
-#line 10
-# Call the server domain and optionally transfer references to it.
-#line 10
-allow racoon keystore:binder { call transfer };
-#line 10
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 10
-allow keystore racoon:binder transfer;
-#line 10
-# Receive and use open files from the server.
-#line 10
-allow racoon keystore:fd use;
-#line 10
-
-
-allow racoon tun_device:chr_file { getattr open read ioctl lock };
-allow racoon cgroup:dir { add_name create };
-allow racoon kernel:system module_request;
-allow racoon port:udp_socket name_bind;
-allow racoon node:udp_socket node_bind;
-
-allow racoon self:{ key_socket udp_socket } { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-allow racoon self:tun_socket create;
-allow racoon self:capability { net_admin net_bind_service net_raw setuid };
-
-# XXX: should we give ip-up-vpn its own label (currently racoon domain)
-allow racoon system_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow racoon vpn_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow racoon vpn_data_file:dir { open search write add_name remove_name };
-#line 1 "external/sepolicy/radio.te"
-# phone subsystem
-type radio, domain;
-
-#line 3
-typeattribute radio appdomain;
-#line 3
-# Label ashmem objects with our own unique type.
-#line 3
-
-#line 3
-type radio_tmpfs, file_type;
-#line 3
-type_transition radio tmpfs:file radio_tmpfs;
-#line 3
-allow radio radio_tmpfs:file { read write };
-#line 3
-
-#line 3
-# Map with PROT_EXEC.
-#line 3
-allow radio radio_tmpfs:file execute;
-#line 3
-
-
-#line 4
-typeattribute radio netdomain;
-#line 4
-
-
-#line 5
-typeattribute radio bluetoothdomain;
-#line 5
-
-
-#line 6
-typeattribute radio binderservicedomain;
-#line 6
-
-
-# Talks to init via the property socket.
-
-#line 9
-allow radio property_socket:sock_file write;
-#line 9
-allow radio init:unix_stream_socket connectto;
-#line 9
-
-
-# Talks to rild via the rild socket.
-
-#line 12
-allow radio rild_socket:sock_file write;
-#line 12
-allow radio rild:unix_stream_socket connectto;
-#line 12
-
-
-# Data file accesses.
-allow radio radio_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow radio radio_data_file:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-allow radio alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Property service
-allow radio radio_prop:property_service set;
-
-# ctl interface
-allow radio ctl_rildaemon_prop:property_service set;
-#line 1 "external/sepolicy/recovery.te"
-# recovery console (used in recovery init.rc for /sbin/recovery)
-type recovery, domain;
-allow recovery rootfs:file entrypoint;
-
-#line 4
-typeattribute recovery mlstrustedsubject;
-#line 4
-typeattribute recovery unconfineddomain;
-#line 4
-
-
-#line 5
-typeattribute recovery relabeltodomain;
-#line 5
-
-
-allow recovery self:capability2 mac_admin;
-
-allow recovery {fs_type dev_type -kmem_device file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-allow recovery unlabeled:filesystem mount;
-allow recovery fs_type:filesystem *;
-
-# Required to e.g. wipe userdata/cache.
-allow recovery dev_type:blk_file { { getattr open read ioctl lock } { open append write } };
-
-allow recovery self:process execmem;
-allow recovery ashmem_device:chr_file execute;
-allow recovery tmpfs:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-## TODO: Investigate whether it is safe to remove these
-allow recovery self:capability { sys_rawio mknod };
-auditallow recovery self:capability { sys_rawio mknod };
-#line 1 "external/sepolicy/release_app.te"
-###
-### Apps signed with the release key (testkey in AOSP).
-###
-
-type release_app, domain;
-
-#line 6
-typeattribute release_app mlstrustedsubject;
-#line 6
-typeattribute release_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute release_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type release_app_tmpfs, file_type;
-#line 7
-type_transition release_app tmpfs:file release_app_tmpfs;
-#line 7
-allow release_app release_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow release_app release_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute release_app platformappdomain;
-#line 8
-typeattribute release_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute release_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute release_app bluetoothdomain;
-#line 12
-
-
-# Write to /cache.
-allow release_app cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow release_app cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/rild.te"
-# rild - radio interface layer daemon
-type rild, domain;
-
-#line 3
-typeattribute rild mlstrustedsubject;
-#line 3
-typeattribute rild unconfineddomain;
-#line 3
-
-type rild_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init rild_exec:file { getattr open read execute };
-#line 6
-allow init rild:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow rild rild_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow rild init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init rild:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init rild:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init rild_exec:process rild;
-#line 6
-
-#line 6
-
-#line 6
-type rild_tmpfs, file_type;
-#line 6
-type_transition rild tmpfs:file rild_tmpfs;
-#line 6
-allow rild rild_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-
-#line 7
-typeattribute rild netdomain;
-#line 7
-
-allow rild self:netlink_route_socket { setopt write };
-allow rild kernel:system module_request;
-
-#line 10
-allow rild property_socket:sock_file write;
-#line 10
-allow rild init:unix_stream_socket connectto;
-#line 10
-
-
-#line 11
-allow rild qemud_socket:sock_file write;
-#line 11
-allow rild qemud:unix_stream_socket connectto;
-#line 11
-
-allow rild self:capability { setuid net_admin net_raw };
-allow rild alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild radio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild radio_device:blk_file { getattr open read ioctl lock };
-allow rild qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow rild mtd_device:dir search;
-allow rild efs_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild efs_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow rild bluetooth_efs_file:file { getattr open read ioctl lock };
-allow rild bluetooth_efs_file:dir { open getattr read search ioctl };
-allow rild radio_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow rild radio_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild sdcard_type:dir { open getattr read search ioctl };
-allow rild system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow rild system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow rild system_file:file { getattr execute execute_no_trans };
-dontaudit rild self:capability sys_admin;
-
-# property service
-allow rild rild_prop:property_service set;
-allow rild radio_prop:property_service set;
-
-# Read/Write to uart driver (for GPS)
-allow rild gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-allow rild tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Allow rild to create, bind, read, write to itself through a netlink socket
-allow rild self:netlink_socket { create bind read write };
-
-allow rild self:netlink_kobject_uevent_socket { bind create getopt read setopt };
-
-# Access to wake locks
-allow rild sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-
-allow rild self:socket { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } };
-#line 1 "external/sepolicy/runas.te"
-type runas, domain, mlstrustedsubject;
-type runas_exec, exec_type, file_type;
-
-# ndk-gdb invokes adb shell run-as.
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow shell runas_exec:file { getattr open read execute };
-#line 5
-allow shell runas:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow runas runas_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow runas shell:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit shell runas:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow shell runas:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition shell runas_exec:process runas;
-#line 5
-
-allow runas adbd:process sigchld;
-allow runas shell:fd use;
-allow runas devpts:chr_file { read write ioctl };
-
-# run-as reads package information.
-allow runas system_data_file:file { getattr open read ioctl lock };
-
-# run-as checks and changes to the app data dir.
-dontaudit runas self:capability dac_override;
-allow runas app_data_file:dir { getattr search };
-
-# run-as switches to the app UID/GID.
-allow runas self:capability { setuid setgid };
-
-# run-as switches to the app security context.
-# read /seapp_contexts and /data/security/seapp_contexts
-
-#line 22
-allow runas security_file:dir { open getattr read search ioctl };
-#line 22
-allow runas security_file:file { getattr open read ioctl lock };
-#line 22
-allow runas security_file:lnk_file { getattr open read ioctl lock };
-#line 22
-allow runas selinuxfs:dir { open getattr read search ioctl };
-#line 22
-allow runas selinuxfs:file { getattr open read ioctl lock };
-#line 22
-allow runas rootfs:dir { open getattr read search ioctl };
-#line 22
-allow runas rootfs:file { getattr open read ioctl lock };
-#line 22
-
-
-#line 23
-allow runas selinuxfs:dir { open getattr read search ioctl };
-#line 23
-allow runas selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 23
-allow runas kernel:security check_context;
-#line 23
- # validate context
-allow runas { appdomain -system_app }:process dyntransition; # setcon
-#line 1 "external/sepolicy/sdcardd.te"
-type sdcardd, domain;
-type sdcardd_exec, exec_type, file_type;
-
-
-#line 4
-
-#line 4
-# Allow the necessary permissions.
-#line 4
-
-#line 4
-# Old domain may exec the file and transition to the new domain.
-#line 4
-allow init sdcardd_exec:file { getattr open read execute };
-#line 4
-allow init sdcardd:process transition;
-#line 4
-# New domain is entered by executing the file.
-#line 4
-allow sdcardd sdcardd_exec:file { entrypoint read execute };
-#line 4
-# New domain can send SIGCHLD to its caller.
-#line 4
-allow sdcardd init:process sigchld;
-#line 4
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 4
-dontaudit init sdcardd:process noatsecure;
-#line 4
-# XXX dontaudit candidate but requires further study.
-#line 4
-allow init sdcardd:process { siginh rlimitinh };
-#line 4
-
-#line 4
-# Make the transition occur by default.
-#line 4
-type_transition init sdcardd_exec:process sdcardd;
-#line 4
-
-#line 4
-
-#line 4
-type sdcardd_tmpfs, file_type;
-#line 4
-type_transition sdcardd tmpfs:file sdcardd_tmpfs;
-#line 4
-allow sdcardd sdcardd_tmpfs:file { read write };
-#line 4
-
-#line 4
-
-
-allow sdcardd cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd fuse_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow sdcardd rootfs:dir mounton;
-allow sdcardd sdcard_type:filesystem mount;
-allow sdcardd self:capability { setuid setgid dac_override sys_admin sys_resource };
-
-allow sdcardd sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-type_transition sdcardd system_data_file:{ dir file } media_rw_data_file;
-allow sdcardd media_rw_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd media_rw_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read /data/system/packages.list.
-allow sdcardd system_data_file:file { getattr open read ioctl lock };
-
-# Compatibility for existing devices with /data/media in system_data_file.
-# TODO: Remove these lines after we have guaranteed that /data/media has been relabeled to media_rw_data_file.
-allow sdcardd system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow sdcardd system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 1 "external/sepolicy/servicemanager.te"
-# servicemanager - the Binder context manager
-type servicemanager, domain;
-type servicemanager_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init servicemanager_exec:file { getattr open read execute };
-#line 5
-allow init servicemanager:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow servicemanager servicemanager_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow servicemanager init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init servicemanager:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init servicemanager:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init servicemanager_exec:process servicemanager;
-#line 5
-
-#line 5
-
-#line 5
-type servicemanager_tmpfs, file_type;
-#line 5
-type_transition servicemanager tmpfs:file servicemanager_tmpfs;
-#line 5
-allow servicemanager servicemanager_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-# Note that we do not use the binder_* macros here.
-# servicemanager is unique in that it only provides
-# name service (aka context manager) for Binder.
-# As such, it only ever receives and transfers other references
-# created by other domains. It never passes its own references
-# or initiates a Binder IPC.
-allow servicemanager self:binder set_context_mgr;
-allow servicemanager domain:binder transfer;
-#line 1 "external/sepolicy/shared_app.te"
-###
-### Apps signed with the shared key.
-###
-
-type shared_app, domain;
-
-#line 6
-typeattribute shared_app mlstrustedsubject;
-#line 6
-typeattribute shared_app unconfineddomain;
-#line 6
-
-
-#line 7
-typeattribute shared_app appdomain;
-#line 7
-# Label ashmem objects with our own unique type.
-#line 7
-
-#line 7
-type shared_app_tmpfs, file_type;
-#line 7
-type_transition shared_app tmpfs:file shared_app_tmpfs;
-#line 7
-allow shared_app shared_app_tmpfs:file { read write };
-#line 7
-
-#line 7
-# Map with PROT_EXEC.
-#line 7
-allow shared_app shared_app_tmpfs:file execute;
-#line 7
-
-
-#line 8
-typeattribute shared_app platformappdomain;
-#line 8
-typeattribute shared_app mlstrustedsubject;
-#line 8
-
-# Access the network.
-
-#line 10
-typeattribute shared_app netdomain;
-#line 10
-
-# Access bluetooth.
-
-#line 12
-typeattribute shared_app bluetoothdomain;
-#line 12
-
-#line 1 "external/sepolicy/shelldomain.te"
-# Rules for all shell domains (e.g. console service and adb shell).
-
-# Access /data/local/tmp.
-allow shelldomain shell_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow shelldomain shell_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow shelldomain shell_data_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# Access sdcard.
-allow shelldomain sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow shelldomain sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# adb bugreport
-
-#line 13
-allow shelldomain dumpstate_socket:sock_file write;
-#line 13
-allow shelldomain dumpstate:unix_stream_socket connectto;
-#line 13
-
-
-allow shelldomain rootfs:dir { open getattr read search ioctl };
-allow shelldomain devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain console_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain input_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow shelldomain system_file:file { getattr execute execute_no_trans };
-allow shelldomain shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-allow shelldomain zygote_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-
-#line 24
-allow shelldomain apk_data_file:dir { open getattr read search ioctl };
-#line 24
-allow shelldomain apk_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 24
-
-
-# Set properties.
-
-#line 27
-allow shelldomain property_socket:sock_file write;
-#line 27
-allow shelldomain init:unix_stream_socket connectto;
-#line 27
-
-allow shelldomain shell_prop:property_service set;
-allow shelldomain ctl_dumpstate_prop:property_service set;
-allow shelldomain debug_prop:property_service set;
-allow shelldomain powerctl_prop:property_service set;
-
-# ndk-gdb invokes adb shell ps to find the app PID.
-
-#line 34
-allow shelldomain { appdomain -system_app }:dir { open getattr read search ioctl };
-#line 34
-allow shelldomain { appdomain -system_app }:{ file lnk_file } { getattr open read ioctl lock };
-#line 34
-
-
-# ndk-gdb invokes adb shell ls to check the app data dir.
-allow shelldomain app_data_file:dir search;
-
-# ps and ps -Z output for app processes.
-
-#line 40
-allow shelldomain appdomain:dir { open getattr read search ioctl };
-#line 40
-allow shelldomain appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 40
-
-allow shelldomain appdomain:process getattr;
-#line 1 "external/sepolicy/shell.te"
-# Domain for shell processes spawned by ADB
-type shell, domain, shelldomain, mlstrustedsubject;
-type shell_exec, exec_type, file_type;
-
-# Create and use network sockets.
-
-#line 6
-typeattribute shell netdomain;
-#line 6
-
-
-# Run app_process.
-# XXX Transition into its own domain?
-
-#line 10
-typeattribute shell appdomain;
-#line 10
-# Label ashmem objects with our own unique type.
-#line 10
-
-#line 10
-type shell_tmpfs, file_type;
-#line 10
-type_transition shell tmpfs:file shell_tmpfs;
-#line 10
-allow shell shell_tmpfs:file { read write };
-#line 10
-
-#line 10
-# Map with PROT_EXEC.
-#line 10
-allow shell shell_tmpfs:file execute;
-#line 10
-
-
-# inherits from shelldomain.te
-#line 1 "external/sepolicy/surfaceflinger.te"
-# surfaceflinger - display compositor service
-type surfaceflinger, domain;
-
-#line 3
-typeattribute surfaceflinger mlstrustedsubject;
-#line 3
-typeattribute surfaceflinger unconfineddomain;
-#line 3
-
-type surfaceflinger_exec, exec_type, file_type;
-
-
-#line 6
-
-#line 6
-# Allow the necessary permissions.
-#line 6
-
-#line 6
-# Old domain may exec the file and transition to the new domain.
-#line 6
-allow init surfaceflinger_exec:file { getattr open read execute };
-#line 6
-allow init surfaceflinger:process transition;
-#line 6
-# New domain is entered by executing the file.
-#line 6
-allow surfaceflinger surfaceflinger_exec:file { entrypoint read execute };
-#line 6
-# New domain can send SIGCHLD to its caller.
-#line 6
-allow surfaceflinger init:process sigchld;
-#line 6
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 6
-dontaudit init surfaceflinger:process noatsecure;
-#line 6
-# XXX dontaudit candidate but requires further study.
-#line 6
-allow init surfaceflinger:process { siginh rlimitinh };
-#line 6
-
-#line 6
-# Make the transition occur by default.
-#line 6
-type_transition init surfaceflinger_exec:process surfaceflinger;
-#line 6
-
-#line 6
-
-#line 6
-type surfaceflinger_tmpfs, file_type;
-#line 6
-type_transition surfaceflinger tmpfs:file surfaceflinger_tmpfs;
-#line 6
-allow surfaceflinger surfaceflinger_tmpfs:file { read write };
-#line 6
-
-#line 6
-
-typeattribute surfaceflinger mlstrustedsubject;
-
-# Talk to init over the property socket.
-
-#line 10
-allow surfaceflinger property_socket:sock_file write;
-#line 10
-allow surfaceflinger init:unix_stream_socket connectto;
-#line 10
-
-
-# Perform Binder IPC.
-
-#line 13
-# Call the servicemanager and transfer references to it.
-#line 13
-allow surfaceflinger servicemanager:binder { call transfer };
-#line 13
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 13
-# all domains in domain.te.
-#line 13
-
-
-#line 14
-# Call the server domain and optionally transfer references to it.
-#line 14
-allow surfaceflinger system_server:binder { call transfer };
-#line 14
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 14
-allow system_server surfaceflinger:binder transfer;
-#line 14
-# Receive and use open files from the server.
-#line 14
-allow surfaceflinger system_server:fd use;
-#line 14
-
-
-#line 15
-# Call the server domain and optionally transfer references to it.
-#line 15
-allow surfaceflinger nfc:binder { call transfer };
-#line 15
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 15
-allow nfc surfaceflinger:binder transfer;
-#line 15
-# Receive and use open files from the server.
-#line 15
-allow surfaceflinger nfc:fd use;
-#line 15
-
-
-#line 16
-# Call the server domain and optionally transfer references to it.
-#line 16
-allow surfaceflinger mediaserver:binder { call transfer };
-#line 16
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 16
-allow mediaserver surfaceflinger:binder transfer;
-#line 16
-# Receive and use open files from the server.
-#line 16
-allow surfaceflinger mediaserver:fd use;
-#line 16
-
-
-#line 17
-typeattribute surfaceflinger binderservicedomain;
-#line 17
-
-
-# Access the GPU.
-allow surfaceflinger gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Access /dev/graphics/fb0.
-allow surfaceflinger graphics_device:dir search;
-allow surfaceflinger graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Access /dev/video1.
-allow surfaceflinger video_device:dir { open getattr read search ioctl };
-allow surfaceflinger video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Create and use netlink kobject uevent sockets.
-allow surfaceflinger self:netlink_kobject_uevent_socket *;
-
-# Set properties.
-allow surfaceflinger system_prop:property_service set;
-allow surfaceflinger ctl_default_prop:property_service set;
-
-# Use open files supplied by an app.
-allow surfaceflinger appdomain:fd use;
-allow surfaceflinger platform_app_data_file:file { read write };
-allow surfaceflinger app_data_file:file { read write };
-
-# Use open file provided by bootanim.
-allow surfaceflinger bootanim:fd use;
-
-# Allow a dumpstate triggered screenshot
-
-#line 46
-# Call the server domain and optionally transfer references to it.
-#line 46
-allow surfaceflinger dumpstate:binder { call transfer };
-#line 46
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 46
-allow dumpstate surfaceflinger:binder transfer;
-#line 46
-# Receive and use open files from the server.
-#line 46
-allow surfaceflinger dumpstate:fd use;
-#line 46
-
-
-#line 47
-# Call the server domain and optionally transfer references to it.
-#line 47
-allow surfaceflinger shell:binder { call transfer };
-#line 47
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 47
-allow shell surfaceflinger:binder transfer;
-#line 47
-# Receive and use open files from the server.
-#line 47
-allow surfaceflinger shell:fd use;
-#line 47
-
-
-# Needed on some devices for playing DRM protected content,
-# but seems expected and appropriate for all devices.
-allow surfaceflinger tee:unix_stream_socket connectto;
-allow surfaceflinger tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-#line 1 "external/sepolicy/su.te"
-# File types must be defined for file_contexts.
-type su_exec, exec_type, file_type;
-
-#line 23
-
-#line 1 "external/sepolicy/system_app.te"
-#
-# Apps that run with the system UID, e.g. com.android.system.ui,
-# com.android.settings. These are not as privileged as the system
-# server.
-#
-type system_app, domain;
-
-#line 7
-typeattribute system_app mlstrustedsubject;
-#line 7
-typeattribute system_app unconfineddomain;
-#line 7
-
-
-#line 8
-typeattribute system_app appdomain;
-#line 8
-# Label ashmem objects with our own unique type.
-#line 8
-
-#line 8
-type system_app_tmpfs, file_type;
-#line 8
-type_transition system_app tmpfs:file system_app_tmpfs;
-#line 8
-allow system_app system_app_tmpfs:file { read write };
-#line 8
-
-#line 8
-# Map with PROT_EXEC.
-#line 8
-allow system_app system_app_tmpfs:file execute;
-#line 8
-
-
-#line 9
-typeattribute system_app binderservicedomain;
-#line 9
-
-
-# Perform binder IPC to any app domain.
-
-#line 12
-# Call the server domain and optionally transfer references to it.
-#line 12
-allow system_app appdomain:binder { call transfer };
-#line 12
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 12
-allow appdomain system_app:binder transfer;
-#line 12
-# Receive and use open files from the server.
-#line 12
-allow system_app appdomain:fd use;
-#line 12
-
-
-# Read and write system data files.
-# May want to split into separate types.
-allow system_app system_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow system_app system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read wallpaper file.
-allow system_app wallpaper_file:file { getattr open read ioctl lock };
-
-# Write to dalvikcache.
-allow system_app dalvikcache_data_file:file { write setattr };
-
-# Talk to keystore.
-
-#line 26
-allow system_app keystore_socket:sock_file write;
-#line 26
-allow system_app keystore:unix_stream_socket connectto;
-#line 26
-
-
-# Read SELinux enforcing status.
-
-#line 29
-allow system_app selinuxfs:dir { open getattr read search ioctl };
-#line 29
-allow system_app selinuxfs:file { getattr open read ioctl lock };
-#line 29
-
-
-# Settings app reads sdcard for storage stats
-allow system_app sdcard_type:dir { open getattr read search ioctl };
-
-# Write to properties
-
-#line 35
-allow system_app property_socket:sock_file write;
-#line 35
-allow system_app init:unix_stream_socket connectto;
-#line 35
-
-allow system_app debug_prop:property_service set;
-allow system_app radio_prop:property_service set;
-allow system_app system_prop:property_service set;
-#line 1 "external/sepolicy/system_server.te"
-#
-# System Server aka system_server spawned by zygote.
-# Most of the framework services run in this process.
-#
-type system_server, domain, mlstrustedsubject;
-
-#line 6
-typeattribute system_server mlstrustedsubject;
-#line 6
-typeattribute system_server unconfineddomain;
-#line 6
-
-
-# Define a type for tmpfs-backed ashmem regions.
-
-#line 9
-type system_server_tmpfs, file_type;
-#line 9
-type_transition system_server tmpfs:file system_server_tmpfs;
-#line 9
-allow system_server system_server_tmpfs:file { read write };
-#line 9
-
-
-# Dalvik Compiler JIT Mapping.
-allow system_server self:process execmem;
-allow system_server ashmem_device:chr_file execute;
-allow system_server system_server_tmpfs:file execute;
-
-# For art.
-allow system_server dalvikcache_data_file:file execute;
-
-# Child of the zygote.
-allow system_server zygote:fd use;
-allow system_server zygote:process sigchld;
-allow system_server zygote_tmpfs:file read;
-
-# Needed to close the zygote socket, which involves getopt / getattr
-# This should be deleted after b/12061011 is fixed
-allow system_server zygote:unix_stream_socket { getopt getattr };
-
-# system server gets network and bluetooth permissions.
-
-#line 29
-typeattribute system_server netdomain;
-#line 29
-
-
-#line 30
-typeattribute system_server bluetoothdomain;
-#line 30
-
-
-# These are the capabilities assigned by the zygote to the
-# system server.
-allow system_server self:capability {
- kill
- net_admin
- net_bind_service
- net_broadcast
- net_raw
- sys_boot
- sys_module
- sys_nice
- sys_resource
- sys_time
- sys_tty_config
-};
-
-allow system_server self:capability2 block_suspend;
-
-# Triggered by /proc/pid accesses, not allowed.
-dontaudit system_server self:capability sys_ptrace;
-
-# Trigger module auto-load.
-allow system_server kernel:system module_request;
-
-# Use netlink uevent sockets.
-allow system_server self:netlink_kobject_uevent_socket *;
-
-# Kill apps.
-allow system_server appdomain:process { sigkill signal };
-
-# Set scheduling info for apps.
-allow system_server appdomain:process { getsched setsched };
-allow system_server mediaserver:process { getsched setsched };
-
-# Read /proc data for apps.
-allow system_server appdomain:dir { open getattr read search ioctl };
-allow system_server appdomain:{ file lnk_file } { { getattr open read ioctl lock } { open append write } };
-
-# Read/Write to /proc/net/xt_qtaguid/ctrl and and /dev/xt_qtaguid.
-allow system_server qtaguid_proc:file { { getattr open read ioctl lock } { open append write } };
-allow system_server qtaguid_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read /sys/kernel/debug/wakeup_sources.
-allow system_server debugfs:file { getattr open read ioctl lock };
-
-# WifiWatchdog uses a packet_socket
-allow system_server self:packet_socket *;
-
-# 3rd party VPN clients require a tun_socket to be created
-allow system_server self:tun_socket create;
-
-# Notify init of death.
-allow system_server init:process sigchld;
-
-# Talk to init and various daemons via sockets.
-
-#line 87
-allow system_server property_socket:sock_file write;
-#line 87
-allow system_server init:unix_stream_socket connectto;
-#line 87
-
-
-#line 88
-allow system_server qemud_socket:sock_file write;
-#line 88
-allow system_server qemud:unix_stream_socket connectto;
-#line 88
-
-
-#line 89
-allow system_server installd_socket:sock_file write;
-#line 89
-allow system_server installd:unix_stream_socket connectto;
-#line 89
-
-
-#line 90
-allow system_server lmkd_socket:sock_file write;
-#line 90
-allow system_server lmkd:unix_stream_socket connectto;
-#line 90
-
-
-#line 91
-allow system_server netd_socket:sock_file write;
-#line 91
-allow system_server netd:unix_stream_socket connectto;
-#line 91
-
-
-#line 92
-allow system_server vold_socket:sock_file write;
-#line 92
-allow system_server vold:unix_stream_socket connectto;
-#line 92
-
-
-#line 93
-allow system_server zygote_socket:sock_file write;
-#line 93
-allow system_server zygote:unix_stream_socket connectto;
-#line 93
-
-
-#line 94
-allow system_server keystore_socket:sock_file write;
-#line 94
-allow system_server keystore:unix_stream_socket connectto;
-#line 94
-
-
-#line 95
-allow system_server gps_socket:sock_file write;
-#line 95
-allow system_server gpsd:unix_stream_socket connectto;
-#line 95
-
-
-#line 96
-allow system_server racoon_socket:sock_file write;
-#line 96
-allow system_server racoon:unix_stream_socket connectto;
-#line 96
-
-
-#line 97
-allow system_server wpa_socket:sock_file write;
-#line 97
-allow system_server wpa:unix_dgram_socket sendto;
-#line 97
-
-
-# Communicate over a socket created by surfaceflinger.
-allow system_server surfaceflinger:unix_stream_socket { read write setopt };
-
-# Perform Binder IPC.
-
-#line 103
-# Call the servicemanager and transfer references to it.
-#line 103
-allow system_server servicemanager:binder { call transfer };
-#line 103
-# rw access to /dev/binder and /dev/ashmem is presently granted to
-#line 103
-# all domains in domain.te.
-#line 103
-
-
-#line 104
-# Call the server domain and optionally transfer references to it.
-#line 104
-allow system_server binderservicedomain:binder { call transfer };
-#line 104
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 104
-allow binderservicedomain system_server:binder transfer;
-#line 104
-# Receive and use open files from the server.
-#line 104
-allow system_server binderservicedomain:fd use;
-#line 104
-
-
-#line 105
-# Call the server domain and optionally transfer references to it.
-#line 105
-allow system_server appdomain:binder { call transfer };
-#line 105
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 105
-allow appdomain system_server:binder transfer;
-#line 105
-# Receive and use open files from the server.
-#line 105
-allow system_server appdomain:fd use;
-#line 105
-
-
-#line 106
-# Call the server domain and optionally transfer references to it.
-#line 106
-allow system_server healthd:binder { call transfer };
-#line 106
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 106
-allow healthd system_server:binder transfer;
-#line 106
-# Receive and use open files from the server.
-#line 106
-allow system_server healthd:fd use;
-#line 106
-
-
-#line 107
-# Call the server domain and optionally transfer references to it.
-#line 107
-allow system_server dumpstate:binder { call transfer };
-#line 107
-# Allow the serverdomain to transfer references to the client on the reply.
-#line 107
-allow dumpstate system_server:binder transfer;
-#line 107
-# Receive and use open files from the server.
-#line 107
-allow system_server dumpstate:fd use;
-#line 107
-
-
-#line 108
-typeattribute system_server binderservicedomain;
-#line 108
-
-
-# Read /proc/pid files for Binder clients.
-
-#line 111
-allow system_server appdomain:dir { open getattr read search ioctl };
-#line 111
-allow system_server appdomain:{ file lnk_file } { getattr open read ioctl lock };
-#line 111
-
-
-#line 112
-allow system_server mediaserver:dir { open getattr read search ioctl };
-#line 112
-allow system_server mediaserver:{ file lnk_file } { getattr open read ioctl lock };
-#line 112
-
-allow system_server appdomain:process getattr;
-allow system_server mediaserver:process getattr;
-
-# Check SELinux permissions.
-
-#line 117
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 117
-allow system_server selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 117
-allow system_server kernel:security compute_av;
-#line 117
-allow system_server self:netlink_selinux_socket *;
-#line 117
-
-
-# XXX Label sysfs files with a specific type?
-allow system_server sysfs:file { { getattr open read ioctl lock } { open append write } };
-allow system_server sysfs_nfc_power_writable:file { { getattr open read ioctl lock } { open append write } };
-
-# Access devices.
-allow system_server device:dir { open getattr read search ioctl };
-allow system_server mdns_socket:sock_file { { getattr open read ioctl lock } { open append write } };
-allow system_server alarm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server gpu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server graphics_device:dir search;
-allow system_server graphics_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server iio_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server input_device:dir { open getattr read search ioctl };
-allow system_server input_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server tty_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server urandom_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server usbaccessory_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server video_device:dir { open getattr read search ioctl };
-allow system_server video_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server qemu_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server adbd_socket:sock_file { { getattr open read ioctl lock } { open append write } };
-
-# tun device used for 3rd party vpn apps
-allow system_server tun_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Manage data files.
-allow system_server data_file_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow system_server data_file_type:{ file lnk_file sock_file fifo_file } { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Read /file_contexts and /data/security/file_contexts
-
-#line 149
-allow system_server security_file:dir { open getattr read search ioctl };
-#line 149
-allow system_server security_file:file { getattr open read ioctl lock };
-#line 149
-allow system_server security_file:lnk_file { getattr open read ioctl lock };
-#line 149
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 149
-allow system_server selinuxfs:file { getattr open read ioctl lock };
-#line 149
-allow system_server rootfs:dir { open getattr read search ioctl };
-#line 149
-allow system_server rootfs:file { getattr open read ioctl lock };
-#line 149
-
-
-# Relabel apk files.
-
-#line 152
-typeattribute system_server relabeltodomain;
-#line 152
-
-allow system_server { apk_tmp_file apk_private_tmp_file }:file { relabelfrom relabelto };
-allow system_server { apk_data_file apk_private_data_file }:file { relabelfrom relabelto };
-
-# Relabel wallpaper.
-allow system_server system_data_file:file relabelfrom;
-allow system_server wallpaper_file:file relabelto;
-allow system_server wallpaper_file:file { { getattr open read ioctl lock } { open append write } };
-
-# Relabel /data/anr.
-allow system_server system_data_file:dir relabelfrom;
-allow system_server anr_data_file:dir relabelto;
-
-# Property Service write
-allow system_server system_prop:property_service set;
-allow system_server radio_prop:property_service set;
-allow system_server debug_prop:property_service set;
-allow system_server powerctl_prop:property_service set;
-
-# ctl interface
-allow system_server ctl_default_prop:property_service set;
-
-# Create a socket for receiving info from wpa.
-type_transition system_server wifi_data_file:sock_file system_wpa_socket;
-type_transition system_server wpa_socket:sock_file system_wpa_socket;
-allow system_server wpa_socket:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow system_server system_wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Remove sockets created by wpa_supplicant
-allow system_server wpa_socket:sock_file unlink;
-
-# Create a socket for connections from debuggerd.
-type_transition system_server system_data_file:sock_file system_ndebug_socket "ndebugsocket";
-allow system_server system_ndebug_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Specify any arguments to zygote.
-allow system_server self:zygote { specifyids specifyrlimits specifyseinfo };
-
-# Manage cache files.
-allow system_server cache_file:dir { relabelfrom { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } };
-allow system_server cache_file:file { relabelfrom { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } } };
-
-# Run system programs, e.g. dexopt.
-allow system_server system_file:file { getattr execute execute_no_trans };
-
-# Allow reading of /proc/pid data for other domains.
-# XXX dontaudit candidate
-allow system_server domain:dir { open getattr read search ioctl };
-allow system_server domain:file { getattr open read ioctl lock };
-
-# LocationManager(e.g, GPS) needs to read and write
-# to uart driver and ctrl proc entry
-allow system_server gps_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server gps_control:file { { getattr open read ioctl lock } { open append write } };
-
-# Allow system_server to use app-created sockets.
-allow system_server appdomain:{ tcp_socket udp_socket } { setopt read write };
-
-# Allow abstract socket connection
-allow system_server rild:unix_stream_socket connectto;
-
-# connect to vpn tunnel
-allow system_server mtp:unix_stream_socket { connectto };
-
-# BackupManagerService lets PMS create a data backup file
-allow system_server cache_backup_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# Relabel /data/backup
-allow system_server backup_data_file:dir { relabelto relabelfrom };
-# Relabel /cache/.*\.{data|restore}
-allow system_server cache_backup_file:file { relabelto relabelfrom };
-# LocalTransport creates and relabels /cache/backup
-allow system_server cache_backup_file:dir { relabelto relabelfrom { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } } };
-
-# Allow system to talk to usb device
-allow system_server usb_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow system_server usb_device:dir { open getattr read search ioctl };
-
-# Allow system to talk to sensors
-allow system_server sensors_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Read from HW RNG (needed by EntropyMixer).
-allow system_server hw_random_device:chr_file { getattr open read ioctl lock };
-
-# Access to wake locks
-allow system_server sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-
-# Read and delete files under /dev/fscklogs.
-
-#line 239
-allow system_server fscklogs:dir { open getattr read search ioctl };
-#line 239
-allow system_server fscklogs:{ file lnk_file } { getattr open read ioctl lock };
-#line 239
-
-allow system_server fscklogs:dir { write remove_name };
-allow system_server fscklogs:file unlink;
-
-# For SELinuxPolicyInstallReceiver
-
-#line 244
-
-#line 244
-allow system_server security_file:dir { open getattr read search ioctl };
-#line 244
-allow system_server security_file:file { getattr open read ioctl lock };
-#line 244
-allow system_server security_file:lnk_file { getattr open read ioctl lock };
-#line 244
-allow system_server selinuxfs:dir { open getattr read search ioctl };
-#line 244
-allow system_server selinuxfs:file { getattr open read ioctl lock };
-#line 244
-allow system_server rootfs:dir { open getattr read search ioctl };
-#line 244
-allow system_server rootfs:file { getattr open read ioctl lock };
-#line 244
-
-#line 244
-
-#line 244
-allow system_server property_socket:sock_file write;
-#line 244
-allow system_server init:unix_stream_socket connectto;
-#line 244
-
-#line 244
-allow system_server security_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-#line 244
-allow system_server security_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-#line 244
-allow system_server security_file:lnk_file { create rename unlink };
-#line 244
-allow system_server security_prop:property_service set;
-#line 244
-
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-# This rule is for dalvikcache mmap/mprotect PROT_EXEC.
-allow system_server unlabeled:file execute;
-
-# logd access, system_server inherit logd write socket
-# (urge is to deprecate this long term)
-allow system_server zygote:unix_dgram_socket write;
-
-# Be consistent with DAC permissions. Allow system_server to write to
-# /sys/module/lowmemorykiller/parameters/adj
-# /sys/module/lowmemorykiller/parameters/minfree
-allow system_server sysfs_lowmemorykiller:file { open append write };
-#line 1 "external/sepolicy/tee.te"
-##
-# trusted execution environment (tee) daemon
-#
-type tee, domain;
-type tee_exec, exec_type, file_type;
-type tee_device, dev_type;
-type tee_data_file, file_type, data_file_type;
-
-
-#line 9
-
-#line 9
-# Allow the necessary permissions.
-#line 9
-
-#line 9
-# Old domain may exec the file and transition to the new domain.
-#line 9
-allow init tee_exec:file { getattr open read execute };
-#line 9
-allow init tee:process transition;
-#line 9
-# New domain is entered by executing the file.
-#line 9
-allow tee tee_exec:file { entrypoint read execute };
-#line 9
-# New domain can send SIGCHLD to its caller.
-#line 9
-allow tee init:process sigchld;
-#line 9
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 9
-dontaudit init tee:process noatsecure;
-#line 9
-# XXX dontaudit candidate but requires further study.
-#line 9
-allow init tee:process { siginh rlimitinh };
-#line 9
-
-#line 9
-# Make the transition occur by default.
-#line 9
-type_transition init tee_exec:process tee;
-#line 9
-
-#line 9
-
-#line 9
-type tee_tmpfs, file_type;
-#line 9
-type_transition tee tmpfs:file tee_tmpfs;
-#line 9
-allow tee tee_tmpfs:file { read write };
-#line 9
-
-#line 9
-
-allow tee self:capability { dac_override };
-allow tee tee_device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow tee tee_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow tee tee_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow tee self:netlink_socket { create bind read };
-#line 1 "external/sepolicy/ueventd.te"
-# ueventd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type ueventd, domain;
-
-#line 4
-type ueventd_tmpfs, file_type;
-#line 4
-type_transition ueventd tmpfs:file ueventd_tmpfs;
-#line 4
-allow ueventd ueventd_tmpfs:file { read write };
-#line 4
-
-
-#line 5
-type_transition ueventd device:chr_file klog_device "__kmsg__";
-#line 5
-allow ueventd klog_device:chr_file { create open write unlink };
-#line 5
-allow ueventd device:dir { write add_name remove_name };
-#line 5
-
-
-#line 6
-allow ueventd security_file:dir { open getattr read search ioctl };
-#line 6
-allow ueventd security_file:file { getattr open read ioctl lock };
-#line 6
-allow ueventd security_file:lnk_file { getattr open read ioctl lock };
-#line 6
-allow ueventd selinuxfs:dir { open getattr read search ioctl };
-#line 6
-allow ueventd selinuxfs:file { getattr open read ioctl lock };
-#line 6
-allow ueventd rootfs:dir { open getattr read search ioctl };
-#line 6
-allow ueventd rootfs:file { getattr open read ioctl lock };
-#line 6
-
-
-#line 7
-typeattribute ueventd relabeltodomain;
-#line 7
-
-allow ueventd rootfs:file entrypoint;
-allow ueventd init:process sigchld;
-allow ueventd self:capability { chown mknod net_admin setgid fsetid sys_rawio dac_override fowner };
-allow ueventd device:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow ueventd device:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ueventd sysfs:file { { getattr open read ioctl lock } { open append write } };
-allow ueventd sysfs:file setattr;
-allow ueventd sysfs_type:file { relabelfrom relabelto };
-allow ueventd sysfs_devices_system_cpu:file { { getattr open read ioctl lock } { open append write } };
-allow ueventd tmpfs:chr_file { { getattr open read ioctl lock } { open append write } };
-allow ueventd dev_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow ueventd dev_type:lnk_file { create unlink };
-allow ueventd dev_type:chr_file { create setattr unlink };
-allow ueventd dev_type:blk_file { create setattr unlink };
-allow ueventd self:netlink_kobject_uevent_socket *;
-allow ueventd efs_file:dir search;
-allow ueventd efs_file:file { getattr open read ioctl lock };
-#line 1 "external/sepolicy/unconfined.te"
-#######################################################
-#
-# This is the unconfined template. This template is the base policy
-# which is used by daemons and other privileged components of
-# Android.
-#
-# Historically, this template was called "unconfined" because it
-# allowed the domain to do anything it wanted. Over time,
-# this has changed, and will continue to change in the future.
-# The rules in this file will be removed when no remaining
-# unconfined domains require it, or when the rules contradict
-# Android security best practices. Domains which need rules not
-# provided by the unconfined template should add them directly to
-# the relevant policy.
-#
-# The use of this template is discouraged.
-######################################################
-
-allow unconfineddomain self:capability ~{ sys_ptrace sys_rawio mknod sys_module };
-allow unconfineddomain self:capability2 ~{ mac_override mac_admin };
-allow unconfineddomain kernel:security ~{ load_policy setenforce setcheckreqprot };
-allow unconfineddomain kernel:system *;
-allow unconfineddomain domain:process ~{ execmem execstack execheap ptrace transition dyntransition };
-allow unconfineddomain domain:fd *;
-allow unconfineddomain domain:dir { open getattr read search ioctl };
-allow unconfineddomain domain:lnk_file { getattr open read ioctl lock };
-allow unconfineddomain domain:{ fifo_file file } { { getattr open read ioctl lock } { open append write } };
-allow unconfineddomain domain:{ socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } *;
-allow unconfineddomain domain:{ sem msgq shm ipc } *;
-allow unconfineddomain domain:key *;
-allow unconfineddomain {fs_type dev_type file_type}:{ dir lnk_file sock_file fifo_file } ~relabelto;
-allow unconfineddomain {fs_type -usermodehelper -proc_security}:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain {dev_type -kmem_device}:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain file_type:{ chr_file file } ~{entrypoint execmod execute relabelto};
-allow unconfineddomain { rootfs system_file exec_type }:file execute;
-allow unconfineddomain node_type:node *;
-allow unconfineddomain node_type:{ tcp_socket udp_socket rawip_socket } node_bind;
-allow unconfineddomain netif_type:netif *;
-allow unconfineddomain port_type:{ socket tcp_socket udp_socket rawip_socket netlink_socket packet_socket key_socket unix_stream_socket unix_dgram_socket appletalk_socket netlink_route_socket netlink_firewall_socket netlink_tcpdiag_socket netlink_nflog_socket netlink_xfrm_socket netlink_selinux_socket netlink_audit_socket netlink_ip6fw_socket netlink_dnrt_socket netlink_kobject_uevent_socket tun_socket } name_bind;
-allow unconfineddomain port_type:{ tcp_socket dccp_socket } name_connect;
-allow unconfineddomain domain:peer recv;
-allow unconfineddomain { domain -init }:binder { call transfer set_context_mgr };
-allow unconfineddomain property_type:property_service set;
-#line 1 "external/sepolicy/uncrypt.te"
-# uncrypt
-type uncrypt, domain;
-type uncrypt_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init uncrypt_exec:file { getattr open read execute };
-#line 5
-allow init uncrypt:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow uncrypt uncrypt_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow uncrypt init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init uncrypt:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init uncrypt:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init uncrypt_exec:process uncrypt;
-#line 5
-
-#line 5
-
-#line 5
-type uncrypt_tmpfs, file_type;
-#line 5
-type_transition uncrypt tmpfs:file uncrypt_tmpfs;
-#line 5
-allow uncrypt uncrypt_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-#line 6
-typeattribute uncrypt mlstrustedsubject;
-#line 6
-typeattribute uncrypt unconfineddomain;
-#line 6
-
-
-allow uncrypt self:capability dac_override;
-
-# Read OTA zip file from /data/data/com.google.android.gsf/app_download
-
-#line 11
-allow uncrypt app_data_file:dir { open getattr read search ioctl };
-#line 11
-allow uncrypt app_data_file:{ file lnk_file } { getattr open read ioctl lock };
-#line 11
-
-
-#line 16
-
-
-# Create tmp file /cache/recovery/command.tmp
-# Read /cache/recovery/command
-# Rename /cache/recovery/command.tmp to /cache/recovery/command
-allow uncrypt cache_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow uncrypt cache_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Set a property to reboot the device.
-
-#line 25
-allow uncrypt property_socket:sock_file write;
-#line 25
-allow uncrypt init:unix_stream_socket connectto;
-#line 25
-
-allow uncrypt powerctl_prop:property_service set;
-
-# Raw writes to block device
-allow uncrypt self:capability sys_rawio;
-allow uncrypt block_device:blk_file { open append write };
-#line 1 "external/sepolicy/untrusted_app.te"
-###
-### Untrusted apps.
-###
-### This file defines the rules for untrusted apps. An "untrusted
-### app" is an APP with UID between APP_AID (10000)
-### and AID_ISOLATED_START (99000).
-###
-### untrusted_app includes all the appdomain rules, plus the
-### additional following rules:
-###
-
-type untrusted_app, domain;
-
-#line 13
-typeattribute untrusted_app mlstrustedsubject;
-#line 13
-typeattribute untrusted_app unconfineddomain;
-#line 13
-
-
-#line 14
-typeattribute untrusted_app appdomain;
-#line 14
-# Label ashmem objects with our own unique type.
-#line 14
-
-#line 14
-type untrusted_app_tmpfs, file_type;
-#line 14
-type_transition untrusted_app tmpfs:file untrusted_app_tmpfs;
-#line 14
-allow untrusted_app untrusted_app_tmpfs:file { read write };
-#line 14
-
-#line 14
-# Map with PROT_EXEC.
-#line 14
-allow untrusted_app untrusted_app_tmpfs:file execute;
-#line 14
-
-
-#line 15
-typeattribute untrusted_app netdomain;
-#line 15
-
-
-#line 16
-typeattribute untrusted_app bluetoothdomain;
-#line 16
-
-
-# Some apps ship with shared libraries and binaries that they write out
-# to their sandbox directory and then execute.
-allow untrusted_app app_data_file:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-allow untrusted_app tun_device:chr_file { { getattr open read ioctl lock } { open append write } };
-
-# Internal SDCard rw access.
-allow untrusted_app sdcard_internal:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow untrusted_app sdcard_internal:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# External SDCard rw access.
-allow untrusted_app sdcard_external:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow untrusted_app sdcard_external:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# ASEC
-allow untrusted_app asec_apk_file:dir { getattr };
-allow untrusted_app asec_apk_file:file { getattr open read ioctl lock };
-# Execute libs in asec containers.
-allow untrusted_app asec_public_file:file execute;
-
-# Create tcp/udp sockets
-allow untrusted_app node_type:{ tcp_socket udp_socket } node_bind;
-allow untrusted_app self:{ tcp_socket udp_socket } { { create { ioctl read getattr write setattr append bind connect getopt setopt shutdown } } accept listen };
-# Bind to a particular hostname/address/interface (e.g., localhost) instead of
-# ANY. Normally, apps should not be listening on all interfaces.
-allow untrusted_app port:{ tcp_socket udp_socket } name_bind;
-
-# Allow the allocation and use of ptys
-# Used by: https://play.google.com/store/apps/details?id=jackpal.androidterm
-
-#line 47
-# Each domain gets a unique devpts type.
-#line 47
-type untrusted_app_devpts, fs_type;
-#line 47
-# Label the pty with the unique type when created.
-#line 47
-type_transition untrusted_app devpts:chr_file untrusted_app_devpts;
-#line 47
-# Allow use of the pty after creation.
-#line 47
-allow untrusted_app untrusted_app_devpts:chr_file { open getattr read write ioctl };
-#line 47
-# Note: devpts:dir search and ptmx_device:chr_file rw_file_perms
-#line 47
-# allowed to everyone via domain.te.
-#line 47
-
-
-# Used by Finsky / Android "Verify Apps" functionality when
-# running "adb install foo.apk".
-# TODO: Long term, we don't want apps probing into shell data files.
-# Figure out a way to remove these rules.
-allow untrusted_app shell_data_file:file { getattr open read ioctl lock };
-allow untrusted_app shell_data_file:dir { open getattr read search ioctl };
-#line 1 "external/sepolicy/vold.te"
-# volume manager
-type vold, domain;
-type vold_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init vold_exec:file { getattr open read execute };
-#line 5
-allow init vold:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow vold vold_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow vold init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init vold:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init vold:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init vold_exec:process vold;
-#line 5
-
-#line 5
-
-#line 5
-type vold_tmpfs, file_type;
-#line 5
-type_transition vold tmpfs:file vold_tmpfs;
-#line 5
-allow vold vold_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-
-typeattribute vold mlstrustedsubject;
-allow vold system_file:file { getattr execute execute_no_trans };
-allow vold block_device:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold block_device:blk_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold device:dir write;
-allow vold devpts:chr_file { { getattr open read ioctl lock } { open append write } };
-allow vold rootfs:dir mounton;
-allow vold sdcard_type:dir mounton;
-allow vold sdcard_type:filesystem { mount remount unmount };
-allow vold sdcard_type:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold sdcard_type:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold tmpfs:filesystem { mount unmount };
-allow vold tmpfs:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow vold tmpfs:dir mounton;
-allow vold self:capability { net_admin dac_override mknod sys_admin chown fowner fsetid };
-allow vold self:netlink_kobject_uevent_socket *;
-allow vold app_data_file:dir search;
-allow vold app_data_file:file { { getattr open read ioctl lock } { open append write } };
-allow vold loop_device:blk_file { { getattr open read ioctl lock } { open append write } };
-allow vold dm_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# For vold Process::killProcessesWithOpenFiles function.
-allow vold domain:dir { open getattr read search ioctl };
-allow vold domain:{ file lnk_file } { getattr open read ioctl lock };
-allow vold domain:process { signal sigkill };
-allow vold self:capability { sys_ptrace kill };
-
-# For blkid
-allow vold shell_exec:file { { getattr open read ioctl lock } { getattr execute execute_no_trans } };
-
-# XXX Label sysfs files with a specific type?
-allow vold sysfs:file { { getattr open read ioctl lock } { open append write } };
-
-
-#line 39
-type_transition vold device:chr_file klog_device "__kmsg__";
-#line 39
-allow vold klog_device:chr_file { create open write unlink };
-#line 39
-allow vold device:dir { write add_name remove_name };
-#line 39
-
-
-# Log fsck results
-allow vold fscklogs:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow vold fscklogs:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-#
-# Rules to support encrypted fs support.
-#
-
-# Set property.
-
-#line 50
-allow vold property_socket:sock_file write;
-#line 50
-allow vold init:unix_stream_socket connectto;
-#line 50
-
-
-# Unmount and mount the fs.
-allow vold labeledfs:filesystem { mount unmount remount };
-
-# Access /efs/userdata_footer.
-# XXX Split into a separate type?
-allow vold efs_file:file { { getattr open read ioctl lock } { open append write } };
-
-# Create and mount on /data/tmp_mnt.
-allow vold system_data_file:dir { create { { open getattr read search ioctl } { open search write add_name remove_name } } mounton };
-
-# Set scheduling policy of kernel processes
-allow vold kernel:process setsched;
-
-# Property Service
-allow vold vold_prop:property_service set;
-allow vold powerctl_prop:property_service set;
-allow vold ctl_default_prop:property_service set;
-
-# ASEC
-allow vold asec_image_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow vold asec_image_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-
-#line 73
-allow vold security_file:dir { open getattr read search ioctl };
-#line 73
-allow vold security_file:file { getattr open read ioctl lock };
-#line 73
-allow vold security_file:lnk_file { getattr open read ioctl lock };
-#line 73
-allow vold selinuxfs:dir { open getattr read search ioctl };
-#line 73
-allow vold selinuxfs:file { getattr open read ioctl lock };
-#line 73
-allow vold rootfs:dir { open getattr read search ioctl };
-#line 73
-allow vold rootfs:file { getattr open read ioctl lock };
-#line 73
-
-
-#line 74
-typeattribute vold relabeltodomain;
-#line 74
-
-allow vold asec_apk_file:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } setattr relabelfrom };
-allow vold asec_public_file:dir { relabelto setattr };
-allow vold asec_apk_file:file { { getattr open read ioctl lock } setattr relabelfrom };
-allow vold asec_public_file:file { relabelto setattr };
-
-# Handle wake locks (used for device encryption)
-allow vold sysfs_wake_lock:file { { getattr open read ioctl lock } { open append write } };
-allow vold self:capability2 block_suspend;
-#line 1 "external/sepolicy/watchdogd.te"
-# watchdogd seclabel is specified in init.<board>.rc
-type watchdogd, domain;
-allow watchdogd rootfs:file { entrypoint { getattr open read ioctl lock } };
-allow watchdogd self:capability mknod;
-allow watchdogd device:dir { add_name write remove_name };
-allow watchdogd watchdog_device:chr_file { { getattr open read ioctl lock } { open append write } };
-# because of /dev/__kmsg__ and /dev/__null__
-
-#line 8
-type_transition watchdogd device:chr_file klog_device "__kmsg__";
-#line 8
-allow watchdogd klog_device:chr_file { create open write unlink };
-#line 8
-allow watchdogd device:dir { write add_name remove_name };
-#line 8
-
-type_transition watchdogd device:chr_file null_device "__null__";
-allow watchdogd null_device:chr_file { create unlink };
-#line 1 "external/sepolicy/wpa_supplicant.te"
-# wpa - wpa supplicant or equivalent
-type wpa, domain;
-type wpa_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init wpa_exec:file { getattr open read execute };
-#line 5
-allow init wpa:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow wpa wpa_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow wpa init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init wpa:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init wpa:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init wpa_exec:process wpa;
-#line 5
-
-#line 5
-
-#line 5
-type wpa_tmpfs, file_type;
-#line 5
-type_transition wpa tmpfs:file wpa_tmpfs;
-#line 5
-allow wpa wpa_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-allow wpa kernel:system module_request;
-allow wpa self:capability { setuid net_admin setgid net_raw };
-allow wpa cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow wpa self:netlink_route_socket *;
-allow wpa self:netlink_socket *;
-allow wpa self:packet_socket *;
-allow wpa self:udp_socket *;
-allow wpa wifi_data_file:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow wpa wifi_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-#line 15
-allow wpa system_wpa_socket:sock_file write;
-#line 15
-allow wpa system_server:unix_dgram_socket sendto;
-#line 15
-
-allow wpa random_device:chr_file { getattr open read ioctl lock };
-
-# Create a socket for receiving info from wpa
-type_transition wpa wifi_data_file:sock_file wpa_socket;
-allow wpa wpa_socket:dir { { { open getattr read search ioctl } { open search write add_name remove_name } } setattr };
-allow wpa wpa_socket:sock_file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-
-# Allow wpa_cli to work. wpa_cli creates a socket in
-# /data/misc/wifi/sockets which wpa supplicant communicates with.
-#line 27
-
-#line 1 "external/sepolicy/zygote.te"
-# zygote
-type zygote, domain;
-type zygote_exec, exec_type, file_type;
-
-
-#line 5
-
-#line 5
-# Allow the necessary permissions.
-#line 5
-
-#line 5
-# Old domain may exec the file and transition to the new domain.
-#line 5
-allow init zygote_exec:file { getattr open read execute };
-#line 5
-allow init zygote:process transition;
-#line 5
-# New domain is entered by executing the file.
-#line 5
-allow zygote zygote_exec:file { entrypoint read execute };
-#line 5
-# New domain can send SIGCHLD to its caller.
-#line 5
-allow zygote init:process sigchld;
-#line 5
-# Enable AT_SECURE, i.e. libc secure mode.
-#line 5
-dontaudit init zygote:process noatsecure;
-#line 5
-# XXX dontaudit candidate but requires further study.
-#line 5
-allow init zygote:process { siginh rlimitinh };
-#line 5
-
-#line 5
-# Make the transition occur by default.
-#line 5
-type_transition init zygote_exec:process zygote;
-#line 5
-
-#line 5
-
-#line 5
-type zygote_tmpfs, file_type;
-#line 5
-type_transition zygote tmpfs:file zygote_tmpfs;
-#line 5
-allow zygote zygote_tmpfs:file { read write };
-#line 5
-
-#line 5
-
-typeattribute zygote mlstrustedsubject;
-# Override DAC on files and switch uid/gid.
-allow zygote self:capability { dac_override setgid setuid fowner };
-# Drop capabilities from bounding set.
-allow zygote self:capability setpcap;
-# Switch SELinux context to app domains.
-allow zygote system_server:process dyntransition;
-allow zygote appdomain:process dyntransition;
-# Allow zygote to read app /proc/pid dirs (b/10455872)
-allow zygote appdomain:dir { getattr search };
-allow zygote appdomain:file { { getattr open read ioctl lock } };
-# Move children into the peer process group.
-allow zygote system_server:process { getpgid setpgid };
-allow zygote appdomain:process { getpgid setpgid };
-# Write to system data.
-allow zygote system_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow zygote system_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-allow zygote dalvikcache_data_file:dir { { open getattr read search ioctl } { open search write add_name remove_name } };
-allow zygote dalvikcache_data_file:file { create setattr { { getattr open read ioctl lock } { open append write } } { getattr link unlink rename } };
-# For art.
-allow zygote dalvikcache_data_file:file execute;
-# Execute dexopt.
-allow zygote system_file:file { getattr execute execute_no_trans };
-# Control cgroups.
-allow zygote cgroup:dir { create reparent rmdir setattr { { open getattr read search ioctl } { open search write add_name remove_name } } { getattr link unlink rename } };
-allow zygote self:capability sys_admin;
-# Check validity of SELinux context before use.
-
-#line 33
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 33
-allow zygote selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 33
-allow zygote kernel:security check_context;
-#line 33
-
-# Check SELinux permissions.
-
-#line 35
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 35
-allow zygote selinuxfs:file { { getattr open read ioctl lock } { open append write } };
-#line 35
-allow zygote kernel:security compute_av;
-#line 35
-allow zygote self:netlink_selinux_socket *;
-#line 35
-
-# Read /seapp_contexts and /data/security/seapp_contexts
-
-#line 37
-allow zygote security_file:dir { open getattr read search ioctl };
-#line 37
-allow zygote security_file:file { getattr open read ioctl lock };
-#line 37
-allow zygote security_file:lnk_file { getattr open read ioctl lock };
-#line 37
-allow zygote selinuxfs:dir { open getattr read search ioctl };
-#line 37
-allow zygote selinuxfs:file { getattr open read ioctl lock };
-#line 37
-allow zygote rootfs:dir { open getattr read search ioctl };
-#line 37
-allow zygote rootfs:file { getattr open read ioctl lock };
-#line 37
-
-
-# Setting up /storage/emulated.
-allow zygote rootfs:dir mounton;
-allow zygote sdcard_type:dir { write search setattr create add_name mounton };
-dontaudit zygote self:capability fsetid;
-allow zygote tmpfs:dir { write create add_name setattr mounton search };
-allow zygote tmpfs:filesystem mount;
-allow zygote labeledfs:filesystem remount;
-
-# Handle --invoke-with command when launching Zygote with a wrapper command.
-allow zygote zygote_exec:file { execute_no_trans open };
-
-# handle bugreports b/10498304
-allow zygote ashmem_device:chr_file execute;
-allow zygote shell_data_file:file { write getattr };
-allow zygote system_server:binder { transfer call };
-allow zygote servicemanager:binder { call };
-
-# For legacy unlabeled userdata on existing devices.
-# See discussion of Unlabeled files in domain.te for more information.
-# This rule is for dalvikcache mmap/mprotect PROT_EXEC.
-allow zygote unlabeled:file execute;
-#line 1 "build/target/board/generic/sepolicy/bootanim.te"
-allow bootanim self:process execmem;
-allow bootanim ashmem_device:chr_file execute;
-#line 1 "build/target/board/generic/sepolicy/domain.te"
-# For /sys/qemu_trace files in the emulator.
-allow domain sysfs_writable:file { { getattr open read ioctl lock } { open append write } };
-#line 1 "build/target/board/generic/sepolicy/surfaceflinger.te"
-allow surfaceflinger self:process execmem;
-allow surfaceflinger ashmem_device:chr_file execute;
-#line 1 "external/sepolicy/roles"
-role r;
-role r types domain;
-#line 1 "external/sepolicy/users"
-user u roles { r } level s0 range s0 - s0:c0.c1023;
-#line 1 "external/sepolicy/initial_sid_contexts"
-sid kernel u:r:kernel:s0
-sid security u:object_r:kernel:s0
-sid unlabeled u:object_r:unlabeled:s0
-sid fs u:object_r:labeledfs:s0
-sid file u:object_r:unlabeled:s0
-sid file_labels u:object_r:unlabeled:s0
-sid init u:object_r:unlabeled:s0
-sid any_socket u:object_r:unlabeled:s0
-sid port u:object_r:port:s0
-sid netif u:object_r:netif:s0
-sid netmsg u:object_r:unlabeled:s0
-sid node u:object_r:node:s0
-sid igmp_packet u:object_r:unlabeled:s0
-sid icmp_socket u:object_r:unlabeled:s0
-sid tcp_socket u:object_r:unlabeled:s0
-sid sysctl_modprobe u:object_r:unlabeled:s0
-sid sysctl u:object_r:proc:s0
-sid sysctl_fs u:object_r:unlabeled:s0
-sid sysctl_kernel u:object_r:unlabeled:s0
-sid sysctl_net u:object_r:unlabeled:s0
-sid sysctl_net_unix u:object_r:unlabeled:s0
-sid sysctl_vm u:object_r:unlabeled:s0
-sid sysctl_dev u:object_r:unlabeled:s0
-sid kmod u:object_r:unlabeled:s0
-sid policy u:object_r:unlabeled:s0
-sid scmp_packet u:object_r:unlabeled:s0
-sid devnull u:object_r:null_device:s0
-#line 1 "external/sepolicy/fs_use"
-# Label inodes via getxattr.
-fs_use_xattr yaffs2 u:object_r:labeledfs:s0;
-fs_use_xattr jffs2 u:object_r:labeledfs:s0;
-fs_use_xattr ext2 u:object_r:labeledfs:s0;
-fs_use_xattr ext3 u:object_r:labeledfs:s0;
-fs_use_xattr ext4 u:object_r:labeledfs:s0;
-fs_use_xattr xfs u:object_r:labeledfs:s0;
-fs_use_xattr btrfs u:object_r:labeledfs:s0;
-
-# Label inodes from task label.
-fs_use_task pipefs u:object_r:pipefs:s0;
-fs_use_task sockfs u:object_r:sockfs:s0;
-
-# Label inodes from combination of task label and fs label.
-# Define type_transition rules if you want per-domain types.
-fs_use_trans devpts u:object_r:devpts:s0;
-fs_use_trans tmpfs u:object_r:tmpfs:s0;
-fs_use_trans devtmpfs u:object_r:device:s0;
-fs_use_trans shm u:object_r:shm:s0;
-fs_use_trans mqueue u:object_r:mqueue:s0;
-
-#line 1 "external/sepolicy/genfs_contexts"
-# Label inodes with the fs label.
-genfscon rootfs / u:object_r:rootfs:s0
-# proc labeling can be further refined (longest matching prefix).
-genfscon proc / u:object_r:proc:s0
-genfscon proc /net u:object_r:proc_net:s0
-genfscon proc /net/xt_qtaguid/ctrl u:object_r:qtaguid_proc:s0
-genfscon proc /sys/fs/protected_hardlinks u:object_r:proc_security:s0
-genfscon proc /sys/fs/protected_symlinks u:object_r:proc_security:s0
-genfscon proc /sys/fs/suid_dumpable u:object_r:proc_security:s0
-genfscon proc /sys/kernel/core_pattern u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/dmesg_restrict u:object_r:proc_security:s0
-genfscon proc /sys/kernel/hotplug u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/kptr_restrict u:object_r:proc_security:s0
-genfscon proc /sys/kernel/modprobe u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/modules_disabled u:object_r:proc_security:s0
-genfscon proc /sys/kernel/poweroff_cmd u:object_r:usermodehelper:s0
-genfscon proc /sys/kernel/randomize_va_space u:object_r:proc_security:s0
-genfscon proc /sys/kernel/usermodehelper u:object_r:usermodehelper:s0
-genfscon proc /sys/net u:object_r:proc_net:s0
-genfscon proc /sys/vm/mmap_min_addr u:object_r:proc_security:s0
-# selinuxfs booleans can be individually labeled.
-genfscon selinuxfs / u:object_r:selinuxfs:s0
-genfscon cgroup / u:object_r:cgroup:s0
-# sysfs labels can be set by userspace.
-genfscon sysfs / u:object_r:sysfs:s0
-genfscon inotifyfs / u:object_r:inotify:s0
-genfscon vfat / u:object_r:sdcard_external:s0
-genfscon debugfs / u:object_r:debugfs:s0
-genfscon fuse / u:object_r:sdcard_internal:s0
-#line 1 "external/sepolicy/port_contexts"
-# portcon statements go here, e.g.
-# portcon tcp 80 u:object_r:http_port:s0
-
diff --git a/tools/selinux/src/gen_SELinux_CTS.py b/tools/selinux/src/gen_SELinux_CTS.py
deleted file mode 100755
index 85d49a8..0000000
--- a/tools/selinux/src/gen_SELinux_CTS.py
+++ /dev/null
@@ -1,58 +0,0 @@
-#!/usr/bin/python
-# genCheckAccessCTS.py - takes an input SELinux policy.conf file and generates
-# an XML file based on the allow and neverallow rules. The file contains rules,
-# which are created by expanding the SELinux rule notation into the individual
-# components which a checkAccess() check, that a policy manager would have to
-# perform, needs.
-#
-# This test does not work with all valid SELinux policy.conf files. It is meant
-# to simply use a given AOSP generated policy.conf file to create sets
-# representing the policy's types, attributes, classes and permissions, which
-# are used to expand the allow and neverallow rules found. For a full parser
-# and compiler of SELinux, see external/checkpolicy.
-# @dcashman
-
-import pdb
-import re
-import sys
-from xml.etree.ElementTree import Element, SubElement, tostring
-from xml.dom import minidom
-
-import SELinux_CTS
-from SELinux_CTS import SELinuxPolicy
-
-usage = "Usage: ./gen_SELinux_CTS.py input_policy_file output_xml_avc_rules_file neverallow_only=[t/f]"
-
-if __name__ == "__main__":
- # check usage
- if len(sys.argv) != 4:
- print usage
- exit()
- input_file = sys.argv[1]
- output_file = sys.argv[2]
- neverallow_only = (sys.argv[3] == "neverallow_only=t")
- policy = SELinuxPolicy()
- policy.from_file_name(input_file) #load data from file
-
- # expand rules into 4-tuples for SELinux.h checkAccess() check
- xml_root = Element('SELinux_AVC_Rules')
- if not neverallow_only:
- count = 1
- for a in policy.allow_rules:
- expanded_xml = SELinux_CTS.expand_avc_rule_to_xml(policy, a, str(count), 'allow')
- if len(expanded_xml):
- xml_root.append(expanded_xml)
- count += 1
- count = 1
- for n in policy.neverallow_rules:
- expanded_xml = SELinux_CTS.expand_avc_rule_to_xml(policy, n, str(count), 'neverallow')
- if len(expanded_xml):
- xml_root.append(expanded_xml)
- count += 1
-
- #print out the xml file
- s = tostring(xml_root)
- s_parsed = minidom.parseString(s)
- output = s_parsed.toprettyxml(indent=" ")
- with open(output_file, 'w') as out_file:
- out_file.write(output)
diff --git a/tools/selinux/test/policy_clean_test.conf b/tools/selinux/test/policy_clean_test.conf
deleted file mode 100644
index 074a63b..0000000
--- a/tools/selinux/test/policy_clean_test.conf
+++ /dev/null
@@ -1,2230 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class capability
-
-# file-related classes
-class file
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
- ioctl
- read
- write
- create
- getattr
- setattr
- lock
- relabelfrom
- relabelto
- append
- unlink
- link
- rename
- execute
- swapon
- quotaon
- mounton
-}
-
-class file
-inherits file
-{
- execute_no_trans
- entrypoint
- execmod
- open
- audit_access
-}
-
-class capability
-{
- # The capabilities are defined in include/linux/capability.h
- # Capabilities >= 32 are defined in the capability2 class.
- # Care should be taken to ensure that these are consistent with
- # those definitions. (Order matters)
-
- chown
- dac_override
- dac_read_search
- fowner
- fsetid
- kill
- setgid
- setuid
- setpcap
- linux_immutable
- net_bind_service
- net_broadcast
- net_admin
- net_raw
- ipc_lock
- ipc_owner
- sys_module
- sys_rawio
- sys_chroot
- sys_ptrace
- sys_pacct
- sys_admin
- sys_boot
- sys_nice
- sys_resource
- sys_time
- sys_tty_config
- mknod
- lease
- audit_write
- audit_control
- setfcap
-}
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0 }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-######################################
-# Attribute declarations
-#
-
-# All types used for processes.
-attribute domain;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-permissive bluetooth;
-
-#line 4
-typeattribute bluetooth appdomain;
-
-#line 5
-typeattribute bluetooth unconfineddomain;
-#line 5
-
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-permissive healthd;
-type healthd_exec, exec_type, file_type;
-
-# New domain is entered by executing the file.
-#line 7
-allow healthd healthd_exec:file { entrypoint read execute };
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-
-# Added to make the neverallow rule make sense in a limited environment.
-# Added at the bottom to not throw off file seek numbers in test suite.
-# This is not a problem, because allow rules are processed after all types
-# are gathered.
-type testTYPE, appdomain, domain;
diff --git a/tools/selinux/test/policy_test.conf b/tools/selinux/test/policy_test.conf
deleted file mode 100644
index d0962cd..0000000
--- a/tools/selinux/test/policy_test.conf
+++ /dev/null
@@ -1,2244 +0,0 @@
-#line 1 "external/sepolicy/security_classes"
-# FLASK
-
-#
-# Define the security object classes
-#
-
-# Classes marked as userspace are classes
-# for userspace object managers
-
-class capability
-
-# file-related classes
-class file
-
-#
-# Define a common prefix for file access vectors.
-#
-
-common file
-{
- ioctl
- read
- write
- create
- getattr
- setattr
- lock
- relabelfrom
- relabelto
- append
- unlink
- link
- rename
- execute
- swapon
- quotaon
- mounton
-}
-
-class file
-inherits file
-{
- execute_no_trans
- entrypoint
- execmod
- open
- audit_access
-}
-
-class capability
-{
- # The capabilities are defined in include/linux/capability.h
- # Capabilities >= 32 are defined in the capability2 class.
- # Care should be taken to ensure that these are consistent with
- # those definitions. (Order matters)
-
- chown
- dac_override
- dac_read_search
- fowner
- fsetid
- kill
- setgid
- setuid
- setpcap
- linux_immutable
- net_bind_service
- net_broadcast
- net_admin
- net_raw
- ipc_lock
- ipc_owner
- sys_module
- sys_rawio
- sys_chroot
- sys_ptrace
- sys_pacct
- sys_admin
- sys_boot
- sys_nice
- sys_resource
- sys_time
- sys_tty_config
- mknod
- lease
- audit_write
- audit_control
- setfcap
-}
-
-########################################
-#
-# Basic level names for system low and high
-#
-
-
-#line 1 "external/sepolicy/mls"
-#########################################
-# MLS declarations
-#
-
-# Generate the desired number of sensitivities and categories.
-
-#line 6
-# Each sensitivity has a name and zero or more aliases.
-#line 6
-sensitivity s0;
-#line 6
-
-#line 6
-
-#line 6
-# Define the ordering of the sensitivity levels (least to greatest)
-#line 6
-dominance { s0 }
-#line 6
-
-category c0;
-#line 7
-category c1;
-#line 7
-category c2;
-#line 7
-category c3;
-#line 7
-category c4;
-#line 7
-category c5;
-#line 7
-category c6;
-#line 7
-category c7;
-#line 7
-category c8;
-#line 7
-category c9;
-#line 7
-category c10;
-#line 7
-category c11;
-#line 7
-category c12;
-#line 7
-category c13;
-#line 7
-category c14;
-#line 7
-category c15;
-#line 7
-category c16;
-#line 7
-category c17;
-#line 7
-category c18;
-#line 7
-category c19;
-#line 7
-category c20;
-#line 7
-category c21;
-#line 7
-category c22;
-#line 7
-category c23;
-#line 7
-category c24;
-#line 7
-category c25;
-#line 7
-category c26;
-#line 7
-category c27;
-#line 7
-category c28;
-#line 7
-category c29;
-#line 7
-category c30;
-#line 7
-category c31;
-#line 7
-category c32;
-#line 7
-category c33;
-#line 7
-category c34;
-#line 7
-category c35;
-#line 7
-category c36;
-#line 7
-category c37;
-#line 7
-category c38;
-#line 7
-category c39;
-#line 7
-category c40;
-#line 7
-category c41;
-#line 7
-category c42;
-#line 7
-category c43;
-#line 7
-category c44;
-#line 7
-category c45;
-#line 7
-category c46;
-#line 7
-category c47;
-#line 7
-category c48;
-#line 7
-category c49;
-#line 7
-category c50;
-#line 7
-category c51;
-#line 7
-category c52;
-#line 7
-category c53;
-#line 7
-category c54;
-#line 7
-category c55;
-#line 7
-category c56;
-#line 7
-category c57;
-#line 7
-category c58;
-#line 7
-category c59;
-#line 7
-category c60;
-#line 7
-category c61;
-#line 7
-category c62;
-#line 7
-category c63;
-#line 7
-category c64;
-#line 7
-category c65;
-#line 7
-category c66;
-#line 7
-category c67;
-#line 7
-category c68;
-#line 7
-category c69;
-#line 7
-category c70;
-#line 7
-category c71;
-#line 7
-category c72;
-#line 7
-category c73;
-#line 7
-category c74;
-#line 7
-category c75;
-#line 7
-category c76;
-#line 7
-category c77;
-#line 7
-category c78;
-#line 7
-category c79;
-#line 7
-category c80;
-#line 7
-category c81;
-#line 7
-category c82;
-#line 7
-category c83;
-#line 7
-category c84;
-#line 7
-category c85;
-#line 7
-category c86;
-#line 7
-category c87;
-#line 7
-category c88;
-#line 7
-category c89;
-#line 7
-category c90;
-#line 7
-category c91;
-#line 7
-category c92;
-#line 7
-category c93;
-#line 7
-category c94;
-#line 7
-category c95;
-#line 7
-category c96;
-#line 7
-category c97;
-#line 7
-category c98;
-#line 7
-category c99;
-#line 7
-category c100;
-#line 7
-category c101;
-#line 7
-category c102;
-#line 7
-category c103;
-#line 7
-category c104;
-#line 7
-category c105;
-#line 7
-category c106;
-#line 7
-category c107;
-#line 7
-category c108;
-#line 7
-category c109;
-#line 7
-category c110;
-#line 7
-category c111;
-#line 7
-category c112;
-#line 7
-category c113;
-#line 7
-category c114;
-#line 7
-category c115;
-#line 7
-category c116;
-#line 7
-category c117;
-#line 7
-category c118;
-#line 7
-category c119;
-#line 7
-category c120;
-#line 7
-category c121;
-#line 7
-category c122;
-#line 7
-category c123;
-#line 7
-category c124;
-#line 7
-category c125;
-#line 7
-category c126;
-#line 7
-category c127;
-#line 7
-category c128;
-#line 7
-category c129;
-#line 7
-category c130;
-#line 7
-category c131;
-#line 7
-category c132;
-#line 7
-category c133;
-#line 7
-category c134;
-#line 7
-category c135;
-#line 7
-category c136;
-#line 7
-category c137;
-#line 7
-category c138;
-#line 7
-category c139;
-#line 7
-category c140;
-#line 7
-category c141;
-#line 7
-category c142;
-#line 7
-category c143;
-#line 7
-category c144;
-#line 7
-category c145;
-#line 7
-category c146;
-#line 7
-category c147;
-#line 7
-category c148;
-#line 7
-category c149;
-#line 7
-category c150;
-#line 7
-category c151;
-#line 7
-category c152;
-#line 7
-category c153;
-#line 7
-category c154;
-#line 7
-category c155;
-#line 7
-category c156;
-#line 7
-category c157;
-#line 7
-category c158;
-#line 7
-category c159;
-#line 7
-category c160;
-#line 7
-category c161;
-#line 7
-category c162;
-#line 7
-category c163;
-#line 7
-category c164;
-#line 7
-category c165;
-#line 7
-category c166;
-#line 7
-category c167;
-#line 7
-category c168;
-#line 7
-category c169;
-#line 7
-category c170;
-#line 7
-category c171;
-#line 7
-category c172;
-#line 7
-category c173;
-#line 7
-category c174;
-#line 7
-category c175;
-#line 7
-category c176;
-#line 7
-category c177;
-#line 7
-category c178;
-#line 7
-category c179;
-#line 7
-category c180;
-#line 7
-category c181;
-#line 7
-category c182;
-#line 7
-category c183;
-#line 7
-category c184;
-#line 7
-category c185;
-#line 7
-category c186;
-#line 7
-category c187;
-#line 7
-category c188;
-#line 7
-category c189;
-#line 7
-category c190;
-#line 7
-category c191;
-#line 7
-category c192;
-#line 7
-category c193;
-#line 7
-category c194;
-#line 7
-category c195;
-#line 7
-category c196;
-#line 7
-category c197;
-#line 7
-category c198;
-#line 7
-category c199;
-#line 7
-category c200;
-#line 7
-category c201;
-#line 7
-category c202;
-#line 7
-category c203;
-#line 7
-category c204;
-#line 7
-category c205;
-#line 7
-category c206;
-#line 7
-category c207;
-#line 7
-category c208;
-#line 7
-category c209;
-#line 7
-category c210;
-#line 7
-category c211;
-#line 7
-category c212;
-#line 7
-category c213;
-#line 7
-category c214;
-#line 7
-category c215;
-#line 7
-category c216;
-#line 7
-category c217;
-#line 7
-category c218;
-#line 7
-category c219;
-#line 7
-category c220;
-#line 7
-category c221;
-#line 7
-category c222;
-#line 7
-category c223;
-#line 7
-category c224;
-#line 7
-category c225;
-#line 7
-category c226;
-#line 7
-category c227;
-#line 7
-category c228;
-#line 7
-category c229;
-#line 7
-category c230;
-#line 7
-category c231;
-#line 7
-category c232;
-#line 7
-category c233;
-#line 7
-category c234;
-#line 7
-category c235;
-#line 7
-category c236;
-#line 7
-category c237;
-#line 7
-category c238;
-#line 7
-category c239;
-#line 7
-category c240;
-#line 7
-category c241;
-#line 7
-category c242;
-#line 7
-category c243;
-#line 7
-category c244;
-#line 7
-category c245;
-#line 7
-category c246;
-#line 7
-category c247;
-#line 7
-category c248;
-#line 7
-category c249;
-#line 7
-category c250;
-#line 7
-category c251;
-#line 7
-category c252;
-#line 7
-category c253;
-#line 7
-category c254;
-#line 7
-category c255;
-#line 7
-category c256;
-#line 7
-category c257;
-#line 7
-category c258;
-#line 7
-category c259;
-#line 7
-category c260;
-#line 7
-category c261;
-#line 7
-category c262;
-#line 7
-category c263;
-#line 7
-category c264;
-#line 7
-category c265;
-#line 7
-category c266;
-#line 7
-category c267;
-#line 7
-category c268;
-#line 7
-category c269;
-#line 7
-category c270;
-#line 7
-category c271;
-#line 7
-category c272;
-#line 7
-category c273;
-#line 7
-category c274;
-#line 7
-category c275;
-#line 7
-category c276;
-#line 7
-category c277;
-#line 7
-category c278;
-#line 7
-category c279;
-#line 7
-category c280;
-#line 7
-category c281;
-#line 7
-category c282;
-#line 7
-category c283;
-#line 7
-category c284;
-#line 7
-category c285;
-#line 7
-category c286;
-#line 7
-category c287;
-#line 7
-category c288;
-#line 7
-category c289;
-#line 7
-category c290;
-#line 7
-category c291;
-#line 7
-category c292;
-#line 7
-category c293;
-#line 7
-category c294;
-#line 7
-category c295;
-#line 7
-category c296;
-#line 7
-category c297;
-#line 7
-category c298;
-#line 7
-category c299;
-#line 7
-category c300;
-#line 7
-category c301;
-#line 7
-category c302;
-#line 7
-category c303;
-#line 7
-category c304;
-#line 7
-category c305;
-#line 7
-category c306;
-#line 7
-category c307;
-#line 7
-category c308;
-#line 7
-category c309;
-#line 7
-category c310;
-#line 7
-category c311;
-#line 7
-category c312;
-#line 7
-category c313;
-#line 7
-category c314;
-#line 7
-category c315;
-#line 7
-category c316;
-#line 7
-category c317;
-#line 7
-category c318;
-#line 7
-category c319;
-#line 7
-category c320;
-#line 7
-category c321;
-#line 7
-category c322;
-#line 7
-category c323;
-#line 7
-category c324;
-#line 7
-category c325;
-#line 7
-category c326;
-#line 7
-category c327;
-#line 7
-category c328;
-#line 7
-category c329;
-#line 7
-category c330;
-#line 7
-category c331;
-#line 7
-category c332;
-#line 7
-category c333;
-#line 7
-category c334;
-#line 7
-category c335;
-#line 7
-category c336;
-#line 7
-category c337;
-#line 7
-category c338;
-#line 7
-category c339;
-#line 7
-category c340;
-#line 7
-category c341;
-#line 7
-category c342;
-#line 7
-category c343;
-#line 7
-category c344;
-#line 7
-category c345;
-#line 7
-category c346;
-#line 7
-category c347;
-#line 7
-category c348;
-#line 7
-category c349;
-#line 7
-category c350;
-#line 7
-category c351;
-#line 7
-category c352;
-#line 7
-category c353;
-#line 7
-category c354;
-#line 7
-category c355;
-#line 7
-category c356;
-#line 7
-category c357;
-#line 7
-category c358;
-#line 7
-category c359;
-#line 7
-category c360;
-#line 7
-category c361;
-#line 7
-category c362;
-#line 7
-category c363;
-#line 7
-category c364;
-#line 7
-category c365;
-#line 7
-category c366;
-#line 7
-category c367;
-#line 7
-category c368;
-#line 7
-category c369;
-#line 7
-category c370;
-#line 7
-category c371;
-#line 7
-category c372;
-#line 7
-category c373;
-#line 7
-category c374;
-#line 7
-category c375;
-#line 7
-category c376;
-#line 7
-category c377;
-#line 7
-category c378;
-#line 7
-category c379;
-#line 7
-category c380;
-#line 7
-category c381;
-#line 7
-category c382;
-#line 7
-category c383;
-#line 7
-category c384;
-#line 7
-category c385;
-#line 7
-category c386;
-#line 7
-category c387;
-#line 7
-category c388;
-#line 7
-category c389;
-#line 7
-category c390;
-#line 7
-category c391;
-#line 7
-category c392;
-#line 7
-category c393;
-#line 7
-category c394;
-#line 7
-category c395;
-#line 7
-category c396;
-#line 7
-category c397;
-#line 7
-category c398;
-#line 7
-category c399;
-#line 7
-category c400;
-#line 7
-category c401;
-#line 7
-category c402;
-#line 7
-category c403;
-#line 7
-category c404;
-#line 7
-category c405;
-#line 7
-category c406;
-#line 7
-category c407;
-#line 7
-category c408;
-#line 7
-category c409;
-#line 7
-category c410;
-#line 7
-category c411;
-#line 7
-category c412;
-#line 7
-category c413;
-#line 7
-category c414;
-#line 7
-category c415;
-#line 7
-category c416;
-#line 7
-category c417;
-#line 7
-category c418;
-#line 7
-category c419;
-#line 7
-category c420;
-#line 7
-category c421;
-#line 7
-category c422;
-#line 7
-category c423;
-#line 7
-category c424;
-#line 7
-category c425;
-#line 7
-category c426;
-#line 7
-category c427;
-#line 7
-category c428;
-#line 7
-category c429;
-#line 7
-category c430;
-#line 7
-category c431;
-#line 7
-category c432;
-#line 7
-category c433;
-#line 7
-category c434;
-#line 7
-category c435;
-#line 7
-category c436;
-#line 7
-category c437;
-#line 7
-category c438;
-#line 7
-category c439;
-#line 7
-category c440;
-#line 7
-category c441;
-#line 7
-category c442;
-#line 7
-category c443;
-#line 7
-category c444;
-#line 7
-category c445;
-#line 7
-category c446;
-#line 7
-category c447;
-#line 7
-category c448;
-#line 7
-category c449;
-#line 7
-category c450;
-#line 7
-category c451;
-#line 7
-category c452;
-#line 7
-category c453;
-#line 7
-category c454;
-#line 7
-category c455;
-#line 7
-category c456;
-#line 7
-category c457;
-#line 7
-category c458;
-#line 7
-category c459;
-#line 7
-category c460;
-#line 7
-category c461;
-#line 7
-category c462;
-#line 7
-category c463;
-#line 7
-category c464;
-#line 7
-category c465;
-#line 7
-category c466;
-#line 7
-category c467;
-#line 7
-category c468;
-#line 7
-category c469;
-#line 7
-category c470;
-#line 7
-category c471;
-#line 7
-category c472;
-#line 7
-category c473;
-#line 7
-category c474;
-#line 7
-category c475;
-#line 7
-category c476;
-#line 7
-category c477;
-#line 7
-category c478;
-#line 7
-category c479;
-#line 7
-category c480;
-#line 7
-category c481;
-#line 7
-category c482;
-#line 7
-category c483;
-#line 7
-category c484;
-#line 7
-category c485;
-#line 7
-category c486;
-#line 7
-category c487;
-#line 7
-category c488;
-#line 7
-category c489;
-#line 7
-category c490;
-#line 7
-category c491;
-#line 7
-category c492;
-#line 7
-category c493;
-#line 7
-category c494;
-#line 7
-category c495;
-#line 7
-category c496;
-#line 7
-category c497;
-#line 7
-category c498;
-#line 7
-category c499;
-#line 7
-category c500;
-#line 7
-category c501;
-#line 7
-category c502;
-#line 7
-category c503;
-#line 7
-category c504;
-#line 7
-category c505;
-#line 7
-category c506;
-#line 7
-category c507;
-#line 7
-category c508;
-#line 7
-category c509;
-#line 7
-category c510;
-#line 7
-category c511;
-#line 7
-category c512;
-#line 7
-category c513;
-#line 7
-category c514;
-#line 7
-category c515;
-#line 7
-category c516;
-#line 7
-category c517;
-#line 7
-category c518;
-#line 7
-category c519;
-#line 7
-category c520;
-#line 7
-category c521;
-#line 7
-category c522;
-#line 7
-category c523;
-#line 7
-category c524;
-#line 7
-category c525;
-#line 7
-category c526;
-#line 7
-category c527;
-#line 7
-category c528;
-#line 7
-category c529;
-#line 7
-category c530;
-#line 7
-category c531;
-#line 7
-category c532;
-#line 7
-category c533;
-#line 7
-category c534;
-#line 7
-category c535;
-#line 7
-category c536;
-#line 7
-category c537;
-#line 7
-category c538;
-#line 7
-category c539;
-#line 7
-category c540;
-#line 7
-category c541;
-#line 7
-category c542;
-#line 7
-category c543;
-#line 7
-category c544;
-#line 7
-category c545;
-#line 7
-category c546;
-#line 7
-category c547;
-#line 7
-category c548;
-#line 7
-category c549;
-#line 7
-category c550;
-#line 7
-category c551;
-#line 7
-category c552;
-#line 7
-category c553;
-#line 7
-category c554;
-#line 7
-category c555;
-#line 7
-category c556;
-#line 7
-category c557;
-#line 7
-category c558;
-#line 7
-category c559;
-#line 7
-category c560;
-#line 7
-category c561;
-#line 7
-category c562;
-#line 7
-category c563;
-#line 7
-category c564;
-#line 7
-category c565;
-#line 7
-category c566;
-#line 7
-category c567;
-#line 7
-category c568;
-#line 7
-category c569;
-#line 7
-category c570;
-#line 7
-category c571;
-#line 7
-category c572;
-#line 7
-category c573;
-#line 7
-category c574;
-#line 7
-category c575;
-#line 7
-category c576;
-#line 7
-category c577;
-#line 7
-category c578;
-#line 7
-category c579;
-#line 7
-category c580;
-#line 7
-category c581;
-#line 7
-category c582;
-#line 7
-category c583;
-#line 7
-category c584;
-#line 7
-category c585;
-#line 7
-category c586;
-#line 7
-category c587;
-#line 7
-category c588;
-#line 7
-category c589;
-#line 7
-category c590;
-#line 7
-category c591;
-#line 7
-category c592;
-#line 7
-category c593;
-#line 7
-category c594;
-#line 7
-category c595;
-#line 7
-category c596;
-#line 7
-category c597;
-#line 7
-category c598;
-#line 7
-category c599;
-#line 7
-category c600;
-#line 7
-category c601;
-#line 7
-category c602;
-#line 7
-category c603;
-#line 7
-category c604;
-#line 7
-category c605;
-#line 7
-category c606;
-#line 7
-category c607;
-#line 7
-category c608;
-#line 7
-category c609;
-#line 7
-category c610;
-#line 7
-category c611;
-#line 7
-category c612;
-#line 7
-category c613;
-#line 7
-category c614;
-#line 7
-category c615;
-#line 7
-category c616;
-#line 7
-category c617;
-#line 7
-category c618;
-#line 7
-category c619;
-#line 7
-category c620;
-#line 7
-category c621;
-#line 7
-category c622;
-#line 7
-category c623;
-#line 7
-category c624;
-#line 7
-category c625;
-#line 7
-category c626;
-#line 7
-category c627;
-#line 7
-category c628;
-#line 7
-category c629;
-#line 7
-category c630;
-#line 7
-category c631;
-#line 7
-category c632;
-#line 7
-category c633;
-#line 7
-category c634;
-#line 7
-category c635;
-#line 7
-category c636;
-#line 7
-category c637;
-#line 7
-category c638;
-#line 7
-category c639;
-#line 7
-category c640;
-#line 7
-category c641;
-#line 7
-category c642;
-#line 7
-category c643;
-#line 7
-category c644;
-#line 7
-category c645;
-#line 7
-category c646;
-#line 7
-category c647;
-#line 7
-category c648;
-#line 7
-category c649;
-#line 7
-category c650;
-#line 7
-category c651;
-#line 7
-category c652;
-#line 7
-category c653;
-#line 7
-category c654;
-#line 7
-category c655;
-#line 7
-category c656;
-#line 7
-category c657;
-#line 7
-category c658;
-#line 7
-category c659;
-#line 7
-category c660;
-#line 7
-category c661;
-#line 7
-category c662;
-#line 7
-category c663;
-#line 7
-category c664;
-#line 7
-category c665;
-#line 7
-category c666;
-#line 7
-category c667;
-#line 7
-category c668;
-#line 7
-category c669;
-#line 7
-category c670;
-#line 7
-category c671;
-#line 7
-category c672;
-#line 7
-category c673;
-#line 7
-category c674;
-#line 7
-category c675;
-#line 7
-category c676;
-#line 7
-category c677;
-#line 7
-category c678;
-#line 7
-category c679;
-#line 7
-category c680;
-#line 7
-category c681;
-#line 7
-category c682;
-#line 7
-category c683;
-#line 7
-category c684;
-#line 7
-category c685;
-#line 7
-category c686;
-#line 7
-category c687;
-#line 7
-category c688;
-#line 7
-category c689;
-#line 7
-category c690;
-#line 7
-category c691;
-#line 7
-category c692;
-#line 7
-category c693;
-#line 7
-category c694;
-#line 7
-category c695;
-#line 7
-category c696;
-#line 7
-category c697;
-#line 7
-category c698;
-#line 7
-category c699;
-#line 7
-category c700;
-#line 7
-category c701;
-#line 7
-category c702;
-#line 7
-category c703;
-#line 7
-category c704;
-#line 7
-category c705;
-#line 7
-category c706;
-#line 7
-category c707;
-#line 7
-category c708;
-#line 7
-category c709;
-#line 7
-category c710;
-#line 7
-category c711;
-#line 7
-category c712;
-#line 7
-category c713;
-#line 7
-category c714;
-#line 7
-category c715;
-#line 7
-category c716;
-#line 7
-category c717;
-#line 7
-category c718;
-#line 7
-category c719;
-#line 7
-category c720;
-#line 7
-category c721;
-#line 7
-category c722;
-#line 7
-category c723;
-#line 7
-category c724;
-#line 7
-category c725;
-#line 7
-category c726;
-#line 7
-category c727;
-#line 7
-category c728;
-#line 7
-category c729;
-#line 7
-category c730;
-#line 7
-category c731;
-#line 7
-category c732;
-#line 7
-category c733;
-#line 7
-category c734;
-#line 7
-category c735;
-#line 7
-category c736;
-#line 7
-category c737;
-#line 7
-category c738;
-#line 7
-category c739;
-#line 7
-category c740;
-#line 7
-category c741;
-#line 7
-category c742;
-#line 7
-category c743;
-#line 7
-category c744;
-#line 7
-category c745;
-#line 7
-category c746;
-#line 7
-category c747;
-#line 7
-category c748;
-#line 7
-category c749;
-#line 7
-category c750;
-#line 7
-category c751;
-#line 7
-category c752;
-#line 7
-category c753;
-#line 7
-category c754;
-#line 7
-category c755;
-#line 7
-category c756;
-#line 7
-category c757;
-#line 7
-category c758;
-#line 7
-category c759;
-#line 7
-category c760;
-#line 7
-category c761;
-#line 7
-category c762;
-#line 7
-category c763;
-#line 7
-category c764;
-#line 7
-category c765;
-#line 7
-category c766;
-#line 7
-category c767;
-#line 7
-category c768;
-#line 7
-category c769;
-#line 7
-category c770;
-#line 7
-category c771;
-#line 7
-category c772;
-#line 7
-category c773;
-#line 7
-category c774;
-#line 7
-category c775;
-#line 7
-category c776;
-#line 7
-category c777;
-#line 7
-category c778;
-#line 7
-category c779;
-#line 7
-category c780;
-#line 7
-category c781;
-#line 7
-category c782;
-#line 7
-category c783;
-#line 7
-category c784;
-#line 7
-category c785;
-#line 7
-category c786;
-#line 7
-category c787;
-#line 7
-category c788;
-#line 7
-category c789;
-#line 7
-category c790;
-#line 7
-category c791;
-#line 7
-category c792;
-#line 7
-category c793;
-#line 7
-category c794;
-#line 7
-category c795;
-#line 7
-category c796;
-#line 7
-category c797;
-#line 7
-category c798;
-#line 7
-category c799;
-#line 7
-category c800;
-#line 7
-category c801;
-#line 7
-category c802;
-#line 7
-category c803;
-#line 7
-category c804;
-#line 7
-category c805;
-#line 7
-category c806;
-#line 7
-category c807;
-#line 7
-category c808;
-#line 7
-category c809;
-#line 7
-category c810;
-#line 7
-category c811;
-#line 7
-category c812;
-#line 7
-category c813;
-#line 7
-category c814;
-#line 7
-category c815;
-#line 7
-category c816;
-#line 7
-category c817;
-#line 7
-category c818;
-#line 7
-category c819;
-#line 7
-category c820;
-#line 7
-category c821;
-#line 7
-category c822;
-#line 7
-category c823;
-#line 7
-category c824;
-#line 7
-category c825;
-#line 7
-category c826;
-#line 7
-category c827;
-#line 7
-category c828;
-#line 7
-category c829;
-#line 7
-category c830;
-#line 7
-category c831;
-#line 7
-category c832;
-#line 7
-category c833;
-#line 7
-category c834;
-#line 7
-category c835;
-#line 7
-category c836;
-#line 7
-category c837;
-#line 7
-category c838;
-#line 7
-category c839;
-#line 7
-category c840;
-#line 7
-category c841;
-#line 7
-category c842;
-#line 7
-category c843;
-#line 7
-category c844;
-#line 7
-category c845;
-#line 7
-category c846;
-#line 7
-category c847;
-#line 7
-category c848;
-#line 7
-category c849;
-#line 7
-category c850;
-#line 7
-category c851;
-#line 7
-category c852;
-#line 7
-category c853;
-#line 7
-category c854;
-#line 7
-category c855;
-#line 7
-category c856;
-#line 7
-category c857;
-#line 7
-category c858;
-#line 7
-category c859;
-#line 7
-category c860;
-#line 7
-category c861;
-#line 7
-category c862;
-#line 7
-category c863;
-#line 7
-category c864;
-#line 7
-category c865;
-#line 7
-category c866;
-#line 7
-category c867;
-#line 7
-category c868;
-#line 7
-category c869;
-#line 7
-category c870;
-#line 7
-category c871;
-#line 7
-category c872;
-#line 7
-category c873;
-#line 7
-category c874;
-#line 7
-category c875;
-#line 7
-category c876;
-#line 7
-category c877;
-#line 7
-category c878;
-#line 7
-category c879;
-#line 7
-category c880;
-#line 7
-category c881;
-#line 7
-category c882;
-#line 7
-category c883;
-#line 7
-category c884;
-#line 7
-category c885;
-#line 7
-category c886;
-#line 7
-category c887;
-#line 7
-category c888;
-#line 7
-category c889;
-#line 7
-category c890;
-#line 7
-category c891;
-#line 7
-category c892;
-#line 7
-category c893;
-#line 7
-category c894;
-#line 7
-category c895;
-#line 7
-category c896;
-#line 7
-category c897;
-#line 7
-category c898;
-#line 7
-category c899;
-#line 7
-category c900;
-#line 7
-category c901;
-#line 7
-category c902;
-#line 7
-category c903;
-#line 7
-category c904;
-#line 7
-category c905;
-#line 7
-category c906;
-#line 7
-category c907;
-#line 7
-category c908;
-#line 7
-category c909;
-#line 7
-category c910;
-#line 7
-category c911;
-#line 7
-category c912;
-#line 7
-category c913;
-#line 7
-category c914;
-#line 7
-category c915;
-#line 7
-category c916;
-#line 7
-category c917;
-#line 7
-category c918;
-#line 7
-category c919;
-#line 7
-category c920;
-#line 7
-category c921;
-#line 7
-category c922;
-#line 7
-category c923;
-#line 7
-category c924;
-#line 7
-category c925;
-#line 7
-category c926;
-#line 7
-category c927;
-#line 7
-category c928;
-#line 7
-category c929;
-#line 7
-category c930;
-#line 7
-category c931;
-#line 7
-category c932;
-#line 7
-category c933;
-#line 7
-category c934;
-#line 7
-category c935;
-#line 7
-category c936;
-#line 7
-category c937;
-#line 7
-category c938;
-#line 7
-category c939;
-#line 7
-category c940;
-#line 7
-category c941;
-#line 7
-category c942;
-#line 7
-category c943;
-#line 7
-category c944;
-#line 7
-category c945;
-#line 7
-category c946;
-#line 7
-category c947;
-#line 7
-category c948;
-#line 7
-category c949;
-#line 7
-category c950;
-#line 7
-category c951;
-#line 7
-category c952;
-#line 7
-category c953;
-#line 7
-category c954;
-#line 7
-category c955;
-#line 7
-category c956;
-#line 7
-category c957;
-#line 7
-category c958;
-#line 7
-category c959;
-#line 7
-category c960;
-#line 7
-category c961;
-#line 7
-category c962;
-#line 7
-category c963;
-#line 7
-category c964;
-#line 7
-category c965;
-#line 7
-category c966;
-#line 7
-category c967;
-#line 7
-category c968;
-#line 7
-category c969;
-#line 7
-category c970;
-#line 7
-category c971;
-#line 7
-category c972;
-#line 7
-category c973;
-#line 7
-category c974;
-#line 7
-category c975;
-#line 7
-category c976;
-#line 7
-category c977;
-#line 7
-category c978;
-#line 7
-category c979;
-#line 7
-category c980;
-#line 7
-category c981;
-#line 7
-category c982;
-#line 7
-category c983;
-#line 7
-category c984;
-#line 7
-category c985;
-#line 7
-category c986;
-#line 7
-category c987;
-#line 7
-category c988;
-#line 7
-category c989;
-#line 7
-category c990;
-#line 7
-category c991;
-#line 7
-category c992;
-#line 7
-category c993;
-#line 7
-category c994;
-#line 7
-category c995;
-#line 7
-category c996;
-#line 7
-category c997;
-#line 7
-category c998;
-#line 7
-category c999;
-#line 7
-category c1000;
-#line 7
-category c1001;
-#line 7
-category c1002;
-#line 7
-category c1003;
-#line 7
-category c1004;
-#line 7
-category c1005;
-#line 7
-category c1006;
-#line 7
-category c1007;
-#line 7
-category c1008;
-#line 7
-category c1009;
-#line 7
-category c1010;
-#line 7
-category c1011;
-#line 7
-category c1012;
-#line 7
-category c1013;
-#line 7
-category c1014;
-#line 7
-category c1015;
-#line 7
-category c1016;
-#line 7
-category c1017;
-#line 7
-category c1018;
-#line 7
-category c1019;
-#line 7
-category c1020;
-#line 7
-category c1021;
-#line 7
-category c1022;
-#line 7
-category c1023;
-#line 7
-
-
-# Generate level definitions for each sensitivity and category.
-level s0:c0.c1023;
-#line 10
-
-######################################
-# Attribute declarations
-#
-
-# All types used for processes.
-attribute domain;
-
-# Domains that are allowed all permissions ("unconfined").
-attribute unconfineddomain;
-
-# All domains used for apps.
-attribute appdomain;
-
-# All types used for files that can exist on a labeled fs.
-# Do not use for pseudo file types.
-attribute file_type;
-
-# All types used for domain entry points.
-attribute exec_type;
-
-#line 1 "external/sepolicy/bluetooth.te"
-# bluetooth subsystem
-type bluetooth, domain;
-permissive bluetooth;
-
-#line 4
-typeattribute bluetooth appdomain;
-
-#line 5
-typeattribute bluetooth unconfineddomain;
-#line 5
-
-#line 1 "external/sepolicy/healthd.te"
-# healthd seclabel is specified in init.rc since
-# it lives in the rootfs and has no unique file type.
-type healthd, domain;
-permissive healthd;
-type healthd_exec, exec_type, file_type;
-
-# New domain is entered by executing the file.
-#line 7
-allow healthd healthd_exec:file { entrypoint read execute };
-
-###
-### Neverallow rules
-###
-### These are things that Android apps should NEVER be able to do
-###
-
-# Superuser capabilities.
-# bluetooth requires net_admin.
-neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-
-# Added to make the neverallow rule make sense in a limited environment.
-# Added at the bottom to not throw off file seek numbers in test suite.
-# This is not a problem, because allow rules are processed after all types
-# are gathered.
-type testTYPE, appdomain, domain;
-
-# added rules for further testing (display full range of needed functionality)
-allow unconfineddomain {fs_type dev_type file_type}:{ chr_file file } ~{entrypoint relabelto};
-
-allow init {fs_type dev_type file_type}:{ dir { { chr_file blk_file } { file lnk_file sock_file fifo_file } } } relabelto;
-
-neverallow { appdomain -unconfineddomain } {
- audio_device
- camera_device
- dm_device
- radio_device
- gps_device
- rpmsg_device
-}:chr_file { read write };
\ No newline at end of file
diff --git a/tools/selinux/test/testrunner.py b/tools/selinux/test/testrunner.py
deleted file mode 100755
index bc424e9..0000000
--- a/tools/selinux/test/testrunner.py
+++ /dev/null
@@ -1,442 +0,0 @@
-#!/usr/bin/python
-import sys
-sys.path.append('../src')
-import unittest
-import SELinux_CTS
-from SELinux_CTS import SELinuxPolicy
-
-policy_file_name = 'policy_test.conf'
-types = set([
- 'bluetooth',
- 'healthd',
- 'healthd_exec',
- 'testTYPE' ]) #testTYPE added for neverallow rule to make sense
-attributes = {
- 'domain': set(['bluetooth', 'healthd', 'testTYPE']),
- 'unconfineddomain': set(['bluetooth']),
- 'appdomain': set(['bluetooth', 'testTYPE']),
- 'file_type': set(['healthd_exec']),
- 'exec_type': set(['healthd_exec']) }
-common_classes = {
- 'file': set([
- 'ioctl',
- 'read',
- 'write',
- 'create',
- 'getattr',
- 'setattr',
- 'lock',
- 'relabelfrom',
- 'relabelto',
- 'append',
- 'unlink',
- 'link',
- 'rename',
- 'execute',
- 'swapon',
- 'quotaon',
- 'mounton' ]) }
-classes = {
- 'capability': set([
- 'chown',
- 'dac_override',
- 'dac_read_search',
- 'fowner',
- 'fsetid',
- 'kill',
- 'setgid',
- 'setuid',
- 'setpcap',
- 'linux_immutable',
- 'net_bind_service',
- 'net_broadcast',
- 'net_admin',
- 'net_raw',
- 'ipc_lock',
- 'ipc_owner',
- 'sys_module',
- 'sys_rawio',
- 'sys_chroot',
- 'sys_ptrace',
- 'sys_pacct',
- 'sys_admin',
- 'sys_boot',
- 'sys_nice',
- 'sys_resource',
- 'sys_time',
- 'sys_tty_config',
- 'mknod',
- 'lease',
- 'audit_write',
- 'audit_control',
- 'setfcap' ]),
- 'file': (set([
- 'execute_no_trans',
- 'entrypoint',
- 'execmod',
- 'open',
- 'audit_access' ]) | common_classes['file']) }
-
-# allow healthd healthd_exec:file { entrypoint read execute };
-allow_rules = [
- { 'source_types': {
- 'set': set([
- 'healthd']),
- 'flags': { 'complement': False } },
- 'target_types': {
- 'set': set([
- 'healthd_exec']),
- 'flags': { 'complement': False } },
- 'classes': {
- 'set': set([
- 'file']),
- 'flags': { 'complement': False } },
- 'permissions': {
- 'set': set([
- 'entrypoint',
- 'read',
- 'execute' ]),
- 'flags': { 'complement': False } } } ]
-
-# neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;
-neverallow_rules = [
- { 'source_types': {
- 'set': set([
- 'appdomain',
- '-unconfineddomain',
- '-bluetooth' ]),
- 'flags': { 'complement': False } },
- 'target_types': {
- 'set': set([
- 'self']),
- 'flags': { 'complement': False } },
- 'classes': {
- 'set': set([
- 'capability']),
- 'flags': { 'complement': False } },
- 'permissions': {
- 'set': set([
- '*' ]),
- 'flags': { 'complement': False } } } ]
-
-expected_final_allow_list = [
- [ ('healthd', 'healthd_exec', 'file', 'entrypoint'),
- ('healthd', 'healthd_exec', 'file', 'read'),
- ('healthd', 'healthd_exec', 'file', 'execute') ] ]
-
-expected_final_neverallow_list = [
- [ ('testTYPE', 'testTYPE', 'capability', 'chown'),
- ('testTYPE', 'testTYPE', 'capability', 'dac_override'),
- ('testTYPE', 'testTYPE', 'capability', 'dac_read_search'),
- ('testTYPE', 'testTYPE', 'capability', 'fowner'),
- ('testTYPE', 'testTYPE', 'capability', 'fsetid'),
- ('testTYPE', 'testTYPE', 'capability', 'kill'),
- ('testTYPE', 'testTYPE', 'capability', 'setgid'),
- ('testTYPE', 'testTYPE', 'capability', 'setuid'),
- ('testTYPE', 'testTYPE', 'capability', 'setpcap'),
- ('testTYPE', 'testTYPE', 'capability', 'linux_immutable'),
- ('testTYPE', 'testTYPE', 'capability', 'net_bind_service'),
- ('testTYPE', 'testTYPE', 'capability', 'net_broadcast'),
- ('testTYPE', 'testTYPE', 'capability', 'net_admin'),
- ('testTYPE', 'testTYPE', 'capability', 'net_raw'),
- ('testTYPE', 'testTYPE', 'capability', 'ipc_lock'),
- ('testTYPE', 'testTYPE', 'capability', 'ipc_owner'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_module'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_rawio'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_chroot'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_ptrace'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_pacct'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_admin'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_boot'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_nice'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_resource'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_time'),
- ('testTYPE', 'testTYPE', 'capability', 'sys_tty_config'),
- ('testTYPE', 'testTYPE', 'capability', 'mknod'),
- ('testTYPE', 'testTYPE', 'capability', 'lease'),
- ('testTYPE', 'testTYPE', 'capability', 'audit_write'),
- ('testTYPE', 'testTYPE', 'capability', 'audit_control'),
- ('testTYPE', 'testTYPE', 'capability', 'setfcap') ] ]
-
-
-class SELinuxPolicyTests(unittest.TestCase):
-
-
- def setUp(self):
- self.test_policy = SELinuxPolicy()
- self.test_file = open(policy_file_name, 'r')
- self.test_policy.types = types
- self.test_policy.attributes = attributes
- self.test_policy.common_classes = common_classes
- self.test_policy.classes = classes
- self.test_policy.allow_rules = allow_rules
- self.test_policy.neverallow_rules = neverallow_rules
- return
-
- def testExpandAvcRule(self):
- #TODO: add more examples here to cover different cases
- expanded_allow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.allow_rules[0])
- for a in expected_final_allow_list[0]:
- self.failUnless(a in expanded_allow_list)
- expanded_neverallow_list = SELinux_CTS.expand_avc_rule(self.test_policy, self.test_policy.neverallow_rules[0])
- for n in expected_final_neverallow_list[0]:
- self.failUnless(n in expanded_neverallow_list)
-
- def testExpandBrackets(self):
- #test position without bracket:
- self.test_file.seek(279)
- self.failIf(SELinux_CTS.expand_brackets(self.test_file))
-
- #test position with bracket:
- self.test_file.seek(26123)
- self.failUnless(SELinux_CTS.expand_brackets(self.test_file) == " entrypoint read execute ")
-
- #test position with nested brackets:
- self.test_file.seek(26873)
- self.failUnless(SELinux_CTS.expand_brackets(self.test_file)
- == " dir chr_file blk_file file lnk_file sock_file fifo_file ")
-
- def testGetAvcRuleComponent(self):
- #test against normal ('allow healthd healthd_exec:file ...)
- self.test_file.seek(26096)
- normal_src = { 'flags': { 'complement': False },
- 'set': set(['healthd']) }
- normal_tgt = { 'flags': { 'complement': False },
- 'set': set(['healthd_exec']) }
- normal_class = { 'flags': { 'complement': False },
- 'set': set(['file']) }
- normal_perm = { 'flags': { 'complement': False },
- 'set': set(['entrypoint', 'read', 'execute']) }
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == normal_src)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == normal_tgt)
- c = SELinux_CTS.advance_past_whitespace(self.test_file)
- if c == ':':
- self.test_file.read(1)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == normal_class)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == normal_perm)
-
- #test against 'hard' ('init {fs_type ...' )
- self.test_file.seek(26838)
- hard_src = { 'flags': { 'complement': False },
- 'set': set(['init']) }
- hard_tgt = { 'flags': { 'complement': False },
- 'set': set(['fs_type', 'dev_type', 'file_type']) }
- hard_class = { 'flags': { 'complement': False },
- 'set': set(['dir', 'chr_file', 'blk_file', 'file', 'lnk_file', 'sock_file', 'fifo_file']) }
- hard_perm = { 'flags': { 'complement': False },
- 'set': set(['relabelto']) }
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == hard_src)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == hard_tgt)
- #mimic ':' check:
- c = SELinux_CTS.advance_past_whitespace(self.test_file)
- if c == ':':
- self.test_file.read(1)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == hard_class)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == hard_perm)
-
- #test against 'multi-line' ('init {fs_type ...' )
- self.test_file.seek(26967)
- multi_src = { 'flags': { 'complement': False },
- 'set': set(['appdomain', '-unconfineddomain']) }
- multi_tgt = { 'flags': { 'complement': False },
- 'set': set(['audio_device', 'camera_device', 'dm_device', 'radio_device', 'gps_device', 'rpmsg_device']) }
- multi_class = { 'flags': { 'complement': False },
- 'set': set(['chr_file']) }
- multi_perm = { 'flags': { 'complement': False },
- 'set': set(['read', 'write']) }
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == multi_src)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == multi_tgt)
- c = SELinux_CTS.advance_past_whitespace(self.test_file)
- if c == ':':
- self.test_file.read(1)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == multi_class)
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == multi_perm)
-
- #test against 'complement'
- self.test_file.seek(26806)
- complement = { 'flags': { 'complement': True },
- 'set': set(['entrypoint', 'relabelto']) }
- self.failUnless(SELinux_CTS.get_avc_rule_component(self.test_file)
- == complement)
-
- def testGetLineType(self):
- self.failUnless(SELinux_CTS.get_line_type('type bluetooth, domain;')
- == SELinux_CTS.TYPE)
- self.failUnless(SELinux_CTS.get_line_type('attribute unconfineddomain;')
- == SELinux_CTS.ATTRIBUTE)
- self.failUnless(SELinux_CTS.get_line_type('typeattribute bluetooth appdomain;')
- == SELinux_CTS.TYPEATTRIBUTE)
- self.failUnless(SELinux_CTS.get_line_type('class file')
- == SELinux_CTS.CLASS)
- self.failUnless(SELinux_CTS.get_line_type('common file')
- == SELinux_CTS.COMMON)
- self.failUnless(SELinux_CTS.get_line_type('allow healthd healthd_exec:file { entrypoint read execute };')
- == SELinux_CTS.ALLOW_RULE)
- self.failUnless(SELinux_CTS.get_line_type('neverallow { appdomain -unconfineddomain -bluetooth } self:capability *;')
- == SELinux_CTS.NEVERALLOW_RULE)
- self.failUnless(SELinux_CTS.get_line_type('# FLASK')
- == SELinux_CTS.OTHER)
-
- def testIsMultiLine(self):
- self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPE))
- self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.ATTRIBUTE))
- self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.TYPEATTRIBUTE))
- self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.CLASS))
- self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.COMMON))
- self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.ALLOW_RULE))
- self.failUnless(SELinux_CTS.is_multi_line(SELinux_CTS.NEVERALLOW_RULE))
- self.failIf(SELinux_CTS.is_multi_line(SELinux_CTS.OTHER))
-
- def testProcessInheritsSegment(self):
- inherit_offset = 448 # needs changing if file changes
- self.test_file.seek(inherit_offset, 0)
- inherit_result = SELinux_CTS.process_inherits_segment(self.test_file)
- self.failUnless(inherit_result == 'file')
- return
-
- def testFromFileName(self):
- #using a special file, since the test_file has some lines which don't 'jive'
- clean_policy_file = 'policy_clean_test.conf'
- from_file_policy = SELinuxPolicy()
- from_file_policy.from_file_name(clean_policy_file)
- self.failUnless(from_file_policy.types == self.test_policy.types)
- self.failUnless(from_file_policy.attributes == self.test_policy.attributes)
- self.failUnless(from_file_policy.classes == self.test_policy.classes)
- self.failUnless(from_file_policy.common_classes == self.test_policy.common_classes)
- self.failUnless(from_file_policy.allow_rules == self.test_policy.allow_rules)
- self.failUnless(from_file_policy.neverallow_rules == self.test_policy.neverallow_rules)
-
- def testExpandPermissions(self):
- #test general case
- test_class_obj = 'file'
- general_set = set(['read', 'write', 'execute'])
- expanded_general_set = general_set
- self.failUnless(self.test_policy.expand_permissions(test_class_obj, general_set)
- == general_set)
- star_set = set(['*'])
- expanded_star_set = self.test_policy.classes['file'] #everything in the class
- self.failUnless(self.test_policy.expand_permissions(test_class_obj, star_set)
- == expanded_star_set)
- complement_set = set(['*', '-open'])
- expanded_complement_set = self.test_policy.classes['file'] - set(['open'])
- self.failUnless(self.test_policy.expand_permissions(test_class_obj, complement_set)
- == expanded_complement_set)
-
- def testExpandTypes(self):
-
- #test general case and '-' handling
- test_source_set = set([
- 'domain',
- '-bluetooth' ])
- expanded_test_source_set = set([
- 'healthd', 'testTYPE' ])
- self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
-
- #test '*' handling
- test_source_set = set([ '*' ])
- expanded_test_source_set = set([
- 'bluetooth', 'healthd', 'testTYPE' ])
- self.failUnless(self.test_policy.expand_types(test_source_set) == types)
- #test - handling
- test_source_set = set([
- '*',
- '-bluetooth'])
- expanded_test_source_set = set([
- 'healthd', 'healthd_exec', 'testTYPE' ])
- self.failUnless(self.test_policy.expand_types(test_source_set) == expanded_test_source_set)
-
- def testProcessAttributeLine(self):
- attribute_policy = SELinuxPolicy()
- #test with 'normal input'
- test_normal_string = 'attribute TEST_att;'
- test_attribute = 'TEST_att'
- attribute_policy.process_attribute_line(test_normal_string)
- self.failUnless( test_attribute in attribute_policy.attributes)
- #TODO: test on bogus inputs
-
- def testProcessClassLine(self):
- class_policy = SELinuxPolicy()
- #offsets need changing if test file changes
- common_offset = 279
- class_initial_offset = 212
- class_perm_offset = 437
- self.test_file.seek(common_offset, 0)
- line = self.test_file.readline()
- class_policy.process_common_line(line, self.test_file)
- self.test_file.seek(class_initial_offset, 0)
- line = self.test_file.readline()
- class_policy.process_class_line(line, self.test_file)
- self.failUnless('file' in class_policy.classes)
- self.test_file.seek(class_perm_offset, 0)
- line = self.test_file.readline()
- class_policy.process_class_line(line, self.test_file)
- self.failUnless(class_policy.classes['file'] == classes['file'])
-
- def testProcessCommonLine(self):
- common_policy = SELinuxPolicy()
- common_offset = 279 # needs changing if file changes
- self.test_file.seek(common_offset, 0)
- line = self.test_file.readline()
- common_policy.process_common_line(line, self.test_file)
- self.failUnless('file' in common_policy.common_classes )
- self.failUnless(common_policy.common_classes['file'] == common_classes['file'])
-
- def testProcessAvcRuleLine(self):
- avc_policy = SELinuxPolicy()
- allow_offset = 26091 # needs changing if file changes
- neverallow_offset = 26311 # needs changing if file changes
- self.test_file.seek(allow_offset, 0)
- line = self.test_file.readline()
- avc_policy.process_avc_rule_line(line, self.test_file)
- self.failUnless(avc_policy.allow_rules[0] == allow_rules[0] ) # always '0'?
- self.test_file.seek(neverallow_offset, 0)
- line = self.test_file.readline()
- avc_policy.process_avc_rule_line(line, self.test_file)
- self.failUnless(avc_policy.neverallow_rules[0] == neverallow_rules[0] ) # always '0'?
-
- def testProcessTypeLine(self):
- type_policy = SELinuxPolicy()
- test_normal_string = 'type TEST_type, TEST_att1, TEST_att2;'
- test_type = 'TEST_type'
- test_atts = ['TEST_att1', 'TEST_att2']
- #test with 'normal input'
- type_policy.process_type_line(test_normal_string)
- self.failUnless(test_type in type_policy.types)
- for a in test_atts:
- self.failUnless(a in type_policy.attributes)
- self.failUnless(test_type in type_policy.attributes[a])
- #TODO: test with domain only, no attributes
- # and test on bogus inputs
-
- def testProcessTypeattributeLine(self):
- typ_att_policy = SELinuxPolicy()
- test_normal_string = 'typeattribute TEST_type TEST_att1, TEST_att2;'
- test_type = 'TEST_type'
- test_atts = ['TEST_att1', 'TEST_att2']
- #test with 'normal input' (type should already be declared)
- typ_att_policy.process_type_line('type ' + test_type + ';')
- typ_att_policy.process_typeattribute_line(test_normal_string)
- self.failUnless(test_type in typ_att_policy.types)
- for a in test_atts:
- self.failUnless(a in typ_att_policy.attributes)
- self.failUnless(test_type in typ_att_policy.attributes[a])
- #TODO: test with domain only, no attributes
- # and test on bogus inputs
-
-def main():
- unittest.main()
-
-if __name__ == '__main__':
- main()
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
index c43183a..d0d3d56 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/build/CtsBuildProvider.java
@@ -31,7 +31,7 @@
@Option(name="cts-install-path", description="the path to the cts installation to use")
private String mCtsRootDirPath = System.getProperty("CTS_ROOT");
- public static final String CTS_BUILD_VERSION = "5.0_r2.5";
+ public static final String CTS_BUILD_VERSION = "5.1_r3";
/**
* {@inheritDoc}
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
index ce48664..c2c2a10 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/CtsTest.java
@@ -707,9 +707,9 @@
}
mTestPackageList.addAll(shardTestPackageList);
} catch (FileNotFoundException e) {
- throw new IllegalArgumentException("failed to find XTS plan file", e);
+ throw new IllegalArgumentException("failed to find test plan file", e);
} catch (ParseException e) {
- throw new IllegalArgumentException("failed to parse XTS plan file", e);
+ throw new IllegalArgumentException("failed to parse test plan file", e);
} catch (ConfigurationException e) {
throw new IllegalArgumentException("failed to process arguments", e);
}
@@ -747,7 +747,7 @@
testPkgDefs.add(testPackageDef);
}
} else if (mPackageNames.size() > 0){
- Log.i(LOG_TAG, String.format("Executing XTS test packages %s", mPackageNames));
+ Log.i(LOG_TAG, String.format("Executing test packages %s", mPackageNames));
Map<String, List<ITestPackageDef>> testPackageDefMap =
testRepo.getTestPackageDefsByName();
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
index 59bfcf8..3dee99e 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/DisplayTestRunner.java
@@ -29,7 +29,7 @@
private static final String OVERLAY_DISPLAY_DEVICES_SETTING_NAME = "overlay_display_devices";
// Use a non-standard pattern, must match values in tests/tests/display/.../DisplayTest.java
- private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "1281x721/214";
+ private static final String OVERLAY_DISPLAY_DEVICES_SETTING_VALUE = "181x161/214";
@Override
public void run(ITestInvocationListener listener) throws DeviceNotAvailableException {
diff --git a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
index 3d92eb3..72dccd4 100644
--- a/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
+++ b/tools/tradefed-host/src/com/android/cts/tradefed/testtype/PrintTestRemoteTestRunner.java
@@ -185,6 +185,11 @@
}
@Override
+ public void setTestCollection(boolean b) {
+ throw new UnsupportedOperationException("Test Collection mode is not supported");
+ }
+
+ @Override
public void setTestSize(TestSize size) {
addInstrumentationArg(SIZE_ARG_NAME, ""/*size.getRunnerValue()*/);
}
diff --git a/tools/utils/CollectAllTests.java b/tools/utils/CollectAllTests.java
index ed74824..95b77f2 100644
--- a/tools/utils/CollectAllTests.java
+++ b/tools/utils/CollectAllTests.java
@@ -78,16 +78,26 @@
final String outputPathPrefix = args[0];
File manifestFile = new File(args[1]);
String jarFileName = args[2];
- final String javaPackageFilter = args[3];
- // Validate the javaPackageFilter value if non null.
- if (javaPackageFilter.length() != 0) {
- if (!isValidJavaPackage(javaPackageFilter)) {
- System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
- javaPackageFilter);
- System.exit(1);
- return;
+ final String javaPackageFilterArg = args[3] != null ? args[3].replaceAll("\\s+", "") : "";
+ final String[] javaPackagePrefixes;
+ // Validate the javaPackageFilter value if non-empty.
+ if (!javaPackageFilterArg.isEmpty()) {
+ javaPackagePrefixes = javaPackageFilterArg.split(":");
+ for (int i = 0; i < javaPackagePrefixes.length; ++i) {
+ final String javaPackageFilter = javaPackagePrefixes[i];
+ if (!isValidJavaPackage(javaPackageFilter)) {
+ System.err.println("Invalid " + ATTRIBUTE_JAVA_PACKAGE_FILTER + ": " +
+ javaPackageFilter);
+ System.exit(1);
+ return;
+ } else {
+ javaPackagePrefixes[i] = javaPackageFilter.trim() + ".";
+ }
}
+ } else {
+ javaPackagePrefixes = new String[0];
}
+
String architecture = args[4];
if (architecture == null || architecture.equals("")) {
System.err.println("Invalid architecture");
@@ -132,7 +142,7 @@
setAttribute(testPackageElem, ATTRIBUTE_RUNNER, runner);
setAttribute(testPackageElem, ATTRIBUTE_PACKAGE, packageName);
setAttribute(testPackageElem, ATTRIBUTE_NS, packageName);
- setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilter);
+ setAttribute(testPackageElem, ATTRIBUTE_JAVA_PACKAGE_FILTER, javaPackageFilterArg);
if (testType.type == TestType.HOST_SIDE_ONLY) {
setAttribute(testPackageElem, ATTRIBUTE_HOST_SIDE_ONLY, "true");
@@ -185,8 +195,6 @@
Map<String,TestClass> testCases = new LinkedHashMap<String, TestClass>();
- String javaPackagePrefix = javaPackageFilter.isEmpty() ? "" : (javaPackageFilter + ".");
-
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
@@ -196,9 +204,22 @@
}
String className
= name.substring(0, name.length() - ".class".length()).replace('/', '.');
- if (!className.startsWith(javaPackagePrefix)) {
+
+ boolean matchesPrefix = false;
+ if (javaPackagePrefixes.length > 0) {
+ for (String javaPackagePrefix : javaPackagePrefixes) {
+ if (className.startsWith(javaPackagePrefix)) {
+ matchesPrefix = true;
+ }
+ }
+ } else {
+ matchesPrefix = true;
+ }
+
+ if (!matchesPrefix) {
continue;
}
+
try {
Class<?> klass = Class.forName(className,
false,