Snap for 8892598 from 4173cce4c7643b053010a001feeada41b4316fcd to mainline-os-statsd-release
Change-Id: Idca1baa81ce7dd0c073ef2ef5fbaa4508da67c43
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py
index 9b29ab7..daa55c6 100644
--- a/apps/CameraITS/tests/its_base_test.py
+++ b/apps/CameraITS/tests/its_base_test.py
@@ -52,6 +52,8 @@
'sensor_fusion': [],
}
+logging.getLogger('matplotlib.font_manager').disabled = True
+
class ItsBaseTest(base_test.BaseTestClass):
"""Base test for CameraITS tests.
diff --git a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
index 7d8b9f2..e407fd6 100644
--- a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
+++ b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py
@@ -71,7 +71,6 @@
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
log_path = self.log_path
- debug = self.debug_mode
test_name_w_path = os.path.join(log_path, NAME)
# check SKIP conditions
@@ -113,11 +112,6 @@
caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV, fmt)
luma_locked = []
for i, cap in enumerate(caps):
- if debug:
- img = image_processing_utils.convert_capture_to_rgb_image(
- cap, props)
- image_processing_utils.write_image(
- img, f'{test_name_w_path}_ev{ev}_frame{i}.jpg')
if cap['metadata']['android.control.aeState'] == LOCKED:
ev_meta = cap['metadata']['android.control.aeExposureCompensation']
logging.debug('cap EV compensation: %d', ev_meta)
@@ -132,7 +126,8 @@
rel_tol=luma_locked_rtol):
raise AssertionError(f'AE locked lumas: {luma_locked}, '
f'RTOL: {luma_locked_rtol}')
- logging.debug('lumas in AE locked captures: %s', str(lumas))
+ logging.debug('lumas per frame ev %d: %s', ev, str(luma_locked))
+ logging.debug('mean lumas in AE locked captures: %s', str(lumas))
if caps[THRESH_CONVERGE_FOR_EV-1]['metadata'][
'android.control.aeState'] != LOCKED:
raise AssertionError(f'No AE lock by {THRESH_CONVERGE_FOR_EV} frame.')
diff --git a/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
index e3143f4..7fffa72 100644
--- a/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
+++ b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py
@@ -32,6 +32,7 @@
_SHADING_MODES = {0: 'LSC_OFF', 1: 'LSC_FAST', 2: 'LSC_HQ'}
_NUM_SHADING_MODES = len(_SHADING_MODES)
_THRESHOLD_DIFF_RATIO = 0.15
+_VGA_W, _VGA_H = 640, 480
def create_plots(shading_maps, reference_maps, num_map_gains, log_path):
@@ -76,9 +77,8 @@
Lens shading correction modes are OFF=0, FAST=1, and HQ=2.
- Uses smallest yuv size matching the aspect ratio of largest yuv size to
- reduce some USB bandwidth overhead since we are only looking at output
- metadata in this test.
+ Uses VGA sized captures to reduce some USB bandwidth overhead since we are
+ only looking at output metadata in this test.
First asserts all modes are supported. Then runs 2 captures.
@@ -118,13 +118,11 @@
% str(props.get('android.shading.availableModes')),
[*_SHADING_MODES])
- # get smallest matching fmt
+ # define fmt
mono_camera = camera_properties_utils.mono_camera(props)
cam.do_3a(mono_camera=mono_camera)
- largest_yuv_fmt = capture_request_utils.get_largest_yuv_format(props)
- largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height'])
- cap_fmt = capture_request_utils.get_smallest_yuv_format(
- props, match_ar=largest_yuv_size)
+ cap_fmt = {'format': 'yuv', 'width': _VGA_W, 'height': _VGA_H}
+ logging.debug('Capture format: %s', str(cap_fmt))
# cap1
reference_maps = [[] for mode in range(_NUM_SHADING_MODES)]
diff --git a/apps/CameraITS/tests/scene2_a/test_auto_flash.py b/apps/CameraITS/tests/scene2_a/test_auto_flash.py
index b20da2d..c00cd5c 100644
--- a/apps/CameraITS/tests/scene2_a/test_auto_flash.py
+++ b/apps/CameraITS/tests/scene2_a/test_auto_flash.py
@@ -107,10 +107,10 @@
test_name = os.path.join(self.log_path, _TEST_NAME)
# check SKIP conditions
- first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
+ vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial)
camera_properties_utils.skip_unless(
camera_properties_utils.flash(props) and
- first_api_level >= its_session_utils.ANDROID13_API_LEVEL)
+ vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL)
# establish connection with lighting controller
arduino_serial_port = lighting_control_utils.lighting_control(
diff --git a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
index 658ec8b..cb5be2a 100644
--- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py
@@ -27,7 +27,7 @@
class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest):
"""Test camera launch latency for S performance class as specified in CDD.
- [7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview
+ [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview
frame) < 600ms as measured by the CTS camera PerformanceTest under ITS
lighting conditions (3000K) for both primary cameras.
"""
diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
index f56d34c..fb19f2f 100644
--- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
+++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py
@@ -27,7 +27,7 @@
class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest):
"""Test jpeg capture latency for S performance class as specified in CDD.
- [7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p
+ [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p
resolution as measured by the CTS camera PerformanceTest under ITS lighting
conditions (3000K) for both primary cameras.
"""
diff --git a/apps/CameraITS/tests/scene3/test_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_edge_enhancement.py
index c6a3bcb..c1b63c7 100644
--- a/apps/CameraITS/tests/scene3/test_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_edge_enhancement.py
@@ -67,17 +67,17 @@
for n in range(NUM_SAMPLES):
cap = cam.do_capture(req, out_surface, repeat_request=req)
y, _, _ = image_processing_utils.convert_capture_to_planes(cap)
- chart.img = image_processing_utils.normalize_img(
- image_processing_utils.get_image_patch(
- y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ chart.img = image_processing_utils.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)
if n == 0:
image_processing_utils.write_image(
chart.img, '%s_edge=%d.jpg' % (
os.path.join(log_path, NAME), edge_mode))
edge_mode_res = cap['metadata']['android.edge.mode']
sharpness_list.append(
- image_processing_utils.compute_image_sharpness(chart.img))
-
+ image_processing_utils.compute_image_sharpness(chart.img)*255)
+ logging.debug('edge mode: %d, sharpness values: %s',
+ edge_mode_res, sharpness_list)
return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)}
@@ -89,7 +89,6 @@
"""
def test_edge_enhancement(self):
- logging.debug('Starting %s', NAME)
with its_session_utils.ItsSession(
device_id=self.dut.serial,
camera_id=self.camera_id,
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index c5e9b19..d67ebe4 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -107,16 +107,15 @@
caps = cam.do_capture([req]*NUM_SAMPLES, [out_surface], reprocess_format)
for n in range(NUM_SAMPLES):
y, _, _ = image_processing_utils.convert_capture_to_planes(caps[n])
- chart.img = image_processing_utils.normalize_img(
- image_processing_utils.get_image_patch(
- y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ chart.img = image_processing_utils.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)
if n == 0:
image_processing_utils.write_image(
chart.img, '%s_reprocess_fmt_%s_edge=%d.jpg' % (
os.path.join(log_path, NAME), reprocess_format, edge_mode))
edge_mode_res = caps[n]['metadata']['android.edge.mode']
sharpness_list.append(
- image_processing_utils.compute_image_sharpness(chart.img))
+ image_processing_utils.compute_image_sharpness(chart.img)*255)
logging.debug('Sharpness list for edge mode %d: %s',
edge_mode, str(sharpness_list))
return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)}
@@ -134,7 +133,6 @@
"""
def test_reprocess_edge_enhancement(self):
- logging.debug('Starting %s', NAME)
logging.debug('Edge modes: %s', str(EDGE_MODES))
with its_session_utils.ItsSession(
device_id=self.dut.serial,
@@ -179,9 +177,10 @@
# Initialize plot
pylab.figure('reprocess_result')
- pylab.title(NAME)
- pylab.xlabel('Edge Enhance Mode')
- pylab.ylabel('Sharpness')
+ pylab.suptitle(NAME)
+ pylab.title(str(EDGE_MODES))
+ pylab.xlabel('Edge Enhancement Mode')
+ pylab.ylabel('Image Sharpness')
pylab.xticks(EDGE_MODES_VALUES)
# Get the sharpness for each edge mode for regular requests
@@ -244,6 +243,7 @@
logging.debug('Check reprocess format: %s', reprocess_format)
check_edge_modes(sharpnesses_reprocess[reprocess_format])
+ # Check reprocessing doesn't make everyting worse
hq_div_off_reprocess = (
sharpnesses_reprocess[reprocess_format][EDGE_MODES['HQ']] /
sharpnesses_reprocess[reprocess_format][EDGE_MODES['OFF']])
@@ -251,11 +251,10 @@
sharpness_regular[EDGE_MODES['HQ']] /
sharpness_regular[EDGE_MODES['OFF']])
logging.debug('Verify reprocess HQ ~= reg HQ relative to OFF')
- if not math.isclose(hq_div_off_reprocess, hq_div_off_regular,
- rel_tol=SHARPNESS_RTOL):
- raise AssertionError(f'HQ/OFF_reprocess: {hq_div_off_reprocess:.4f}, '
- f'HQ/OFF_reg: {hq_div_off_regular:.4f}, '
- f'RTOL: {SHARPNESS_RTOL}')
+ if hq_div_off_reprocess < hq_div_off_regular*(1-SHARPNESS_RTOL):
+ raise AssertionError(
+ f'HQ/OFF_{reprocess_format}: {hq_div_off_reprocess:.4f}, '
+ f'HQ/OFF_reg: {hq_div_off_regular:.4f}, RTOL: {SHARPNESS_RTOL}')
if __name__ == '__main__':
diff --git a/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py b/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py
index 1f5b7c0..2781f18 100644
--- a/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py
+++ b/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py
@@ -35,9 +35,9 @@
_ROUNDESS_DELTA_THRESHOLD = 0.05
_MAX_CENTER_THRESHOLD_PERCENT = 0.075
-_MAX_DIMENSION_SIZE = (1920, 1440) # max mandatory preview stream resolution
+_MAX_AREA = 1920 * 1440 # max mandatory preview stream resolution
_MIN_CENTER_THRESHOLD_PERCENT = 0.02
-_MIN_DIMENSION_SIZE = (176, 144) # assume QCIF to be min preview size
+_MIN_AREA = 176 * 144 # assume QCIF to be min preview size
def _collect_data(cam, video_size, stabilize):
@@ -93,23 +93,19 @@
threshold value ratio between which the circle centers can differ
"""
- max_diagonal = _point_distance(0, 0,
- _MAX_DIMENSION_SIZE[0], _MAX_DIMENSION_SIZE[1])
- min_diagonal = _point_distance(0, 0,
- _MIN_DIMENSION_SIZE[0], _MIN_DIMENSION_SIZE[1])
+ img_area = image_size[0] * image_size[1]
- img_diagonal = _point_distance(0, 0, image_size[0], image_size[1])
+ normalized_area = ((img_area - _MIN_AREA) /
+ (_MAX_AREA - _MIN_AREA))
- normalized_diagonal = ((img_diagonal - min_diagonal) /
- (max_diagonal - min_diagonal))
-
- if normalized_diagonal > 1 or normalized_diagonal < 0:
- raise AssertionError(f'normalized diagonal > 1 or < 0!'
- f' img_diag: {img_diagonal}, '
- f' normalized_diagonal: {normalized_diagonal}')
+ if normalized_area > 1 or normalized_area < 0:
+ raise AssertionError(f'normalized area > 1 or < 0! '
+ f'image_size[0]: {image_size[0]}, '
+ f'image_size[1]: {image_size[1]}, '
+ f'normalized_area: {normalized_area}')
# Threshold should be larger for images with smaller resolution
- normalized_threshold_percent = ((1 - normalized_diagonal) *
+ normalized_threshold_percent = ((1 - normalized_area) *
(_MAX_CENTER_THRESHOLD_PERCENT -
_MIN_CENTER_THRESHOLD_PERCENT))
@@ -144,7 +140,7 @@
# Load scene.
its_session_utils.load_scene(cam, props, self.scene,
- self.tablet, chart_distance=0)
+ self.tablet, self.chart_distance)
# Check skip condition
first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
@@ -253,7 +249,8 @@
f'{_ROUNDESS_DELTA_THRESHOLD}, '
f'actual ratio difference: {roundness_diff}. ')
- # Distance between centers
+ # Distance between centers, x_offset and y_offset are relative to the
+ # radius of the circle, so they're normalized. Not pixel values.
unstab_center = (ustab_circle['x_offset'], ustab_circle['y_offset'])
logging.debug('unstabilized center: %s', unstab_center)
stab_center = (stab_circle['x_offset'], stab_circle['y_offset'])
diff --git a/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py
index 6eaca19..bb71367 100644
--- a/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py
+++ b/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py
@@ -137,13 +137,13 @@
logging.debug('physical available focal lengths: %s', str(fls_physical))
# Check SKIP conditions.
- first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
+ vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial)
camera_properties_utils.skip_unless(
- first_api_level >= its_session_utils.ANDROID13_API_LEVEL)
+ vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL)
# Load scene.
its_session_utils.load_scene(cam, props, self.scene,
- self.tablet, chart_distance=0)
+ self.tablet, self.chart_distance)
# Determine camera capabilities.
supported_video_qualities = cam.get_supported_video_qualities(
diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py
index d4fa21c..c1f4dff 100644
--- a/apps/CameraITS/tests/scene6/test_zoom.py
+++ b/apps/CameraITS/tests/scene6/test_zoom.py
@@ -78,10 +78,6 @@
test_tols = {}
test_yuv_sizes = []
for i in physical_ids:
- min_fd = physical_props[i]['android.lens.info.minimumFocusDistance']
- focal_l = physical_props[i]['android.lens.info.availableFocalLengths'][0]
- logging.debug('cam[%s] min_fd: %.3f (diopters), fl: %.2f',
- i, min_fd, focal_l)
yuv_sizes = capture_request_utils.get_available_output_sizes(
'yuv', physical_props[i])
test_yuv_sizes.append(yuv_sizes)
@@ -89,13 +85,16 @@
logging.debug('cam[%s] yuv sizes: %s', i, str(yuv_sizes))
# determine if minimum focus distance is less than rig depth
- if (math.isclose(min_fd, 0.0, rel_tol=1E-6) or # fixed focus
- 1.0/min_fd < chart_distance_m*MIN_FOCUS_DIST_TOL):
- test_tols[focal_l] = (RADIUS_RTOL, OFFSET_RTOL)
- else:
- test_tols[focal_l] = (RADIUS_RTOL_MIN_FD, OFFSET_RTOL_MIN_FD)
- logging.debug('loosening RTOL for cam[%s]: '
- 'min focus distance too large.', i)
+ min_fd = physical_props[i]['android.lens.info.minimumFocusDistance']
+ for fl in physical_props[i]['android.lens.info.availableFocalLengths']:
+ logging.debug('cam[%s] min_fd: %.3f (diopters), fl: %.2f', i, min_fd, fl)
+ if (math.isclose(min_fd, 0.0, rel_tol=1E-6) or # fixed focus
+ (1.0/min_fd < chart_distance_m*MIN_FOCUS_DIST_TOL)):
+ test_tols[fl] = (RADIUS_RTOL, OFFSET_RTOL)
+ else:
+ test_tols[fl] = (RADIUS_RTOL_MIN_FD, OFFSET_RTOL_MIN_FD)
+ logging.debug('loosening RTOL for cam[%s]: '
+ 'min focus distance too large.', i)
# find intersection of formats for max common format
common_sizes = list(set.intersection(*[set(list) for list in test_yuv_sizes]))
if debug:
diff --git a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py
index 0be38b5..195d567 100644
--- a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py
+++ b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py
@@ -129,7 +129,7 @@
in gyroscope movement. Test is a PASS if rotation is reduced in video.
"""
- def test_video_stability(self):
+ def test_video_stabilization(self):
rot_rig = {}
log_path = self.log_path
@@ -139,12 +139,12 @@
hidden_physical_id=self.hidden_physical_id) as cam:
props = cam.get_camera_properties()
props = cam.override_with_hidden_physical_camera_props(props)
- first_api_level = its_session_utils.get_first_api_level(self.dut.serial)
+ vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial)
supported_stabilization_modes = props[
'android.control.availableVideoStabilizationModes']
camera_properties_utils.skip_unless(
- first_api_level >= its_session_utils.ANDROID13_API_LEVEL and
+ vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL and
_VIDEO_STABILIZATION_MODE in supported_stabilization_modes)
# Raise error if not FRONT or REAR facing camera
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 9bf89bd..dae63c3 100755
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -240,7 +240,7 @@
img_name = os.path.join(out_path, f'test_{scene}.jpg')
logging.info('Please check scene setup in %s', img_name)
image_processing_utils.write_image(img, img_name)
- choice = input('Is the image okay for ITS {scene}? (Y/N)').lower()
+ choice = input(f'Is the image okay for ITS {scene}? (Y/N)').lower()
if choice == 'y':
break
diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py
index e91565e..c2065ba 100644
--- a/apps/CameraITS/utils/its_session_utils.py
+++ b/apps/CameraITS/utils/its_session_utils.py
@@ -1612,6 +1612,18 @@
return first_api_level
+def get_vendor_api_level(device_id):
+ """Return the int value for the vendor API level of the device."""
+ cmd = 'adb -s %s shell getprop ro.vendor.api_level' % device_id
+ try:
+ vendor_api_level = int(subprocess.check_output(cmd.split()).rstrip())
+ logging.debug('First vendor API level: %d', vendor_api_level)
+ except (subprocess.CalledProcessError, ValueError):
+ logging.error('No vendor_api_level. Setting to build version.')
+ vendor_api_level = get_build_sdk_version(device_id)
+ return vendor_api_level
+
+
class ItsSessionUtilsTests(unittest.TestCase):
"""Run a suite of unit tests on this module."""
diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp
index b4485d1..024e038 100644
--- a/apps/CtsVerifier/Android.bp
+++ b/apps/CtsVerifier/Android.bp
@@ -91,6 +91,7 @@
"CtsForceStopHelper-constants",
"ctsmediautil",
"DpmWrapper",
+ "MediaPerformanceClassCommon",
],
libs: ["telephony-common"] + ["android.test.runner.stubs"] + ["android.test.base.stubs"] + ["android.test.mock.stubs"] + ["android.car-test-stubs"] + ["voip-common"] + ["truth-prebuilt"],
@@ -114,7 +115,7 @@
compile_multilib: "both",
- manifest: "AndroidManifest-verifierConfig.xml",
+ additional_manifests: ["AndroidManifest-verifierConfig.xml"],
jni_libs: [
"libctsverifier_jni",
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 5a257de..6e7f4ac 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -248,6 +248,7 @@
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
+ <meta-data android:name="CddTest" android:value="3.8.17/C-1-1,C-2-1" />
<meta-data android:name="test_category" android:value="@string/test_category_features" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" />
@@ -2052,6 +2053,9 @@
<meta-data android:name="test_required_features" android:value="android.hardware.wifi" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest" android:value="7.4.5.2" />
+ <meta-data android:name="ApiTest"
+ android:value="android.net.ConnectivityManager#registerNetworkCallback|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getLinkProperties" />
</activity>
<activity android:name=".net.MultiNetworkConnectivityTestActivity"
@@ -2068,6 +2072,8 @@
android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.net.ConnectivityManager#getNetworkCapabilities|android.net.ConnectivityManager#getAllNetworks|android.net.ConnectivityManager#requestNetwork|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getActiveNetwork|android.net.ConnectivityManager#getNetworkInfo|android.net.ConnectivityManager#reportNetworkConnectivity" />
</activity>
<activity android:name=".nfc.NfcTestActivity"
@@ -2827,6 +2833,16 @@
android:value="android.hardware.type.automotive"/>
<meta-data android:name="display_mode"
android:value="single_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.hardware.Camera#getNumberOfCameras|
+ android.hardware.Camera#open|
+ android.hardware.Camera#startPreview|
+ android.hardware.Camera#stopPreview|
+ android.hardware.Camera#takePicture|
+ android.hardware.Camera#setParameters|
+ android.hardware.Camera#setDisplayOrientation|
+ android.hardware.Camera.Parameters#setHorizontalViewAngle|
+ android.hardware.Camera.Parameters#setVerticalViewAngle" />
</activity>
<activity
android:name=".camera.fov.DetermineFovActivity"
@@ -2836,6 +2852,11 @@
android:value="android.hardware.type.automotive"/>
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.media.ExifInterface#TAG_ORIENTATION|
+ android.media.ExifInterface#ORIENTATION_ROTATE_90|
+ android.media.ExifInterface#ORIENTATION_ROTATE_180|
+ android.media.ExifInterface#ORIENTATION_ROTATE_270" />
</activity>
<activity
android:name=".camera.fov.CalibrationPreferenceActivity"
@@ -2861,6 +2882,32 @@
android:value="android.hardware.type.automotive"/>
<meta-data android:name="display_mode"
android:value="single_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.hardware.Camera#getParameters|
+ android.hardware.Camera#lock|
+ android.hardware.Camera#setDisplayOrientation|
+ android.hardware.Camera#setPreviewCallback|
+ android.hardware.Camera#setParameters|
+ android.hardware.Camera#setPreviewTexture|
+ android.hardware.Camera#startPreview|
+ android.hardware.Camera#stopPreview|
+ android.hardware.Camera#unlock|
+ android.media.MediaRecorder#prepare|
+ android.media.MediaRecorder#release|
+ android.media.MediaRecorder#reset|
+ android.media.MediaRecorder#setAudioEncoder|
+ android.media.MediaRecorder#setAudioSource|
+ android.media.MediaRecorder#setCamera|
+ android.media.MediaRecorder#setOnErrorListener|
+ android.media.MediaRecorder#setOutputFormat|
+ android.media.MediaRecorder#setOutputFile|
+ android.media.MediaRecorder#setProfile|
+ android.media.MediaRecorder#setVideoEncoder|
+ android.media.MediaRecorder#setVideoEncodingBitRate|
+ android.media.MediaRecorder#setVideoSize|
+ android.media.MediaRecorder#setVideoSource|
+ android.media.MediaRecorder#start|
+ android.media.MediaRecorder#stop" />
</activity>
<activity android:name=".camera.its.ItsTestActivity"
@@ -3901,174 +3948,6 @@
android:value="single_display_mode" />
</activity>
- <!-- CTS Verifier Presence Test Top Screen -->
- <activity
- android:name=".presence.PresenceTestActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:exported="true"
- android:label="@string/presence_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_networking" />
- <meta-data android:name="display_mode"
- android:value="single_display_mode" />
- </activity>
-
- <!--
- CTS Verifier Uwb Precision Test Screen
- test category : uwb
- test parent : PresenceTestActivity
- -->
- <activity
- android:name=".presence.UwbPrecisionActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:exported="true"
- android:label="@string/uwb_precision" >
- <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/uwb" />
- <meta-data
- android:name="test_parent"
- android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
- <meta-data android:name="display_mode"
- android:value="single_display_mode" />
- <meta-data android:name="CddTest"
- android:value="7.4.9/C-1-1" />
- </activity>
-
- <!--
- CTS Verifier Uwb Short Range Test Screen
- test category : uwb
- test parent : PresenceTestActivity
- -->
- <activity
- android:name=".presence.UwbShortRangeActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:exported="true"
- android:label="@string/uwb_short_range" >
- <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/uwb" />
- <meta-data
- android:name="test_parent"
- android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
- <meta-data
- android:name="display_mode"
- android:value="single_display_mode" />
- <meta-data
- android:name="CddTest"
- android:value="7.4.9/C-1-2" />
- </activity>
-
- <!--
- CTS Verifier BLE RSSI Precision Test Screen
- test category : BLE
- test parent : PresenceTestActivity
- -->
- <activity
- android:name=".presence.BleRssiPrecisionActivity"
- android:exported="true"
- android:label="@string/ble_rssi_precision_name">
- <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/ble" />
- <meta-data
- android:name="test_parent"
- android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
- <meta-data
- android:name="test_required_features"
- android:value="android.hardware.bluetooth_le" />
- <meta-data
- android:name="display_mode"
- android:value="single_display_mode" />
- <meta-data
- android:name="CddText"
- android:value="7.4.3/C-7-1" />
- </activity>
-
- <!--
- CTS Verifier BLE Rx/Tx Calibration Test Screen
- test category : BLE
- test parent : PresenceTestActivity
- -->
- <activity
- android:name=".presence.BleRxTxCalibrationActivity"
- android:exported="true"
- android:label="@string/ble_rx_tx_calibration_name">
- <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/ble" />
- <meta-data
- android:name="test_parent"
- android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
- <meta-data
- android:name="test_required_features"
- android:value="android.hardware.bluetooth_le" />
- <meta-data
- android:name="display_mode"
- android:value="single_display_mode" />
- <meta-data
- android:name="CddText"
- android:value="7.4.3/C-7-2" />
- </activity>
-
- <!-- CTS Verifier Nan Precision and Bias Test Screen
- test category : wifi_nan
- test parent : PresenceTestActivity
- -->
- <activity
- android:name=".presence.NanPrecisionTestActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:exported="true"
- android:label="@string/nan_precision" >
- <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/wifi_nan" />
- <meta-data
- android:name="test_parent"
- android:value="com.android.cts.verifier.presence.PresenceTestActivity" />
- <meta-data android:name="display_mode"
- android:value="single_display_mode" />
- <meta-data android:name="CddTest"
- android:value="7.4.2.5/H-1-1|7.4.2.5/H-1-2" />
- </activity>
-
<activity-alias
android:name=".CtsVerifierActivity"
android:label="@string/app_name"
@@ -4975,6 +4854,8 @@
android:value="android.hardware.type.automotive" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="ApiTest"
+ android:value="android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback" />
</activity>
<activity android:name=".tv.TvInputDiscoveryTestActivity"
@@ -6159,6 +6040,7 @@
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_logcat" />
+ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" />
</activity>
<activity android:name=".displaycutout.DisplayCutoutTestActivity"
diff --git a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml b/apps/CtsVerifier/res/layout/ble_rssi_precision.xml
deleted file mode 100644
index 5f01e1f..0000000
--- a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml
+++ /dev/null
@@ -1,60 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 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"
- android:orientation="vertical"
- style="@style/RootLayoutPadding"
- tools:ignore="Autofill">
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:text="@string/ble_rssi_precision_test_instructions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scrollbars="vertical" />
-
- <EditText
- android:id="@+id/report_rssi_range"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_ble_rssi_range"
- android:inputType="number" />
-
- <EditText
- android:id="@+id/report_reference_device"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_reference_device"
- android:inputType="text" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/pass_fail_buttons" />
- </LinearLayout>
- </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml b/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml
deleted file mode 100644
index 3ca955b..0000000
--- a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?><!--
- ~ Copyright (C) 2022 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"
- android:orientation="vertical"
- style="@style/RootLayoutPadding"
- tools:ignore="Autofill">
-
- <ScrollView
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
-
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
-
- <TextView
- android:text="@string/ble_rx_tx_calibration_test_instructions"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scrollbars="vertical" />
-
- <EditText
- android:id="@+id/report_channels_rssi_range"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_channels_ble_rssi_range"
- android:inputType="number" />
-
- <EditText
- android:id="@+id/report_cores_rssi_range"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_cores_ble_rssi_range"
- android:inputType="number" />
-
- <EditText
- android:id="@+id/report_reference_device"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_reference_device"
- android:inputType="text" />
-
- <include
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/pass_fail_buttons" />
- </LinearLayout>
- </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/clipboard_preview.xml b/apps/CtsVerifier/res/layout/clipboard_preview.xml
index 85f38c8..efec118 100644
--- a/apps/CtsVerifier/res/layout/clipboard_preview.xml
+++ b/apps/CtsVerifier/res/layout/clipboard_preview.xml
@@ -31,86 +31,6 @@
android:layout_marginBottom="100dp"
android:layout_marginTop="30dp"
android:text="@string/clipboard_preview_test_copy_button"/>
-
- <TableLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="50dp">
- <View android:layout_weight="3"
- android:layout_height="50dp"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b1"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="1"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b2"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="2"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b3"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="3"/>
- <View android:layout_weight="3"
- android:layout_height="50dp"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="50dp">
- <View android:layout_weight="3"
- android:layout_height="50dp"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b4"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="4"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b5"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="5"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b6"
- android:layout_width="50dp"
- android:layout_height="50dp"
- android:text="6"/>
- <View android:layout_weight="3"
- android:layout_height="50dp"/>
- </TableRow>
- <TableRow
- android:layout_width="match_parent"
- android:layout_height="50dp">
- <View android:layout_weight="3"
- android:layout_height="match_parent"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b7"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:text="7"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b8"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:text="8"/>
- <Button android:layout_weight="1"
- android:id="@+id/clipboard_preview_test_b9"
- android:layout_width="50dp"
- android:layout_height="match_parent"
- android:text="9"/>
- <View android:layout_weight="3"
- android:layout_height="50dp"/>
- </TableRow>
- </TableLayout>
- <Button
- android:id="@+id/clipboard_preview_test_b0"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:text="0"/>
<include
android:id="@+id/clipboard_preview_test_pass_fail"
android:layout_width="match_parent"
diff --git a/apps/CtsVerifier/res/layout/nan_precision.xml b/apps/CtsVerifier/res/layout/nan_precision.xml
deleted file mode 100644
index c81a9c4..0000000
--- a/apps/CtsVerifier/res/layout/nan_precision.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License.
- -->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- style="@style/RootLayoutPadding">
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:text="@string/nan_precision_instruction"
- android:id="@+id/nan_precision_instruction"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"/>
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <EditText android:id="@+id/nan_bandwidth"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="number"
- android:hint="@string/report_nan_bandwidth_mhz"/>
- <EditText android:id="@+id/distance_range_10cm_gt_68p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_10cm_gt_68p"/>
- <EditText android:id="@+id/distance_range_1m_gt_68p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_1m_gt_68p"/>
- <EditText android:id="@+id/distance_range_3m_gt_68p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_3m_gt_68p"/>
- <EditText android:id="@+id/distance_range_5m_gt_68p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_5m_gt_68p"/>
- <EditText android:id="@+id/distance_range_10cm_gt_90p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_10cm_gt_90p"/>
- <EditText android:id="@+id/distance_range_1m_gt_90p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_1m_gt_90p"/>
- <EditText android:id="@+id/distance_range_3m_gt_90p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_3m_gt_90p"/>
- <EditText android:id="@+id/distance_range_5m_gt_90p"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_5m_gt_90p"/>
- <EditText android:id="@+id/reference_device"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_reference_device"/>
- </LinearLayout>
-
- <include android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/pass_fail_buttons"/>
- </LinearLayout>
- </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/uwb_precision.xml b/apps/CtsVerifier/res/layout/uwb_precision.xml
deleted file mode 100644
index 14e996d..0000000
--- a/apps/CtsVerifier/res/layout/uwb_precision.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- style="@style/RootLayoutPadding">
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:text="@string/uwb_precision_instruction"
- android:id="@+id/uwb_precision_instruction"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"/>
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <EditText android:id="@+id/distance_range_cm"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:inputType="numberDecimal"
- android:hint="@string/report_distance_range_cm"/>
- <EditText android:id="@+id/reference_device"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_reference_device"/>
- </LinearLayout>
-
- <include android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/pass_fail_buttons"/>
- </LinearLayout>
- </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/layout/uwb_short_range.xml b/apps/CtsVerifier/res/layout/uwb_short_range.xml
deleted file mode 100644
index 9afc6e5..0000000
--- a/apps/CtsVerifier/res/layout/uwb_short_range.xml
+++ /dev/null
@@ -1,51 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2022 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- style="@style/RootLayoutPadding">
- <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content">
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <TextView android:text="@string/uwb_short_range_instruction"
- android:id="@+id/uwb_short_range_instruction"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:scrollbars="vertical"/>
- <LinearLayout android:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
- <EditText android:id="@+id/distance_median_meters"
- android:layout_width="wrap_content"
- android:inputType="numberDecimal"
- android:layout_height="wrap_content"
- android:hint="@string/report_distance_median_meters"/>
- <EditText android:id="@+id/reference_device"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:hint="@string/report_reference_device"/>
- </LinearLayout>
-
- <include android:layout_width="match_parent"
- android:layout_height="wrap_content"
- layout="@layout/pass_fail_buttons"/>
- </LinearLayout>
- </ScrollView>
-</RelativeLayout>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 4862965..0f32b34 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -32,6 +32,16 @@
<string name="finish_button_text">Finish</string>
<string name="fail_and_next_button_text">Fail and Next</string>
+ <!-- Strings for CtsReportLog warning -->
+ <string name="reportlog_warning_title">CTS-Verifier Report Log</string>
+ <string name="reportlog_warning_body">Can\'t create folder for CTS-Verifier Report Logs.
+ \n\nPlease enable Report Log creation by exiting CTS Verifier and running the following commands:
+ \n\n<code>adb shell appops set com.android.cts.verifier android:read_device_identifiers allow</code>
+ \n\n<code>adb shell appops set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE 0</code>
+ \n\nTest instructions are found in the \"Using CTS Verifier\" document found at
+ <a href="https://source.android.com/compatibility/cts/verifier">https://source.android.com/compatibility/cts/verifier</a>
+ </string>
+
<!-- Strings for TestListActivity -->
<string name="test_category_audio">Audio</string>
<string name="test_category_camera">Camera</string>
@@ -301,9 +311,11 @@
</string>
<string name="clipboard_preview_test_instructions">
Press the \'Copy\' button to copy the secret code to the clipboard.
- \n\nUse the clipboard preview UI, or the clipboard editor component to view the secret code.
- \n\nEnter the secret code using the buttons below.
+ \n\n If nothing happens, press Fail.
+ \n\n If you see the word "FAIL" appear on screen, press Fail.
+ \n\n If you see a confirmation that content has been copied to the clipboard, press Pass.
</string>
+ <string name="clipboard_preview_test_secret">FAIL</string>
<string name="clipboard_preview_test_copy_button">Copy</string>
@@ -778,7 +790,7 @@
<!-- BLE Advertising Set test strings -->
<string name="ble_advertising_set_test_name">Bluetooth LE Advertising Set Test</string>
<string name="ble_advertising_set_test_info">Bluetooth LE Advertising Set tests AdvertisingSet and AdvertisingSetCallback APIs.</string>
- <string name="ble_advertising_set_test_instruction">Press the \"Set Up\" button first, then start the test by pressing the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string>
+ <string name="ble_advertising_set_test_instruction">Press the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string>
<string name="ble_advertising_set_start_test">Start Test</string>
<string name="ble_advertising_set_running_test">Running Test...</string>
<string name="ble_advertising_set_finished_test">Finished Test</string>
@@ -5480,6 +5492,7 @@
<string name="audio_general_test_not_run">Test Not Run</string>
<string name="audio_general_testnotcompleted">Test not completed.</string>
+ <string name="audio_general_reportlogtest">[Can\'t Write ReportLog]</string>
<!-- Audio Loopback Latency Test -->
<string name="audio_loopback_latency_test">Audio Loopback Latency Test</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
index b013bb7..a93e3b5 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java
@@ -74,6 +74,10 @@
}
}
+ public boolean isOpen() {
+ return mStore != null;
+ }
+
/**
* Closes report file. Static functions that do not have access to instrumentation can
* use this to close report logs. Summary, if present, is not reported to instrumentation, hence
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
index 0294ff7..36975c2 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java
@@ -32,7 +32,6 @@
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
@@ -143,11 +142,13 @@
public static class Activity extends android.app.Activity implements PassFailActivity {
private WakeLock mWakeLock;
- private final CtsVerifierReportLog mReportLog;
+ private CtsVerifierReportLog mReportLog;
private final TestResultHistoryCollection mHistoryCollection;
+ protected boolean mRequireReportLogToPass;
+
public Activity() {
- this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName());
+ newReportLog();
this.mHistoryCollection = new TestResultHistoryCollection();
}
@@ -159,6 +160,10 @@
.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "PassFailButtons");
mWakeLock.acquire();
}
+
+ if (!this.mReportLog.isOpen()) {
+ showReportLogWarningDialog(this);
+ }
}
@Override
@@ -206,12 +211,25 @@
getHistoryCollection());
}
+ protected CtsVerifierReportLog newReportLog() {
+ return mReportLog = new CtsVerifierReportLog(
+ getReportFileName(), getReportSectionName());
+ }
+
@Override
public CtsVerifierReportLog getReportLog() {
return mReportLog;
}
/**
+ * A mechanism to block tests from passing if no ReportLog data has been collected.
+ * @return true if the ReportLog is open OR if the test does not require that.
+ */
+ public boolean isReportLogOkToPass() {
+ return !mRequireReportLogToPass || mReportLog.isOpen();
+ }
+
+ /**
* @return The name of the file to store the (suite of) ReportLog information.
*/
@Override
@@ -527,6 +545,12 @@
activity.showDialog(INFO_DIALOG_ID, args);
}
+ protected static void showReportLogWarningDialog(final android.app.Activity activity) {
+ showInfoDialog(activity,
+ R.string.reportlog_warning_title, R.string.reportlog_warning_body, -1);
+ }
+
+
protected static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) {
switch (id) {
case INFO_DIALOG_ID:
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
index 528d914..83b32de 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java
@@ -182,12 +182,15 @@
mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass_noheadset));
return true;
} else {
- boolean pass = mPlugIntentReceived &&
- mHeadsetDeviceInfo != null &&
- mPlaybackSuccess &&
- (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown;
+ boolean pass = isReportLogOkToPass()
+ && mPlugIntentReceived
+ && mHeadsetDeviceInfo != null
+ && mPlaybackSuccess
+ && (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown;
if (pass) {
mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass));
+ } else if (!isReportLogOkToPass()) {
+ mResultsTxt.setText(getResources().getString(R.string.audio_general_reportlogtest));
}
return pass;
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
index 48795ef..3c1b8a1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java
@@ -542,8 +542,10 @@
Log.v(TAG, "Test EndedOk. " + testId + " str:"+str);
showView(mProgress, false);
mResultTest.setText("test completed. " + str);
- if (mTestAECPassed) {
- getPassButton().setEnabled(true);;
+ if (!isReportLogOkToPass()) {
+ mResultTest.setText(getResources().getString(R.string.audio_general_reportlogtest));
+ } else if (mTestAECPassed) {
+ getPassButton().setEnabled(true);
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
index a6bd4ad..413734b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java
@@ -127,11 +127,11 @@
private TestSpec[] mTestSpecs = new TestSpec[NUM_TEST_ROUTES];
class TestSpec {
+ private static final String TAG = "AudioLoopbackLatencyActivity.TestSpec";
// impossibly low latencies (indicating something in the test went wrong).
protected static final double LOWEST_REASONABLE_LATENCY_MILLIS = 1.0;
final int mRouteId;
- // final double mMustLatencyMS;
// runtime assigned device ID
static final int DEVICEID_NONE = -1;
@@ -151,7 +151,6 @@
boolean mRouteAvailable; // Have we seen this route/device at any time
boolean mRouteConnected; // is the route available NOW
boolean mTestRun;
- // boolean mTestPass;
TestSpec(int routeId, double requiredConfidence) {
mRouteId = routeId;
@@ -181,14 +180,6 @@
mMeanConfidence = StatUtils.calculateMean(mConfidence);
}
- boolean getRouteAvailable() {
- return mRouteAvailable;
- }
-
- boolean getTestRun() {
- return mTestRun;
- }
-
boolean isMeasurementValid() {
return mTestRun && mMeanLatencyMS > 1.0 && mMeanConfidence >= mRequiredConfidence;
}
@@ -224,60 +215,42 @@
}
// ReportLog Schema (per route)
- private static final String KEY_ROUTEAVAILABLE = "route_available";
- private static final String KEY_ROUTECONNECTED = "route_connected";
- private static final String KEY_ROUTERUN = "route_run";
- private static final String KEY_LATENCY = "route_latency";
- private static final String KEY_CONFIDENCE = "route_confidence";
- private static final String KEY_MEANABSDEVIATION = "route_mean_absolute_deviation";
- private static final String KEY_IS_PERIPHERAL_ATTACHED = "route_is_peripheral_attached";
- private static final String KEY_INPUT_PERIPHERAL_NAME = "route_input_peripheral";
- private static final String KEY_OUTPUT_PERIPHERAL_NAME = "route_output_peripheral";
- private static final String KEY_TEST_PERIPHERAL = "route_test_peripheral";
-
- String makeSectionKey(String key) {
- return Integer.toString(mRouteId) + "_" + key;
- }
+ private static final String KEY_ROUTEINDEX = "route_index";
+ private static final String KEY_LATENCY = "latency";
+ private static final String KEY_CONFIDENCE = "confidence";
+ private static final String KEY_MEANABSDEVIATION = "mean_absolute_deviation";
+ private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached";
+ private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral";
+ private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral";
+ private static final String KEY_TEST_PERIPHERAL = "test_peripheral";
void recordTestResults(CtsVerifierReportLog reportLog) {
reportLog.addValue(
- makeSectionKey(KEY_ROUTEAVAILABLE),
- mRouteAvailable ? 1 : 0,
+ KEY_ROUTEINDEX,
+ mRouteId,
ResultType.NEUTRAL,
ResultUnit.NONE);
reportLog.addValue(
- makeSectionKey(KEY_ROUTECONNECTED),
- mRouteConnected ? 1 : 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- makeSectionKey(KEY_ROUTERUN),
- mTestRun ? 1 : 0,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- makeSectionKey(KEY_LATENCY),
+ KEY_LATENCY,
mMeanLatencyMS,
ResultType.LOWER_BETTER,
ResultUnit.MS);
reportLog.addValue(
- makeSectionKey(KEY_CONFIDENCE),
+ KEY_CONFIDENCE,
mMeanConfidence,
ResultType.HIGHER_BETTER,
ResultUnit.NONE);
reportLog.addValue(
- makeSectionKey(KEY_MEANABSDEVIATION),
+ KEY_MEANABSDEVIATION,
mMeanAbsoluteDeviation,
ResultType.NEUTRAL,
ResultUnit.NONE);
reportLog.addValue(
- makeSectionKey(KEY_TEST_PERIPHERAL),
+ KEY_TEST_PERIPHERAL,
mDeviceName,
ResultType.NEUTRAL,
ResultUnit.NONE);
@@ -294,6 +267,8 @@
getPassButton().setEnabled(false);
setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1);
+ mRequireReportLogToPass = true;
+
mClaimsOutput = AudioSystemFlags.claimsOutput(this);
mClaimsInput = AudioSystemFlags.claimsInput(this);
mClaimsProAudio = AudioSystemFlags.claimsProAudio(this);
@@ -474,7 +449,6 @@
mTestSpecs[TESTROUTE_USB].mDeviceName = devInfo.getProductName().toString();
}
- // setTestButtonsState();
enableStartButtons(true);
}
}
@@ -533,69 +507,66 @@
return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity");
}
- // Schema
+ // Test-Schema
private static final String KEY_SAMPLE_RATE = "sample_rate";
private static final String KEY_IS_PRO_AUDIO = "is_pro_audio";
private static final String KEY_IS_LOW_LATENCY = "is_low_latency";
private static final String KEY_TEST_MMAP = "supports_mmap";
private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive";
private static final String KEY_LEVEL = "level";
- //
- // Subclasses should call this explicitly. SubClasses should call submit() after their logs
- //
+
+ private void recordRouteResults(int routeIndex) {
+ if (mTestSpecs[routeIndex].mTestRun) {
+ CtsVerifierReportLog reportLog = newReportLog();
+
+ int audioLevel = mAudioLevelSeekbar.getProgress();
+ reportLog.addValue(
+ KEY_LEVEL,
+ audioLevel,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_PRO_AUDIO,
+ mClaimsProAudio,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAP,
+ mSupportsMMAP,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_TEST_MMAPEXCLUSIVE,
+ mSupportsMMAPExclusive,
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_SAMPLE_RATE,
+ mNativeAnalyzerThread.getSampleRate(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ reportLog.addValue(
+ KEY_IS_LOW_LATENCY,
+ mNativeAnalyzerThread.isLowLatencyStream(),
+ ResultType.NEUTRAL,
+ ResultUnit.NONE);
+
+ mTestSpecs[routeIndex].recordTestResults(reportLog);
+
+ reportLog.submit();
+ }
+ }
+
@Override
public void recordTestResults() {
- Log.i(TAG, "recordTestResults() mNativeAnalyzerThread:" + mNativeAnalyzerThread);
-
- // We need to rework that
- CtsVerifierReportLog reportLog = getReportLog();
-
- int audioLevel = mAudioLevelSeekbar.getProgress();
- reportLog.addValue(
- KEY_LEVEL,
- audioLevel,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_PRO_AUDIO,
- mClaimsProAudio,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAP,
- mSupportsMMAP,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_TEST_MMAPEXCLUSIVE ,
- mSupportsMMAPExclusive,
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- if (mNativeAnalyzerThread == null) {
- return; // no test results to report
+ for (int route = 0; route < NUM_TEST_ROUTES; route++) {
+ recordRouteResults(route);
}
-
- reportLog.addValue(
- KEY_SAMPLE_RATE,
- mNativeAnalyzerThread.getSampleRate(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- reportLog.addValue(
- KEY_IS_LOW_LATENCY,
- mNativeAnalyzerThread.isLowLatencyStream(),
- ResultType.NEUTRAL,
- ResultUnit.NONE);
-
- for (TestSpec testSpec : mTestSpecs) {
- testSpec.recordTestResults(reportLog);
- }
-
- reportLog.submit();
}
private void startAudioTest(Handler messageHandler, int testRouteId) {
@@ -658,6 +629,7 @@
e.printStackTrace();
}
+
mTestPhase++;
if (mTestPhase >= NUM_TEST_PHASES) {
handleTestCompletion();
@@ -683,7 +655,8 @@
mResultsText[mTestRoute].setText(testSpec.getResultString());
LoopbackLatencyRequirements requirements = new LoopbackLatencyRequirements();
- boolean pass = requirements.evaluate(mClaimsProAudio,
+ boolean pass = isReportLogOkToPass()
+ && requirements.evaluate(mClaimsProAudio,
Build.VERSION.MEDIA_PERFORMANCE_CLASS,
mTestSpecs[TESTROUTE_DEVICE].isMeasurementValid()
? mTestSpecs[TESTROUTE_DEVICE].mMeanLatencyMS : 0.0,
@@ -694,8 +667,12 @@
getPassButton().setEnabled(pass);
- String resultText = requirements.getResultsString();
- mTestStatusText.setText(resultText);
+ StringBuilder sb = new StringBuilder();
+ if (!isReportLogOkToPass()) {
+ sb.append(getResources().getString(R.string.audio_general_reportlogtest) + "\n");
+ }
+ sb.append(requirements.getResultsString());
+ mTestStatusText.setText(sb.toString());
showWait(false);
enableStartButtons(true);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
index 215d26f..8ff2358 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java
@@ -154,6 +154,8 @@
String yesString = getResources().getString(R.string.audio_general_yes);
String noString = getResources().getString(R.string.audio_general_no);
+ mRequireReportLogToPass = true;
+
boolean claimsProAudio = AudioSystemFlags.claimsProAudio(this);
boolean claimsLowLatencyAudio = AudioSystemFlags.claimsLowLatencyAudio(this);
@@ -311,11 +313,14 @@
}
double averageLatency = mLatencyAve[mActiveTestAPI];
- boolean pass = averageLatency != 0 && averageLatency <= mMaxRequiredLatency;
+ boolean pass = isReportLogOkToPass()
+ && averageLatency != 0 && averageLatency <= mMaxRequiredLatency;
if (pass) {
mSpecView.setText("Average: " + averageLatency + " ms <= "
+ mMaxRequiredLatency + " ms -- PASS");
+ } else if (!isReportLogOkToPass()) {
+ mSpecView.setText(getResources().getString(R.string.audio_general_reportlogtest));
} else {
mSpecView.setText("Average: " + averageLatency + " ms > "
+ mMaxRequiredLatency + " ms -- FAIL");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
index 126d15f..e93d2b3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java
@@ -158,11 +158,9 @@
boolean usbOK = mClaimsUSBHostMode && mClaimsUSBPeripheralMode;
boolean hdmiOK = !mClaimsHDMI || isHDMIValid();
- boolean hasPassed = !mClaimsProAudio ||
- (mClaimsLowLatencyAudio &&
- mClaimsMIDI &&
- usbOK &&
- hdmiOK);
+ boolean hasPassed = isReportLogOkToPass()
+ && !mClaimsProAudio
+ || (mClaimsLowLatencyAudio && mClaimsMIDI && usbOK && hdmiOK);
getPassButton().setEnabled(hasPassed);
return hasPassed;
@@ -172,7 +170,9 @@
boolean hasPassed = calculatePass();
Resources strings = getResources();
- if (hasPassed) {
+ if (!isReportLogOkToPass()) {
+ mTestStatusLbl.setText(getResources().getString(R.string.audio_general_reportlogtest));
+ } else if (hasPassed) {
mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_pass));
} else if (!mClaimsMIDI) {
mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_midinotreported));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
index bc23048..6270a5a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java
@@ -20,23 +20,17 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-
import android.media.AudioDeviceCallback;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
-
import android.os.Bundle;
import android.os.Handler;
-
import android.util.Log;
-
import android.widget.TextView;
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.ReportLog;
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
-
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R; // needed to access resource in CTSVerifier project namespace.
@@ -173,9 +167,10 @@
// Test Status
//
private boolean calculatePass() {
- return mUsbHeadsetInReceived && mUsbHeadsetOutReceived &&
- mUsbDeviceInReceived && mUsbDeviceOutReceived &&
- mPlugIntentReceived;
+ return isReportLogOkToPass()
+ && mUsbHeadsetInReceived && mUsbHeadsetOutReceived
+ && mUsbDeviceInReceived && mUsbDeviceOutReceived
+ && mPlugIntentReceived;
}
//
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 8855dc7..212f988 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
@@ -1847,6 +1847,8 @@
// s1440p which is the max supported stream size in a combination, when preview
// stabilization is on.
Size maxPreviewSize = new Size(1920, 1440);
+ // QCIF, we test only sizes >= this.
+ Size minPreviewSize = new Size(176, 144);
Size[] outputSizes = configMap.getOutputSizes(ImageFormat.YUV_420_888);
if (outputSizes == null) {
mSocketRunnableObj.sendResponse("supportedPreviewSizes", "");
@@ -1857,6 +1859,8 @@
.distinct()
.filter(s -> s.getWidth() * s.getHeight()
<= maxPreviewSize.getWidth() * maxPreviewSize.getHeight())
+ .filter(s -> s.getWidth() * s.getHeight()
+ >= minPreviewSize.getWidth() * minPreviewSize.getHeight())
.sorted(Comparator.comparingInt(s -> s.getWidth() * s.getHeight()))
.map(Size::toString)
.collect(Collectors.joining(";"));
@@ -2985,6 +2989,8 @@
CaptureResult.CONTROL_AE_STATE_FLASH_REQUIRED ||
result.get(CaptureResult.CONTROL_AE_STATE) ==
CaptureResult.CONTROL_AE_STATE_LOCKED;
+ mLockedAE = result.get(CaptureResult.CONTROL_AE_STATE) ==
+ CaptureResult.CONTROL_AE_STATE_LOCKED;
if (!mPrecaptureTriggered) {
mPrecaptureTriggered = result.get(CaptureResult.CONTROL_AE_STATE) ==
CaptureResult.CONTROL_AE_STATE_PRECAPTURE;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index d83464b..692538a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -21,9 +21,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Configuration;
-import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
-import android.os.Build;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
@@ -31,6 +30,11 @@
import android.widget.TextView;
import android.widget.Toast;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@@ -39,17 +43,11 @@
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.io.BufferedReader;
-import java.io.FileReader;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-
import com.android.compatibility.common.util.ResultType;
import com.android.compatibility.common.util.ResultUnit;
import com.android.cts.verifier.ArrayTestListAdapter;
@@ -59,6 +57,7 @@
import org.json.JSONArray;
import org.json.JSONObject;
+import org.junit.rules.TestName;
/**
* Test for Camera features that require that the camera be aimed at a specific test scene.
@@ -81,12 +80,6 @@
Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}));
private static final int MAX_SUMMARY_LEN = 200;
- private static final int MPC12_CAMERA_LAUNCH_THRESHOLD = 600; // ms
- private static final int MPC12_JPEG_CAPTURE_THRESHOLD = 1000; // ms
-
- private static final String MPC_TESTS_REPORT_LOG_NAME = "MediaPerformanceClassLogs";
- private static final String MPC_TESTS_REPORT_LOG_SECTION = "CameraIts";
-
private static final Pattern MPC12_CAMERA_LAUNCH_PATTERN =
Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)");
private static final Pattern MPC12_JPEG_CAPTURE_PATTERN =
@@ -95,8 +88,12 @@
private final ResultReceiver mResultsReceiver = new ResultReceiver();
private boolean mReceiverRegistered = false;
+ public final TestName mTestName = new TestName();
+
// Initialized in onCreate
List<String> mToBeTestedCameraIds = null;
+ String mPrimaryRearCameraId = null;
+ String mPrimaryFrontCameraId = null;
// Scenes
private static final ArrayList<String> mSceneIds = new ArrayList<String> () {{
@@ -132,8 +129,15 @@
private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>();
// All primary cameras for which MPC level test has run
private Set<ResultKey> mExecutedMpcTests = null;
- // Map primary camera id to MPC level
- private final HashMap<String, Integer> mMpcLevelMap = new HashMap<>();
+ private static final String MPC_LAUNCH_REQ_NUM = "2.2.7.2/7.5/H-1-6";
+ private static final String MPC_JPEG_CAPTURE_REQ_NUM = "2.2.7.2/7.5/H-1-5";
+ // Performance class evaluator used for writing test result
+ PerformanceClassEvaluator mPce = new PerformanceClassEvaluator(mTestName);
+ PerformanceClassEvaluator.CameraLatencyRequirement mJpegLatencyReq =
+ mPce.addR7_5__H_1_5();
+ PerformanceClassEvaluator.CameraLatencyRequirement mLaunchLatencyReq =
+ mPce.addR7_5__H_1_6();
+
final class ResultKey {
public final String cameraId;
@@ -266,10 +270,7 @@
JSONArray metrics = sceneResult.getJSONArray("mpc_metrics");
for (int i = 0; i < metrics.length(); i++) {
String mpcResult = metrics.getString(i);
- if (!matchMpcResult(cameraId, mpcResult, MPC12_CAMERA_LAUNCH_PATTERN,
- "2.2.7.2/7.5/H-1-6", MPC12_CAMERA_LAUNCH_THRESHOLD) &&
- !matchMpcResult(cameraId, mpcResult, MPC12_JPEG_CAPTURE_PATTERN,
- "2.2.7.2/7.5/H-1-5", MPC12_JPEG_CAPTURE_THRESHOLD)) {
+ if (!matchMpcResult(cameraId, mpcResult)) {
Log.e(TAG, "Error parsing MPC result string:" + mpcResult);
return;
}
@@ -294,17 +295,6 @@
summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
}
- // Save MPC info once both front primary and rear primary data are collected.
- if (mExecutedMpcTests.size() == 4) {
- ItsTestActivity.this.getReportLog().addValue(
- "Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE);
- for (Map.Entry<String, Integer> entry : mMpcLevelMap.entrySet()) {
- ItsTestActivity.this.getReportLog().addValue(entry.getKey(),
- entry.getValue(), ResultType.NEUTRAL, ResultUnit.NONE);
- }
- ItsTestActivity.this.getReportLog().submit();
- }
-
// Display current progress
StringBuilder progress = new StringBuilder();
for (ResultKey k : mAllScenes) {
@@ -367,28 +357,44 @@
}
}
- private boolean matchMpcResult(String cameraId, String mpcResult, Pattern pattern,
- String reqNum, float threshold) {
- Matcher matcher = pattern.matcher(mpcResult);
- boolean match = matcher.matches();
- final int LATEST_MPC_LEVEL = Build.VERSION_CODES.TIRAMISU;
+ private boolean matchMpcResult(String cameraId, String mpcResult) {
+ Matcher launchMatcher = MPC12_CAMERA_LAUNCH_PATTERN.matcher(mpcResult);
+ boolean launchMatches = launchMatcher.matches();
- if (match) {
- // Store test result
- ItsTestActivity.this.getReportLog().addValue("Cam" + cameraId,
- mpcResult, ResultType.NEUTRAL, ResultUnit.NONE);
+ Matcher jpegMatcher = MPC12_JPEG_CAPTURE_PATTERN.matcher(mpcResult);
+ boolean jpegMatches = jpegMatcher.matches();
- float latency = Float.parseFloat(matcher.group(1));
- int mpcLevel = latency < threshold ? LATEST_MPC_LEVEL : 0;
- mExecutedMpcTests.add(new ResultKey(cameraId, reqNum));
-
- if (mMpcLevelMap.containsKey(reqNum)) {
- mpcLevel = Math.min(mpcLevel, mMpcLevelMap.get(reqNum));
- }
- mMpcLevelMap.put(reqNum, mpcLevel);
+ if (!launchMatches && !jpegMatches) {
+ return false;
+ }
+ if (!cameraId.equals(mPrimaryRearCameraId) &&
+ !cameraId.equals(mPrimaryFrontCameraId)) {
+ return false;
}
- return match;
+ if (launchMatches) {
+ float latency = Float.parseFloat(launchMatcher.group(1));
+ if (cameraId.equals(mPrimaryRearCameraId)) {
+ mLaunchLatencyReq.setRearCameraLatency(latency);
+ } else {
+ mLaunchLatencyReq.setFrontCameraLatency(latency);
+ }
+ mExecutedMpcTests.add(new ResultKey(cameraId, MPC_LAUNCH_REQ_NUM));
+ } else {
+ float latency = Float.parseFloat(jpegMatcher.group(1));
+ if (cameraId.equals(mPrimaryRearCameraId)) {
+ mJpegLatencyReq.setRearCameraLatency(latency);
+ } else {
+ mJpegLatencyReq.setFrontCameraLatency(latency);
+ }
+ mExecutedMpcTests.add(new ResultKey(cameraId, MPC_JPEG_CAPTURE_REQ_NUM));
+ }
+
+ // Save MPC info once both front primary and rear primary data are collected.
+ if (mExecutedMpcTests.size() == 4) {
+ mPce.submit();
+ }
+ return true;
}
}
@@ -397,8 +403,11 @@
// Hide the test if all camera devices are legacy
CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE);
try {
- ItsUtils.ItsCameraIdList cameraIdList = ItsUtils.getItsCompatibleCameraIds(manager);
+ ItsUtils.ItsCameraIdList cameraIdList =
+ ItsUtils.getItsCompatibleCameraIds(manager);
mToBeTestedCameraIds = cameraIdList.mCameraIdCombos;
+ mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId;
+ mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId;
} catch (ItsException e) {
Toast.makeText(ItsTestActivity.this,
"Received error from camera service while checking device capabilities: "
@@ -499,14 +508,4 @@
setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1);
setPassFailButtonClickListeners();
}
-
- @Override
- public String getReportFileName() {
- return MPC_TESTS_REPORT_LOG_NAME;
- }
-
- @Override
- public String getReportSectionName() {
- return MPC_TESTS_REPORT_LOG_SECTION;
- }
}
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 c648e8e..734b4a2 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
@@ -16,28 +16,24 @@
package com.android.cts.verifier.camera.its;
-import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.Rect;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
-import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.Image.Plane;
-import android.net.Uri;
-import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import android.util.Size;
import com.android.ex.camera2.blocking.BlockingCameraManager;
-import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException;
import com.android.ex.camera2.blocking.BlockingStateCallback;
import org.json.JSONArray;
@@ -322,6 +318,9 @@
// Camera Id combos (ids from CameraIdList, and hidden physical camera Ids
// in the form of [logical camera id]:[hidden physical camera id]
public List<String> mCameraIdCombos;
+ // Primary rear and front camera Ids (as defined in MPC)
+ public String mPrimaryRearCameraId;
+ public String mPrimaryFrontCameraId;
}
public static ItsCameraIdList getItsCompatibleCameraIds(CameraManager manager)
@@ -345,6 +344,18 @@
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE;
final int LOGICAL_MULTI_CAMERA =
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA;
+
+ final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
+ if (facing != null) {
+ if (facing == CameraMetadata.LENS_FACING_BACK
+ && outList.mPrimaryRearCameraId == null) {
+ outList.mPrimaryRearCameraId = id;
+ } else if (facing == CameraMetadata.LENS_FACING_FRONT
+ && outList.mPrimaryFrontCameraId == null) {
+ outList.mPrimaryFrontCameraId = id;
+ }
+ }
+
for (int capability : actualCapabilities) {
if (capability == BACKWARD_COMPAT) {
haveBC = true;
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
index 3587e6f..63b8904 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java
@@ -18,49 +18,22 @@
import android.content.ClipData;
+import android.content.ClipDescription;
import android.content.ClipboardManager;
-import android.graphics.Color;
import android.os.Bundle;
+import android.os.PersistableBundle;
import android.view.View;
import android.widget.Button;
import com.android.cts.verifier.PassFailButtons;
import com.android.cts.verifier.R;
-import java.util.concurrent.ThreadLocalRandom;
-
/**
- * A CTS Verifier test case for validating the user-visible clipboard preview.
- *
- * This test assumes bluetooth is turned on and the device is already paired with a second device.
- * Note: the second device need not be an Android device; it could be a laptop or desktop.
+ * A CTS Verifier test case for validating the user-visible clipboard confirmation.
*/
public class ClipboardPreviewTestActivity extends PassFailButtons.Activity {
- /**
- * The content of the test file being transferred.
- */
- private static final String TEST_STRING = "Sample Test String";
- /**
- * The name of the test file being transferred.
- */
- private final int[] mSecretCode = new int[4];
- private final int[] mSecretGuess = new int[4];
- private final int[] mButtons = {
- R.id.clipboard_preview_test_b0,
- R.id.clipboard_preview_test_b1,
- R.id.clipboard_preview_test_b2,
- R.id.clipboard_preview_test_b3,
- R.id.clipboard_preview_test_b4,
- R.id.clipboard_preview_test_b5,
- R.id.clipboard_preview_test_b6,
- R.id.clipboard_preview_test_b7,
- R.id.clipboard_preview_test_b8,
- R.id.clipboard_preview_test_b9
- };
- private int mGuessIndex = 0;
-
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -74,87 +47,29 @@
copyButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
- generateAndCopySecret();
+ setClipboardData();
}
});
- disableKeypad();
+ disablePassFail();
}
- private void generateAndCopySecret() {
- String s = "";
- resetState();
- for (int i = 0; i < mSecretCode.length; ++i) {
- mSecretCode[i] = ThreadLocalRandom.current().nextInt(0, 10);
- s += mSecretCode[i];
- }
+ private void setClipboardData() {
ClipboardManager cm = this.getSystemService(ClipboardManager.class);
- cm.setPrimaryClip(ClipData.newPlainText("Secret", s));
- enableKeypad();
+
+ ClipData cd = ClipData.newPlainText("",
+ getString(R.string.clipboard_preview_test_secret));
+ PersistableBundle pb = new PersistableBundle(1);
+ pb.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
+ cd.getDescription().setExtras(pb);
+ cm.setPrimaryClip(cd);
+ enablePassFail();
}
- private void enableKeypad() {
- for (int i = 0; i < mButtons.length; ++i) {
- Button numButton = findViewById(mButtons[i]);
- numButton.setBackgroundColor(Color.GREEN);
- int finalI = i;
- numButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- buttonClicked(finalI);
- }
- });
- }
- }
-
- private void disableKeypad() {
- for (int i = 0; i < mButtons.length; ++i) {
- Button numButton = findViewById(mButtons[i]);
- numButton.setOnClickListener(null);
- numButton.setBackgroundColor(Color.LTGRAY);
- }
- }
-
- private void resetState() {
- for (int i = 0; i < mSecretGuess.length; ++i) {
- mSecretGuess[i] = -1;
- }
- mGuessIndex = 0;
- View v = findViewById(R.id.clipboard_preview_test_pass_fail);
+ private void disablePassFail() {
findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.INVISIBLE);
- findViewById(R.id.fail_button).setVisibility(View.VISIBLE);
- findViewById(R.id.pass_button).setVisibility(View.VISIBLE);
}
- private void buttonClicked(int i) {
- if (mGuessIndex < mSecretGuess.length) {
- mSecretGuess[mGuessIndex] = i;
- ++mGuessIndex;
- }
- checkSolution();
- }
-
- private void checkSolution() {
- boolean testPassed = true;
- if (mGuessIndex == mSecretGuess.length) {
- for (int i = 0; i < mSecretGuess.length && i < mSecretCode.length; ++i) {
- if (mSecretGuess[i] != mSecretCode[i]) {
- testPassed = false;
- }
- }
- markPassed(testPassed);
- disableKeypad();
- }
- }
-
- private void markPassed(boolean passed) {
+ private void enablePassFail() {
findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.VISIBLE);
- if (passed) {
- findViewById(R.id.fail_button).setVisibility(View.INVISIBLE);
- } else {
- findViewById(R.id.pass_button).setVisibility(View.INVISIBLE);
- }
-
}
-
-
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java
index b8b9602..97ec07a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java
@@ -118,7 +118,7 @@
/** Stop observing to associated device and then disassociate. */
private void disassociate(AssociationInfo association) {
- String deviceAddress = association.getDeviceMacAddressAsString();
+ String deviceAddress = association.getDeviceMacAddress().toString();
mCompanionDeviceManager.stopObservingDevicePresence(deviceAddress);
mCompanionDeviceManager.disassociate(association.getId());
Log.d(LOG_TAG, "Disassociated with device: " + deviceAddress);
@@ -142,11 +142,14 @@
AssociationInfo association =
data.getParcelableExtra(CompanionDeviceManager.EXTRA_ASSOCIATION,
AssociationInfo.class);
- String deviceAddress = association.getDeviceMacAddressAsString();
// This test is for bluetooth devices, which should all have a MAC address.
- if (deviceAddress == null) fail("The device was present but its address was null.");
+ if (association == null || association.getDeviceMacAddress() == null) {
+ fail("The device was present but its address was null.");
+ return;
+ }
+ String deviceAddress = association.getDeviceMacAddress().toString();
mCompanionDeviceManager.startObservingDevicePresence(deviceAddress);
mCurrentAssociation = getAssociation(association.getId());
Log.d(LOG_TAG, "Associated with device: " + deviceAddress);
@@ -285,7 +288,9 @@
@Override
boolean verify() {
// Check that it is associated and being observed.
- return mCurrentAssociation != null && mCurrentAssociation.isNotifyOnDeviceNearby();
+ // Bypass inaccessible AssociationInfo#isNotifyOnDeviceNearby() with toString()
+ return mCurrentAssociation != null
+ && mCurrentAssociation.toString().contains("mNotifyOnDeviceNearby=true");
}
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java
index 37760d5..11829c3 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java
@@ -45,7 +45,7 @@
@Override
public void onDeviceAppeared(AssociationInfo association) {
NEARBY_DEVICES.add(association.getId());
- String message = "Device appeared: " + association.getDeviceMacAddressAsString();
+ String message = "Device appeared: " + association.getDeviceMacAddress();
Log.d(LOG_TAG, message);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
@@ -53,7 +53,7 @@
@Override
public void onDeviceDisappeared(AssociationInfo association) {
NEARBY_DEVICES.remove(association.getId());
- String message = "Device disappeared: " + association.getDeviceMacAddressAsString();
+ String message = "Device disappeared: " + association.getDeviceMacAddress();
Log.d(LOG_TAG, message);
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java
index ff12bd2..171d612 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java
@@ -54,15 +54,18 @@
*/
private static final String TAG = "ReadLogsTestActivity";
- private static final String PERMISSION = "android.permission.READ_LOGS";
-
- private static final String ALLOW_LOGD_ACCESS = "Allow logd access";
- private static final String DENY_LOGD_ACCESS = "Decline logd access";
private static final String SYSTEM_LOG_START = "--------- beginning of system";
private static final int NUM_OF_LINES_FG = 10;
private static final int NUM_OF_LINES_BG = 0;
- private static final int LOG_ACCESS_INTERVAL = 1000 * 60 * 2;
+ private static final int LOG_ACCESS_INTERVAL_MILLIS = 1000 * 60 * 2;
+
+ private static final List<String> LOG_CAT_TEST_COMMAND = Arrays.asList("logcat",
+ "-b", "system",
+ "-v", "uid",
+ "-v", "process",
+ "-t", Integer.toString(NUM_OF_LINES_FG));
+
private volatile long mLastLogAccess = 0;
private static Context sContext;
@@ -71,11 +74,14 @@
private static String sAppPackageName;
private static ExecutorService sExecutorService;
+ private static String sLogCatUidFilterRegex;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sContext = this;
+ sLogCatUidFilterRegex = "^[A-Z]{1}\\(\\s" + sContext.getApplicationInfo().uid;
sActivityManager = sContext.getSystemService(ActivityManager.class);
sExecutorService = Executors.newSingleThreadExecutor();
@@ -114,72 +120,70 @@
public void runLogcatInForegroundAllowOnlyOnce() {
Log.d(TAG, "Inside runLogcatInForegroundAllowOnlyOnce()");
- if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL)) {
+ if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL_MILLIS)) {
String reason = "Please wait for "
- + ((mLastLogAccess + LOG_ACCESS_INTERVAL - SystemClock.elapsedRealtime())
+ + ((mLastLogAccess + LOG_ACCESS_INTERVAL_MILLIS - SystemClock.elapsedRealtime())
/ 1000) + " seconds before running the test.";
Toast.makeText(this, reason, Toast.LENGTH_LONG).show();
return;
}
- sExecutorService.execute(new Runnable() {
+ sExecutorService.execute(() -> {
+ BufferedReader reader = null;
+ try {
- public void run() {
- BufferedReader reader = null;
+ // Dump the logcat most recent 10 lines before the compile command,
+ // and check if there are logs about compiling the test package.
+ Process logcat = new ProcessBuilder(LOG_CAT_TEST_COMMAND).start();
+ reader = new BufferedReader(new InputStreamReader(logcat.getInputStream()));
+ logcat.waitFor();
+
+ List<String> logcatOutput = new ArrayList<>();
+ String current;
+ Integer lineCount = 0;
+ while ((current = reader.readLine()) != null) {
+ logcatOutput.add(current);
+ lineCount++;
+ }
+
+ Log.d(TAG, "Logcat system allow line count: " + lineCount);
+ Log.d(TAG, "Logcat system allow output: " + logcatOutput);
+
try {
+ assertTrue("System log output is null", logcatOutput.size() != 0);
- // Dump the logcat most recent 10 lines before the compile command,
- // and check if there are logs about compiling the test package.
- java.lang.Process logcat = new ProcessBuilder(
- Arrays.asList("logcat", "-b", "system", "-t",
- Integer.toString(NUM_OF_LINES_FG))).start();
- reader = new BufferedReader(new InputStreamReader(logcat.getInputStream()));
- logcat.waitFor();
+ // Check if the logcatOutput is not null. If logcatOutput is null,
+ // it throws an assertion error
+ assertNotNull(logcatOutput.get(0), "logcat output should not be null");
- List<String> logcatOutput = new ArrayList<>();
- String current;
- Integer lineCount = 0;
- while ((current = reader.readLine()) != null) {
- logcatOutput.add(current);
- lineCount++;
+ boolean allowLog = logcatOutput.get(0).contains(SYSTEM_LOG_START);
+ assertTrue("Allow system log access contains log", allowLog);
+
+ boolean allowLineCount = lineCount > NUM_OF_LINES_FG;
+ assertTrue("Allow system log access count", allowLineCount);
+
+ Log.d(TAG, "Logcat system allow log contains: " + allowLog + " lineCount: "
+ + lineCount + " larger than: " + allowLineCount);
+
+ mLastLogAccess = SystemClock.elapsedRealtime();
+
+ runOnUiThread(() ->
+ Toast.makeText(this, "User Consent Allow Testing passed",
+ Toast.LENGTH_LONG).show());
+
+ } catch (AssertionError e) {
+ fail("User Consent Allow Testing failed");
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG, "User Consent Testing failed");
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
}
-
- Log.d(TAG, "Logcat system allow line count: " + lineCount);
- Log.d(TAG, "Logcat system allow output: " + logcatOutput);
-
- try {
-
- assertTrue("System log output is null", logcatOutput.size() != 0);
-
- // Check if the logcatOutput is not null. If logcatOutput is null,
- // it throws an assertion error
- assertNotNull(logcatOutput.get(0), "logcat output should not be null");
-
- boolean allowLog = logcatOutput.get(0).contains(SYSTEM_LOG_START);
- assertTrue("Allow system log access containe log", allowLog);
-
- boolean allowLineCount = lineCount > NUM_OF_LINES_FG;
- assertTrue("Allow system log access count", allowLineCount);
-
- Log.d(TAG, "Logcat system allow log contains: " + allowLog + " lineCount: "
- + lineCount + " larger than: " + allowLineCount);
-
- mLastLogAccess = SystemClock.elapsedRealtime();
-
- } catch (AssertionError e) {
- fail("User Consent Allow Testing failed");
- }
-
- } catch (Exception e) {
- Log.e(TAG, "User Consent Testing failed");
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- Log.d(TAG, "Could not close reader: " + e.getMessage());
- }
+ } catch (IOException e) {
+ Log.d(TAG, "Could not close reader: " + e.getMessage());
}
}
});
@@ -199,56 +203,53 @@
public void runLogcatInForegroundDontAllow() {
Log.d(TAG, "Inside runLogcatInForegroundDontAllow()");
- if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL)) {
+ if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL_MILLIS)) {
String reason = "Please wait for "
- + ((mLastLogAccess + LOG_ACCESS_INTERVAL - SystemClock.elapsedRealtime())
+ + ((mLastLogAccess + LOG_ACCESS_INTERVAL_MILLIS - SystemClock.elapsedRealtime())
/ 1000) + " seconds before running the test.";
Toast.makeText(this, reason, Toast.LENGTH_LONG).show();
return;
}
- sExecutorService.execute(new Runnable() {
+ sExecutorService.execute(() -> {
+ BufferedReader reader = null;
+ try {
+ Process logcat = new ProcessBuilder(LOG_CAT_TEST_COMMAND).start();
+ logcat.waitFor();
- public void run() {
- BufferedReader reader = null;
+ // Merge several logcat streams, and take the last N lines
+ reader = new BufferedReader(new InputStreamReader(logcat.getInputStream()));
+ assertNotNull(reader);
+
+ String current;
+ int lineCount = 0;
+ while ((current = reader.readLine()) != null
+ && current.matches(sLogCatUidFilterRegex)) {
+ lineCount++;
+ }
+
+ Log.d(TAG, "Logcat system deny line count:" + lineCount);
+
+ mLastLogAccess = SystemClock.elapsedRealtime();
+
try {
- java.lang.Process logcat = new ProcessBuilder(
- Arrays.asList("logcat", "-b", "system", "-t",
- Integer.toString(NUM_OF_LINES_FG))).start();
- logcat.waitFor();
+ assertTrue("Deny System log access", lineCount == NUM_OF_LINES_BG);
- // Merge several logcat streams, and take the last N lines
- reader = new BufferedReader(new InputStreamReader(logcat.getInputStream()));
- assertNotNull(reader);
-
- List<String> logcatOutput = new ArrayList<>();
- String current;
- int lineCount = 0;
- while ((current = reader.readLine()) != null) {
- logcatOutput.add(current);
- lineCount++;
+ runOnUiThread(() ->
+ Toast.makeText(this, "User Consent Deny Testing passed",
+ Toast.LENGTH_LONG).show());
+ } catch (AssertionError e) {
+ fail("User Consent Deny Testing failed");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "User Consent Testing failed");
+ } finally {
+ try {
+ if (reader != null) {
+ reader.close();
}
-
- Log.d(TAG, "Logcat system deny line count:" + lineCount);
-
- mLastLogAccess = SystemClock.elapsedRealtime();
-
- try {
- assertTrue("Deny System log access", lineCount == NUM_OF_LINES_BG);
- } catch (AssertionError e) {
- fail("User Consent Deny Testing failed");
- }
-
- } catch (Exception e) {
- Log.e(TAG, "User Consent Testing failed");
- } finally {
- try {
- if (reader != null) {
- reader.close();
- }
- } catch (IOException e) {
- Log.d(TAG, "Could not close reader: " + e.getMessage());
- }
+ } catch (IOException e) {
+ Log.d(TAG, "Could not close reader: " + e.getMessage());
}
}
});
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java
deleted file mode 100644
index 1548910..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/** Tests the precision of the device's RSSI measurement wtfdelet */
-public class BleRssiPrecisionActivity extends PassFailButtons.Activity {
- private static final String TAG = BleRssiPrecisionActivity.class.getName();
-
- // Report log schema
- private static final String KEY_RSSI_RANGE_DBM = "rssi_range_dbm";
- private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
- // Thresholds
- private static final int MAX_RSSI_RANGE_DBM = 18;
-
- private EditText reportRssiRangeEditText;
- private EditText reportReferenceDeviceEditText;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_rssi_precision);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- reportRssiRangeEditText = findViewById(R.id.report_rssi_range);
- reportReferenceDeviceEditText = findViewById(R.id.report_reference_device);
-
- DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
- PackageManager.FEATURE_BLUETOOTH_LE);
-
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (!adapter.isEnabled()) {
- new AlertDialog.Builder(this)
- .setTitle(R.string.ble_bluetooth_disable_title)
- .setMessage(R.string.ble_bluetooth_disable_message)
- .setOnCancelListener(dialog -> finish())
- .create().show();
- }
-
- reportRssiRangeEditText.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- reportReferenceDeviceEditText.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- }
-
- private void checkTestInputs() {
- getPassButton().setEnabled(checkDistanceRangeInput() && checkReferenceDeviceInput());
- }
-
- private boolean checkDistanceRangeInput() {
- String rssiRangeInput = reportRssiRangeEditText.getText().toString();
-
- if (!rssiRangeInput.isEmpty()) {
- int rssiRange = Integer.parseInt(rssiRangeInput);
- // RSSI range must be inputted and within acceptable range before test can be passed
- return rssiRange <= MAX_RSSI_RANGE_DBM;
- }
- return false;
- }
-
- private boolean checkReferenceDeviceInput() {
- // Reference device must be inputted before test can be passed
- return !reportReferenceDeviceEditText.getText().toString().isEmpty();
- }
-
- @Override
- public void recordTestResults() {
- String rssiRange = reportRssiRangeEditText.getText().toString();
- String referenceDevice = reportReferenceDeviceEditText.getText().toString();
-
- if (!rssiRange.isEmpty()) {
- Log.i(TAG, "BLE RSSI Range (dBm): " + rssiRange);
- getReportLog().addValue(KEY_RSSI_RANGE_DBM, Integer.parseInt(rssiRange),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!referenceDevice.isEmpty()) {
- Log.i(TAG, "BLE Reference Device: " + referenceDevice);
- getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
- getReportLog().submit();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java
deleted file mode 100644
index 2de7edf..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.cts.verifier.presence;
-
-import android.app.AlertDialog;
-import android.bluetooth.BluetoothAdapter;
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Tests that the device's Rx/Tx calibration results in a median range (cm) within the specified
- * bounds
- */
-public class BleRxTxCalibrationActivity extends PassFailButtons.Activity {
- private static final String TAG = BleRxTxCalibrationActivity.class.getName();
-
- // Report log schema
- private static final String KEY_CHANNEL_RSSI_RANGE = "channel_rssi_range";
- private static final String KEY_CORE_RSSI_RANGE = "core_rssi_range";
- private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
- // Thresholds
- private static final int MAX_RSSI_RANGE = 6;
-
- private EditText reportChannelsRssiRangeEditText;
- private EditText reportCoresRssiRangeEditText;
- private EditText reportReferenceDeviceEditText;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.ble_rx_tx_calibration);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- reportChannelsRssiRangeEditText = findViewById(R.id.report_channels_rssi_range);
- reportCoresRssiRangeEditText = findViewById(R.id.report_cores_rssi_range);
- reportReferenceDeviceEditText = findViewById(R.id.report_reference_device);
-
- DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
- PackageManager.FEATURE_BLUETOOTH_LE);
-
- BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
-
- if (!adapter.isEnabled()) {
- new AlertDialog.Builder(this)
- .setTitle(R.string.ble_bluetooth_disable_title)
- .setMessage(R.string.ble_bluetooth_disable_message)
- .setOnCancelListener(dialog -> finish())
- .create().show();
- }
-
- reportChannelsRssiRangeEditText.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- reportCoresRssiRangeEditText.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- reportReferenceDeviceEditText.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- }
-
- private void checkTestInputs() {
- getPassButton().setEnabled(
- checkChannelRssiInput() && checkCoreRssiInput() && checkReferenceDeviceInput());
- }
-
- private boolean checkChannelRssiInput() {
- String channelsRssiRangeInput = reportChannelsRssiRangeEditText.getText().toString();
- if (!channelsRssiRangeInput.isEmpty()) {
- int channelsRssiRange = Integer.parseInt(channelsRssiRangeInput);
- // RSSI range must be inputted and within acceptable range before test can be passed
- return channelsRssiRange <= MAX_RSSI_RANGE;
- }
- return false;
- }
-
- private boolean checkCoreRssiInput() {
- String coresRssiRangeInput = reportCoresRssiRangeEditText.getText().toString();
- if (!coresRssiRangeInput.isEmpty()) {
- int coresRssiRange = Integer.parseInt(coresRssiRangeInput);
- // RSSI range must be inputted and within acceptable range before test can be passed
- return coresRssiRange <= MAX_RSSI_RANGE;
- }
- // This field is optional, so return true even if the user has not inputted anything
- return true;
- }
-
- private boolean checkReferenceDeviceInput() {
- // Reference device must be inputted before test can be passed
- return !reportReferenceDeviceEditText.getText().toString().isEmpty();
- }
-
- @Override
- public void recordTestResults() {
- String channelRssiRange = reportChannelsRssiRangeEditText.getText().toString();
- String coreRssiRange = reportCoresRssiRangeEditText.getText().toString();
- String referenceDevice = reportReferenceDeviceEditText.getText().toString();
-
- if (!channelRssiRange.isEmpty()) {
- Log.i(TAG, "BLE RSSI Range Across Channels (dBm): " + channelRssiRange);
- getReportLog().addValue(KEY_CHANNEL_RSSI_RANGE, Integer.parseInt(channelRssiRange),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!coreRssiRange.isEmpty()) {
- Log.i(TAG, "BLE RSSI Range Across Cores (dBm): " + coreRssiRange);
- getReportLog().addValue(KEY_CORE_RSSI_RANGE, Integer.parseInt(coreRssiRange),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!referenceDevice.isEmpty()) {
- Log.i(TAG, "BLE Reference Device: " + referenceDevice);
- getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- getReportLog().submit();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java
deleted file mode 100644
index 256fe3a..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-import android.app.Activity;
-import android.content.Context;
-import android.util.Log;
-import android.view.View;
-import android.widget.Toast;
-
-/**
- * Checks if a device supports a hardware feature needed for a test, and passes the test
- * automatically otherwise.
- */
-public class DeviceFeatureChecker {
-
- /** Checks if a feature is supported.
- *
- * @param feature must be a string defined in PackageManager
- */
- public static void checkFeatureSupported(Context context, View passButton, String feature) {
- if (!context.getPackageManager().hasSystemFeature(feature)) {
- String message = String.format("Device does not support %s, automatically passing test",
- feature);
- Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
- Log.e(context.getClass().getName(), message);
- passButton.performClick();
- Activity activity = (Activity) (context);
- activity.finish();
- }
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java
deleted file mode 100644
index 2de68a5..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-
-import android.text.Editable;
-import android.text.TextWatcher;
-
-/**
- * Handles editable text inputted into test activities.
- */
-public class InputTextHandler {
-
- /** Callback that is executed when text is changed. Takes modified text as input. */
- public interface OnTextChanged {
- void run(Editable s);
- }
-
- /**
- * Generic text changed handler that will execute the provided callback when text is modified.
- *
- * @param callback called when text is changed, and passed the modified text
- */
- public static TextWatcher getOnTextChangedHandler(OnTextChanged callback) {
- return new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {}
-
- @Override
- public void afterTextChanged(Editable s) {
- callback.run(s);
- }
- };
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java
deleted file mode 100644
index 458d192..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Activity for testing that NAN measurements are within the acceptable ranges
- */
-public class NanPrecisionTestActivity extends PassFailButtons.Activity {
- private static final String TAG = NanPrecisionTestActivity.class.getName();
-
- // Report log schema
- private static final String KEY_BANDWIDTH = "nan_bandwidth";
- private static final String KEY_MEASUREMENT_RANGE_10CM_AT_68P = "measurement_range_10cm_68p";
- private static final String KEY_MEASUREMENT_RANGE_1M_AT_68P = "measurement_range_1m_68p";
- private static final String KEY_MEASUREMENT_RANGE_3M_AT_68p = "measurement_range_3m_68p";
- private static final String KEY_MEASUREMENT_RANGE_5M_AT_68p = "measurement_range_5m_68p";
- private static final String KEY_MEASUREMENT_RANGE_10CM_AT_90P = "measurement_range_10cm_90p";
- private static final String KEY_MEASUREMENT_RANGE_1M_AT_90P = "measurement_range_1m_90p";
- private static final String KEY_MEASUREMENT_RANGE_3M_AT_90p = "measurement_range_3m_90p";
- private static final String KEY_MEASUREMENT_RANGE_5M_AT_90p = "measurement_range_5m_90p";
- private static final String KEY_REFERENCE_DEVICE = "reference_device";
-
- // Thresholds
- private static final int MAX_DISTANCE_RANGE_METERS_160MHZ = 2;
- private static final int MAX_DISTANCE_RANGE_METERS_80MHZ = 4;
- private static final int MAX_DISTANCE_RANGE_METERS_40MHZ = 8;
- private static final int MAX_DISTANCE_RANGE_METERS_20MHZ = 16;
-
- // Maps NAN bandwidths to acceptable range thresholds
- private static final ImmutableMap<Integer, Integer> BANDWIDTH_TO_THRESHOLD_MAP =
- ImmutableMap.of(160, MAX_DISTANCE_RANGE_METERS_160MHZ, 80,
- MAX_DISTANCE_RANGE_METERS_80MHZ, 40, MAX_DISTANCE_RANGE_METERS_40MHZ, 20,
- MAX_DISTANCE_RANGE_METERS_20MHZ);
-
- private EditText mBandwidthMhz;
- private EditText mMeasurementRange10cmGt68p;
- private EditText mMeasurementRange1mGt68p;
- private EditText mMeasurementRange3mGt68p;
- private EditText mMeasurementRange5mGt68p;
- private EditText mMeasurementRange10cmGt90p;
- private EditText mMeasurementRange1mGt90p;
- private EditText mMeasurementRange3mGt90p;
- private EditText mMeasurementRange5mGt90p;
- private EditText mReferenceDeviceInput;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.nan_precision);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- mBandwidthMhz = (EditText) findViewById(R.id.nan_bandwidth);
- mMeasurementRange10cmGt68p = (EditText) findViewById(R.id.distance_range_10cm_gt_68p);
- mMeasurementRange1mGt68p = (EditText) findViewById(R.id.distance_range_1m_gt_68p);
- mMeasurementRange3mGt68p = (EditText) findViewById(R.id.distance_range_3m_gt_68p);
- mMeasurementRange5mGt68p = (EditText) findViewById(R.id.distance_range_5m_gt_68p);
- mMeasurementRange10cmGt90p = (EditText) findViewById(R.id.distance_range_10cm_gt_90p);
- mMeasurementRange1mGt90p = (EditText) findViewById(R.id.distance_range_1m_gt_90p);
- mMeasurementRange3mGt90p = (EditText) findViewById(R.id.distance_range_3m_gt_90p);
- mMeasurementRange5mGt90p = (EditText) findViewById(R.id.distance_range_5m_gt_90p);
- mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
- DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
- PackageManager.FEATURE_WIFI_AWARE);
-
- mBandwidthMhz.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange10cmGt68p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange1mGt68p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange3mGt68p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange5mGt68p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange10cmGt90p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange1mGt90p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange3mGt90p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mMeasurementRange5mGt90p.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mReferenceDeviceInput.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- }
-
- private void checkTestInputs() {
- getPassButton().setEnabled(checkMeasurementRange68thPercentileInput()
- && checkMeasurementRange90thPercentileInput()
- && checkReferenceDeviceInput());
- }
-
- private boolean checkMeasurementRange68thPercentileInput() {
- return checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt68p,
- mMeasurementRange1mGt68p, mMeasurementRange3mGt68p, mMeasurementRange5mGt68p);
- }
-
- private boolean checkMeasurementRange90thPercentileInput() {
- String measurementRangeInput10cmGt90p = mMeasurementRange10cmGt90p.getText().toString();
- String measurementRangeInput1mGt90p = mMeasurementRange1mGt90p.getText().toString();
- String measurementRangeInput3mGt90p = mMeasurementRange3mGt90p.getText().toString();
- String measurementRangeInput5mGt90p = mMeasurementRange5mGt90p.getText().toString();
- List<String> optionalMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt90p,
- measurementRangeInput1mGt90p,
- measurementRangeInput3mGt90p, measurementRangeInput5mGt90p);
-
- boolean inputted = false;
- for (String input : optionalMeasurementRangeList) {
- if (!input.isEmpty()) {
- inputted = true;
- break;
- }
- }
- // If one of the ranges is inputted for one of the distances, then it becomes required
- // that the ranges are inputted for all the distances and for tests to pass, must be
- // acceptable values
- return !inputted || checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt90p,
- mMeasurementRange1mGt90p, mMeasurementRange3mGt90p, mMeasurementRange5mGt90p);
- }
-
- private boolean checkRequiredMeasurementRangeInput(EditText rangeInput10cm,
- EditText rangeInput1m, EditText rangeInput3m, EditText rangeInput5m) {
- String bandwidthInputMhz = mBandwidthMhz.getText().toString();
- String measurementRangeInput10cmGt = rangeInput10cm.getText().toString();
- String measurementRangeInput1mGt = rangeInput1m.getText().toString();
- String measurementRangeInput3mGt = rangeInput3m.getText().toString();
- String measurementRangeInput5mGt = rangeInput5m.getText().toString();
- List<String> requiredMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt,
- measurementRangeInput1mGt,
- measurementRangeInput3mGt, measurementRangeInput5mGt);
-
- for (String input : requiredMeasurementRangeList) {
- if (bandwidthInputMhz.isEmpty() || input.isEmpty()) {
- // Distance range must be inputted for all fields so fail early otherwise
- return false;
- }
- if (!BANDWIDTH_TO_THRESHOLD_MAP.containsKey(Integer.parseInt(bandwidthInputMhz))) {
- // bandwidth must be one of the expected thresholds
- return false;
- }
- double distanceRange = Double.parseDouble(input);
- int bandwidth = Integer.parseInt(bandwidthInputMhz);
- if (distanceRange > BANDWIDTH_TO_THRESHOLD_MAP.get(bandwidth)) {
- // All inputs must be in acceptable range so fail early otherwise
- return false;
- }
- }
- return true;
- }
-
- private boolean checkReferenceDeviceInput() {
- // Reference device used must be inputted before test can be passed.
- return !mReferenceDeviceInput.getText().toString().isEmpty();
- }
-
- @Override
- public void recordTestResults() {
- String nanBandwidthMhz = mBandwidthMhz.getText().toString();
- String measurementRange10cmGt68p = mMeasurementRange10cmGt68p.getText().toString();
- String measurementRange1mGt68p = mMeasurementRange1mGt68p.getText().toString();
- String measurementRange3mGt68p = mMeasurementRange3mGt68p.getText().toString();
- String measurementRange5mGt68p = mMeasurementRange5mGt68p.getText().toString();
- String measurementRange10cmGt90p = mMeasurementRange10cmGt90p.getText().toString();
- String measurementRange1mGt90p = mMeasurementRange1mGt90p.getText().toString();
- String measurementRange3mGt90p = mMeasurementRange3mGt90p.getText().toString();
- String measurementRange5mGt90p = mMeasurementRange5mGt90p.getText().toString();
- String referenceDevice = mReferenceDeviceInput.getText().toString();
-
- if (!nanBandwidthMhz.isEmpty()) {
- Log.i(TAG, "NAN Bandwidth at which data was collected: " + nanBandwidthMhz);
- getReportLog().addValue(KEY_BANDWIDTH,
- Integer.parseInt(nanBandwidthMhz),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange10cmGt68p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_68P,
- Double.parseDouble(measurementRange10cmGt68p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange1mGt68p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt68p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_68P,
- Double.parseDouble(measurementRange1mGt68p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange3mGt68p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt68p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_68p,
- Double.parseDouble(measurementRange3mGt68p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange5mGt68p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt68p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_68p,
- Double.parseDouble(measurementRange5mGt68p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange10cmGt90p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_90P,
- Double.parseDouble(measurementRange10cmGt90p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange1mGt90p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt90p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_90P,
- Double.parseDouble(measurementRange1mGt90p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange3mGt90p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt90p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_90p,
- Double.parseDouble(measurementRange3mGt90p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!measurementRange5mGt90p.isEmpty()) {
- Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt90p);
- getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_90p,
- Double.parseDouble(measurementRange5mGt90p),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
-
- if (!referenceDevice.isEmpty()) {
- Log.i(TAG, "NAN Reference Device: " + referenceDevice);
- getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
- getReportLog().submit();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS
deleted file mode 100644
index e874499..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS
+++ /dev/null
@@ -1,4 +0,0 @@
-# Bug component: 1106357
-asalo@google.com
-jbabs@google.com
-christinatao@google.com
\ No newline at end of file
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java
deleted file mode 100644
index 6776693..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.SystemProperties;
-
-import com.android.cts.verifier.ManifestTestListAdapter;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class PresenceTestActivity extends PassFailButtons.TestListActivity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.pass_fail_list);
- setPassFailButtonClickListeners();
-
- List<String> disabledTest = new ArrayList<String>();
- boolean isTv = getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
- if (isTv) {
- setInfoResources(R.string.presence_test, R.string.presence_test_tv_info, -1);
- int firstSdk = SystemProperties.getInt("ro.product.first_api_level", 0);
- if (firstSdk < Build.VERSION_CODES.TIRAMISU) {
- disabledTest.add("com.android.cts.verifier.presence.UwbPrecisionActivity");
- disabledTest.add("com.android.cts.verifier.presence.UwbShortRangeActivity");
- disabledTest.add("com.android.cts.verifier.presence.BleRssiPrecisionActivity");
- disabledTest.add("com.android.cts.verifier.presence.BleRxTxCalibrationActivity");
- disabledTest.add("com.android.cts.verifier.presence.BleRxOffsetActivity");
- disabledTest.add("com.android.cts.verifier.presence.BleTxOffsetActivity");
- disabledTest.add("com.android.cts.verifier.presence.NanPrecisionTestActivity");
- }
- } else {
- setInfoResources(R.string.presence_test, R.string.presence_test_info, -1);
- }
-
- setTestListAdapter(new ManifestTestListAdapter(this, PresenceTestActivity.class.getName(),
- disabledTest.toArray(new String[disabledTest.size()])));
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java
deleted file mode 100644
index 5d1ff86..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Activity for testing that UWB distance and angle of arrival measurements are within the right
- * range.
- */
-public class UwbPrecisionActivity extends PassFailButtons.Activity {
- private static final String TAG = UwbPrecisionActivity.class.getName();
- // Report log schema
- private static final String KEY_DISTANCE_RANGE_CM = "distance_range_cm";
- private static final String KEY_REFERENCE_DEVICE = "reference_device";
- // Thresholds
- private static final int MAX_DISTANCE_RANGE_CM = 30;
-
- private EditText mDistanceRangeInput;
- private EditText mReferenceDeviceInput;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.uwb_precision);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- mDistanceRangeInput = (EditText) findViewById(R.id.distance_range_cm);
- mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
- DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
- PackageManager.FEATURE_UWB);
-
- mDistanceRangeInput.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mReferenceDeviceInput.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- }
-
- private void checkTestInputs() {
- getPassButton().setEnabled(
- checkDistanceRangeInput() && checkReferenceDeviceInput());
- }
-
- private boolean checkDistanceRangeInput() {
- String distanceRangeInput = mDistanceRangeInput.getText().toString();
- if (!distanceRangeInput.isEmpty()) {
- double distanceRange = Double.parseDouble(distanceRangeInput);
- // Distance range must be inputted and within acceptable range before test can be
- // passed.
- return distanceRange <= MAX_DISTANCE_RANGE_CM;
- }
- return false;
- }
-
- private boolean checkReferenceDeviceInput() {
- // Reference device must be inputted before test can be passed.
- return !mReferenceDeviceInput.getText().toString().isEmpty();
- }
-
- @Override
- public void recordTestResults() {
- String distanceRange = mDistanceRangeInput.getText().toString();
- String referenceDevice = mReferenceDeviceInput.getText().toString();
- if (!distanceRange.isEmpty()) {
- Log.i(TAG, "UWB Distance Range: " + distanceRange);
- getReportLog().addValue(KEY_DISTANCE_RANGE_CM, Double.parseDouble(distanceRange),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
- if (!referenceDevice.isEmpty()) {
- Log.i(TAG, "UWB Reference Device: " + referenceDevice);
- getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice,
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
- getReportLog().submit();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java
deleted file mode 100644
index 7f14800..0000000
--- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2022 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.presence;
-
-import android.content.pm.PackageManager;
-import android.os.Bundle;
-import android.text.Editable;
-import android.util.Log;
-import android.widget.EditText;
-
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
-import com.android.cts.verifier.PassFailButtons;
-import com.android.cts.verifier.R;
-
-/**
- * Activity for testing that UWB distance measurements are within the acceptable median.
- */
-public class UwbShortRangeActivity extends PassFailButtons.Activity {
- private static final String TAG = UwbShortRangeActivity.class.getName();
- // Report log schema
- private static final String KEY_DISTANCE_MEDIAN_CM = "distance_median_cm";
- private static final String KEY_REFERENCE_DEVICE = "reference_device";
- // Median Thresholds
- private static final double MIN_MEDIAN = 0.75;
- private static final double MAX_MEDIAN = 1.25;
- private EditText mMedianInput;
- private EditText mReferenceDeviceInput;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.uwb_short_range);
- setPassFailButtonClickListeners();
- getPassButton().setEnabled(false);
-
- mMedianInput = (EditText) findViewById(R.id.distance_median_meters);
- mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device);
-
- DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(),
- PackageManager.FEATURE_UWB);
-
- mMedianInput.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- mReferenceDeviceInput.addTextChangedListener(
- InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs()));
- }
-
- private void checkTestInputs() {
- getPassButton().setEnabled(checkMedianInput() && checkReferenceDeviceInput());
- }
-
- private boolean checkMedianInput() {
- String medianInput = mMedianInput.getText().toString();
- if (!medianInput.isEmpty()) {
- double median = Double.parseDouble(medianInput);
- return median >= MIN_MEDIAN && median <= MAX_MEDIAN;
- }
- return false;
- }
-
- private boolean checkReferenceDeviceInput() {
- return !mReferenceDeviceInput.getText().toString().isEmpty();
- }
-
- @Override
- public void recordTestResults() {
- String medianInput = mMedianInput.getText().toString();
- String referenceDeviceInput = mReferenceDeviceInput.getText().toString();
- if (!medianInput.isEmpty()) {
- Log.i(TAG, "UWB Distance Median: " + medianInput);
- getReportLog().addValue(KEY_DISTANCE_MEDIAN_CM, Double.parseDouble(medianInput),
- ResultType.NEUTRAL, ResultUnit.NONE);
- }
- if (!referenceDeviceInput.isEmpty()) {
- Log.i(TAG, "UWB Reference Device: " + referenceDeviceInput);
- getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDeviceInput, ResultType.NEUTRAL,
- ResultUnit.NONE);
- }
- getReportLog().submit();
- }
-}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
index 055f26f..c0df10c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java
@@ -66,9 +66,9 @@
private void playVideo() {
try {
- mPlayer.start();
mPlayer.prepare();
- mPlayer.startThread();
+ mPlayer.startCodec();
+ mPlayer.play();
mHandler.postDelayed(this::pauseStep, 5000);
} catch(Exception e) {
Log.d(TAG, "Could not play video", e);
@@ -95,7 +95,7 @@
private void resumeStep() {
try {
- mPlayer.start();
+ mPlayer.resume();
mHandler.postDelayed(this::enablePassButton, 3000);
} catch(Exception e) {
Log.d(TAG, "Could not resume video", e);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS
new file mode 100644
index 0000000..4744ab8
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS
@@ -0,0 +1,3 @@
+# Buganizer component id: 687598
+blindahl@google.com
+narcisaam@google.com
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
index 0163c62f..c446143 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java
@@ -209,10 +209,10 @@
private void playVideo() {
try {
- mPlayer.start();
mPlayer.prepare();
+ mPlayer.startCodec();
mPlayer.setLoopEnabled(true);
- mPlayer.startThread();
+ mPlayer.play();
} catch (Exception e) {
Log.d(TAG, "Could not play the video.", e);
}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
index 641ab20..94ef536 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java
@@ -221,10 +221,10 @@
private void playVideo() {
try {
- mPlayer.start();
mPlayer.prepare();
+ mPlayer.startCodec();
mPlayer.setLoopEnabled(true);
- mPlayer.startThread();
+ mPlayer.play();
} catch (Exception e) {
Log.d(TAG, "Could not play video", e);
}
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java
new file mode 100644
index 0000000..03d21f4
--- /dev/null
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2021 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.bedstead.harrier.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to indicate that a test requires multi-user support.
+ *
+ * <p>This can be enforced by using {@code DeviceState}.
+ */
+@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+//@Experimental
+public @interface RequireMultiUserSupport {
+}
diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
index b28b815a..7bd649d 100644
--- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
+++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java
@@ -67,6 +67,7 @@
import com.android.bedstead.harrier.annotations.RequireFeature;
import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode;
import com.android.bedstead.harrier.annotations.RequireLowRamDevice;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
import com.android.bedstead.harrier.annotations.RequireNotHeadlessSystemUserMode;
import com.android.bedstead.harrier.annotations.RequireNotLowRamDevice;
import com.android.bedstead.harrier.annotations.RequirePackageInstalled;
@@ -786,6 +787,12 @@
ensureGlobalSettingSet(
ensureGlobalSettingSetAnnotation.key(),
ensureGlobalSettingSetAnnotation.value());
+ continue;
+ }
+
+ if (annotation instanceof RequireMultiUserSupport) {
+ requireMultiUserSupport();
+ continue;
}
}
@@ -2528,4 +2535,9 @@
}
TestApis.settings().global().putString(key, value);
}
+
+ private void requireMultiUserSupport() {
+ assumeTrue("This test is only supported on multi user devices",
+ TestApis.users().supportsMultipleUsers());
+ }
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
index 200feb7..0ce2d7f 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java
@@ -40,6 +40,7 @@
import androidx.annotation.Nullable;
import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.annotations.Experimental;
import com.android.bedstead.nene.exceptions.AdbException;
import com.android.bedstead.nene.exceptions.AdbParseException;
import com.android.bedstead.nene.exceptions.NeneException;
@@ -485,6 +486,11 @@
return mCachedUsers.get(id);
}
+ @Experimental
+ public boolean supportsMultipleUsers() {
+ return UserManager.supportsMultipleUsers();
+ }
+
static Stream<UserInfo> users() {
if (Permissions.sIgnorePermissions.get()) {
return sUserManager.getUsers(
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
index ff389a1..4e798df 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
@@ -19,17 +19,15 @@
import static org.junit.Assume.assumeTrue;
import android.app.Instrumentation;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
+import android.graphics.Insets;
+import android.graphics.Rect;
+import android.os.SystemClock;
import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
-import android.util.ArrayMap;
+import android.view.WindowInsets;
+import android.view.WindowManager;
import androidx.test.InstrumentationRegistry;
@@ -37,33 +35,31 @@
import org.junit.rules.ExternalResource;
import java.io.IOException;
-import java.util.Map;
/**
* Test rule to enable gesture navigation on the device. Designed to be a {@link ClassRule}.
*/
public class GestureNavRule extends ExternalResource {
- private static final String SETTINGS_PACKAGE_NAME = "com.android.settings";
private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode";
- private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2;
+ private static final int NAV_BAR_MODE_3BUTTON = 0;
+ private static final int NAV_BAR_MODE_2BUTTON = 1;
+ private static final int NAV_BAR_MODE_GESTURAL = 2;
+
+ private static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
+ "com.android.internal.systemui.navbar.threebutton";
+ private static final String NAV_BAR_MODE_2BUTTON_OVERLAY =
+ "com.android.internal.systemui.navbar.twobutton";
private static final String GESTURAL_OVERLAY_NAME =
"com.android.internal.systemui.navbar.gestural";
- /** Most application's res id must be larger than 0x7f000000 */
- public static final int MIN_APPLICATION_RES_ID = 0x7f000000;
- public static final String SETTINGS_CLASS =
- SETTINGS_PACKAGE_NAME + ".Settings$SystemDashboardActivity";
+ private static final int WAIT_OVERLAY_TIMEOUT = 3000;
+ private static final int PEEK_INTERVAL = 200;
- private final Map<String, Boolean> mSystemGestureOptionsMap = new ArrayMap<>();
private final Context mTargetContext;
private final UiDevice mDevice;
+ private final WindowManager mWindowManager;
- // Bounds for actions like swipe and click.
- private String mEdgeToEdgeNavigationTitle;
- private String mSystemNavigationTitle;
- private String mGesturePreferenceTitle;
- private boolean mConfiguredInSettings;
- private boolean mRevertOverlay;
+ private final String mOriginalOverlayPackage;
@Override
protected void before() throws Throwable {
@@ -74,7 +70,9 @@
@Override
protected void after() {
- disableGestureNav();
+ if (!mOriginalOverlayPackage.equals(GESTURAL_OVERLAY_NAME)) {
+ disableGestureNav();
+ }
}
/**
@@ -85,39 +83,16 @@
Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
mDevice = UiDevice.getInstance(instrumentation);
mTargetContext = instrumentation.getTargetContext();
- PackageManager packageManager = mTargetContext.getPackageManager();
- Resources res;
- try {
- res = packageManager.getResourcesForApplication(SETTINGS_PACKAGE_NAME);
- } catch (PackageManager.NameNotFoundException e) {
- return;
- }
- if (res == null) {
- return;
- }
- mEdgeToEdgeNavigationTitle = getSettingsString(res, "edge_to_edge_navigation_title");
- mGesturePreferenceTitle = getSettingsString(res, "gesture_preference_title");
- mSystemNavigationTitle = getSettingsString(res, "system_navigation_title");
-
- String text = getSettingsString(res, "edge_to_edge_navigation_title");
- if (text != null) {
- mSystemGestureOptionsMap.put(text, false);
- }
- text = getSettingsString(res, "swipe_up_to_switch_apps_title");
- if (text != null) {
- mSystemGestureOptionsMap.put(text, false);
- }
- text = getSettingsString(res, "legacy_navigation_title");
- if (text != null) {
- mSystemGestureOptionsMap.put(text, false);
- }
-
- mConfiguredInSettings = false;
+ mOriginalOverlayPackage = getCurrentOverlayPackage();
+ mWindowManager = mTargetContext.getSystemService(WindowManager.class);
}
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean hasSystemGestureFeature() {
+ if (!containsNavigationBar()) {
+ return false;
+ }
final PackageManager pm = mTargetContext.getPackageManager();
// No bars on embedded devices.
@@ -128,59 +103,21 @@
|| pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE));
}
-
- private UiObject2 findSystemNavigationObject(String text, boolean addCheckSelector) {
- BySelector widgetFrameSelector = By.res("android", "widget_frame");
- BySelector checkboxSelector = By.checkable(true);
- if (addCheckSelector) {
- checkboxSelector = checkboxSelector.checked(true);
+ private String getCurrentOverlayPackage() {
+ final int currentNavMode = getCurrentNavMode();
+ switch (currentNavMode) {
+ case NAV_BAR_MODE_GESTURAL:
+ return GESTURAL_OVERLAY_NAME;
+ case NAV_BAR_MODE_2BUTTON:
+ return NAV_BAR_MODE_2BUTTON_OVERLAY;
+ case NAV_BAR_MODE_3BUTTON:
+ default:
+ return NAV_BAR_MODE_3BUTTON_OVERLAY;
}
- BySelector textSelector = By.text(text);
- BySelector targetSelector = By.hasChild(widgetFrameSelector).hasDescendant(textSelector)
- .hasDescendant(checkboxSelector);
-
- return mDevice.findObject(targetSelector);
}
- private boolean launchToSettingsSystemGesture() {
-
- // Open the Settings app as close as possible to the gesture Fragment
- Intent intent = new Intent(Intent.ACTION_MAIN);
- ComponentName settingComponent = new ComponentName(SETTINGS_PACKAGE_NAME, SETTINGS_CLASS);
- intent.setComponent(settingComponent);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
- mTargetContext.startActivity(intent);
-
- // Wait for the app to appear
- mDevice.wait(Until.hasObject(By.pkg("com.android.settings").depth(0)),
- 5000);
- mDevice.wait(Until.hasObject(By.text(mGesturePreferenceTitle)), 5000);
- if (mDevice.findObject(By.text(mGesturePreferenceTitle)) == null) {
- return false;
- }
- mDevice.findObject(By.text(mGesturePreferenceTitle)).click();
- mDevice.wait(Until.hasObject(By.text(mSystemNavigationTitle)), 5000);
- if (mDevice.findObject(By.text(mSystemNavigationTitle)) == null) {
- return false;
- }
- mDevice.findObject(By.text(mSystemNavigationTitle)).click();
- mDevice.wait(Until.hasObject(By.text(mEdgeToEdgeNavigationTitle)), 5000);
-
- return mDevice.hasObject(By.text(mEdgeToEdgeNavigationTitle));
- }
-
- private void leaveSettings() {
- mDevice.pressBack(); /* Back to Gesture */
- mDevice.waitForIdle();
- mDevice.pressBack(); /* Back to System */
- mDevice.waitForIdle();
- mDevice.pressBack(); /* back to Settings */
- mDevice.waitForIdle();
- mDevice.pressBack(); /* Back to Home */
- mDevice.waitForIdle();
-
- mDevice.pressHome(); /* double confirm back to home */
- mDevice.waitForIdle();
+ private void insetsToRect(Insets insets, Rect outRect) {
+ outRect.set(insets.left, insets.top, insets.right, insets.bottom);
}
private void enableGestureNav() {
@@ -188,71 +125,68 @@
return;
}
try {
- if (mDevice.executeShellCommand("cmd overlay list").contains(GESTURAL_OVERLAY_NAME)) {
+ if (!mDevice.executeShellCommand("cmd overlay list").contains(GESTURAL_OVERLAY_NAME)) {
+ return;
+ }
+ } catch (IOException ignore) {
+ //
+ }
+ monitorOverlayChange(() -> {
+ try {
mDevice.executeShellCommand("cmd overlay enable " + GESTURAL_OVERLAY_NAME);
- mDevice.waitForIdle();
+ } catch (IOException e) {
+ // Do nothing
}
- } catch (IOException e) {
- // Do nothing
- }
-
- if (isGestureMode()) {
- mRevertOverlay = true;
- return;
- }
-
- // Set up the gesture navigation by enabling it via the Settings app
- boolean isOperatedSettingsToExpectedOption = launchToSettingsSystemGesture();
- if (isOperatedSettingsToExpectedOption) {
- for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) {
- UiObject2 uiObject2 = findSystemNavigationObject(entry.getKey(), true);
- entry.setValue(uiObject2 != null);
- }
- UiObject2 edgeToEdgeObj = mDevice.findObject(By.text(mEdgeToEdgeNavigationTitle));
- if (edgeToEdgeObj != null) {
- edgeToEdgeObj.click();
- mConfiguredInSettings = true;
- }
- }
- mDevice.waitForIdle();
- leaveSettings();
-
- mDevice.pressHome();
- mDevice.waitForIdle();
-
- mDevice.waitForIdle();
+ });
}
- /**
- * Restore the original configured value for the system gesture by operating Settings.
- */
private void disableGestureNav() {
if (!hasSystemGestureFeature()) {
return;
}
-
- if (mRevertOverlay) {
+ monitorOverlayChange(() -> {
try {
- mDevice.executeShellCommand("cmd overlay disable " + GESTURAL_OVERLAY_NAME);
- } catch (IOException e) {
+ mDevice.executeShellCommand("cmd overlay enable " + mOriginalOverlayPackage);
+ } catch (IOException ignore) {
// Do nothing
}
- if (!isGestureMode()) {
- return;
- }
- }
+ });
+ }
- if (mConfiguredInSettings) {
- launchToSettingsSystemGesture();
- for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) {
- if (entry.getValue()) {
- UiObject2 navigationObject = findSystemNavigationObject(entry.getKey(), false);
- if (navigationObject != null) {
- navigationObject.click();
- }
+ private void getCurrentInsetsSize(Rect outSize) {
+ outSize.setEmpty();
+ if (mWindowManager != null) {
+ WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets();
+ Insets navInsets = insets.getInsetsIgnoringVisibility(
+ WindowInsets.Type.navigationBars());
+ insetsToRect(navInsets, outSize);
+ }
+ }
+
+ // Monitoring the navigation bar insets size change as a hint of gesture mode has changed, not
+ // the best option for every kind of devices. We can consider listening OVERLAY_CHANGED
+ // broadcast in U.
+ private void monitorOverlayChange(Runnable overlayChangeCommand) {
+ if (mWindowManager != null) {
+ final Rect initSize = new Rect();
+ getCurrentInsetsSize(initSize);
+
+ overlayChangeCommand.run();
+ // wait for insets size change
+ final Rect peekSize = new Rect();
+ int t = 0;
+ while (t < WAIT_OVERLAY_TIMEOUT) {
+ SystemClock.sleep(PEEK_INTERVAL);
+ t += PEEK_INTERVAL;
+ getCurrentInsetsSize(peekSize);
+ if (!peekSize.equals(initSize)) {
+ break;
}
}
- leaveSettings();
+ } else {
+ // shouldn't happen
+ overlayChangeCommand.run();
+ SystemClock.sleep(WAIT_OVERLAY_TIMEOUT);
}
}
@@ -266,20 +200,23 @@
assumeTrue("Gesture navigation required", isGestureMode);
}
- private boolean isGestureMode() {
- // TODO: b/153032202 consider the CTS on GSI case.
+ private int getCurrentNavMode() {
Resources res = mTargetContext.getResources();
int naviModeId = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android");
- int naviMode = res.getInteger(naviModeId);
- return naviMode == NAV_BAR_INTERACTION_MODE_GESTURAL;
+ return res.getInteger(naviModeId);
}
- private static String getSettingsString(Resources res, String strResName) {
- int resIdString = res.getIdentifier(strResName, "string", SETTINGS_PACKAGE_NAME);
- if (resIdString <= MIN_APPLICATION_RES_ID) {
- return null;
- }
+ private boolean containsNavigationBar() {
+ final Rect peekSize = new Rect();
+ getCurrentInsetsSize(peekSize);
+ return peekSize.height() != 0;
+ }
- return res.getString(resIdString);
+ private boolean isGestureMode() {
+ if (!containsNavigationBar()) {
+ return false;
+ }
+ final int naviMode = getCurrentNavMode();
+ return naviMode == NAV_BAR_MODE_GESTURAL;
}
}
diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
index 820fd95..8884bf6 100644
--- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
+++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java
@@ -83,6 +83,7 @@
private static ImmutableList<String> sSystemserverclasspathJars;
private static ImmutableList<String> sSharedLibJars;
private static ImmutableList<SharedLibraryInfo> sSharedLibs;
+ private static ImmutableMultimap<String, String> sSharedLibsPathsToName;
private static ImmutableMultimap<String, String> sJarsToClasses;
private static ImmutableMultimap<String, String> sJarsToFiles;
@@ -206,6 +207,7 @@
"Landroid/os/IVoldListener;",
"Landroid/os/IVoldMountCallback;",
"Landroid/os/IVoldTaskListener;",
+ "Landroid/os/TouchOcclusionMode;",
"Landroid/os/storage/CrateMetadata;",
"Landroid/view/LayerMetadataKey;",
"Lcom/android/internal/annotations/CompositeRWLock;",
@@ -795,6 +797,13 @@
.filter(file -> !file.contains("GmsCore"))
.filter(file -> !file.contains("com.google.android.gms"))
.collect(ImmutableList.toImmutableList());
+ final ImmutableSetMultimap.Builder<String, String> sharedLibsPathsToName =
+ ImmutableSetMultimap.builder();
+ sSharedLibs.forEach(sharedLibraryInfo -> {
+ sharedLibraryInfo.paths.forEach(path ->
+ sharedLibsPathsToName.putAll(path, sharedLibraryInfo.name));
+ });
+ sSharedLibsPathsToName = sharedLibsPathsToName.build();
final ImmutableSetMultimap.Builder<String, String> jarsToFiles =
ImmutableSetMultimap.builder();
@@ -1038,17 +1047,19 @@
// WARNING: Do not add more exceptions here, no androidx should be in bootclasspath.
// See go/androidx-api-guidelines#module-naming for more details.
final ImmutableMap<String, ImmutableSet<String>>
- LegacyExemptAndroidxSharedLibsJarToClasses =
+ LegacyExemptAndroidxSharedLibsNamesToClasses =
new ImmutableMap.Builder<String, ImmutableSet<String>>()
- .put("/vendor/framework/androidx.camera.extensions.impl.jar",
+ .put("androidx.camera.extensions.impl",
ImmutableSet.of("Landroidx/camera/extensions/impl/"))
- .put("/system_ext/framework/androidx.window.extensions.jar",
+ .put("androidx.window.extensions",
ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/extensions/",
"Landroidx/window/util/"))
- .put("/system_ext/framework/androidx.window.sidecar.jar",
+ .put("androidx.window.sidecar",
ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/sidecar",
"Landroidx/window/util"))
- .put("/vendor/framework/com.google.android.camera.experimental2020_midyear.jar",
+ .put("com.google.android.camera.experimental2019",
+ ImmutableSet.of("Landroidx/annotation"))
+ .put("com.google.android.camera.experimental2020_midyear",
ImmutableSet.of("Landroidx/annotation"))
.build();
assertWithMessage("There must not be any androidx classes on the "
@@ -1057,7 +1068,7 @@
.that(sJarsToClasses.entries().stream()
.filter(e -> e.getValue().startsWith("Landroidx/"))
.filter(e -> !isLegacyAndroidxDependency(
- LegacyExemptAndroidxSharedLibsJarToClasses, e.getKey(), e.getValue()))
+ LegacyExemptAndroidxSharedLibsNamesToClasses, e.getKey(), e.getValue()))
.collect(Collectors.toList())
).isEmpty();
}
@@ -1067,7 +1078,8 @@
* and shared library jars.
*/
@Test
- public void testNoKotlinFilesInClasspaths() {
+ public void testNoKotlinFilesInClasspaths() throws Exception {
+ assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT());
ImmutableList<String> kotlinFiles =
Stream.of(sBootclasspathJars.stream(),
sSystemserverclasspathJars.stream(),
@@ -1103,11 +1115,12 @@
}
private boolean isLegacyAndroidxDependency(
- ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsJarToClasses,
- String jar, String className) {
- return legacyExemptAndroidxSharedLibsJarToClasses.containsKey(jar)
- && legacyExemptAndroidxSharedLibsJarToClasses.get(jar).stream().anyMatch(
- v -> className.startsWith(v));
+ ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsNamesToClasses,
+ String path, String className) {
+ return sSharedLibsPathsToName.get(path).stream()
+ .filter(legacyExemptAndroidxSharedLibsNamesToClasses::containsKey)
+ .flatMap(name -> legacyExemptAndroidxSharedLibsNamesToClasses.get(name).stream())
+ .anyMatch(className::startsWith);
}
private String[] collectApkInApexPaths() {
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
index 4da5fff..9c8a81d 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java
@@ -25,6 +25,8 @@
import android.content.pm.PackageManager;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+
import java.util.ArrayList;
/**
@@ -41,6 +43,7 @@
private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp";
private static final String SIMPLE_APP_ACTIVITY =
"com.android.cts.launcherapps.simpleapp.SimpleActivityImmediateExit";
+ private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp";
public void testSetUserControlDisabledPackages() throws Exception {
ArrayList<String> protectedPackages = new ArrayList<>();
@@ -86,14 +89,15 @@
// Check if package is part of UserControlDisabledPackages before checking if
// package is stopped since it is a necessary condition to prevent stopping of
// package
-
assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho()))
.containsExactly(SIMPLE_APP_PKG);
- assertPackageRunningState(/* running= */ true);
+ assertPackageRunningState(/* running= */ true,
+ InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1"));
}
public void testFgsStopWithUserControlEnabled() throws Exception {
- assertPackageRunningState(/* running= */ false);
+ assertPackageRunningState(/* running= */ false,
+ InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1"));
assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())).isEmpty();
}
@@ -120,9 +124,15 @@
return pid.length() > 0;
}
- private void assertPackageRunningState(boolean shouldBeRunning) throws Exception {
+ private void assertPackageRunningState(boolean shouldBeRunning, String argPid)
+ throws Exception {
+ String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim();
+
+ final boolean samePid = pid.equals(argPid);
+ final boolean stillRunning = samePid && isPackageRunning(SIMPLE_APP_PKG);
+
assertWithMessage("Package %s running for user %s", SIMPLE_APP_PKG,
getCurrentUser().getIdentifier())
- .that(isPackageRunning(SIMPLE_APP_PKG)).isEqualTo(shouldBeRunning);
+ .that(stillRunning).isEqualTo(shouldBeRunning);
}
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index c87a7f8..d1773d0 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -70,6 +70,7 @@
private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/";
private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount";
+ private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp";
private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK =
"CtsHasLauncherActivityApp.apk";
@@ -1059,13 +1060,16 @@
*/
private void tryFgsStoppingProtectedPackage(int userId, boolean canUserStopPackage)
throws Exception {
+ String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim();
fgsStopPackageForUser(SIMPLE_APP_PKG, userId);
if (canUserStopPackage) {
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
- "testFgsStopWithUserControlEnabled");
+ "testFgsStopWithUserControlEnabled",
+ Collections.singletonMap(ARG_PID_BEFORE_STOP, pid));
} else {
executeDeviceTestMethod(".UserControlDisabledPackagesTest",
- "testFgsStopWithUserControlDisabled");
+ "testFgsStopWithUserControlDisabled",
+ Collections.singletonMap(ARG_PID_BEFORE_STOP, pid));
}
}
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
index 11f24fa..d6ac485 100755
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java
@@ -25,6 +25,7 @@
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
+import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
@@ -36,6 +37,41 @@
private static final String TEST_PKG = "com.android.cts.framestatstestapp";
/**
+ * Parse each line from output of dumpsys to handle special fields such as
+ * 'aaa,"bbb,ccc",ddd', to capture properly.
+ */
+ private static String[] parseCsv(String line) {
+ ArrayList<String> parts = new ArrayList<>();
+ String[] splitStrings = line.split(",", -1);
+ String s = "";
+ boolean escaping = false;
+ for (String splitString : splitStrings) {
+ if (escaping) {
+ s += "," + splitString;
+ } else {
+ if (splitString.startsWith("\"")) {
+ // Field start with ". Start escaping.
+ s = splitString;
+ escaping = true;
+ } else {
+ parts.add(splitString);
+ }
+ }
+ if (escaping && s.length() > 1 && s.endsWith("\"")) {
+ // Field end with ". Stop escaping.
+ parts.add(s.substring(1, s.length() - 1));
+ escaping = false;
+ }
+ }
+ if (escaping) {
+ // Unclosed escaping string. Add it anyway.
+ parts.add(s.substring(1));
+ }
+
+ return parts.toArray(new String[parts.size()]);
+ }
+
+ /**
* Tests the output of "dumpsys batterystats --checkin".
*
* @throws Exception
@@ -58,11 +94,7 @@
try {
- // With a default limit of 0, empty strings at the end are discarded.
- // We still consider the empty string as a valid value in some cases.
- // Using any negative number for the limit will preserve a trailing empty string.
- // @see String#split(String, int)
- String[] parts = line.split(",", -1);
+ String[] parts = parseCsv(line);
assertInteger(parts[0]); // old version
assertInteger(parts[1]); // UID
switch (parts[2]) { // aggregation type
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index 527e8ef..0e2a887 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -91,7 +91,7 @@
// Tag as a CTS artifact
test_suites: [
"general-tests",
- "mts",
+ "mts-mediaprovider",
"cts",
],
}
@@ -155,7 +155,7 @@
// Tag as a CTS artifact
test_suites: [
"general-tests",
- "mts",
+ "mts-mediaprovider",
"cts",
],
}
@@ -171,7 +171,7 @@
// Tag as a CTS artifact
test_suites: [
"general-tests",
- "mts",
+ "mts-mediaprovider",
"cts",
],
}
@@ -187,7 +187,7 @@
// Tag as a CTS artifact
test_suites: [
"general-tests",
- "mts",
+ "mts-mediaprovider",
"cts",
],
}
diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
index 099a0ab..4df785c 100644
--- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
+++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java
@@ -79,6 +79,7 @@
import static android.scopedstorage.cts.lib.TestUtils.isAppInstalled;
import static android.scopedstorage.cts.lib.TestUtils.listAs;
import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider;
+import static android.scopedstorage.cts.lib.TestUtils.queryAudioFile;
import static android.scopedstorage.cts.lib.TestUtils.queryFile;
import static android.scopedstorage.cts.lib.TestUtils.queryFileExcludingPending;
import static android.scopedstorage.cts.lib.TestUtils.queryImageFile;
@@ -1772,6 +1773,37 @@
}
/**
+ * Test that renaming file paths to an external directory such as Android/* and Android/* /*
+ * except Android/media/* /* is not allowed.
+ */
+ @Test
+ public void testRenameFileToAppSpecificDir() throws Exception {
+ final File testFile = new File(getExternalMediaDir(), IMAGE_FILE_NAME);
+ final File testFileNew = new File(getExternalMediaDir(), NONMEDIA_FILE_NAME);
+
+ try {
+ // Create a file in app's external media directory
+ if (!testFile.exists()) {
+ assertThat(testFile.createNewFile()).isTrue();
+ }
+
+ final String androidDirPath = getExternalStorageDir().getPath() + "/Android";
+
+ // Verify that we can't rename a file to Android/ or Android/data or
+ // Android/media directory
+ assertCantRenameFile(testFile, new File(androidDirPath, IMAGE_FILE_NAME));
+ assertCantRenameFile(testFile, new File(androidDirPath + "/data", IMAGE_FILE_NAME));
+ assertCantRenameFile(testFile, new File(androidDirPath + "/media", IMAGE_FILE_NAME));
+
+ // Verify that we can rename a file to app specific media directory.
+ assertCanRenameFile(testFile, testFileNew);
+ } finally {
+ testFile.delete();
+ testFileNew.delete();
+ }
+ }
+
+ /**
* Test that renaming directories is allowed and aligns to default directory restrictions.
*/
@Test
@@ -2992,6 +3024,45 @@
}
}
+ /**
+ * Test that renaming a file to {@link Environment#DIRECTORY_RINGTONES} sets
+ * {@link MediaStore.Audio.AudioColumns#IS_RINGTONE}
+ */
+
+ @Test
+ public void testRenameToRingtoneDirectory() throws Exception {
+ final File fileInDownloads = new File(getDownloadDir(), AUDIO_FILE_NAME);
+ final File fileInRingtones = new File(getRingtonesDir(), AUDIO_FILE_NAME);
+
+ try {
+ assertThat(fileInDownloads.createNewFile()).isTrue();
+ assertThat(MediaStore.scanFile(getContentResolver(), fileInDownloads)).isNotNull();
+
+ assertCanRenameFile(fileInDownloads, fileInRingtones);
+
+ try (Cursor c = queryAudioFile(fileInRingtones,
+ MediaStore.Audio.AudioColumns.IS_RINGTONE)) {
+ assertTrue(c.moveToFirst());
+ assertWithMessage("Expected " + MediaStore.Audio.AudioColumns.IS_RINGTONE
+ + " to be set after renaming to " + fileInRingtones)
+ .that(c.getInt(0)).isEqualTo(1);
+ }
+
+ assertCanRenameFile(fileInRingtones, fileInDownloads);
+
+ try (Cursor c = queryAudioFile(fileInDownloads,
+ MediaStore.Audio.AudioColumns.IS_RINGTONE)) {
+ assertTrue(c.moveToFirst());
+ assertWithMessage("Expected " + MediaStore.Audio.AudioColumns.IS_RINGTONE
+ + " to be unset after renaming to " + fileInDownloads)
+ .that(c.getInt(0)).isEqualTo(0);
+ }
+ } finally {
+ fileInDownloads.delete();
+ fileInRingtones.delete();
+ }
+ }
+
@Test
@SdkSuppress(minSdkVersion = 31, codeName = "S")
public void testTransformsDirFileOperations() throws Exception {
diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
index 0670639..2e694ed 100644
--- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
+++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java
@@ -751,6 +751,17 @@
}
/**
+ * Queries {@link ContentResolver} for an audio file and returns a {@link Cursor} with the given
+ * columns.
+ */
+ @NonNull
+ public static Cursor queryAudioFile(File file, String... projection) {
+ return queryFile(getContentResolver(),
+ MediaStore.Audio.Media.getContentUri(sStorageVolumeName), file,
+ /*includePending*/ true, projection);
+ }
+
+ /**
* Queries {@link ContentResolver} for a file and returns the corresponding mime type for its
* entry in the database.
*/
diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
index 573035c..dea2688 100644
--- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
+++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java
@@ -116,8 +116,6 @@
private File devicePcFile;
private File deviceSvcFile;
private File seappNeverAllowFile;
- private File libsepolwrap;
- private File libcpp;
private File copyLibcpp;
private File sepolicyTests;
@@ -907,29 +905,8 @@
return (os.startsWith("mac") || os.startsWith("darwin"));
}
- private void setupLibraries() throws Exception {
- // The host side binary tests are host OS specific. Use Linux
- // libraries on Linux and Mac libraries on Mac.
- CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild);
- if (isMac()) {
- libsepolwrap = buildHelper.getTestFile("libsepolwrap.dylib");
- libcpp = buildHelper.getTestFile("libc++.dylib");
- copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib");
- Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING);
- } else {
- libsepolwrap = buildHelper.getTestFile("libsepolwrap.so");
- libcpp = buildHelper.getTestFile("libc++.so");
- copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.so");
- Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING);
- }
- libsepolwrap.deleteOnExit();
- libcpp.deleteOnExit();
- copyLibcpp.deleteOnExit();
- }
-
private void assertSepolicyTests(String test, String testExecutable,
boolean includeVendorSepolicy) throws Exception {
- setupLibraries();
sepolicyTests = copyResourceToTempFile(testExecutable);
sepolicyTests.setExecutable(true);
@@ -951,12 +928,6 @@
}
ProcessBuilder pb = new ProcessBuilder(args);
- Map<String, String> env = pb.environment();
- if (isMac()) {
- env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
- } else {
- env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir"));
- }
pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
pb.redirectErrorStream(true);
Process p = pb.start();
@@ -1367,7 +1338,7 @@
@CddTest(requirement="9.7")
@Test
public void testDrmServerDomain() throws DeviceNotAvailableException {
- assertDomainN("u:r:drmserver:s0", "/system/bin/drmserver", "/system/bin/drmserver64");
+ assertDomainHasExecutable("u:r:drmserver:s0", "/system/bin/drmserver", "/system/bin/drmserver64");
}
/* Installd is always running */
diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp
index 30b6161..2524227 100644
--- a/hostsidetests/securitybulletin/Android.bp
+++ b/hostsidetests/securitybulletin/Android.bp
@@ -43,6 +43,7 @@
cc_defaults {
name: "cts_hostsidetests_securitybulletin_defaults",
+ auto_gen_config: false,
compile_multilib: "both",
multilib: {
lib32: {
diff --git a/hostsidetests/securitybulletin/res/cve_2021_39623.ogg b/hostsidetests/securitybulletin/res/cve_2021_39623.ogg
new file mode 100644
index 0000000..1992a17
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2021_39623.ogg
Binary files differ
diff --git a/hostsidetests/securitybulletin/res/cve_2022_22082.dsf b/hostsidetests/securitybulletin/res/cve_2022_22082.dsf
new file mode 100644
index 0000000..60d1a5a
--- /dev/null
+++ b/hostsidetests/securitybulletin/res/cve_2022_22082.dsf
Binary files differ
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp
new file mode 100644
index 0000000..50662fd
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+cc_test {
+ name: "CVE-2021-39623",
+ defaults: ["cts_hostsidetests_securitybulletin_defaults"],
+ srcs: [
+ "poc.cpp",
+ ],
+ header_libs: [
+ "libmediametrics_headers",
+ ],
+ shared_libs: [
+ "libstagefright",
+ "libdatasource",
+ "libutils",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp
new file mode 100644
index 0000000..d9e38ba
--- /dev/null
+++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2022 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 "../includes/common.h"
+#include <datasource/DataSourceFactory.h>
+#include <dlfcn.h>
+#include <gui/SurfaceComposerClient.h>
+#include <media/IMediaHTTPService.h>
+#include <media/stagefright/InterfaceUtils.h>
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/MediaExtractorFactory.h>
+#include <media/stagefright/SimpleDecodingSource.h>
+#include <sys/mman.h>
+
+typedef void *(*mmap_t)(void *, size_t, int, int, int, off_t);
+mmap_t real_mmap = nullptr;
+
+using namespace android;
+
+bool testInProgress = false;
+constexpr size_t kTargetBufferSize = 32768;
+struct sigaction new_action, old_action;
+void sigsegv_handler(int signum, siginfo_t *info, void *context) {
+ if (testInProgress && info->si_signo == SIGSEGV) {
+ (*old_action.sa_sigaction)(signum, info, context);
+ return;
+ }
+ exit(EXIT_FAILURE);
+}
+
+void *mmap(void *addr, size_t length, int prot, int flags, int fd,
+ off_t offset) {
+ real_mmap = (mmap_t)dlsym(RTLD_NEXT, "mmap");
+ if (!real_mmap) {
+ exit(EXIT_FAILURE);
+ }
+ if (length == kTargetBufferSize) {
+ char *tmp_ptr = (char *)real_mmap(addr, length + PAGE_SIZE, prot,
+ flags | MAP_ANONYMOUS, -1, offset);
+ mprotect(tmp_ptr + length, PAGE_SIZE, PROT_NONE);
+ return tmp_ptr;
+ }
+ return real_mmap(addr, length, prot, flags, fd, offset);
+}
+
+int main(int argc, char **argv) {
+ FAIL_CHECK(argc > 1);
+ sigemptyset(&new_action.sa_mask);
+ new_action.sa_flags = SA_SIGINFO;
+ new_action.sa_sigaction = sigsegv_handler;
+ sigaction(SIGSEGV, &new_action, &old_action);
+
+ sp<DataSource> dataSource = DataSourceFactory::getInstance()->CreateFromURI(
+ nullptr /* httpService */, argv[1]);
+ FAIL_CHECK(dataSource);
+
+ sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource);
+ FAIL_CHECK(extractor);
+
+ sp<MediaSource> mediaSource =
+ CreateMediaSourceFromIMediaSource(extractor->getTrack(0));
+ FAIL_CHECK(mediaSource);
+
+ sp<MediaSource> rawSource = SimpleDecodingSource::Create(
+ mediaSource, MediaCodecList::kPreferSoftwareCodecs, nullptr, nullptr,
+ false);
+ FAIL_CHECK(rawSource);
+
+ status_t err = rawSource->start();
+ FAIL_CHECK(err == OK);
+
+ MediaSource::ReadOptions options = {};
+ MediaBufferBase *buffer = nullptr;
+
+ testInProgress = true;
+ rawSource->read(&buffer, &options);
+ testInProgress = false;
+ if (buffer) {
+ buffer->release();
+ buffer = nullptr;
+ }
+ options.clearSeekTo();
+ options.setSeekTo(0);
+ rawSource->stop();
+ return EXIT_SUCCESS;
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
index 12287b0..95c90d4 100644
--- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -16,43 +16,39 @@
package android.security.cts;
-import android.platform.test.annotations.AppModeFull;
+import static org.junit.Assume.assumeNoException;
+
import android.platform.test.annotations.AsbSecurityTest;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
-import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
-import org.junit.Assert;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(DeviceJUnit4ClassRunner.class)
public class CVE_2021_0954 extends StsExtraBusinessLogicHostTestBase {
- private static final String TEST_PKG = "android.security.cts.cve_2021_0954";
- private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest";
- private static final String TEST_APP = "CVE-2021-0954.apk";
- private ITestDevice device;
+ private static final String TEST_PKG = "android.security.cts.CVE_2021_0954";
- @Before
- public void setUp() throws Exception {
- device = getDevice();
- uninstallPackage(device, TEST_PKG);
-
- /* Wake up the screen */
- AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
- AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
- AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
- }
-
- @AppModeFull
@AsbSecurityTest(cveBugId = 143559931)
@Test
public void testPocCVE_2021_0954() throws Exception {
- installPackage(TEST_APP);
- AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW",
- device);
- runDeviceTests(TEST_PKG, TEST_CLASS, "testVulnerableActivityPresence");
+ try {
+ ITestDevice device = getDevice();
+ uninstallPackage(device, TEST_PKG);
+
+ /* Wake up the screen */
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ installPackage("CVE-2021-0954.apk");
+ AdbUtils.runCommandLine(
+ "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", device);
+ runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence");
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
}
}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java
new file mode 100644
index 0000000..9ab3f08
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2022 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.security.cts;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.compatibility.common.util.CrashUtils;
+import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39623 extends SecurityTestCase {
+
+ /**
+ * b/194105348
+ * Vulnerability Behaviour: SIGSEGV in self
+ * Vulnerable Library: libstagefright (As per AOSP code)
+ * Vulnerable Function: doRead (As per AOSP code)
+ */
+ @AsbSecurityTest(cveBugId = 194105348)
+ @Test
+ public void testPocCVE_2021_39623() throws Exception {
+ String binaryName = "CVE-2021-39623";
+ AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice());
+ testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName)
+ .setBacktraceIncludes(new BacktraceFilterPattern("libstagefright",
+ "android::SimpleDecodingSource::doRead"));
+ String signals[] = {CrashUtils.SIGSEGV};
+ testConfig.config.setSignals(signals);
+ testConfig.inputFilesDestination = AdbUtils.TMP_PATH;
+ String inputFiles[] = {"cve_2021_39623.ogg"};
+ testConfig.inputFiles = Arrays.asList(inputFiles);
+ testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0];
+ AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java
new file mode 100644
index 0000000..e661b4f
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2022 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.security.cts;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2022_20353 extends StsExtraBusinessLogicHostTestBase {
+
+ @AsbSecurityTest(cveBugId = 221041256)
+ @Test
+ public void testPocCVE_2022_20353() {
+ try {
+ final String testPkg = "android.security.cts.CVE_2022_20353";
+ ITestDevice device = getDevice();
+
+ AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device);
+ AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device);
+
+ // to generate NOTICE.html if not already present
+ AdbUtils.runCommandLine("am start -a android.settings.LICENSE", device);
+
+ installPackage("CVE-2022-20353.apk");
+
+ runDeviceTests(testPkg, testPkg + ".DeviceTest", "testDefaultRingtonePreference");
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java
new file mode 100644
index 0000000..e71a1f3
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (C) 2022 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.security.cts;
+
+import static org.junit.Assume.*;
+
+import android.platform.test.annotations.AsbSecurityTest;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2022_22082 extends SecurityTestCase {
+
+ /**
+ * CVE-2022-22082
+ */
+ @AsbSecurityTest(cveBugId = 223211217)
+ @Test
+ public void testPocCVE_2022_22082() throws Exception {
+ /*
+ * Non StageFright test.
+ */
+ safeReboot();
+ AdbUtils.pushResource("/cve_2022_22082.dsf", "/sdcard/cve_2022_22082.dsf", getDevice());
+ AdbUtils.runCommandLine("logcat -c", getDevice());
+ AdbUtils.runCommandLine(
+ "am start -a android.intent.action.VIEW -t audio/dsf -d file:///sdcard/cve_2022_22082.dsf",
+ getDevice());
+ Thread.sleep(10000);
+ AdbUtils.assertNoCrashes(getDevice(), "media.extractor");
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml
index 7f819bc..ef3e6cd 100644
--- a/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml
@@ -17,6 +17,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android.security.cts.BUG_182282630"
android:targetSandboxVersion="2">
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<application
android:label="@string/app_name"
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp
index aa9f71f..59350cf 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -18,10 +18,10 @@
android_test_helper_app {
name: "CVE-2021-0954",
defaults: ["cts_support_defaults"],
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java"
+ ],
test_suites: [
- "cts",
- "vts10",
"sts",
],
static_libs: [
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml
index a7e0218..75299c4 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml
@@ -1,5 +1,5 @@
<!--
- Copyright 2021 The Android Open Source Project
+ Copyright 2022 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,25 +13,19 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
- package="android.security.cts.cve_2021_0954"
+ package="android.security.cts.CVE_2021_0954"
android:versionCode="1"
android:versionName="1.0">
-
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-
- <application
- android:allowBackup="true"
- android:label="CVE_2021_0954"
- android:supportsRtl="true">
- <uses-library android:name="android.test.runner" />
+ <application>
<service android:name=".PocService"
android:enabled="true"
- android:exported="false" />
+ android:exported="true" />
</application>
-
<instrumentation
android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="android.security.cts.cve_2021_0954" />
+ android:targetPackage="android.security.cts.CVE_2021_0954" />
</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml
new file mode 100644
index 0000000..363df00
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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>
+ <integer name="assumptionFailure">-1</integer>
+ <integer name="noAssumptionFailure">0</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml
new file mode 100644
index 0000000..7c4d959
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string>
+ <string name="cmdDumpsysActivity">dumpsys activity %1$s</string>
+ <string name="empty"></string>
+ <string name="overlayErrorMessage">Device is vulnerable to b/143559931 hence any app with
+ "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="messageKey">message</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="resultKey">result</string>
+ <string name="sharedPreferences">CVE_2021_0954_prefs</string>
+ <string name="timedOutPocActivity">Timed out waiting on a result from PocActivity</string>
+ <string name="vulClass">com.android.internal.app.ResolverActivity</string>
+ <string name="vulClassAuto">com.android.car.activityresolver.CarResolverActivity</string>
+ <string name="vulPkg">android</string>
+ <string name="vulPkgAuto">com.android.car.activityresolver</string>
+ <string name="vulActivityNotRunningError">The %1$s is not currently running on the device
+ </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java
index f986906..9a94ef9 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,17 +14,20 @@
* limitations under the License.
*/
-package android.security.cts.cve_2021_0954;
+package android.security.cts.CVE_2021_0954;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assume.assumeNoException;
-import android.content.ActivityNotFoundException;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.pm.PackageManager;
import android.provider.Settings;
import androidx.test.runner.AndroidJUnit4;
@@ -32,90 +35,107 @@
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.io.IOException;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
@RunWith(AndroidJUnit4.class)
public class DeviceTest {
- private static final String TEST_PKG = "android.security.cts.cve_2021_0954";
- private static final String TEST_VULNERABLE_PKG = "android";
- private static final String TEST_VULNERABLE_ACTIVITY =
- "com.android.internal.app.ResolverActivity";
- private static final int LAUNCH_TIMEOUT_MS = 20000;
- private static final String vulnerableActivityName = "ResolverActivity";
- private UiDevice mDevice;
- String activityDump = "";
+ private Context mContext = getApplicationContext();
+ private static final int TIMEOUT_MS = 10000;
- private void startOverlayService() {
- Context context = getApplicationContext();
- assertNotNull(context);
- Intent intent = new Intent(context, PocService.class);
- assertNotNull(intent);
-
- if (Settings.canDrawOverlays(getApplicationContext())) {
- context.startService(intent);
- } else {
- try {
- context.startService(intent);
- } catch (Exception e) {
- throw new RuntimeException("Unable to start the overlay service", e);
- }
- }
+ private boolean hasFeature(String feature) {
+ return mContext.getPackageManager().hasSystemFeature(feature);
}
- public void startVulnerableActivity() {
- Context context = getApplicationContext();
- Intent intent = new Intent();
- intent.setClassName(TEST_VULNERABLE_PKG, TEST_VULNERABLE_ACTIVITY);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- try {
- context.startActivity(intent);
- } catch (ActivityNotFoundException e) {
- assumeNoException("Activity not found on device", e);
- }
+ private boolean isAuto() {
+ return hasFeature(PackageManager.FEATURE_AUTOMOTIVE);
}
- @Before
- public void setUp() throws Exception {
- mDevice = UiDevice.getInstance(getInstrumentation());
+ String getStringRes(int key) {
+ return mContext.getResources().getString(key);
+ }
- /* Start the vulnerable activity */
- startVulnerableActivity();
- if (!mDevice.wait(Until.hasObject(By.res("android:id/contentPanel")
- .clazz("android.widget.ScrollView").pkg("android")), LAUNCH_TIMEOUT_MS)) {
- return;
- }
+ String getStringResWithArg(int key, String arg) {
+ return mContext.getResources().getString(key, arg);
+ }
- /* Start the overlay service */
- startOverlayService();
+ int getIntegerRes(int key) {
+ return mContext.getResources().getInteger(key);
}
@Test
- public void testVulnerableActivityPresence() {
- Pattern overlayTextPattern = Pattern.compile("OverlayButton", Pattern.CASE_INSENSITIVE);
- if (!mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)) {
- return;
- }
-
- /*
- * Check if the currently running activity is the vulnerable activity, if not abort the test
- */
+ public void testOverlayButtonPresence() {
try {
- activityDump = mDevice.executeShellCommand("dumpsys activity");
- } catch (IOException e) {
- throw new RuntimeException("Could not execute dumpsys activity command");
+ UiDevice device = UiDevice.getInstance(getInstrumentation());
+
+ /* Start the overlay service */
+ assumeTrue(getStringRes(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(mContext));
+ Intent intent = new Intent(mContext, PocService.class);
+ mContext.startService(intent);
+
+ /* Wait for a result from overlay service */
+ SharedPreferences sharedPrefs = mContext.getSharedPreferences(
+ getStringRes(R.string.sharedPreferences), Context.MODE_PRIVATE);
+ final Semaphore preferenceChanged = new Semaphore(0);
+ OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
+ if (key.equals(getStringRes(R.string.resultKey))) {
+ preferenceChanged.release();
+ }
+ }
+ };
+ sharedPrefs.registerOnSharedPreferenceChangeListener(listener);
+ assumeTrue(preferenceChanged.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS));
+ int result = sharedPrefs.getInt(getStringRes(R.string.resultKey),
+ getIntegerRes(R.integer.assumptionFailure));
+ String message = sharedPrefs.getString(getStringRes(R.string.messageKey),
+ getStringRes(R.string.defaultSemaphoreMsg));
+ assumeTrue(message, result != getIntegerRes(R.integer.assumptionFailure));
+
+ /* Wait for the UI of overlay window to appear */
+ Pattern overlayTextPattern = Pattern.compile(
+ mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE);
+ assumeTrue(mContext.getString(R.string.overlayUiScreenError),
+ device.wait(Until.hasObject(By.text(overlayTextPattern)), TIMEOUT_MS));
+
+ /* Start the vulnerable activity */
+ intent = new Intent();
+ String vulActivity = getStringRes(R.string.vulClass);
+ String vulPkg = getStringRes(R.string.vulPkg);
+ if (isAuto()) {
+ vulActivity = getStringRes(R.string.vulClassAuto);
+ vulPkg = getStringRes(R.string.vulPkgAuto);
+ }
+ intent.setClassName(vulPkg, vulActivity);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+
+ /* Wait until the object of overlay window is gone */
+ boolean overlayDisallowed =
+ device.wait(Until.gone(By.pkg(mContext.getPackageName())), TIMEOUT_MS);
+
+ /*
+ * Check if the currently running activity is the vulnerable activity, if not abort the
+ * test
+ */
+ String activityDump = device.executeShellCommand(
+ getStringResWithArg(R.string.cmdDumpsysActivity, vulActivity));
+ Pattern activityPattern = Pattern.compile(getStringRes(R.string.mResumedTrue));
+ assumeTrue(getStringRes(R.string.vulActivityNotRunningError),
+ activityPattern.matcher(activityDump).find());
+
+ /* Failing the test as fix is not present */
+ assertTrue(getStringResWithArg(R.string.overlayErrorMessage, vulActivity),
+ overlayDisallowed);
+ } catch (Exception e) {
+ assumeNoException(e);
}
- Pattern activityPattern =
- Pattern.compile("mResumedActivity.*" + vulnerableActivityName + ".*\n");
- if (!activityPattern.matcher(activityDump).find()) {
- return;
- }
- String message = "Device is vulnerable to b/143559931 hence any app with "
- + "SYSTEM_ALERT_WINDOW can overlay the ResolverActivity screen";
- assertNull(message, mDevice.findObject(By.text(overlayTextPattern)));
}
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java
index 82b78a2..79270ba 100644
--- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2021 The Android Open Source Project
+ * Copyright (C) 2022 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.
@@ -14,47 +14,65 @@
* limitations under the License.
*/
-package android.security.cts.cve_2021_0954;
+package android.security.cts.CVE_2021_0954;
import android.app.Service;
+import android.content.Context;
import android.content.Intent;
+import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.PixelFormat;
-import android.os.Handler;
import android.os.IBinder;
-import android.provider.Settings;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.Button;
public class PocService extends Service {
- public static Button mButton;
- private WindowManager mWindowManager;
- private WindowManager.LayoutParams mLayoutParams;
+ Button mButton;
+ WindowManager mWindowManager;
- private static int getScreenWidth() {
+ private int getScreenWidth() {
return Resources.getSystem().getDisplayMetrics().widthPixels;
}
- private static int getScreenHeight() {
+ private int getScreenHeight() {
return Resources.getSystem().getDisplayMetrics().heightPixels;
}
+ String getStringRes(int key) {
+ return getResources().getString(key);
+ }
+
+ int getIntegerRes(int key) {
+ return getResources().getInteger(key);
+ }
+
@Override
public void onCreate() {
- super.onCreate();
- mWindowManager = getSystemService(WindowManager.class);
- mLayoutParams = new WindowManager.LayoutParams();
- mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
- mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
- | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
- mLayoutParams.format = PixelFormat.OPAQUE;
- mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
- mLayoutParams.width = getScreenWidth();
- mLayoutParams.height = getScreenHeight();
- mLayoutParams.x = getScreenWidth() / 2;
- mLayoutParams.y = getScreenHeight() / 2;
+ try {
+ super.onCreate();
+ mWindowManager = getSystemService(WindowManager.class);
+ LayoutParams layoutParams = new LayoutParams();
+ layoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY;
+ layoutParams.flags =
+ LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;
+ layoutParams.format = PixelFormat.OPAQUE;
+ layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
+ layoutParams.width = getScreenWidth();
+ layoutParams.height = getScreenHeight();
+ layoutParams.x = getScreenWidth() / 2;
+ layoutParams.y = getScreenHeight() / 2;
+
+ /* Show the floating window */
+ mButton = new Button(this);
+ mButton.setText(getString(R.string.overlayButtonText));
+ mWindowManager.addView(mButton, layoutParams);
+ } catch (Exception e) {
+ sendTestResult(getIntegerRes(R.integer.assumptionFailure), e.getMessage());
+ return;
+ }
+ sendTestResult(getIntegerRes(R.integer.noAssumptionFailure), getStringRes(R.string.empty));
}
@Override
@@ -63,31 +81,27 @@
}
@Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- showFloatingWindow();
- return super.onStartCommand(intent, flags, startId);
- }
-
- @Override
public void onDestroy() {
- if (mWindowManager != null && mButton != null) {
- mWindowManager.removeView(mButton);
+ try {
+ if (mWindowManager != null && mButton != null) {
+ mWindowManager.removeView(mButton);
+ }
+ super.onDestroy();
+ } catch (Exception e) {
+ sendTestResult(getIntegerRes(R.integer.assumptionFailure), e.getMessage());
}
- super.onDestroy();
}
- private void showFloatingWindow() {
- if (Settings.canDrawOverlays(this)) {
- mButton = new Button(getApplicationContext());
- mButton.setText("OverlayButton");
- mWindowManager.addView(mButton, mLayoutParams);
- new Handler().postDelayed(new Runnable() {
- @Override
- public void run() {
- onDestroy();
- }
- }, 60000); // one minute
- mButton.setTag(mButton.getVisibility());
+ private void sendTestResult(int result, String message) {
+ try {
+ SharedPreferences sh = getSharedPreferences(getStringRes(R.string.sharedPreferences),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor edit = sh.edit();
+ edit.putInt(getStringRes(R.string.resultKey), result);
+ edit.putString(getStringRes(R.string.messageKey), message);
+ edit.commit();
+ } catch (Exception e) {
+ // ignore the exception
}
}
}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp
new file mode 100644
index 0000000..37d35eb
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+android_test_helper_app {
+ name: "CVE-2022-20353",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+ sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml
new file mode 100644
index 0000000..d4129ac
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.cts.CVE_2022_20353">
+ <application
+ android:label="@string/appName"
+ android:supportsRtl="true">
+ <activity
+ android:name=".PocActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.RINGTONE_PICKER" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2022_20353" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml
new file mode 100644
index 0000000..3207c29
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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>
+ <integer name="assumptionFailure">-1</integer>
+ <integer name="success">0</integer>
+ <integer name="timeoutMs">20000</integer>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml
new file mode 100644
index 0000000..27e87f6
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2022 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>
+ <string name="alwaysButtonId">android:id/button_always</string>
+ <string name="appName">CVE-2022-20353</string>
+ <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string>
+ <string name="failureMessage">
+ Device is vulnerable to b/221041256!! Privilege escalation possible in
+ com.android.settings.DefaultRingtonePreference
+ </string>
+ <string name="fileName">NOTICE.html</string>
+ <string name="getRingtoneCmd">settings get system ringtone</string>
+ <string name="messageKey">message</string>
+ <string name="noticeUri">
+ content://com.android.settings.files/my_cache/NOTICE.html
+ </string>
+ <string name="resType">string</string>
+ <string name="resultKey">result</string>
+ <string name="setRingtoneCmd">settings put system ringtone</string>
+ <string name="sharedPreferences">sharedPreferences</string>
+ <string name="textResId">ringtone_title</string>
+ <string name="uiObjectNotFoundMsg">Unable to find UiObject with %1$s text/id</string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java
new file mode 100644
index 0000000..af1f978
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2022 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.security.cts.CVE_2022_20353;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
+import android.content.res.Resources;
+import android.provider.Settings;
+
+import androidx.test.runner.AndroidJUnit4;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiScrollable;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ Resources mResources;
+ UiDevice mDevice;
+ Context mContext;
+
+ // Wait for UiObject to appear and click on the UiObject if it is visible
+ private boolean clickUiObject(BySelector selector) {
+ boolean objectFound =
+ mDevice.wait(Until.hasObject(selector), mResources.getInteger(R.integer.timeoutMs));
+ if (objectFound) {
+ mDevice.findObject(selector).click();
+ }
+ return objectFound;
+ }
+
+ @Test
+ public void testDefaultRingtonePreference() {
+ String defaultRingtone = null;
+ try {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ mContext = getInstrumentation().getContext();
+ mResources = mContext.getResources();
+ defaultRingtone =
+ mDevice.executeShellCommand(mContext.getString(R.string.getRingtoneCmd));
+
+ Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(intent);
+
+ String settingsPackageName =
+ intent.resolveActivity(mContext.getPackageManager()).getPackageName();
+ Context settingsContext = mContext.createPackageContext(settingsPackageName,
+ Context.CONTEXT_IGNORE_SECURITY);
+ Resources res = settingsContext.getPackageManager()
+ .getResourcesForApplication(settingsPackageName);
+ String text = settingsContext
+ .getString(res.getIdentifier(mContext.getString(R.string.textResId),
+ mContext.getString(R.string.resType), settingsPackageName));
+ // scroll until text 'Phone ringtone' is visible
+ UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true));
+ uiScrollable.scrollTextIntoView(text);
+ // click on 'Phone ringtone'
+ BySelector selector = By.text(text);
+ assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, text),
+ clickUiObject(selector));
+ // select CTS PoC app
+ text = mContext.getString(R.string.appName);
+ selector = By.text(text);
+ assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, text),
+ clickUiObject(selector));
+ // select 'Always'
+ String resId = mContext.getString(R.string.alwaysButtonId);
+ selector = By.res(resId);
+ assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, resId),
+ clickUiObject(selector));
+
+ SharedPreferences sharedPrefs = mContext.getSharedPreferences(
+ mContext.getString(R.string.sharedPreferences), Context.MODE_APPEND);
+ Semaphore preferenceChanged = new Semaphore(0);
+ OnSharedPreferenceChangeListener sharedPrefListener =
+ new OnSharedPreferenceChangeListener() {
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
+ String key) {
+ if (key.equals(mContext.getString(R.string.resultKey))) {
+ preferenceChanged.release();
+ }
+ }
+ };
+ sharedPrefs.registerOnSharedPreferenceChangeListener(sharedPrefListener);
+ // wait for PocActivity to complete
+ assumeTrue(preferenceChanged.tryAcquire(mResources.getInteger(R.integer.timeoutMs),
+ TimeUnit.MILLISECONDS));
+ int result = sharedPrefs.getInt(mContext.getString(R.string.resultKey),
+ mResources.getInteger(R.integer.assumptionFailure));
+ String message = sharedPrefs.getString(mContext.getString(R.string.messageKey),
+ mContext.getString(R.string.defaultSemaphoreMsg));
+ assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure));
+
+ String ringtoneUri = "";
+ boolean isVulnerable = false;
+ long startTime = System.currentTimeMillis();
+ while ((System.currentTimeMillis() - startTime) < mResources
+ .getInteger(R.integer.timeoutMs)) {
+ ringtoneUri =
+ mDevice.executeShellCommand(mContext.getString(R.string.getRingtoneCmd));
+ if (ringtoneUri.contains(mContext.getString(R.string.fileName))) {
+ isVulnerable = true;
+ break;
+ }
+ }
+ assertFalse(mContext.getString(R.string.failureMessage), isVulnerable);
+ } catch (Exception e) {
+ assumeNoException(e);
+ } finally {
+ try {
+ // reset ringtone to default (other than 'null') present before test
+ mDevice.executeShellCommand(
+ mContext.getString(R.string.setRingtoneCmd) + " " + defaultRingtone);
+ mDevice.pressHome();
+ } catch (Exception e) {
+ // ignore exception here
+ }
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java
new file mode 100644
index 0000000..977e647
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2022 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.security.cts.CVE_2022_20353;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.os.Bundle;
+
+/* PocActivity is required in this test since it is required that CTS PoC app is selected when */
+/* choosing an app for setting default ringtone. RingtonePicker appears due to actions done in */
+/* DeviceTest. */
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ try {
+ super.onCreate(savedInstanceState);
+ Intent intent = new Intent();
+ /* set NOTICE.html file uri as EXTRA_RINGTONE_PICKED_URI which sets NOTICE.html as */
+ /* default ringtone if vulnerability is present */
+ intent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI,
+ Uri.parse(getString(R.string.noticeUri)));
+ setResult(Activity.RESULT_OK, intent);
+ finish();
+ sendTestResult(getResources().getInteger(R.integer.success), "");
+ } catch (Exception e) {
+ sendTestResult(getResources().getInteger(R.integer.assumptionFailure), e.getMessage());
+ }
+ }
+
+ void sendTestResult(int result, String message) {
+ try {
+ SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences),
+ Context.MODE_PRIVATE);
+ SharedPreferences.Editor edit = sh.edit();
+ edit.putInt(getString(R.string.resultKey), result);
+ edit.putString(getString(R.string.messageKey), message);
+ edit.commit();
+ } catch (Exception e) {
+ // ignore exception here
+ }
+ }
+
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
index be665ba..7f29d44 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java
@@ -69,10 +69,6 @@
.addAllowedLogSource("AID_SYSTEM")
.addAllowedLogSource("AID_BLUETOOTH")
.addAllowedLogSource("com.android.bluetooth")
- // TODO(b/236681553): Remove this.
- .addAllowedLogSource("com.android.bluetooth.services")
- // TODO(b/236681553): Remove this.
- .addAllowedLogSource("com.google.android.bluetooth.services")
.addAllowedLogSource("AID_LMKD")
.addAllowedLogSource("AID_MEDIA")
.addAllowedLogSource("AID_RADIO")
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
index 7b7e9e03..4ff8d9e 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java
@@ -41,6 +41,7 @@
import org.junit.After;
import org.junit.AfterClass;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -73,6 +74,12 @@
public static void setUpBeforeClass() throws Exception {
sDocumentsUiPackageName = getDocumentsUiPackageName();
sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState();
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
clearPackageData(sDocumentsUiPackageName);
}
@@ -82,6 +89,37 @@
}
@Test
+ public void testMimeTypeFilter() throws Exception {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("audio/*");
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mDevice.waitForIdle();
+ // Should open documentsUi
+ assertThatShowsDocumentsUiButtons();
+
+ // We don't test the result of the picker here because the intention of the test is only to
+ // test that DocumentsUi is opened.
+ }
+
+ @Test
+ public void testExtraMimeTypeFilter() throws Exception {
+ final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.addCategory(Intent.CATEGORY_OPENABLE);
+ intent.setType("image/*");
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*", "audio/*"});
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ mDevice.waitForIdle();
+ // Should open documentsUi
+ assertThatShowsDocumentsUiButtons();
+
+ // We don't test the result of the picker here because the intention of the test is only to
+ // test that DocumentsUi is opened.
+ }
+
+ @Test
public void testBrowse_singleSelect() throws Exception {
final int itemCount = 1;
List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(itemCount,
@@ -135,6 +173,13 @@
}
}
+ private void assertThatShowsDocumentsUiButtons() {
+ // Assert that "Recent files" header for DocumentsUi shows
+ // Add a short timeout wait for DocumentsUi to show
+ assertThat(new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName
+ + ":id/header_title")).waitForExists(SHORT_TIMEOUT)).isTrue();
+ }
+
private UiObject findSaveButton() {
return new UiObject(new UiSelector().resourceId(
sDocumentsUiPackageName + ":id/container_save")
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
index bd17374..0f61dc7 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java
@@ -29,7 +29,10 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
+
import android.app.Activity;
+import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
@@ -136,4 +139,21 @@
assertPersistedGrant(uri, mContext.getContentResolver());
assertRedactedReadOnlyAccess(uri);
}
+
+ @Test
+ public void testMimeTypeFilter() throws Exception {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.setType("audio/*");
+ assertThrows(ActivityNotFoundException.class,
+ () -> mActivity.startActivityForResult(intent, REQUEST_CODE));
+ }
+
+ @Test
+ public void testExtraMimeTypeFilter() throws Exception {
+ final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES);
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"audio/*"});
+ mActivity.startActivityForResult(intent, REQUEST_CODE);
+ final GetResultActivity.Result res = mActivity.getResult();
+ assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED);
+ }
}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
index 3d0bcd3..440e472 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java
@@ -18,6 +18,7 @@
import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData;
import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName;
+import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertContainsMimeType;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant;
import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat;
@@ -578,6 +579,89 @@
}
}
+ @Test
+ public void testExtraMimeTypeFilter() throws Exception {
+ final int dngVideoCount = 2;
+ // Creates 2 videos with mime type: "video/dng"
+ mUriList.addAll(createDNGVideosAndGetUris(dngVideoCount, mContext.getUserId()));
+
+ final int mp4VideoCount = 3;
+ // Creates 3 videos with mime type: "video/mp4"
+ mUriList.addAll(createVideosAndGetUris(mp4VideoCount, mContext.getUserId()));
+
+ final int imageCount = 4;
+ // Creates 4 images with mime type: "image/dng"
+ mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId()));
+
+ Intent intent = new Intent(mAction);
+ addMultipleSelectionFlag(intent);
+
+ if (Intent.ACTION_GET_CONTENT.equals(intent.getAction())) {
+ intent.setType("*/*");
+ }
+ final String[] mimeTypes = new String[]{"video/dng", "image/dng"};
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
+ launchPhotoPickerForIntent(intent);
+
+ final int totalCount = dngVideoCount + imageCount;
+ final List<UiObject> itemList = findItemList(totalCount);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isAtLeast(totalCount);
+ for (int i = 0; i < itemCount; i++) {
+ clickAndWait(mDevice, itemList.get(i));
+ }
+
+ clickAndWait(mDevice, findAddButton());
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ assertWithMessage("Expected number of items returned to be: " + itemCount)
+ .that(clipData.getItemCount()).isEqualTo(itemCount);
+ for (int i = 0; i < itemCount; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertPersistedGrant(uri, mContext.getContentResolver());
+ assertRedactedReadOnlyAccess(uri);
+ assertContainsMimeType(uri, mimeTypes);
+ }
+ }
+
+ @Test
+ public void testMimeTypeFilterPriority() throws Exception {
+ final int videoCount = 2;
+ mUriList.addAll(createDNGVideosAndGetUris(videoCount, mContext.getUserId()));
+ final int imageCount = 1;
+ mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId()));
+
+ Intent intent = new Intent(mAction);
+ addMultipleSelectionFlag(intent);
+ // setType has lower priority than EXTRA_MIME_TYPES filters.
+ intent.setType("image/*");
+ final String mimeType = "video/dng";
+ intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {mimeType});
+ launchPhotoPickerForIntent(intent);
+
+ // find all items
+ final List<UiObject> itemList = findItemList(-1);
+ final int itemCount = itemList.size();
+ assertThat(itemCount).isAtLeast(videoCount);
+ for (int i = 0; i < itemCount; i++) {
+ clickAndWait(mDevice, itemList.get(i));
+ }
+
+ clickAndWait(mDevice, findAddButton());
+
+ final ClipData clipData = mActivity.getResult().data.getClipData();
+ assertWithMessage("Expected number of items returned to be: " + itemCount)
+ .that(clipData.getItemCount()).isEqualTo(itemCount);
+ for (int i = 0; i < itemCount; i++) {
+ final Uri uri = clipData.getItemAt(i).getUri();
+ assertPickerUriFormat(uri, mContext.getUserId());
+ assertPersistedGrant(uri, mContext.getContentResolver());
+ assertRedactedReadOnlyAccess(uri);
+ assertMimeType(uri, mimeType);
+ }
+ }
+
private void assertMuteButtonState(UiObject muteButton, boolean isMuted)
throws UiObjectNotFoundException {
// We use content description to assert the state of the mute button, there is no other way
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java
index 493f641..0ccdd3f 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java
@@ -67,6 +67,11 @@
public static void clearPackageData(String packageName) throws Exception {
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.executeShellCommand("pm clear " + packageName);
+
+ // We should ideally be listening to an effective measure to know if package data was
+ // cleared, like listening to a broadcasts or checking a value. But that information is
+ // very package private and not available.
+ Thread.sleep(500);
}
public static String getDocumentsUiPackageName() {
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
index d9749f2..6d86cee 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java
@@ -80,6 +80,12 @@
assertThat(resultMimeType).isEqualTo(expectedMimeType);
}
+ public static void assertContainsMimeType(Uri uri, String[] expectedMimeTypes) {
+ final Context context = InstrumentationRegistry.getTargetContext();
+ final String resultMimeType = context.getContentResolver().getType(uri);
+ assertThat(Arrays.asList(expectedMimeTypes).contains(resultMimeType)).isTrue();
+ }
+
public static void assertRedactedReadOnlyAccess(Uri uri) throws Exception {
assertThat(uri).isNotNull();
final String[] projection = new String[]{ PickerMediaColumns.MIME_TYPE };
diff --git a/tests/app/app/assets/picture_800_by_600.png b/tests/app/app/assets/picture_800_by_600.png
new file mode 100644
index 0000000..dc8f3d4
--- /dev/null
+++ b/tests/app/app/assets/picture_800_by_600.png
Binary files differ
diff --git a/tests/app/src/android/app/cts/NotificationTemplateTest.kt b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
index 6db8aa6..bef1319 100644
--- a/tests/app/src/android/app/cts/NotificationTemplateTest.kt
+++ b/tests/app/src/android/app/cts/NotificationTemplateTest.kt
@@ -32,6 +32,7 @@
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.test.filters.SmallTest
+import com.android.compatibility.common.util.CddTest;
import com.google.common.truth.Truth.assertThat
import org.junit.Assume
import kotlin.test.assertFailsWith
@@ -283,8 +284,9 @@
}
}
+ @CddTest(requirement = "3.8.3.1/C-2-1")
fun testPromoteBigPicture_withBigPictureUriIcon() {
- val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png")
+ val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png")
val pictureIcon = Icon.createWithContentUri(pictureUri)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
@@ -385,8 +387,9 @@
!!.sameAs(picture)).isTrue()
}
+ @CddTest(requirement = "3.8.3.1/C-2-1")
fun testBigPicture_withBigLargeIcon_withContentUri() {
- val iconUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png")
+ val iconUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png")
val icon = Icon.createWithContentUri(iconUri)
val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_media_play)
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
index 70b68b4..affdaa8 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java
@@ -676,6 +676,7 @@
.build())
.build();
mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get();
+
// Creates a large batch of Documents, since we have max document size in Framework which is
// 512KiB, we will create 1KiB * 4000 docs = 4MiB total size > 1MiB binder transaction limit
char[] chars = new char[1024]; // 1KiB
@@ -3851,4 +3852,104 @@
documents = convertSearchResultsToDocuments(searchResults);
assertThat(documents).containsExactly(inEmail1);
}
+
+ @Test
+ public void testSetSchemaWithIncompatibleNestedSchema() throws Exception {
+ // 1. Set the original schema. This should succeed without any problems.
+ AppSearchSchema originalNestedSchema =
+ new AppSearchSchema.Builder("TypeA")
+ .addProperty(
+ new StringPropertyConfig.Builder("prop1")
+ .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL)
+ .build())
+ .build();
+ SetSchemaRequest originalRequest =
+ new SetSchemaRequest.Builder().addSchemas(originalNestedSchema).build();
+ mDb1.setSchemaAsync(originalRequest).get();
+
+ // 2. Set a new schema with a new type that refers to "TypeA" and an incompatible change to
+ // "TypeA". This should fail.
+ AppSearchSchema newNestedSchema =
+ new AppSearchSchema.Builder("TypeA")
+ .addProperty(
+ new StringPropertyConfig.Builder("prop1")
+ .setCardinality(PropertyConfig.CARDINALITY_REQUIRED)
+ .build())
+ .build();
+ AppSearchSchema newSchema =
+ new AppSearchSchema.Builder("TypeB")
+ .addProperty(
+ new AppSearchSchema.DocumentPropertyConfig.Builder("prop2", "TypeA")
+ .build())
+ .build();
+ final SetSchemaRequest newRequest =
+ new SetSchemaRequest.Builder().addSchemas(newNestedSchema, newSchema).build();
+ Throwable throwable =
+ assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(newRequest).get())
+ .getCause();
+ assertThat(throwable).isInstanceOf(AppSearchException.class);
+ AppSearchException exception = (AppSearchException) throwable;
+ assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_SCHEMA);
+ assertThat(exception).hasMessageThat().contains("Schema is incompatible.");
+ assertThat(exception).hasMessageThat().contains("Incompatible types: {TypeA}");
+
+ // 3. Now set that same set of schemas but with forceOverride=true. This should succeed.
+ SetSchemaRequest newRequestForced =
+ new SetSchemaRequest.Builder()
+ .addSchemas(newNestedSchema, newSchema)
+ .setForceOverride(true)
+ .build();
+ mDb1.setSchemaAsync(newRequestForced).get();
+ }
+
+ @Test
+ public void testEmojiSnippet() throws Exception {
+ // Schema registration
+ mDb1.setSchemaAsync(
+ new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build())
+ .get();
+
+ // String: "Luca Brasi sleeps with the 🐟🐟🐟."
+ // ^ ^ ^ ^ ^ ^ ^ ^ ^
+ // UTF8 idx: 0 5 11 18 23 27 3135 39
+ // UTF16 idx: 0 5 11 18 23 27 2931 33
+ // Breaks into segments: "Luca", "Brasi", "sleeps", "with", "the", "🐟", "🐟"
+ // and "🐟".
+ // Index a document to instance 1.
+ String sicilianMessage = "Luca Brasi sleeps with the 🐟🐟🐟.";
+ AppSearchEmail inEmail1 =
+ new AppSearchEmail.Builder("namespace", "uri1").setBody(sicilianMessage).build();
+ checkIsBatchResultSuccess(
+ mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build()));
+
+ AppSearchEmail inEmail2 =
+ new AppSearchEmail.Builder("namespace", "uri2")
+ .setBody("Some other content.")
+ .build();
+ checkIsBatchResultSuccess(
+ mDb1.putAsync(
+ new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build()));
+
+ // Query for "🐟"
+ SearchResultsShim searchResults =
+ mDb1.search(
+ "🐟",
+ new SearchSpec.Builder()
+ .setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
+ .setSnippetCount(1)
+ .setSnippetCountPerProperty(1)
+ .build());
+ List<SearchResult> page = searchResults.getNextPageAsync().get();
+ assertThat(page).hasSize(1);
+ assertThat(page.get(0).getGenericDocument()).isEqualTo(inEmail1);
+ List<SearchResult.MatchInfo> matches = page.get(0).getMatchInfos();
+ assertThat(matches).hasSize(1);
+ assertThat(matches.get(0).getPropertyPath()).isEqualTo("body");
+ assertThat(matches.get(0).getFullText()).isEqualTo(sicilianMessage);
+ assertThat(matches.get(0).getExactMatch()).isEqualTo("🐟");
+ if (mDb1.getFeatures().isFeatureSupported(Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)) {
+ assertThat(matches.get(0).getSubmatch()).isEqualTo("🐟");
+ }
+ }
}
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
index 5dd2eac..0bfa693 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java
@@ -576,6 +576,30 @@
}
@Test
+ public void testNestedProperties_buildBlankPaths() {
+ Exception e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ new GenericDocument.Builder<>("namespace", "id1", "schema1")
+ .setPropertyString("", "foo"));
+ assertThat(e.getMessage()).isEqualTo("Property name cannot be blank.");
+
+ e =
+ assertThrows(
+ IllegalArgumentException.class,
+ () ->
+ new GenericDocument.Builder<>("namespace", "id1", "schema1")
+ .setPropertyDocument(
+ "propDoc",
+ new GenericDocument.Builder<>(
+ "namespace", "id2", "schema1")
+ .setPropertyString("", "Bat", "Hawk")
+ .build()));
+ assertThat(e.getMessage()).isEqualTo("Property name cannot be blank.");
+ }
+
+ @Test
public void testNestedProperties_invalidPaths() {
GenericDocument doc =
new GenericDocument.Builder<>("namespace", "id1", "schema1")
@@ -592,25 +616,28 @@
.build())
.build();
- // Some paths are invalid because they don't apply to the given document --- these should
+ // These paths are invalid because they don't apply to the given document --- these should
// return null. It's not the querier's fault.
assertThat(doc.getPropertyStringArray("propString.propInts")).isNull();
assertThat(doc.getPropertyStringArray("propDocs.propFoo")).isNull();
assertThat(doc.getPropertyStringArray("propDocs.propNestedString.propFoo")).isNull();
+ }
- // Some paths are invalid because they are malformed. These throw an exception --- the
- // querier shouldn't provide such paths.
- assertThrows(
- IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[0"));
- assertThrows(
- IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[0.]"));
- assertThrows(
- IllegalArgumentException.class,
- () -> doc.getPropertyStringArray("propString[banana]"));
- assertThrows(
- IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[-1]"));
- assertThrows(
- IllegalArgumentException.class, () -> doc.getPropertyStringArray("propDocs[0]cat"));
+ @Test
+ public void testNestedProperties_arrayTypesInvalidPath() {
+ GenericDocument doc = new GenericDocument.Builder<>("namespace", "id1", "schema1").build();
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyString("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDocument("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBoolean("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDouble("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyLong("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBytes("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyStringArray("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDocumentArray("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBooleanArray("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDoubleArray("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyLongArray("."));
+ assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBytesArray("."));
}
@Test
diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
index 71822ca..47337ca 100644
--- a/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
+++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java
@@ -1641,6 +1641,11 @@
@Test
public void testAddObserver_schemaChange_added() throws Exception {
+ assumeTrue(
+ mDb1.getFeatures()
+ .isFeatureSupported(
+ Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK));
+
// Register an observer
TestObserverCallback observer = new TestObserverCallback();
mGlobalSearchSession.registerObserverCallback(
@@ -1688,6 +1693,11 @@
@Test
public void testAddObserver_schemaChange_removed() throws Exception {
+ assumeTrue(
+ mDb1.getFeatures()
+ .isFeatureSupported(
+ Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK));
+
// Add a schema type
mDb1.setSchemaAsync(
new SetSchemaRequest.Builder()
@@ -1723,6 +1733,11 @@
@Test
public void testAddObserver_schemaChange_contents() throws Exception {
+ assumeTrue(
+ mDb1.getFeatures()
+ .isFeatureSupported(
+ Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK));
+
// Add a schema
mDb1.setSchemaAsync(
new SetSchemaRequest.Builder()
@@ -1794,6 +1809,11 @@
@Test
public void testAddObserver_schemaChange_contents_skipBySpec() throws Exception {
+ assumeTrue(
+ mDb1.getFeatures()
+ .isFeatureSupported(
+ Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK));
+
// Add a schema
mDb1.setSchemaAsync(
new SetSchemaRequest.Builder()
@@ -1862,6 +1882,11 @@
@Test
public void testRegisterObserver_schemaMigration() throws Exception {
+ assumeTrue(
+ mDb1.getFeatures()
+ .isFeatureSupported(
+ Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK));
+
// Add a schema with two types
mDb1.setSchemaAsync(
new SetSchemaRequest.Builder()
diff --git a/tests/camera/Android.bp b/tests/camera/Android.bp
index aae58c6..c2334fc 100644
--- a/tests/camera/Android.bp
+++ b/tests/camera/Android.bp
@@ -65,6 +65,7 @@
"truth-prebuilt",
"androidx.heifwriter_heifwriter",
"androidx.test.rules",
+ "MediaPerformanceClassCommon",
],
jni_libs: [
"libctscamera2_jni",
diff --git a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
index feb5567..772e7a5 100644
--- a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
+++ b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java
@@ -36,6 +36,7 @@
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
import android.media.ImageReader;
+import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.test.AndroidTestCase;
@@ -210,8 +211,9 @@
* Version.MEDIA_PERFORMANCE_CLASS
*/
public void testSPerfClassJpegSizes() throws Exception {
- boolean isSPerfClass = CameraTestUtils.isSPerfClass();
- if (!isSPerfClass) {
+ final boolean isAtLeastSPerfClass =
+ (Build.VERSION.MEDIA_PERFORMANCE_CLASS >= Build.VERSION_CODES.S);
+ if (!isAtLeastSPerfClass) {
return;
}
diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
index 6af7758..c9d0314 100644
--- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java
@@ -55,6 +55,10 @@
import android.hardware.camera2.params.RecommendedStreamConfigurationMap;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.cts.helpers.CameraUtils;
+import android.mediapc.cts.common.Requirement;
+import android.mediapc.cts.common.RequiredMeasurement;
+import android.mediapc.cts.common.RequirementConstants;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
import android.media.CamcorderProfile;
import android.media.ImageReader;
import android.os.Build;
@@ -74,15 +78,11 @@
import androidx.test.rule.ActivityTestRule;
-import androidx.test.InstrumentationRegistry;
-
import com.android.compatibility.common.util.CddTest;
-import com.android.compatibility.common.util.DeviceReportLog;
-import com.android.compatibility.common.util.ResultType;
-import com.android.compatibility.common.util.ResultUnit;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -92,12 +92,10 @@
import java.util.List;
import java.util.Objects;
import java.util.Set;
+import java.util.function.BiPredicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import static android.hardware.camera2.cts.CameraTestUtils.MPC_REPORT_LOG_NAME;
-import static android.hardware.camera2.cts.CameraTestUtils.MPC_STREAM_NAME;
-
/**
* Extended tests for static camera characteristics.
*/
@@ -113,6 +111,9 @@
*/
private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5
+ @Rule
+ public final TestName mTestName = new TestName();
+
private List<CameraCharacteristics> mCharacteristics;
private static final Size FULLHD = new Size(1920, 1080);
@@ -131,10 +132,6 @@
private static final long PREVIEW_RUN_MS = 500;
private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30;
- private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000;
- private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000;
- private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000;
-
private static final long MIN_UHR_SENSOR_RESOLUTION = 24000000;
/*
* HW Levels short hand
@@ -2857,28 +2854,70 @@
}
/**
- * Update performance class level based on condition
- *
- * @param condition whether the condition is met for passLevel
- * @param passLevel the highest performance class level when condition is true
- * @param failLevel the performance class when condition is false
+ * Camera hardware level requirement for Media Performance Class
*/
- private int updatePerfClassLevel(boolean condition, int passLevel, int failLevel) {
- return condition ? passLevel : failLevel;
- }
+ public static class PrimaryCameraHwLevelReq extends Requirement {
+ private static final String TAG = PrimaryCameraHwLevelReq.class.getSimpleName();
- /**
- * Update perf class level based on meetSPerfClass and meetRPerfClass.
- */
- private int updatePerfClassLevelRS(boolean meetSPerfClass, boolean meetRPerfClass,
- int perfClassLevel) {
- if (!meetRPerfClass) {
- return CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- } else if (!meetSPerfClass &&
- perfClassLevel > CameraTestUtils.PERFORMANCE_CLASS_R) {
- return Math.min(CameraTestUtils.PERFORMANCE_CLASS_R, perfClassLevel);
+ /**
+ * Creates a >= predicate for camera hardware level
+ */
+ private static BiPredicate<Integer, Integer> camHwLevelGte() {
+ return new BiPredicate<Integer, Integer>() {
+ @Override
+ public boolean test(Integer actual, Integer expected) {
+ return StaticMetadata.hardwareLevelPredicate(actual, expected);
+ }
+
+ @Override
+ public String toString() {
+ return "Camera Hardware Level Greater than or equal to";
+ }
+ };
}
- return perfClassLevel;
+ private static final BiPredicate<Integer, Integer> CAM_HW_LEVEL_GTE = camHwLevelGte();
+ private PrimaryCameraHwLevelReq(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setPrimaryRearCameraHwlLevel(Integer hwLevel) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_HWL_LEVEL, hwLevel);
+ }
+
+ public void setPrimaryFrontCameraHwlLevel(Integer hwLevel) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_HWL_LEVEL, hwLevel);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-3] MUST support android.info.supportedHardwareLevel property as FULL or
+ * better for back primary and LIMITED or better for front primary camera.
+ */
+ public static PrimaryCameraHwLevelReq createPrimaryCameraHwLevelReq() {
+ RequiredMeasurement<Integer> rearCameraHwlLevel = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.REAR_CAMERA_HWL_LEVEL)
+ .setPredicate(CAM_HW_LEVEL_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+ .addRequiredValue(Build.VERSION_CODES.S,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+ .build();
+ RequiredMeasurement<Integer> frontCameraHwlLevel = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_HWL_LEVEL)
+ .setPredicate(CAM_HW_LEVEL_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED)
+ .addRequiredValue(Build.VERSION_CODES.S,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+ CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL)
+ .build();
+ return new PrimaryCameraHwLevelReq(RequirementConstants.R7_5__H_1_3,
+ rearCameraHwlLevel, frontCameraHwlLevel);
+ }
}
/**
@@ -2886,34 +2925,47 @@
* in CDD camera section 7.5
*/
@Test
- @CddTest(requirement = "7.5/H-1-1,H-1-2,H-1-3,H-1-4,H-1-8,H-1-9,H-1-10,H-1-11,H-1-12,H-1-13,H-1-14")
+ @CddTest(requirements = {
+ "2.2.7.2/7.5/H-1-1",
+ "2.2.7.2/7.5/H-1-2",
+ "2.2.7.2/7.5/H-1-3",
+ "2.2.7.2/7.5/H-1-4",
+ "2.2.7.2/7.5/H-1-8",
+ "2.2.7.2/7.5/H-1-9",
+ "2.2.7.2/7.5/H-1-10",
+ "2.2.7.2/7.5/H-1-11",
+ "2.2.7.2/7.5/H-1-12",
+ "2.2.7.2/7.5/H-1-13",
+ "2.2.7.2/7.5/H-1-14"})
public void testCameraPerfClassCharacteristics() throws Exception {
if (mAdoptShellPerm) {
// Skip test for system camera. Performance class is only applicable for public camera
// ids.
return;
}
- boolean assertRPerfClass = CameraTestUtils.isRPerfClass();
- boolean assertSPerfClass = CameraTestUtils.isSPerfClass();
- boolean assertTPerfClass = CameraTestUtils.isTPerfClass();
- boolean assertPerfClass = (assertRPerfClass || assertSPerfClass || assertTPerfClass);
-
- // R & S Performance Class
- int perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH13 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH14 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH18 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-
- // T Performance Class
- int perfClassLevelH19 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH110 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH111 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH112 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH113 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
- int perfClassLevelH114 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT;
-
- DeviceReportLog reportLog = new DeviceReportLog(MPC_REPORT_LOG_NAME, MPC_STREAM_NAME);
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.PrimaryCameraRequirement primaryRearReq =
+ pce.addPrimaryRearCameraReq();
+ PerformanceClassEvaluator.PrimaryCameraRequirement primaryFrontReq =
+ pce.addPrimaryFrontCameraReq();
+ PrimaryCameraHwLevelReq hwLevelReq = pce.addRequirement(
+ PrimaryCameraHwLevelReq.createPrimaryCameraHwLevelReq());
+ PerformanceClassEvaluator.CameraTimestampSourceRequirement timestampSourceReq =
+ pce.addR7_5__H_1_4();
+ PerformanceClassEvaluator.CameraRawRequirement rearRawReq =
+ pce.addR7_5__H_1_8();
+ PerformanceClassEvaluator.Camera240FpsRequirement hfrReq =
+ pce.addR7_5__H_1_9();
+ PerformanceClassEvaluator.UltraWideZoomRatioRequirement ultrawideZoomRatioReq =
+ pce.addR7_5__H_1_10();
+ PerformanceClassEvaluator.ConcurrentRearFrontRequirement concurrentRearFrontReq =
+ pce.addR7_5__H_1_11();
+ PerformanceClassEvaluator.PreviewStabilizationRequirement previewStabilizationReq =
+ pce.addR7_5__H_1_12();
+ PerformanceClassEvaluator.LogicalMultiCameraRequirement logicalMultiCameraReq =
+ pce.addR7_5__H_1_13();
+ PerformanceClassEvaluator.StreamUseCaseRequirement streamUseCaseReq =
+ pce.addR7_5__H_1_14();
String primaryRearId = null;
String primaryFrontId = null;
@@ -2940,42 +2992,29 @@
List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(cameraId,
mCameraManager, null /*bound*/);
+ Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
if (isPrimaryRear) {
primaryRearId = cameraId;
- if (sensorResolution < MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) {
- mCollector.expectTrue("Primary rear camera resolution should be at least " +
- MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION + " pixels, is "+
- sensorResolution, !assertPerfClass);
- perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- }
- reportLog.addValue("rear camera resolution", sensorResolution,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ primaryRearReq.setPrimaryCameraSupported(true);
+ primaryRearReq.setResolution(sensorResolution);
+ hwLevelReq.setPrimaryRearCameraHwlLevel(staticInfo.getHardwareLevelChecked());
+ timestampSourceReq.setRearCameraTimestampSource(timestampSource);
// 4K @ 30fps
boolean supportUHD = videoSizes.contains(UHD);
boolean supportDC4K = videoSizes.contains(DC4K);
- reportLog.addValue("rear camera 4k support", supportUHD | supportDC4K,
- ResultType.NEUTRAL, ResultUnit.NONE);
- if (!supportUHD && !supportDC4K) {
- mCollector.expectTrue("Primary rear camera should support 4k video recording",
- !assertPerfClass);
- perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- } else {
+ boolean support4K = (supportUHD || supportDC4K);
+ primaryRearReq.setVideoSizeReqSatisfied(support4K);
+ if (support4K) {
long minFrameDuration = config.getOutputMinFrameDuration(
android.media.MediaRecorder.class, supportDC4K ? DC4K : UHD);
- reportLog.addValue("rear camera 4k frame duration", minFrameDuration,
- ResultType.NEUTRAL, ResultUnit.NONE);
- if (minFrameDuration >= (1e9 / 29.9)) {
- mCollector.expectTrue("Primary rear camera should support 4k video @ 30fps",
- !assertPerfClass);
- perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- }
+ primaryRearReq.setVideoFps(1e9 / minFrameDuration);
+ } else {
+ primaryRearReq.setVideoFps(-1);
}
// H-1-9
boolean supportHighSpeed = staticInfo.isCapabilitySupported(CONSTRAINED_HIGH_SPEED);
- mCollector.expectTrue("Primary rear camera should support high speed recording",
- !assertTPerfClass || supportHighSpeed);
boolean support240Fps = false;
if (supportHighSpeed) {
Size[] availableHighSpeedSizes = config.getHighSpeedVideoSizes();
@@ -2995,101 +3034,31 @@
break;
}
}
- mCollector.expectTrue("Primary rear camera should support HD or FULLHD @ 240",
- !assertTPerfClass || support240Fps);
}
- perfClassLevelH19 = updatePerfClassLevel(support240Fps,
- perfClassLevelH19, CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue("rear camera 720p/1080p @ 240fps support", support240Fps,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ hfrReq.setRear240FpsSupported(support240Fps);
} else {
primaryFrontId = cameraId;
- if (sensorResolution < MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION) {
- mCollector.expectTrue("Primary front camera resolution should be at least "
- + MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is "
- + sensorResolution, !(assertSPerfClass || assertTPerfClass));
- perfClassLevelH12 = Math.min(
- perfClassLevelH12, CameraTestUtils.PERFORMANCE_CLASS_R);
- }
- if (sensorResolution < MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION) {
- mCollector.expectTrue("Primary front camera resolution should be at least " +
- MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is "+
- sensorResolution, !assertRPerfClass);
- perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- }
- reportLog.addValue("front camera resolution", sensorResolution,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ primaryFrontReq.setPrimaryCameraSupported(true);
+ primaryFrontReq.setResolution(sensorResolution);
+ hwLevelReq.setPrimaryFrontCameraHwlLevel(staticInfo.getHardwareLevelChecked());
+ timestampSourceReq.setFrontCameraTimestampSource(timestampSource);
// 1080P @ 30fps
boolean supportFULLHD = videoSizes.contains(FULLHD);
- reportLog.addValue("front camera 1080p support", supportFULLHD,
- ResultType.NEUTRAL, ResultUnit.NONE);
- if (!supportFULLHD) {
- mCollector.expectTrue(
- "Primary front camera should support 1080P video recording",
- !assertPerfClass);
- perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- } else {
+ primaryFrontReq.setVideoSizeReqSatisfied(supportFULLHD);
+ if (supportFULLHD) {
long minFrameDuration = config.getOutputMinFrameDuration(
android.media.MediaRecorder.class, FULLHD);
- if (minFrameDuration >= (1e9 / 29.9)) {
- mCollector.expectTrue(
- "Primary front camera should support 1080P video @ 30fps",
- !assertPerfClass);
- perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- }
- reportLog.addValue("front camera 1080p frame duration", minFrameDuration,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ primaryFrontReq.setVideoFps(1e9 / minFrameDuration);
+ } else {
+ primaryFrontReq.setVideoFps(-1);
}
}
- String facingString = isPrimaryRear ? "rear" : "front";
- // H-1-3
- if (assertTPerfClass || assertSPerfClass || (assertRPerfClass && isPrimaryRear)) {
- mCollector.expectTrue("Primary " + facingString +
- " camera should be at least FULL, but is " +
- toStringHardwareLevel(staticInfo.getHardwareLevelChecked()),
- staticInfo.isHardwareLevelAtLeastFull());
- } else if (assertRPerfClass) {
- mCollector.expectTrue("Primary " + facingString +
- " camera should be at least LIMITED, but is " +
- toStringHardwareLevel(staticInfo.getHardwareLevelChecked()),
- staticInfo.isHardwareLevelAtLeastLimited());
- }
-
- reportLog.addValue(facingString + " camera hardware level",
- staticInfo.getHardwareLevelChecked(), ResultType.NEUTRAL, ResultUnit.NONE);
- if (isPrimaryRear) {
- perfClassLevelH13 = updatePerfClassLevel(staticInfo.isHardwareLevelAtLeastFull(),
- perfClassLevelH13, CameraTestUtils.PERFORMANCE_CLASS_NOT_MET);
- } else {
- perfClassLevelH13 = updatePerfClassLevelRS(staticInfo.isHardwareLevelAtLeastFull(),
- staticInfo.isHardwareLevelAtLeastLimited(), perfClassLevelH13);
- }
-
- // H-1-4
- Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE);
- reportLog.addValue(facingString + " timestampSource",
- timestampSource, ResultType.NEUTRAL, ResultUnit.NONE);
- boolean realtimeTimestamp = (timestampSource != null &&
- timestampSource.equals(CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME));
- mCollector.expectTrue(
- "Primary " + facingString + " camera should support real-time timestamp source",
- !assertPerfClass || realtimeTimestamp);
- perfClassLevelH14 = updatePerfClassLevel(realtimeTimestamp, perfClassLevelH14,
- CameraTestUtils.PERFORMANCE_CLASS_NOT_MET);
-
// H-1-8
if (isPrimaryRear) {
boolean supportRaw = staticInfo.isCapabilitySupported(RAW);
- reportLog.addValue(facingString + " camera raw support",
- supportRaw, ResultType.NEUTRAL, ResultUnit.NONE);
- if (assertSPerfClass || assertTPerfClass) {
- mCollector.expectTrue("Primary rear camera should support RAW capability",
- supportRaw);
- }
- perfClassLevelH18 = updatePerfClassLevel(supportRaw, perfClassLevelH18,
- CameraTestUtils.PERFORMANCE_CLASS_R);
+ rearRawReq.setRearRawSupported(supportRaw);
}
// H-1-10
@@ -3098,96 +3067,76 @@
Range<Float> zoomRatioRange = staticInfo.getZoomRatioRangeChecked();
boolean meetH110 = (primaryToMaxFovRatio >= 1.0f - FOV_THRESHOLD)
|| (zoomRatioRange.getLower() < 1.0f - FOV_THRESHOLD);
- mCollector.expectTrue("Primary " + facingString + " camera must support zoomRatio < "
- + "1.0f if there is an ultrawide lens with the same facing",
- !assertTPerfClass || meetH110);
- perfClassLevelH110 = updatePerfClassLevel(meetH110, perfClassLevelH110,
- CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue(facingString + " camera supports maximum FOV using zoom ratio",
- meetH110, ResultType.NEUTRAL, ResultUnit.NONE);
+ if (isPrimaryRear) {
+ ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(meetH110);
+ } else {
+ ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(meetH110);
+ }
// H-1-12
- boolean meetH112 = staticInfo.isPreviewStabilizationSupported();
- mCollector.expectTrue("Primary " + facingString + " camera must support preview "
- + "stabilization", !assertTPerfClass || meetH112);
- perfClassLevelH112 = updatePerfClassLevel(meetH112, perfClassLevelH112,
- CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue(facingString + " camera preview stabilization", meetH112,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ boolean previewStab = staticInfo.isPreviewStabilizationSupported();
+ if (isPrimaryRear) {
+ previewStabilizationReq.setRearPreviewStabilizationSupported(previewStab);
+ } else {
+ previewStabilizationReq.setFrontPreviewStabilizationSupported(previewStab);
+ }
// H-1-13
int facing = staticInfo.getLensFacingChecked();
int numOfPhysicalRgbCameras = getNumberOfRgbPhysicalCameras(facing);
- boolean meetH113 = (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera();
- mCollector.expectTrue("Primary " + facingString + " camera must be LOGICAL_MULTI_CAMERA"
- + " in case of multiple RGB cameras with same facing",
- !assertTPerfClass || meetH113);
- perfClassLevelH113 = updatePerfClassLevel(meetH113, perfClassLevelH113,
- CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue(facingString + " camera is LOGICAL_MULTI_CAMERA in case of multiple "
- + "RGB cameras with same facing", meetH113, ResultType.NEUTRAL,
- ResultUnit.NONE);
+ boolean logicalMultiCameraReqMet =
+ (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera();
+ if (isPrimaryRear) {
+ logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(logicalMultiCameraReqMet);
+ } else {
+ logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(logicalMultiCameraReqMet);
+ }
// H-1-14
- boolean meetH114 = staticInfo.isStreamUseCaseSupported();
- mCollector.expectTrue("Primary " + facingString + " camera must support stream "
- + "use case", !assertTPerfClass || meetH114);
- perfClassLevelH114 = updatePerfClassLevel(meetH114, perfClassLevelH114,
- CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue(facingString + " camera stream use case", meetH114,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ boolean streamUseCaseSupported = staticInfo.isStreamUseCaseSupported();
+ if (isPrimaryRear) {
+ streamUseCaseReq.setRearStreamUseCaseSupported(streamUseCaseSupported);
+ } else {
+ streamUseCaseReq.setFrontStreamUseCaseSupported(streamUseCaseSupported);
+ }
}
- HashSet<String> primaryCameras = new HashSet<String>();
+
if (primaryRearId == null) {
- mCollector.expectTrue("There must be a primary rear camera for performance class.",
- !assertPerfClass);
- perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- } else {
- primaryCameras.add(primaryRearId);
+ primaryRearReq.setPrimaryCameraSupported(false);
+ primaryRearReq.setResolution(-1);
+ primaryRearReq.setVideoSizeReqSatisfied(false);
+ primaryRearReq.setVideoFps(-1);
+ hwLevelReq.setPrimaryRearCameraHwlLevel(-1);
+ timestampSourceReq.setRearCameraTimestampSource(
+ CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
+ rearRawReq.setRearRawSupported(false);
+ hfrReq.setRear240FpsSupported(false);
+ ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(false);
+ previewStabilizationReq.setRearPreviewStabilizationSupported(false);
+ logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(false);
+ streamUseCaseReq.setRearStreamUseCaseSupported(false);
}
if (primaryFrontId == null) {
- mCollector.expectTrue("There must be a primary front camera for performance class.",
- !assertPerfClass);
- perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET;
- } else {
- primaryCameras.add(primaryFrontId);
+ primaryFrontReq.setPrimaryCameraSupported(false);
+ primaryFrontReq.setResolution(-1);
+ primaryFrontReq.setVideoSizeReqSatisfied(false);
+ primaryFrontReq.setVideoFps(-1);
+ hwLevelReq.setPrimaryFrontCameraHwlLevel(-1);
+ timestampSourceReq.setFrontCameraTimestampSource(
+ CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN);
+ ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(false);
+ previewStabilizationReq.setFrontPreviewStabilizationSupported(false);
+ logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(false);
+ streamUseCaseReq.setFrontStreamUseCaseSupported(false);
}
// H-1-11
Set<Set<String>> concurrentCameraIds = mCameraManager.getConcurrentCameraIds();
+ Set<String> primaryCameras = new HashSet<>(Arrays.asList(primaryRearId, primaryFrontId));
boolean supportPrimaryFrontBack = concurrentCameraIds.contains(primaryCameras);
- mCollector.expectTrue("Concurrent primary front and primary back streaming must be "
- + "supported", !assertTPerfClass || supportPrimaryFrontBack);
- perfClassLevelH111 = updatePerfClassLevel(supportPrimaryFrontBack,
- perfClassLevelH111, CameraTestUtils.PERFORMANCE_CLASS_S);
- reportLog.addValue("concurrent front back support", supportPrimaryFrontBack,
- ResultType.NEUTRAL, ResultUnit.NONE);
+ concurrentRearFrontReq.setConcurrentRearFrontSupported(supportPrimaryFrontBack);
- reportLog.addValue("Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE);
- final String PERF_CLASS_REQ_NUM_PREFIX = "2.2.7.2/7.5/";
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-1",
- perfClassLevelH11, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-2",
- perfClassLevelH12, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-3",
- perfClassLevelH13, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-4",
- perfClassLevelH14, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-8",
- perfClassLevelH18, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-9",
- perfClassLevelH19, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-10",
- perfClassLevelH110, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-11",
- perfClassLevelH111, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-12",
- perfClassLevelH112, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-13",
- perfClassLevelH113, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-14",
- perfClassLevelH114, ResultType.NEUTRAL, ResultUnit.NONE);
- reportLog.submit(InstrumentationRegistry.getInstrumentation());
+ pce.submitAndCheck();
}
/**
diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
index 3c88a3a..c9c28ea 100644
--- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java
@@ -1234,8 +1234,13 @@
mOutMediaFileName = mDebugFileNameBase + "/test_cslowMo_video_" +
captureRate + "fps_" + id + "_" + size.toString() + ".mp4";
- Log.v(TAG, "previewFrameRate:" + previewFrameRate);
- prepareRecording(size, previewFrameRate, captureRate);
+
+ // b/239101664 It appears that video frame rates higher than 30 fps may not
+ // trigger slow motion recording consistently.
+ int videoFrameRate = previewFrameRate > VIDEO_FRAME_RATE ?
+ VIDEO_FRAME_RATE : previewFrameRate;
+ Log.v(TAG, "videoFrameRate:" + videoFrameRate);
+ prepareRecording(size, videoFrameRate, captureRate);
SystemClock.sleep(PREVIEW_DURATION_MS);
@@ -1243,7 +1248,7 @@
SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
// Start recording
- startSlowMotionRecording(/*useMediaRecorder*/true, previewFrameRate,
+ startSlowMotionRecording(/*useMediaRecorder*/true, videoFrameRate,
captureRate, fpsRange, resultListener,
/*useHighSpeedSession*/true);
@@ -1256,7 +1261,7 @@
startConstrainedPreview(fpsRange, previewResultListener);
// Convert number of frames camera produced into the duration in unit of ms.
- float frameDurationMs = 1000.0f / previewFrameRate;
+ float frameDurationMs = 1000.0f / videoFrameRate;
float durationMs = resultListener.getTotalNumFrames() * frameDurationMs;
// Validation.
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
index e8d6168..4a86b49 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -142,8 +142,6 @@
public static final String OFFLINE_CAMERA_ID = "offline_camera_id";
public static final String REPORT_LOG_NAME = "CtsCameraTestCases";
- public static final String MPC_REPORT_LOG_NAME = "MediaPerformanceClassLogs";
- public static final String MPC_STREAM_NAME = "CameraCts";
private static final int EXIF_DATETIME_LENGTH = 19;
private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60;
@@ -3815,33 +3813,6 @@
return zoomRatios;
}
- public static final int PERFORMANCE_CLASS_NOT_MET = 0;
- public static final int PERFORMANCE_CLASS_R = Build.VERSION_CODES.R;
- public static final int PERFORMANCE_CLASS_S = Build.VERSION_CODES.R + 1;
- public static final int PERFORMANCE_CLASS_T = Build.VERSION_CODES.S + 2;
- public static final int PERFORMANCE_CLASS_CURRENT = PERFORMANCE_CLASS_T;
-
- /**
- * Check whether this mobile device is R performance class as defined in CDD
- */
- public static boolean isRPerfClass() {
- return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R;
- }
-
- /**
- * Check whether this mobile device is S performance class as defined in CDD
- */
- public static boolean isSPerfClass() {
- return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S;
- }
-
- /**
- * Check whether this mobile device is T performance class as defined in CDD
- */
- public static boolean isTPerfClass() {
- return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_T;
- }
-
/**
* Check whether a camera Id is a primary rear facing camera
*/
diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
index fc8c4db..f188685 100644
--- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
+++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java
@@ -229,6 +229,13 @@
* at least the desired one (but could be higher)
*/
public boolean isHardwareLevelAtLeast(int level) {
+ int deviceLevel = getHardwareLevelChecked();
+
+ return hardwareLevelPredicate(deviceLevel, level);
+ }
+
+ // Return true if level1 is at least level2
+ public static boolean hardwareLevelPredicate(int level1, int level2) {
final int[] sortedHwLevels = {
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL,
@@ -236,19 +243,19 @@
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL,
CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
};
- int deviceLevel = getHardwareLevelChecked();
- if (level == deviceLevel) {
+
+ if (level1 == level2) {
return true;
}
for (int sortedlevel : sortedHwLevels) {
- if (sortedlevel == level) {
+ if (sortedlevel == level2) {
return true;
- } else if (sortedlevel == deviceLevel) {
+ } else if (sortedlevel == level1) {
return false;
}
}
- Assert.fail("Unknown hardwareLevel " + level + " and device hardware level " + deviceLevel);
+ Assert.fail("Unknown hardwareLevel " + level1 + " and device hardware level " + level2);
return false;
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
index 2c3934c..bdf4bc6 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java
@@ -27,6 +27,7 @@
import com.android.bedstead.harrier.BedsteadJUnit4;
import com.android.bedstead.harrier.DeviceState;
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner;
@@ -52,6 +53,7 @@
@EnsureHasDeviceOwner
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@RequireRunOnPrimaryUser
+ @RequireMultiUserSupport
public void createCloneProfile_hasDeviceOwner_fails() {
assertThrows(NeneException.class,
() -> TestApis.users().createUser()
@@ -67,6 +69,7 @@
@EnsureHasNoDeviceOwner
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@RequireRunOnPrimaryUser
+ @RequireMultiUserSupport
public void createCloneProfile_noDeviceOwner_succeeds() {
UserReference cloneUser = TestApis.users().createUser()
.parent(TestApis.users().instrumented())
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
index 2688d11..9c2714c 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java
@@ -23,6 +23,7 @@
import static org.testng.Assert.assertThrows;
import android.app.admin.RemoteDevicePolicyManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Context;
import android.provider.Telephony;
@@ -65,6 +66,7 @@
private ComponentName mAdmin;
private RemoteDevicePolicyManager mDpm;
private TelephonyManager mTelephonyManager;
+ private RoleManager mRoleManager;
@Before
public void setUp() {
@@ -72,13 +74,15 @@
mAdmin = dpc.componentName();
mDpm = dpc.devicePolicyManager();
mTelephonyManager = sContext.getSystemService(TelephonyManager.class);
+ mRoleManager = sContext.getSystemService(RoleManager.class);
}
// TODO(b/198588696): Add support is @RequireSmsCapable and @RequireNotSmsCapable
@Postsubmit(reason = "new test")
@PolicyAppliesTest(policy = DefaultSmsApplication.class)
public void setDefaultSmsApplication_works() {
- assumeTrue(mTelephonyManager.isSmsCapable());
+ assumeTrue(mTelephonyManager.isSmsCapable()
+ || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)));
String previousSmsAppName = getDefaultSmsPackage();
try (TestAppInstance smsApp = sSmsApp.install()) {
mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
@@ -93,7 +97,8 @@
@Postsubmit(reason = "new test")
@PolicyDoesNotApplyTest(policy = DefaultSmsApplication.class)
public void setDefaultSmsApplication_unchanged() {
- assumeTrue(mTelephonyManager.isSmsCapable());
+ assumeTrue(mTelephonyManager.isSmsCapable()
+ || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)));
String previousSmsAppName = getDefaultSmsPackage();
try (TestAppInstance smsApp = sSmsApp.install()) {
mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
@@ -108,7 +113,8 @@
@Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = DefaultSmsApplication.class)
public void setDefaultSmsApplication_smsPackageDoesNotExist_unchanged() {
- assumeTrue(mTelephonyManager.isSmsCapable());
+ assumeTrue(mTelephonyManager.isSmsCapable()
+ || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)));
String previousSmsAppName = getDefaultSmsPackage();
mDpm.setDefaultSmsApplication(mAdmin, FAKE_SMS_APP_NAME);
@@ -135,7 +141,8 @@
@Postsubmit(reason = "new test")
@CanSetPolicyTest(policy = DefaultSmsApplication.class)
public void setDefaultSmsApplication_notSmsCapable_unchanged() {
- assumeTrue(!mTelephonyManager.isSmsCapable());
+ assumeTrue(!mTelephonyManager.isSmsCapable()
+ && (mRoleManager == null || !mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS)));
String previousSmsAppName = getDefaultSmsPackage();
try (TestAppInstance smsApp = sSmsApp.install()) {
mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName());
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
index 286ccae..1c35d48 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
@@ -54,6 +54,7 @@
import com.android.bedstead.harrier.annotations.EnsureHasPermission;
import com.android.bedstead.harrier.annotations.Postsubmit;
import com.android.bedstead.harrier.annotations.RequireFeature;
+import com.android.bedstead.harrier.annotations.RequireMultiUserSupport;
import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDpc;
@@ -65,6 +66,7 @@
import com.android.bedstead.remotedpc.RemoteDpc;
import com.android.bedstead.testapp.TestApp;
import com.android.bedstead.testapp.TestAppInstance;
+import com.android.compatibility.common.util.CddTest;
import com.android.eventlib.truth.EventLogsSubject;
import com.android.queryable.queries.ActivityQuery;
@@ -140,6 +142,7 @@
@EnsureHasNoDpc
@EnsureHasNoSecondaryUser
@Test
+ @CddTest(requirements = {"3.9.4/C-3-1"})
public void createAndProvisionManagedProfile_roleHolderIsInWorkProfile()
throws ProvisioningException, InterruptedException {
UserHandle profile = null;
@@ -172,7 +175,9 @@
@EnsureHasDeviceOwner
@RequireRunOnPrimaryUser
@EnsureHasNoSecondaryUser
+ @RequireMultiUserSupport
@Test
+ @CddTest(requirements = {"3.9.4/C-3-1"})
public void createAndManageUser_roleHolderIsInManagedUser() throws InterruptedException {
UserHandle managedUser = null;
String roleHolderPackageName = null;
@@ -313,6 +318,7 @@
@EnsureHasNoWorkProfile
@RequireRunOnPrimaryUser
@EnsureHasNoDpc
+ @RequireMultiUserSupport
public void shouldAllowBypassingDevicePolicyManagementRoleQualification_withUsers_returnsFalse()
throws Exception {
resetInternalShouldAllowBypassingState();
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
index fcf71d0..dfe77fb 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java
@@ -45,6 +45,7 @@
import com.android.bedstead.metricsrecorder.EnterpriseMetricsRecorder;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.packages.Package;
+import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.testapp.TestApp;
import com.android.bedstead.testapp.TestAppInstance;
import com.android.queryable.queries.StringQuery;
@@ -235,6 +236,11 @@
private void assertPackageStopped(String packageName)
throws Exception {
+ Poll.forValue("Package " + packageName + " stopped", () -> isPackageStopped(packageName))
+ .toBeEqualTo(true)
+ .errorOnFail()
+ .await();
+
assertWithMessage("Package %s not stopped", packageName)
.that(isPackageStopped(packageName)).isTrue();
}
diff --git a/tests/framework/base/windowmanager/Android.bp b/tests/framework/base/windowmanager/Android.bp
index 168ee3f..b786d83 100644
--- a/tests/framework/base/windowmanager/Android.bp
+++ b/tests/framework/base/windowmanager/Android.bp
@@ -67,6 +67,7 @@
"cts-wm-overlayapp-base",
"cts-wm-shared",
"platform-compat-test-rules",
+ "cts_window_jetpack_utils",
],
test_suites: [
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index 39c3d3e..1a86422 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -38,6 +38,8 @@
android:enableOnBackInvokedCallback="true"
android:testOnly="true">
<uses-library android:name="android.test.runner"/>
+ <uses-library android:name="androidx.window.extensions"
+ android:required="false" />
<activity android:name="android.server.wm.ActivityManagerTestBase$ConfigChangeHandlingActivity"
android:resizeableActivity="true"
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
index d12b7c1d..477cc8d 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java
@@ -16,6 +16,7 @@
package android.server.wm.jetpack.utils;
+import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds;
import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getMaximumActivityBounds;
@@ -28,6 +29,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.app.Activity;
import android.content.ComponentName;
@@ -51,6 +53,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -447,6 +450,13 @@
}
}
+ public static void assumeActivityEmbeddingSupportedDevice() {
+ assumeExtensionSupportedDevice();
+ assumeTrue("Device does not support ActivityEmbedding",
+ Objects.requireNonNull(getWindowExtensions())
+ .getActivityEmbeddingComponent() != null);
+ }
+
private static void assertSplitInfoTopSplitIsCorrect(@NonNull List<SplitInfo> splitInfoList,
@NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) {
assertFalse("Split info callback should not be empty", splitInfoList.isEmpty());
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
index b6c43f1..a26d3c2 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java
@@ -23,10 +23,14 @@
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
+import android.os.SystemClock;
import android.server.wm.TestJournalProvider.TestJournalContainer;
import android.server.wm.backlegacyapp.Components;
import android.support.test.uiautomator.UiDevice;
+import android.view.InputEvent;
+import android.view.MotionEvent;
+import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.GestureNavRule;
@@ -74,7 +78,45 @@
private void doBackGesture() {
int midHeight = mUiDevice.getDisplayHeight() / 2;
int midWidth = mUiDevice.getDisplayWidth() / 2;
- mUiDevice.swipe(0, midHeight, midWidth, midHeight, 100);
+ quickSwipe(0, midHeight, midWidth, midHeight, 10);
mUiDevice.waitForIdle();
}
+
+ private void injectInputEventUnSynced(@NonNull InputEvent event) {
+ mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */,
+ false /* waitForAnimations */);
+ }
+
+ /**
+ * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction.
+ */
+ private void quickSwipe(float startX, float startY, float endX, float endY, int steps) {
+ if (steps <= 0) {
+ steps = 1;
+ }
+ final long startDownTime = SystemClock.uptimeMillis();
+ MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime,
+ MotionEvent.ACTION_DOWN, startX, startY, 0);
+ injectInputEventUnSynced(firstDown);
+
+ // inject in every 5 ms.
+ final int delayMillis = 5;
+ long nextEventTime = startDownTime + delayMillis;
+ final float stepGapX = (endX - startX) / steps;
+ final float stepGapY = (endY - startY) / steps;
+ for (int i = 0; i < steps; i++) {
+ SystemClock.sleep(delayMillis);
+ final float nextX = startX + stepGapX * i;
+ final float nextY = startY + stepGapY * i;
+ MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime,
+ MotionEvent.ACTION_MOVE, nextX, nextY, 0);
+ injectInputEventUnSynced(move);
+ nextEventTime += delayMillis;
+ }
+
+ SystemClock.sleep(delayMillis);
+ MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime,
+ MotionEvent.ACTION_UP, endX, endY, 0);
+ injectInputEventUnSynced(up);
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
index 385a5af..9fd1a41 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java
@@ -18,8 +18,10 @@
import static android.server.wm.WindowManagerState.STATE_RESUMED;
import static android.server.wm.jetpack.second.Components.SECOND_UNTRUSTED_EMBEDDING_ACTIVITY;
+import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assumeActivityEmbeddingSupportedDevice;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -32,6 +34,7 @@
import android.graphics.Rect;
import android.os.Binder;
import android.os.IBinder;
+import android.platform.test.annotations.Presubmit;
import android.server.wm.WindowManagerState.Task;
import android.window.TaskFragmentCreationParams;
import android.window.TaskFragmentInfo;
@@ -39,6 +42,7 @@
import androidx.annotation.NonNull;
+import org.junit.Before;
import org.junit.Test;
/**
@@ -47,11 +51,19 @@
* Build/Install/Run:
* atest CtsWindowManagerDeviceTestCases:TaskFragmentTrustedModeTest
*/
+@Presubmit
public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase {
private final ComponentName mTranslucentActivity = new ComponentName(mContext,
TranslucentActivity.class);
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ assumeActivityEmbeddingSupportedDevice();
+ }
+
/**
* Verifies the visibility of a task fragment that has overlays on top of activities embedded
* in untrusted mode when there is an overlay over the task fragment.
@@ -239,7 +251,7 @@
*/
@Test
public void testUntrustedModeTaskFragment_startActivityInTaskFragmentOutsideOfParentBounds() {
- final Task parentTask = mWmState.getRootTask(mOwnerTaskId);
+ Task parentTask = mWmState.getRootTask(mOwnerTaskId);
final Rect parentBounds = new Rect(parentTask.getBounds());
final IBinder errorCallbackToken = new Binder();
final WindowContainerTransaction wct = new WindowContainerTransaction()
@@ -254,8 +266,11 @@
// It is disallowed to start activity to TaskFragment with bounds outside of its parent
// in untrusted mode.
assertTaskFragmentError(errorCallbackToken, SecurityException.class);
- mWmState.waitForAppTransitionIdleOnDisplay(mOwnerActivity.getDisplayId());
- mWmState.assertNotExist(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY);
+
+ parentTask = mWmState.getRootTask(mOwnerTaskId);
+ assertWithMessage("Activity must be started in parent Task because it's not"
+ + " allowed to be embedded").that(parentTask.mActivities).contains(
+ mWmState.getActivity(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY));
}
/**
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
index 65465b2..8dc33b1 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java
@@ -59,6 +59,7 @@
import android.app.Instrumentation;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.os.Bundle;
import android.os.SystemClock;
import android.platform.test.annotations.Presubmit;
@@ -226,6 +227,11 @@
final Instrumentation instrumentation = getInstrumentation();
assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()),
nullValue());
+ final Resources resources = instrumentation.getContext().getResources();
+ final boolean isHideNavBarForKeyboardEnabled = resources.getBoolean(
+ resources.getIdentifier("config_hideNavBarForKeyboard", "bool", "android"));
+ assumeFalse("Device is configured to not show navigation bar for keyboard",
+ isHideNavBarForKeyboardEnabled);
final MockImeSession imeSession = MockImeHelper.createManagedMockImeSession(this);
final ImeEventStream stream = imeSession.openEventStream();
final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class);
diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
index ccd8659..8855dee 100644
--- a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
+++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java
@@ -32,7 +32,7 @@
*
* <p>See Bug 174534092 about why we ended up having this.</p>
*/
- private static final int TOLERANCE = 4;
+ private static final int TOLERANCE = 6;
/**
* A utility class that represents A8R8G8B bitmap as an integer array.
diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
index 08ddca1..2baa2f1 100644
--- a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
+++ b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java
@@ -33,10 +33,12 @@
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.location.Geocoder;
import android.location.Geocoder.GeocodeListener;
+import android.platform.test.annotations.AppModeFull;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.compatibility.common.util.ApiTest;
import com.android.compatibility.common.util.RetryRule;
import org.junit.Before;
@@ -73,6 +75,8 @@
}
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocation() {
assumeTrue(Geocoder.isPresent());
@@ -82,6 +86,8 @@
verify(listener, timeout(10000)).onGeocode(anyList());
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocation_sync() throws Exception {
assumeTrue(Geocoder.isPresent());
@@ -89,6 +95,8 @@
mGeocoder.getFromLocation(60, 30, 5);
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocation")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocation_badInput() {
GeocodeListener listener = mock(GeocodeListener.class);
@@ -102,6 +110,8 @@
() -> mGeocoder.getFromLocation(10, 181, 5, listener));
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocationName() {
assumeTrue(Geocoder.isPresent());
@@ -111,6 +121,8 @@
verify(listener, timeout(10000)).onGeocode(anyList());
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocationName_sync() throws Exception {
assumeTrue(Geocoder.isPresent());
@@ -118,6 +130,8 @@
mGeocoder.getFromLocationName("Dalvik,Iceland", 5);
}
+ @ApiTest(apis = "android.location.Geocoder#getFromLocationName")
+ @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps")
@Test
public void testGetFromLocationName_badInput() {
GeocodeListener listener = mock(GeocodeListener.class);
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index 5f9aa09..14ebd28 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -158,10 +158,10 @@
.noneMatch(x -> x == COLOR_FormatSurface));
}
- // For devices launching with Android T, if a codec supports an HDR profile, it must
- // advertise P010 support
+ // For devices launching with Android T, if a codec supports an HDR profile and device
+ // supports HDR display, it must advertise P010 support
int[] HdrProfileArray = mProfileHdrMap.get(mMediaType);
- if (FIRST_SDK_IS_AT_LEAST_T && HdrProfileArray != null) {
+ if (VNDK_IS_AT_LEAST_T && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) {
for (CodecProfileLevel pl : caps.profileLevels) {
if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) {
assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," +
diff --git a/tests/mediapc/AndroidTest.xml b/tests/mediapc/AndroidTest.xml
index dcc8995..dc36e58 100644
--- a/tests/mediapc/AndroidTest.xml
+++ b/tests/mediapc/AndroidTest.xml
@@ -24,6 +24,11 @@
<option name="config-filename" value="CtsMediaPerformanceClassTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaPerformanceClassTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaPerformanceClassTestCases-1.2" />
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
index 327fb9c..d3fd292 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
@@ -20,6 +20,7 @@
import static org.junit.Assume.assumeTrue;
+import android.hardware.camera2.CameraMetadata;
import android.media.MediaFormat;
import android.os.Build;
@@ -849,7 +850,493 @@
}
}
- private <R extends Requirement> R addRequirement(R req) {
+ public static class PrimaryCameraRequirement extends Requirement {
+ private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000;
+ private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000;
+ private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000;
+ private static final String TAG = PrimaryCameraRequirement.class.getSimpleName();
+
+ private PrimaryCameraRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setPrimaryCameraSupported(boolean hasPrimaryCamera) {
+ this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_AVAILABLE,
+ hasPrimaryCamera);
+ }
+
+ public void setResolution(long resolution) {
+ this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_RESOLUTION,
+ resolution);
+ }
+
+ public void setVideoSizeReqSatisfied(boolean videoSizeReqSatisfied) {
+ this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED,
+ videoSizeReqSatisfied);
+ }
+
+ public void setVideoFps(double videoFps) {
+ this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS, videoFps);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-1] MUST have a primary rear facing camera with a resolution of at
+ * least 12 megapixels supporting video capture at 4k@30fps
+ */
+ public static PrimaryCameraRequirement createRearPrimaryCamera() {
+ RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, true)
+ .addRequiredValue(Build.VERSION_CODES.S, true)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ RequiredMeasurement<Long> cameraResolution = RequiredMeasurement
+ .<Long>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION)
+ .setPredicate(RequirementConstants.LONG_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+ .addRequiredValue(Build.VERSION_CODES.S, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION)
+ .build();
+
+ RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, true)
+ .addRequiredValue(Build.VERSION_CODES.S, true)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ RequiredMeasurement<Double> videoFps = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 29.9)
+ .addRequiredValue(Build.VERSION_CODES.S, 29.9)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9)
+ .build();
+
+ return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_1,
+ hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied,
+ videoFps);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-2] MUST have a primary front facing camera with a resolution of
+ * at least 4 megapixels supporting video capture at 1080p@30fps.
+ */
+ public static PrimaryCameraRequirement createFrontPrimaryCamera() {
+ RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, true)
+ .addRequiredValue(Build.VERSION_CODES.S, true)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ RequiredMeasurement<Long> cameraResolution = RequiredMeasurement
+ .<Long>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION)
+ .setPredicate(RequirementConstants.LONG_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION)
+ .addRequiredValue(Build.VERSION_CODES.S, MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU,
+ MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION)
+ .build();
+
+ RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, true)
+ .addRequiredValue(Build.VERSION_CODES.S, true)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ RequiredMeasurement<Double> videoFps = RequiredMeasurement
+ .<Double>builder()
+ .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS)
+ .setPredicate(RequirementConstants.DOUBLE_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 29.9)
+ .addRequiredValue(Build.VERSION_CODES.S, 29.9)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9)
+ .build();
+
+ return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_2,
+ hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied,
+ videoFps);
+ }
+ }
+
+ public static class CameraTimestampSourceRequirement extends Requirement {
+ private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName();
+ private static final int TIMESTAMP_REALTIME =
+ CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME;
+
+ private CameraTimestampSourceRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearCameraTimestampSource(Integer timestampSource) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE,
+ timestampSource);
+ }
+
+ public void setFrontCameraTimestampSource(Integer timestampSource) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE,
+ timestampSource);
+ }
+ /**
+ * [2.2.7.2/7.5/H-1-4] MUST support CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME
+ * for both primary cameras.
+ */
+ public static CameraTimestampSourceRequirement createTimestampSourceReq() {
+ RequiredMeasurement<Integer> rearTimestampSource = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE)
+ .setPredicate(RequirementConstants.INTEGER_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME)
+ .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME)
+ .build();
+ RequiredMeasurement<Integer> frontTimestampSource = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE)
+ .setPredicate(RequirementConstants.INTEGER_EQ)
+ .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME)
+ .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME)
+ .build();
+
+ return new CameraTimestampSourceRequirement(RequirementConstants.R7_5__H_1_4,
+ rearTimestampSource, frontTimestampSource);
+ }
+ }
+
+ public static class CameraLatencyRequirement extends Requirement {
+ private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName();
+
+ private CameraLatencyRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearCameraLatency(float latency) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LATENCY, latency);
+ }
+
+ public void setFrontCameraLatency(float latency) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LATENCY, latency);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p resolution
+ * as measured by the CTS camera PerformanceTest under ITS lighting conditions
+ * (3000K) for both primary cameras.
+ */
+ public static CameraLatencyRequirement createJpegLatencyReq() {
+ RequiredMeasurement<Float> rearJpegLatency = RequiredMeasurement
+ .<Float>builder()
+ .setId(RequirementConstants.REAR_CAMERA_LATENCY)
+ .setPredicate(RequirementConstants.FLOAT_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 1000.0f)
+ .addRequiredValue(Build.VERSION_CODES.S, 1000.0f)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f)
+ .build();
+ RequiredMeasurement<Float> frontJpegLatency = RequiredMeasurement
+ .<Float>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_LATENCY)
+ .setPredicate(RequirementConstants.FLOAT_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 1000.0f)
+ .addRequiredValue(Build.VERSION_CODES.S, 1000.0f)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f)
+ .build();
+
+ return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_5,
+ rearJpegLatency, frontJpegLatency);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first
+ * preview frame) < 600ms as measured by the CTS camera PerformanceTest under ITS lighting
+ * conditions (3000K) for both primary cameras.
+ */
+ public static CameraLatencyRequirement createLaunchLatencyReq() {
+ RequiredMeasurement<Float> rearLaunchLatency = RequiredMeasurement
+ .<Float>builder()
+ .setId(RequirementConstants.REAR_CAMERA_LATENCY)
+ .setPredicate(RequirementConstants.FLOAT_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 600.0f)
+ .addRequiredValue(Build.VERSION_CODES.S, 600.0f)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f)
+ .build();
+ RequiredMeasurement<Float> frontLaunchLatency = RequiredMeasurement
+ .<Float>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_LATENCY)
+ .setPredicate(RequirementConstants.FLOAT_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 600.0f)
+ .addRequiredValue(Build.VERSION_CODES.S, 600.0f)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f)
+ .build();
+
+ return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_6,
+ rearLaunchLatency, frontLaunchLatency);
+ }
+ }
+
+ public static class CameraRawRequirement extends Requirement {
+ private static final String TAG = CameraRawRequirement.class.getSimpleName();
+
+ private CameraRawRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearRawSupported(boolean rearRawSupported) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED,
+ rearRawSupported);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-8] MUST support CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW and
+ * android.graphics.ImageFormat.RAW_SENSOR for the primary back camera.
+ */
+ public static CameraRawRequirement createRawReq() {
+ RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.S, true)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new CameraRawRequirement(RequirementConstants.R7_5__H_1_8, requirement);
+ }
+ }
+
+ public static class Camera240FpsRequirement extends Requirement {
+ private static final String TAG = Camera240FpsRequirement.class.getSimpleName();
+
+ private Camera240FpsRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRear240FpsSupported(boolean rear240FpsSupported) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED,
+ rear240FpsSupported);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-9] MUST have a rear-facing primary camera supporting 720p or 1080p @ 240fps.
+ */
+ public static Camera240FpsRequirement create240FpsReq() {
+ RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new Camera240FpsRequirement(RequirementConstants.R7_5__H_1_9, requirement);
+ }
+ }
+
+ public static class UltraWideZoomRatioRequirement extends Requirement {
+ private static final String TAG =
+ UltraWideZoomRatioRequirement.class.getSimpleName();
+
+ private UltraWideZoomRatioRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET,
+ ultrawideZoomRatioReqMet);
+ }
+
+ public void setFrontUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET,
+ ultrawideZoomRatioReqMet);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-10] MUST have min ZOOM_RATIO < 1.0 for the primary cameras if
+ * there is an ultrawide RGB camera facing the same direction.
+ */
+ public static UltraWideZoomRatioRequirement createUltrawideZoomRatioReq() {
+ RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+ RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new UltraWideZoomRatioRequirement(RequirementConstants.R7_5__H_1_10,
+ rearRequirement, frontRequirement);
+ }
+ }
+
+ public static class ConcurrentRearFrontRequirement extends Requirement {
+ private static final String TAG = ConcurrentRearFrontRequirement.class.getSimpleName();
+
+ private ConcurrentRearFrontRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setConcurrentRearFrontSupported(boolean concurrentRearFrontSupported) {
+ this.setMeasuredValue(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED,
+ concurrentRearFrontSupported);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-11] MUST implement concurrent front-back streaming on primary cameras.
+ */
+ public static ConcurrentRearFrontRequirement createConcurrentRearFrontReq() {
+ RequiredMeasurement<Boolean> requirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new ConcurrentRearFrontRequirement(RequirementConstants.R7_5__H_1_11,
+ requirement);
+ }
+ }
+
+ public static class PreviewStabilizationRequirement extends Requirement {
+ private static final String TAG =
+ PreviewStabilizationRequirement.class.getSimpleName();
+
+ private PreviewStabilizationRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearPreviewStabilizationSupported(boolean supported) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED,
+ supported);
+ }
+
+ public void setFrontPreviewStabilizationSupported(boolean supported) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED,
+ supported);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-12] MUST support CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION
+ * for both primary front and primary back camera.
+ */
+ public static PreviewStabilizationRequirement createPreviewStabilizationReq() {
+ RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+ RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new PreviewStabilizationRequirement(RequirementConstants.R7_5__H_1_12,
+ rearRequirement, frontRequirement);
+ }
+ }
+
+ public static class LogicalMultiCameraRequirement extends Requirement {
+ private static final String TAG =
+ LogicalMultiCameraRequirement.class.getSimpleName();
+
+ private LogicalMultiCameraRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearLogicalMultiCameraReqMet(boolean reqMet) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET,
+ reqMet);
+ }
+
+ public void setFrontLogicalMultiCameraReqMet(boolean reqMet) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET,
+ reqMet);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-13] MUST support LOGICAL_MULTI_CAMERA capability for the primary
+ * cameras if there are greater than 1 RGB cameras facing the same direction.
+ */
+ public static LogicalMultiCameraRequirement createLogicalMultiCameraReq() {
+ RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+ RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new LogicalMultiCameraRequirement(RequirementConstants.R7_5__H_1_13,
+ rearRequirement, frontRequirement);
+ }
+ }
+
+ public static class StreamUseCaseRequirement extends Requirement {
+ private static final String TAG =
+ StreamUseCaseRequirement.class.getSimpleName();
+
+ private StreamUseCaseRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setRearStreamUseCaseSupported(boolean supported) {
+ this.setMeasuredValue(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED,
+ supported);
+ }
+
+ public void setFrontStreamUseCaseSupported(boolean supported) {
+ this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED,
+ supported);
+ }
+
+ /**
+ * [2.2.7.2/7.5/H-1-14] MUST support STREAM_USE_CASE capability for both primary
+ * front and primary back camera.
+ */
+ public static StreamUseCaseRequirement createStreamUseCaseReq() {
+ RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+ RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement
+ .<Boolean>builder()
+ .setId(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED)
+ .setPredicate(RequirementConstants.BOOLEAN_EQ)
+ .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true)
+ .build();
+
+ return new StreamUseCaseRequirement(RequirementConstants.R7_5__H_1_14,
+ rearRequirement, frontRequirement);
+ }
+ }
+
+ public <R extends Requirement> R addRequirement(R req) {
if (!this.mRequirements.add(req)) {
throw new IllegalStateException("Requirement " + req.id() + " already added");
}
@@ -1000,16 +1487,69 @@
return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_10());
}
+ public PrimaryCameraRequirement addPrimaryRearCameraReq() {
+ return this.addRequirement(PrimaryCameraRequirement.createRearPrimaryCamera());
+ }
+
+ public PrimaryCameraRequirement addPrimaryFrontCameraReq() {
+ return this.addRequirement(PrimaryCameraRequirement.createFrontPrimaryCamera());
+ }
+
+ public CameraTimestampSourceRequirement addR7_5__H_1_4() {
+ return this.addRequirement(CameraTimestampSourceRequirement.createTimestampSourceReq());
+ }
+
+ public CameraLatencyRequirement addR7_5__H_1_5() {
+ return this.addRequirement(CameraLatencyRequirement.createJpegLatencyReq());
+ }
+
+ public CameraLatencyRequirement addR7_5__H_1_6() {
+ return this.addRequirement(CameraLatencyRequirement.createLaunchLatencyReq());
+ }
+
+ public CameraRawRequirement addR7_5__H_1_8() {
+ return this.addRequirement(CameraRawRequirement.createRawReq());
+ }
+
+ public Camera240FpsRequirement addR7_5__H_1_9() {
+ return this.addRequirement(Camera240FpsRequirement.create240FpsReq());
+ }
+
+ public UltraWideZoomRatioRequirement addR7_5__H_1_10() {
+ return this.addRequirement(UltraWideZoomRatioRequirement.createUltrawideZoomRatioReq());
+ }
+
+ public ConcurrentRearFrontRequirement addR7_5__H_1_11() {
+ return this.addRequirement(ConcurrentRearFrontRequirement.createConcurrentRearFrontReq());
+ }
+
+ public PreviewStabilizationRequirement addR7_5__H_1_12() {
+ return this.addRequirement(PreviewStabilizationRequirement.createPreviewStabilizationReq());
+ }
+
+ public LogicalMultiCameraRequirement addR7_5__H_1_13() {
+ return this.addRequirement(LogicalMultiCameraRequirement.createLogicalMultiCameraReq());
+ }
+
+ public StreamUseCaseRequirement addR7_5__H_1_14() {
+ return this.addRequirement(StreamUseCaseRequirement.createStreamUseCaseReq());
+ }
+
public void submitAndCheck() {
- boolean perfClassMet = true;
- for (Requirement req: this.mRequirements) {
- perfClassMet &= req.writeLogAndCheck(this.mTestName);
- }
+ boolean perfClassMet = submit();
// check performance class
assumeTrue("Build.VERSION.MEDIA_PERFORMANCE_CLASS is not declared", Utils.isPerfClass());
assertThat(perfClassMet).isTrue();
-
- this.mRequirements.clear(); // makes sure report isn't submitted twice
}
+
+ public boolean submit() {
+ boolean perfClassMet = true;
+ for (Requirement req: this.mRequirements) {
+ perfClassMet &= req.writeLogAndCheck(this.mTestName);
+ }
+ this.mRequirements.clear(); // makes sure report isn't submitted twice
+ return perfClassMet;
+ }
+
}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
index d93cb2e..ad0d0d5 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
@@ -16,8 +16,6 @@
package android.mediapc.cts.common;
-import android.os.Build;
-
import java.util.function.BiPredicate;
public class RequirementConstants {
@@ -54,8 +52,13 @@
public static final String R7_5__H_1_4 = "r7_5__h_1_4"; // 7.5/H-1-4
public static final String R7_5__H_1_5 = "r7_5__h_1_5"; // 7.5/H-1-5
public static final String R7_5__H_1_6 = "r7_5__h_1_6"; // 7.5/H-1-6
- public static final String R7_5__H_1_7 = "r7_5__h_1_7"; // 7.5/H-1-7
public static final String R7_5__H_1_8 = "r7_5__h_1_8"; // 7.5/H-1-8
+ public static final String R7_5__H_1_9 = "r7_5__h_1_9"; // 7.5/H-1-9
+ public static final String R7_5__H_1_10 = "r7_5__h_1_10"; // 7.5/H-1-10
+ public static final String R7_5__H_1_11 = "r7_5__h_1_11"; // 7.5/H-1-11
+ public static final String R7_5__H_1_12 = "r7_5__h_1_12"; // 7.5/H-1-12
+ public static final String R7_5__H_1_13 = "r7_5__h_1_13"; // 7.5/H-1-13
+ public static final String R7_5__H_1_14 = "r7_5__h_1_14"; // 7.5/H-1-14
public static final String R7_1_1_1__H_1_1 = "r7_1_1_1__h_1_1"; // 7.1.1.1/H-1-1
public static final String R7_1_1_3__H_1_1 = "r7_1_1_3__h_1_1"; // 7.1.1.3/H-1-1
public static final String R7_6_1__H_1_1 = "r7_6_1__h_1_1"; // 7.6.1/H-1-1
@@ -90,14 +93,50 @@
public static final String NUM_CRYPTO_HW_SECURE_ALL_SUPPORT =
"number_crypto_hw_secure_all_support";
+ public static final String PRIMARY_CAMERA_AVAILABLE = "primary_camera_available";
+ public static final String PRIMARY_CAMERA_RESOLUTION = "primary_camera_resolution";
+ public static final String PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED =
+ "primary_camera_video_size_req_satisfied";
+ public static final String PRIMARY_CAMERA_VIDEO_FPS =
+ "primary_camera_video_fps";
+ public static final String REAR_CAMERA_HWL_LEVEL = "rear_primary_camera_hwl_level";
+ public static final String FRONT_CAMERA_HWL_LEVEL = "front_primary_camera_hwl_level";
+ public static final String REAR_CAMERA_TIMESTAMP_SOURCE =
+ "rear_primary_camera_timestamp_source";
+ public static final String FRONT_CAMERA_TIMESTAMP_SOURCE =
+ "front_primary_camera_timestamp_source";
+ public static final String REAR_CAMERA_LATENCY = "rear_camera_latency";
+ public static final String FRONT_CAMERA_LATENCY = "front_camera_latency";
+ public static final String REAR_CAMERA_RAW_SUPPORTED = "rear_camera_raw_supported";
+ public static final String REAR_CAMERA_240FPS_SUPPORTED = "rear_camera_240fps_supported";
+ public static final String REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET =
+ "rear_camera_ultrawide_zoom_req_met";
+ public static final String FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET =
+ "front_camera_ultrawide_zoom_req_met";
+ public static final String CONCURRENT_REAR_FRONT_SUPPORTED = "rear_front_concurrent_camera";
+ public static final String REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED =
+ "rear_camera_preview_stabilization_supported";
+ public static final String FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED =
+ "front_camera_preview_stabilization_supported";
+ public static final String REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET =
+ "rear_camera_logical_multi_camera_req_met";
+ public static final String FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET =
+ "front_camera_logical_multi_camera_req_met";
+ public static final String REAR_CAMERA_STREAM_USECASE_SUPPORTED =
+ "rear_camera_stream_usecase_supported";
+ public static final String FRONT_CAMERA_STREAM_USECASE_SUPPORTED =
+ "front_camera_stream_usecase_supported";
+
public enum Result {
NA, MET, UNMET
}
public static final BiPredicate<Long, Long> LONG_GTE = RequirementConstants.gte();
public static final BiPredicate<Long, Long> LONG_LTE = RequirementConstants.lte();
+ public static final BiPredicate<Float, Float> FLOAT_LTE = RequirementConstants.lte();
public static final BiPredicate<Integer, Integer> INTEGER_GTE = RequirementConstants.gte();
public static final BiPredicate<Integer, Integer> INTEGER_LTE = RequirementConstants.lte();
+ public static final BiPredicate<Integer, Integer> INTEGER_EQ = RequirementConstants.eq();
public static final BiPredicate<Double, Double> DOUBLE_EQ = RequirementConstants.eq();
public static final BiPredicate<Boolean, Boolean> BOOLEAN_EQ = RequirementConstants.eq();
public static final BiPredicate<Double, Double> DOUBLE_GTE = RequirementConstants.gte();
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
index ac03705..28a122b 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
@@ -73,16 +73,24 @@
Context context = InstrumentationRegistry.getInstrumentation().getContext();
DisplayMetrics metrics = new DisplayMetrics();
- WindowManager windowManager = context.getSystemService(WindowManager.class);
- windowManager.getDefaultDisplay().getMetrics(metrics);
- DISPLAY_DPI = metrics.densityDpi;
- DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels);
- DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels);
+ // When used from ItsService, context will be null
+ if (context != null) {
+ WindowManager windowManager = context.getSystemService(WindowManager.class);
+ windowManager.getDefaultDisplay().getMetrics(metrics);
+ DISPLAY_DPI = metrics.densityDpi;
+ DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels);
+ DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels);
- ActivityManager activityManager = context.getSystemService(ActivityManager.class);
- ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
- activityManager.getMemoryInfo(memoryInfo);
- TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024;
+ ActivityManager activityManager = context.getSystemService(ActivityManager.class);
+ ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
+ activityManager.getMemoryInfo(memoryInfo);
+ TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024;
+ } else {
+ DISPLAY_DPI = 0;
+ DISPLAY_LONG_PIXELS = 0;
+ DISPLAY_SHORT_PIXELS = 0;
+ TOTAL_MEMORY_MB = 0;
+ }
}
/**
diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
index 2508845..8da22f3 100644
--- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
@@ -41,6 +41,7 @@
import java.util.List;
import java.util.UUID;
import org.junit.Assume;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -58,6 +59,11 @@
@Rule
public final TestName mTestName = new TestName();
+ @Before
+ public void isPerformanceClassCandidate() {
+ Utils.assumeDeviceMeetsPerformanceClassPreconditions();
+ }
+
static {
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_AVC);
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
diff --git a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
index bbe26dc..2ee8b3b 100644
--- a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java
@@ -21,12 +21,14 @@
import static android.mediapc.cts.CodecTestBase.SELECT_VIDEO;
import static android.mediapc.cts.CodecTestBase.getMimesOfAvailableCodecs;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+import static org.junit.Assert.assertTrue;
import android.media.MediaCodec;
import android.media.MediaCodecInfo.CodecCapabilities;
import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
import android.media.MediaFormat;
import android.mediapc.cts.common.PerformanceClassEvaluator;
+import android.mediapc.cts.common.Utils;
import android.util.Log;
import androidx.test.filters.LargeTest;
import com.android.compatibility.common.util.CddTest;
@@ -35,6 +37,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TestName;
@@ -47,6 +50,11 @@
@Rule
public final TestName mTestName = new TestName();
+ @Before
+ public void isPerformanceClassCandidate() {
+ Utils.assumeDeviceMeetsPerformanceClassPreconditions();
+ }
+
private Set<String> get4k60HwCodecSet(boolean isEncoder) throws IOException {
Set<String> codecSet = new HashSet<>();
Set<String> codecMediaTypes = getMimesOfAvailableCodecs(SELECT_VIDEO, SELECT_HARDWARE);
@@ -60,6 +68,7 @@
codec.getCodecInfo().getCapabilitiesForType(codecMediaType);
List<PerformancePoint> pps =
capabilities.getVideoCapabilities().getSupportedPerformancePoints();
+ assertTrue(hwVideoCodec + " doesn't advertise performance points", pps.size() > 0);
for (PerformancePoint pp : pps) {
if (pp.covers(PP4k60)) {
codecSet.add(hwVideoCodec);
diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java
index 0ffcb271..98e8576 100755
--- a/tests/tests/assist/common/src/android/assist/common/Utils.java
+++ b/tests/tests/assist/common/src/android/assist/common/Utils.java
@@ -56,6 +56,7 @@
public static final String COMPARE_SCREENSHOT_KEY = "compare_screenshot";
public static final String DISPLAY_WIDTH_KEY = "display_width";
public static final String DISPLAY_HEIGHT_KEY = "dislay_height";
+ public static final String DISPLAY_AREA_BOUNDS_KEY = "display_area_bounds";
public static final String SCROLL_X_POSITION = "scroll_x_position";
public static final String SCROLL_Y_POSITION = "scroll_y_position";
public static final String SHOW_SESSION_FLAGS_TO_SET = "show_session_flags_to_set";
diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
index 7f35367..25c080b 100644
--- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
+++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java
@@ -56,6 +56,7 @@
private int mCurColor;
private int mDisplayHeight;
private int mDisplayWidth;
+ private Rect mDisplayAreaBounds;
private BroadcastReceiver mReceiver;
private String mTestName;
private View mContentView;
@@ -106,7 +107,7 @@
public void onPrepareShow(Bundle args, int showFlags) {
if (Utils.LIFECYCLE_NOUI.equals(args.getString(Utils.TESTCASE_TYPE, ""))) {
setUiEnabled(false);
- } else {
+ } else {
setUiEnabled(true);
}
}
@@ -122,6 +123,7 @@
mCurColor = args.getInt(Utils.SCREENSHOT_COLOR_KEY);
mDisplayHeight = args.getInt(Utils.DISPLAY_HEIGHT_KEY);
mDisplayWidth = args.getInt(Utils.DISPLAY_WIDTH_KEY);
+ mDisplayAreaBounds = args.getParcelable(Utils.DISPLAY_AREA_BOUNDS_KEY);
mRemoteCallback = args.getParcelable(Utils.EXTRA_REMOTE_CALLBACK);
super.onShow(args, showFlags);
if (mContentView == null) return; // Happens when ui is not enabled.
@@ -256,6 +258,11 @@
int[] pixels = new int[size.x * size.y];
screenshot.getPixels(pixels, 0, size.x, 0, 0, size.x, size.y);
+ // screenshot bitmap contains the screenshot for the entire physical display. A single
+ // physical display could have multiple display area with different applications.
+ // Let's grab the region of the display area from the original screenshot.
+ Bitmap displayAreaScreenshot = Bitmap.createBitmap(screenshot, mDisplayAreaBounds.left,
+ mDisplayAreaBounds.top, mDisplayAreaBounds.width(), mDisplayAreaBounds.height());
int expectedColor = 0;
for (int pixel : pixels) {
// Check for roughly the same because there are rounding errors converting from the
@@ -267,7 +274,7 @@
}
}
- int pixelCount = screenshot.getWidth() * screenshot.getHeight();
+ int pixelCount = displayAreaScreenshot.getWidth() * displayAreaScreenshot.getHeight();
double colorRatio = (double) expectedColor / pixelCount;
Log.i(TAG, "the ratio is " + colorRatio);
return colorRatio >= 0.6;
diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
index 44a3109..c89119b 100644
--- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
+++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java
@@ -35,9 +35,9 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Point;
+import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
-import android.os.HandlerThread;
import android.os.LocaleList;
import android.os.RemoteCallback;
import android.provider.Settings;
@@ -341,6 +341,8 @@
Display.Mode dMode = mTestActivity.getWindowManager().getDefaultDisplay().getMode();
mDisplaySize = new Point(dMode.getPhysicalWidth(), dMode.getPhysicalHeight());
}
+ Rect bounds = mTestActivity.getWindowManager().getMaximumWindowMetrics().getBounds();
+ intent.putExtra(Utils.DISPLAY_AREA_BOUNDS_KEY, bounds);
intent.putExtra(Utils.DISPLAY_WIDTH_KEY, mDisplaySize.x);
intent.putExtra(Utils.DISPLAY_HEIGHT_KEY, mDisplaySize.y);
}
diff --git a/tests/tests/bluetooth/AndroidTest.xml b/tests/tests/bluetooth/AndroidTest.xml
index 9a3075b..9818962 100644
--- a/tests/tests/bluetooth/AndroidTest.xml
+++ b/tests/tests/bluetooth/AndroidTest.xml
@@ -33,6 +33,6 @@
<!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. -->
<object type="module_controller"
class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
- <option name="mainline-module-package-name" value="com.google.android.bluetooth" />
+ <option name="mainline-module-package-name" value="com.android.btservices" />
</object>
</configuration>
diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
index 49b72549..8bd1bb8 100644
--- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
+++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java
@@ -43,6 +43,9 @@
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
+import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
import android.util.Log;
@@ -140,7 +143,7 @@
} catch (Throwable t) {
if (scrollUps < 10) {
// The notification we search for is below the fold, scroll to find it
- swipeUp(uiDevice);
+ scrollNotifications();
scrollUps++;
continue;
}
@@ -200,6 +203,18 @@
50 /* numberOfSteps */);
}
+ private boolean scrollNotifications() {
+ UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true));
+ if (!scrollable.exists()) {
+ return false;
+ }
+ try {
+ return scrollable.scrollForward(50);
+ } catch (UiObjectNotFoundException e) {
+ return false;
+ }
+ }
+
private boolean isRunningInVR() {
final Context context = InstrumentationRegistry.getTargetContext();
return ((context.getResources().getConfiguration().uiMode &
diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
index 4766268..ef44528 100644
--- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java
@@ -41,6 +41,7 @@
import android.os.Build;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
+import android.os.SystemProperties;
import android.platform.test.annotations.LargeTest;
import android.platform.test.annotations.RequiresDevice;
import android.system.ErrnoException;
@@ -1012,6 +1013,9 @@
public void testDecode10BitHEIFTo10BitBitmap() {
assumeTrue(
"Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+ assumeTrue(
+ "Test needs VNDK at least T.",
+ SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
BitmapFactory.Options opt = new BitmapFactory.Options();
@@ -1028,6 +1032,9 @@
public void testDecode10BitHEIFTo8BitBitmap() {
assumeTrue(
"Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+ assumeTrue(
+ "Test needs VNDK at least T.",
+ SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
BitmapFactory.Options opt = new BitmapFactory.Options();
diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
index 6741c07..b6689d8 100644
--- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
+++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java
@@ -49,6 +49,7 @@
import android.media.MediaFormat;
import android.net.Uri;
import android.os.Build;
+import android.os.SystemProperties;
import android.util.DisplayMetrics;
import android.util.Size;
import android.util.TypedValue;
@@ -246,6 +247,9 @@
public void testDecode10BitHeif() {
assumeTrue(
"Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU));
+ assumeTrue(
+ "Test needs VNDK at least T.",
+ SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU);
assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder());
try {
diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
index 848d74c..c273a18 100644
--- a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
+++ b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java
@@ -808,7 +808,7 @@
}
private MediaRecorder allocMediaRecorder() throws Exception {
- final String outputPath = new File(Environment.getExternalStorageDirectory(),
+ final String outputPath = new File(mContext.getExternalFilesDir(null),
"record.out").getAbsolutePath();
mOutFile = new File(outputPath);
MediaRecorder mediaRecorder = new MediaRecorder();
diff --git a/tests/tests/media/codec/AndroidTest.xml b/tests/tests/media/codec/AndroidTest.xml
index ce2b7ed..a2f5d2b 100644
--- a/tests/tests/media/codec/AndroidTest.xml
+++ b/tests/tests/media/codec/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaCodecTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaCodecTestCases" />
+ <option name="version" value="7.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaCodecTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaCodecTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaCodecTestCases" />
- <option name="version" value="7.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.codec.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/common/src/android/media/cts/CodecState.java b/tests/tests/media/common/src/android/media/cts/CodecState.java
index 13e56f8..4aa9db4 100644
--- a/tests/tests/media/common/src/android/media/cts/CodecState.java
+++ b/tests/tests/media/common/src/android/media/cts/CodecState.java
@@ -169,13 +169,15 @@
}
}
- public void start() {
+ public void startCodec() {
mCodec.start();
mCodecInputBuffers = mCodec.getInputBuffers();
if (!mIsTunneled || mIsAudio) {
mCodecOutputBuffers = mCodec.getOutputBuffers();
}
+ }
+ public void play() {
if (mAudioTrack != null) {
mAudioTrack.play();
}
@@ -358,7 +360,7 @@
return null;
}
- if (mIsTunneled && !mIsAudio) {
+ if (mIsTunneled) {
if (mFirstSampleTimeUs == -1) {
mFirstSampleTimeUs = sampleTime;
}
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
index 5e4df7f..888cf23 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java
@@ -487,11 +487,13 @@
}
for (CodecState state : mVideoCodecStates.values()) {
- state.start();
+ state.startCodec();
+ state.play();
}
for (CodecState state : mAudioCodecStates.values()) {
- state.start();
+ state.startCodec();
+ state.play();
}
mDeltaTimeUs = -1;
diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
index 0b495dd..879f561 100644
--- a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
+++ b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java
@@ -44,11 +44,13 @@
/** State the player starts in, before configuration. */
private static final int STATE_IDLE = 1;
/** State of the player during initial configuration. */
- private static final int STATE_PREPARING = 2;
+ private static final int STATE_PREPARED = 2;
+ /** State of the player after starting the codecs */
+ private static final int STATE_STARTED = 3;
/** State of the player during playback. */
- private static final int STATE_PLAYING = 3;
- /** State of the player when configured but not playing. */
- private static final int STATE_PAUSED = 4;
+ private static final int STATE_PLAYING = 4;
+ /** State of the player when playback is paused. */
+ private static final int STATE_PAUSED = 5;
private Boolean mThreadStarted = false;
private byte[] mSessionId;
@@ -194,7 +196,12 @@
return true;
}
+ // Creates the extractors, identifies tracks and formats, and then calls MediaCodec.configure
public boolean prepare() throws IOException {
+ if (mState != STATE_IDLE) {
+ throw new IllegalStateException("Expected STATE_IDLE, got " + mState);
+ }
+
if (null == mAudioExtractor) {
mAudioExtractor = new MediaExtractor();
if (null == mAudioExtractor) {
@@ -237,9 +244,7 @@
return false;
}
- synchronized (mState) {
- mState = STATE_PAUSED;
- }
+ mState = STATE_PREPARED;
return true;
}
@@ -306,70 +311,56 @@
return format.containsKey(key) ? format.getInteger(key) : 0;
}
- public boolean start() {
+ // Calls MediaCodec.start
+ public void startCodec() {
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("Expected STATE_PAUSED, got " + mState);
- }
-
- for (CodecState state : mVideoCodecStates.values()) {
- state.start();
- }
-
- for (CodecState state : mAudioCodecStates.values()) {
- state.start();
- }
-
- mDeltaTimeUs = -1;
- mState = STATE_PLAYING;
+ if (mState != STATE_PREPARED) {
+ throw new IllegalStateException("Expected STATE_PREAPRED, got " + mState);
}
- return false;
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.startCodec();
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.startCodec();
+ }
+
+ mDeltaTimeUs = -1;
+ mState = STATE_STARTED;
}
- 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;
+ // Starts the decoding threads and then starts AudioTrack playback
+ public void play() {
+ if (mState != STATE_STARTED) {
+ throw new IllegalStateException("Expected STATE_STARTED, got " + mState);
}
+ mState = STATE_PLAYING;
synchronized (mThreadStarted) {
mThreadStarted = true;
mThread.start();
}
- }
- public void startThread() {
- start();
- synchronized (mThreadStarted) {
- mThreadStarted = true;
- mThread.start();
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.play();
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.play();
}
}
- // Pauses the audio track
+ // Pauses playback by pausing the AudioTrack
public void pause() {
Log.d(TAG, "pause");
- synchronized (mState) {
- if (mState == STATE_PAUSED) {
- return;
- } else if (mState != STATE_PLAYING) {
- throw new IllegalStateException();
- }
+ if (mState != STATE_PLAYING) {
+ throw new IllegalStateException("Expected STATE_PLAYING, got " + mState);
+ }
+ synchronized (mState) {
for (CodecState state : mVideoCodecStates.values()) {
state.pause();
}
@@ -382,43 +373,60 @@
}
}
- public void flush() {
- Log.d(TAG, "flush");
+ // Resume playback when paused
+ public void resume() {
+ Log.d(TAG, "resume");
+
+ if (mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+ }
synchronized (mState) {
- if (mState == STATE_PLAYING || mState == STATE_PREPARING) {
- return;
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.play();
}
for (CodecState state : mAudioCodecStates.values()) {
- state.flush();
+ state.play();
}
- for (CodecState state : mVideoCodecStates.values()) {
- state.flush();
- }
+ mState = STATE_PLAYING;
}
}
- /** Seek all tracks to their very beginning.
+ public void flush() {
+ Log.d(TAG, "flush");
+
+ if (mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.flush();
+ }
+
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.flush();
+ }
+ }
+
+ /** Seek all tracks to the first sample time.
*
* @param presentationTimeOffsetUs The offset for the presentation time to start at.
* @throws IllegalStateException if the player is not paused
*/
public void seekToBeginning(long presentationTimeOffsetUs) {
Log.d(TAG, "seekToBeginning");
- synchronized (mState) {
- if (mState != STATE_PAUSED) {
- throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
- }
+ if (mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
+ }
- for (CodecState state : mVideoCodecStates.values()) {
- state.seekToBeginning(presentationTimeOffsetUs);
- }
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.seekToBeginning(presentationTimeOffsetUs);
+ }
- for (CodecState state : mAudioCodecStates.values()) {
- state.seekToBeginning(presentationTimeOffsetUs);
- }
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.seekToBeginning(presentationTimeOffsetUs);
}
}
@@ -426,53 +434,50 @@
* Enables or disables looping. Should be called after {@link #prepare()}.
*/
public void setLoopEnabled(boolean enabled) {
- synchronized (mState) {
- if (mVideoCodecStates != null) {
- for (CodecState state : mVideoCodecStates.values()) {
- state.setLoopEnabled(enabled);
- }
- }
+ if (mState != STATE_PREPARED) {
+ throw new IllegalStateException("Expected STATE_PREPARED, got " + mState);
+ }
- if (mAudioCodecStates != null) {
- for (CodecState state : mAudioCodecStates.values()) {
- state.setLoopEnabled(enabled);
- }
- }
+ for (CodecState state : mVideoCodecStates.values()) {
+ state.setLoopEnabled(enabled);
+ }
+
+ for (CodecState state : mAudioCodecStates.values()) {
+ state.setLoopEnabled(enabled);
}
}
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;
+ 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;
}
@@ -607,6 +612,14 @@
return mVideoCodecStates.get(0).getVideoTimeUs();
}
+ public long getVideoSystemTimeNs() {
+ if (mVideoCodecStates == null || mVideoCodecStates.get(0) == null) {
+ return -1;
+ }
+ return mVideoCodecStates.get(0).getVideoTimeUs();
+
+ }
+
/**
* Returns the ordered list of video frame timestamps rendered in tunnel mode.
*
@@ -643,39 +656,23 @@
public Long queueOneVideoFrame() {
Log.d(TAG, "queueOneVideoFrame");
- if (mVideoCodecStates == null || !(mState == STATE_PLAYING || mState == STATE_PAUSED)) {
- return null;
+ if (mState != STATE_STARTED && mState != STATE_PAUSED) {
+ throw new IllegalStateException("Expected STARTED or PAUSED, got " + mState);
}
Long result = null;
- for (CodecState state : mVideoCodecStates.values()) {
- Long timestamp = state.doSomeWork(true /* mustWait */);
- if (timestamp != null) {
- result = timestamp;
+ if (mVideoCodecStates != null) {
+ for (CodecState state : mVideoCodecStates.values()) {
+ Long timestamp = state.doSomeWork(true /* mustWait */);
+ if (timestamp != null) {
+ result = timestamp;
+ }
}
}
return result;
}
/**
- * Resume playback when paused.
- *
- * @throws IllegalStateException if playback is not paused or if there is no configured audio
- * track.
- */
- public void resume() {
- Log.d(TAG, "resume");
- if (mAudioTrackState == null) {
- throw new IllegalStateException("Resuming playback with no audio track");
- }
- if (mState != STATE_PAUSED) {
- throw new IllegalStateException("Expected STATE_PAUSED, got " + mState);
- }
- mAudioTrackState.playAudioTrack();
- mState = STATE_PLAYING;
- }
-
- /**
* Configure video peek for the video codecs attached to the player.
*/
public void setVideoPeek(boolean enable) {
diff --git a/tests/tests/media/common/src/android/media/cts/TestUtils.java b/tests/tests/media/common/src/android/media/cts/TestUtils.java
index f98b3ab..20bf143 100644
--- a/tests/tests/media/common/src/android/media/cts/TestUtils.java
+++ b/tests/tests/media/common/src/android/media/cts/TestUtils.java
@@ -190,6 +190,7 @@
if (name.startsWith("c2.android.")) {
return true;
}
+ Log.d(TAG, "Test mode MTS does not test codec " + name);
return false;
}
diff --git a/tests/tests/media/decoder/AndroidTest.xml b/tests/tests/media/decoder/AndroidTest.xml
index c7d4550..55fe608 100644
--- a/tests/tests/media/decoder/AndroidTest.xml
+++ b/tests/tests/media/decoder/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaDecoderTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaDecoderTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaDecoderTestCases-1.1" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaDecoderTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaDecoderTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.decoder.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
index 145cfaf..c982376 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java
@@ -25,7 +25,6 @@
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaFormat;
-import android.media.cts.MediaCodecTunneledPlayer;
import android.media.cts.MediaHeavyPresubmitTest;
import android.media.cts.TestArgs;
import android.os.Environment;
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
index a424edb..9926f04 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java
@@ -73,6 +73,7 @@
import androidx.test.filters.SdkSuppress;
import com.android.compatibility.common.util.ApiLevelUtil;
+import com.android.compatibility.common.util.ApiTest;
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.DeviceReportLog;
import com.android.compatibility.common.util.DynamicConfigDeviceSide;
@@ -124,10 +125,10 @@
private static final int CONFIG_MODE_NONE = 0;
private static final int CONFIG_MODE_QUEUE = 1;
- private static final int CODEC_ALL = 0; // All codecs must support
- private static final int CODEC_ANY = 1; // At least one codec must support
- private static final int CODEC_DEFAULT = 2; // Default codec must support
- private static final int CODEC_OPTIONAL = 3; // Codec support is optional
+ public static final int CODEC_ALL = 0; // All codecs must support
+ public static final int CODEC_ANY = 1; // At least one codec must support
+ public static final int CODEC_DEFAULT = 2; // Default codec must support
+ public static final int CODEC_OPTIONAL = 3; // Codec support is optional
short[] mMasterBuffer;
static final String mInpPrefix = WorkDir.getMediaDirString();
@@ -141,8 +142,6 @@
private DisplayManager mDisplayManager;
static final Map<String, String> sDefaultDecoders = new HashMap<>();
- private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S);
-
protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res)
throws FileNotFoundException {
File inpFile = new File(mInpPrefix + res);
@@ -3884,11 +3883,10 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -3921,8 +3919,8 @@
/**
* Test tunneled video playback mode with HEVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoPlaybackHevc() throws Exception {
tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -3931,8 +3929,8 @@
/**
* Test tunneled video playback mode with AVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoPlaybackAvc() throws Exception {
tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -3941,8 +3939,8 @@
/**
* Test tunneled video playback mode with VP9 if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoPlaybackVp9() throws Exception {
tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
@@ -3966,11 +3964,10 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -3990,8 +3987,8 @@
/**
* Test tunneled video playback flush with HEVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoFlushHevc() throws Exception {
testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -4000,8 +3997,8 @@
/**
* Test tunneled video playback flush with AVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoFlushAvc() throws Exception {
testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -4010,23 +4007,19 @@
/**
* Test tunneled video playback flush with VP9 if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledVideoFlushVp9() throws Exception {
testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
}
/**
- * Test tunneled video peek renders the first frame when on
+ * Test that the first frame is rendered when video peek is on in tunneled mode.
*
* TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
*/
private void testTunneledVideoPeekOn(String mimeType, String videoName) throws Exception {
- if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOn requires Android 12")) {
- return;
- }
-
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4040,9 +4033,8 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
- mMediaCodecPlayer.start();
+ mMediaCodecPlayer.startCodec();
mMediaCodecPlayer.setVideoPeek(true); // Enable video peek
// Assert that onFirstTunnelFrameReady is called
@@ -4061,30 +4053,30 @@
}
/**
- * Test tunneled video peek with HEVC renders the first frame when on
+ * Test that the first frame is rendered when video peek is on for HEVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOnHevc() throws Exception {
testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
}
/**
- * Test tunneled video peek with AVC renders the first frame when on
+ * Test that the first frame is rendered when video peek is on for AVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOnAvc() throws Exception {
testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
}
/**
- * Test tunneled video peek with VP9 renders the first frame when on
+ * Test that the first frame is rendered when video peek is on for VP9 in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOnVp9() throws Exception {
testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
@@ -4092,15 +4084,11 @@
/**
- * Test tunneled video peek doesn't render the first frame when off and then turned on
+ * Test that peek off doesn't render the first frame until turned on in tunneled mode.
*
* TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
*/
private void testTunneledVideoPeekOff(String mimeType, String videoName) throws Exception {
- if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOff requires Android 12")) {
- return;
- }
-
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4114,9 +4102,8 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
- mMediaCodecPlayer.start();
+ mMediaCodecPlayer.startCodec();
mMediaCodecPlayer.setVideoPeek(false); // Disable video peek
// Assert that onFirstTunnelFrameReady is called
@@ -4142,75 +4129,40 @@
}
/**
- * Test tunneled video peek with HEVC doesn't render the first frame when off and then turned on
+ * Test that peek off doesn't render the first frame until turned on for HEC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOffHevc() throws Exception {
testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
}
/**
- * Test tunneled video peek with AVC doesn't render the first frame when off and then turned on
+ * Test that peek off doesn't render the first frame until turned on for AVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOffAvc() throws Exception {
testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
}
/**
- * Test tunneled video peek with VP9 doesn't render the first frame when off and then turned on
+ * Test that peek off doesn't render the first frame until turned on for VP9 in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"})
public void testTunneledVideoPeekOffVp9() throws Exception {
testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
}
- /**
- * Test tunneled audio PTS gaps with HEVC if supported.
- * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
- * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
- * gap.
- */
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioPtsGapsHevc() throws Exception {
- testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_HEVC,
- "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
- }
-
- /**
- * Test tunneled audio PTS gaps with AVC if supported
- * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
- * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
- * gap.
- */
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioPtsGapsAvc() throws Exception {
- testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_AVC,
- "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
- }
-
- /**
- * Test tunneled audio PTS gaps with VP9 if supported
- * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by
- * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the
- * gap.
- */
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioPtsGapsVp9() throws Exception {
- testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_VP9,
- "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
- }
-
- private void testTunneledAudioPtsGaps(String mimeType, String fileName) throws Exception {
+ /**
+ * Test that audio timestamps don't progress during audio PTS gaps in tunneled mode.
+ */
+ private void testTunneledAudioProgressWithPtsGaps(String mimeType, String fileName)
+ throws Exception {
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4224,11 +4176,10 @@
final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -4291,37 +4242,40 @@
}
/**
- * Test tunneled audioTimestamp progress with underrun, with HEVC if supported
+ * Test that audio timestamps don't progress during audio PTS gaps for HEVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
- public void testTunneledAudioTimestampProgressWithUnderrunHevc() throws Exception {
- testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPtsGapsHevc() throws Exception {
+ testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
}
/**
- * Test tunneled audioTimestamp progress with underrun, with AVC if supported.
+ * Test that audio timestamps don't progress during audio PTS gaps for AVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
- public void testTunneledAudioTimestampProgressWithUnderrunAvc() throws Exception {
- testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPtsGapsAvc() throws Exception {
+ testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
}
/**
- * Test tunneled audioTimestamp progress with underrun, with VP9 if supported.
+ * Test that audio timestamps don't progress during audio PTS gaps for VP9 in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
- public void testTunneledAudioTimestampProgressWithUnderrunVp9() throws Exception {
- testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPtsGapsVp9() throws Exception {
+ testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
}
- private void testTunneledAudioTimestampProgressWithUnderrun(
- String mimeType, String fileName) throws Exception {
+ /**
+ * Test that audio timestamps stop progressing during underrun in tunneled mode.
+ */
+ private void testTunneledAudioProgressWithUnderrun(String mimeType, String fileName)
+ throws Exception {
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4335,11 +4289,10 @@
final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -4380,9 +4333,39 @@
}
/**
- * Test accurate video rendering after a video MediaCodec flush.
+ * Test that audio timestamps stop progressing during underrun for HEVC in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithUnderrunHevc() throws Exception {
+ testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
+ "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+ }
+
+ /**
+ * Test that audio timestamps stop progressing during underrun for AVC in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithUnderrunAvc() throws Exception {
+ testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
+ "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+ }
+
+ /**
+ * Test that audio timestamps stop progressing during underrun for VP9 in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithUnderrunVp9() throws Exception {
+ testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+ }
+
+ /**
+ * Test accurate video rendering after a flush in tunneled mode.
*
- * On some devices, queuing content when the player is paused, then triggering a flush, then
+ * Test On some devices, queuing content when the player is paused, then triggering a flush, then
* queuing more content does not behave as expected. The queued content gets lost and the flush
* is really only applied once playback has resumed.
*
@@ -4390,10 +4373,6 @@
*/
private void testTunneledAccurateVideoFlush(String mimeType, String videoName)
throws Exception {
- if (!MediaUtils.check(mIsAtLeastS, "testTunneledAccurateVideoFlush requires Android 12")) {
- return;
- }
-
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4414,15 +4393,14 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
// Video peek might interfere with the test: we want to ensure that queuing more data during
// a pause does not cause displaying more video frames, which is precisely what video peek
// does.
mMediaCodecPlayer.setVideoPeek(false);
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -4434,22 +4412,72 @@
assertNotEquals("Audio timestamp has a zero frame position",
mMediaCodecPlayer.getTimestamp().framePosition, 0);
+ // Allow some time for playback to commence
+ Thread.sleep(500);
+
// Pause playback
mMediaCodecPlayer.pause();
- // Allow some time for playback to pause
- Thread.sleep(maxDrainTimeMs);
- // Verify that playback has paused
- long pauseAudioFramePositionUs = mMediaCodecPlayer.getTimestamp().framePosition;
- long pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs();
- Thread.sleep(maxDrainTimeMs);
- assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioFramePositionUs);
+ // Wait for audio to pause
+ AudioTimestamp pauseAudioTimestamp;
+ {
+ AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ // If it takes longer to pause, the UX won't feel responsive to the user
+ int audioPauseTimeoutMs = 250;
+ assertTrue(String.format("No audio pause after %d milliseconds",
+ audioPauseTimeoutMs),
+ System.currentTimeMillis() - startTimeMs < audioPauseTimeoutMs);
+ pauseAudioTimestamp = currentAudioTimestamp;
+ Thread.sleep(50);
+ currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ } while (currentAudioTimestamp.framePosition != pauseAudioTimestamp.framePosition);
+ }
+ long pauseAudioSystemTimeMs = pauseAudioTimestamp.nanoTime / 1000 / 1000;
+
+ // Wait for video to pause
+ long pauseVideoSystemTimeNs;
+ long pauseVideoPositionUs;
+ {
+ long currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano();
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ int videoUnderrunTimeoutMs = 2000;
+ assertTrue(String.format("No video pause after %d milliseconds",
+ videoUnderrunTimeoutMs),
+ System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
+ pauseVideoSystemTimeNs = currentVideoSystemTimeNs;
+ Thread.sleep(250); // onFrameRendered can get delayed in the Framework
+ currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano();
+ } while (currentVideoSystemTimeNs != pauseVideoSystemTimeNs);
+ pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs();
+ }
+ long pauseVideoSystemTimeMs = pauseVideoSystemTimeNs / 1000 / 1000;
+
+ // Video should not continue running for a long period of time after audio pauses
+ long pauseVideoToleranceMs = 500;
+ assertTrue(String.format(
+ "Video ran %d milliseconds longer than audio (video:%d audio:%d)",
+ pauseVideoToleranceMs, pauseVideoSystemTimeMs, pauseAudioSystemTimeMs),
+ pauseVideoSystemTimeMs - pauseAudioSystemTimeMs < pauseVideoToleranceMs);
+
+ // Verify that playback stays paused
+ Thread.sleep(500);
+ assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioTimestamp.framePosition);
+ assertEquals(mMediaCodecPlayer.getCurrentRenderedSystemTimeNano(), pauseVideoSystemTimeNs);
assertEquals(mMediaCodecPlayer.getVideoTimeUs(), pauseVideoPositionUs);
- // Verify audio and video are in sync
- assertTrue(String.format("Video pts (%d) is ahead of audio pts (%d)",
- pauseVideoPositionUs, pauseAudioFramePositionUs),
- pauseVideoPositionUs <= pauseAudioFramePositionUs);
+ // Verify audio and video are roughly in sync when paused
+ long framePosition = mMediaCodecPlayer.getTimestamp().framePosition;
+ long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate();
+ long pauseAudioPositionMs = pauseAudioTimestamp.framePosition * 1000 / playbackRateFps;
+ long pauseVideoPositionMs = pauseVideoPositionUs / 1000;
+ long deltaMs = pauseVideoPositionMs - pauseAudioPositionMs;
+ assertTrue(String.format(
+ "Video is %d milliseconds out of sync from audio (video:%d audio:%d)",
+ deltaMs, pauseVideoPositionMs, pauseAudioPositionMs),
+ deltaMs > -80 && deltaMs < pauseVideoToleranceMs);
// Flush both audio and video pipelines
mMediaCodecPlayer.flush();
@@ -4493,8 +4521,8 @@
/**
* Test accurate video rendering after a video MediaCodec flush with HEVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAccurateVideoFlushHevc() throws Exception {
testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC,
"video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
@@ -4503,8 +4531,8 @@
/**
* Test accurate video rendering after a video MediaCodec flush with AVC if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAccurateVideoFlushAvc() throws Exception {
testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC,
"video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
@@ -4513,49 +4541,18 @@
/**
* Test accurate video rendering after a video MediaCodec flush with VP9 if supported
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAccurateVideoFlushVp9() throws Exception {
testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9,
"bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
}
/**
- * Test tunneled audioTimestamp progress with HEVC if supported
+ * Test that audio timestamps stop progressing during pause in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioTimestampProgressHevc() throws Exception {
- testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_HEVC,
- "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
- }
-
- /**
- * Test tunneled audioTimestamp progress with AVC if supported
- */
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioTimestampProgressAvc() throws Exception {
- testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_AVC,
- "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
- }
-
- /**
- * Test tunneled audioTimestamp progress with VP9 if supported
- */
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
- @Test
- public void testTunneledAudioTimestampProgressVp9() throws Exception {
- testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_VP9,
- "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
- }
-
- /**
- * Test that AudioTrack timestamps don't advance after pause.
- */
- private void
- testTunneledAudioTimestampProgress(String mimeType, String videoName) throws Exception
- {
+ private void testTunneledAudioProgressWithPause(String mimeType, String videoName)
+ throws Exception {
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
return;
@@ -4568,11 +4565,10 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -4604,14 +4600,43 @@
assertEquals(audioTimestampAfterPause.nanoTime, mMediaCodecPlayer.getTimestamp().nanoTime);
}
+
/**
- * Test tunneled audio underrun, if supported.
- *
- * Underrun test with lower pts after underrun.
+ * Test that audio timestamps stop progressing during pause for HEVC in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPauseHevc() throws Exception {
+ testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_HEVC,
+ "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
+ }
+
+ /**
+ * Test that audio timestamps stop progressing during pause for AVC in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPauseAvc() throws Exception {
+ testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_AVC,
+ "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
+ }
+
+ /**
+ * Test that audio timestamps stop progressing during pause for VP9 in tunneled mode.
+ */
+ @Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
+ public void testTunneledAudioProgressWithPauseVp9() throws Exception {
+ testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_VP9,
+ "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
+ }
+
+ /**
+ * Test that audio underrun pauses video and resumes in-sync in tunneled mode.
*
* TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content
*/
- private void tunneledAudioUnderrun(String mimeType, String videoName, int frameRate)
+ private void tunneledAudioUnderrun(String mimeType, String videoName)
throws Exception {
if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback),
"No tunneled video playback codec found for MIME " + mimeType)) {
@@ -4625,11 +4650,10 @@
Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName));
mMediaCodecPlayer.setAudioDataSource(mediaUri, null);
mMediaCodecPlayer.setVideoDataSource(mediaUri, null);
- assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start());
assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare());
+ mMediaCodecPlayer.startCodec();
- // Starts video playback
- mMediaCodecPlayer.startThread();
+ mMediaCodecPlayer.play();
sleepUntil(() ->
mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP
&& mMediaCodecPlayer.getTimestamp() != null
@@ -4645,36 +4669,39 @@
mMediaCodecPlayer.simulateAudioUnderrun(true);
// Wait for audio underrun
- final int audioUnderrunTimeoutMs = 1000; // Arbitrary upper time limit on loop time duration
- long startTimeMs = System.currentTimeMillis();
- AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
AudioTimestamp underrunAudioTimestamp;
- do {
- assertTrue(String.format("No audio underrun after %d milliseconds",
- System.currentTimeMillis() - startTimeMs),
- System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
- underrunAudioTimestamp = currentAudioTimestamp;
- Thread.sleep(50);
- currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
- } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition);
+ {
+ AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ int audioUnderrunTimeoutMs = 1000;
+ assertTrue(String.format("No audio underrun after %d milliseconds",
+ System.currentTimeMillis() - startTimeMs),
+ System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs);
+ underrunAudioTimestamp = currentAudioTimestamp;
+ Thread.sleep(50);
+ currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition);
+ }
+ // Wait until video playback pauses due to underrunning audio
+ long pausedVideoTimeUs = -1;
+ {
+ long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ int videoPauseTimeoutMs = 2000;
+ assertTrue(String.format("No video pause after %d milliseconds",
+ videoPauseTimeoutMs),
+ System.currentTimeMillis() - startTimeMs < videoPauseTimeoutMs);
+ pausedVideoTimeUs = currentVideoTimeUs;
+ Thread.sleep(250); // onFrameRendered messages can get delayed in the Framework
+ currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+ } while (currentVideoTimeUs != pausedVideoTimeUs);
+ }
- // Wait until video playback stalls
- final int videoUnderrunTimeoutMs = 1000;
- startTimeMs = System.currentTimeMillis();
- long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
- long underrunVideoTimeUs = -1;
- do {
- assertTrue(String.format("No video underrun after %d milliseconds",
- videoUnderrunTimeoutMs),
- System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs);
- underrunVideoTimeUs = currentVideoTimeUs;
- Thread.sleep(50);
- currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
- } while (currentVideoTimeUs != underrunVideoTimeUs);
-
- // Retrieve index for the video rendered frame at the time of underrun
- int underrunVideoRenderedTimestampIndex =
+ // Retrieve index for the video rendered frame at the time of video pausing
+ int pausedVideoRenderedTimestampIndex =
mMediaCodecPlayer.getRenderedVideoFrameTimestampList().size() - 1;
// Resume audio buffering with a negative offset, in order to simulate a desynchronisation.
@@ -4683,35 +4710,38 @@
mMediaCodecPlayer.simulateAudioUnderrun(false);
// Wait until audio playback resumes
- final int audioResumeTimeoutMs = 1000;
- startTimeMs = System.currentTimeMillis();
- currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
AudioTimestamp postResumeAudioTimestamp;
- do {
- assertTrue(String.format("Audio has not resumed after %d milliseconds",
- audioResumeTimeoutMs),
- System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs);
- postResumeAudioTimestamp = currentAudioTimestamp;
- Thread.sleep(50);
- currentAudioTimestamp = mMediaCodecPlayer.getTimestamp();
- } while(currentAudioTimestamp.framePosition == postResumeAudioTimestamp.framePosition);
+ {
+ AudioTimestamp previousAudioTimestamp;
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ int audioResumeTimeoutMs = 1000;
+ assertTrue(String.format("Audio has not resumed after %d milliseconds",
+ audioResumeTimeoutMs),
+ System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs);
+ previousAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ Thread.sleep(50);
+ postResumeAudioTimestamp = mMediaCodecPlayer.getTimestamp();
+ } while (postResumeAudioTimestamp.framePosition == previousAudioTimestamp.framePosition);
+ }
// Now that audio playback has resumed, wait until video playback resumes
- // We care about the timestamp of the first output frame, rather than the exact time the
- // video resumed, which is why we only start polling after we are sure audio playback has
- // resumed.
- final int videoResumeTimeoutMs = 1000;
- startTimeMs = System.currentTimeMillis();
- currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
- long resumeVideoTimeUs = -1;
- do {
- assertTrue(String.format("Video has not resumed after %d milliseconds",
- videoResumeTimeoutMs),
- System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs);
- resumeVideoTimeUs = currentVideoTimeUs;
- Thread.sleep(50);
- currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
- } while (currentVideoTimeUs == resumeVideoTimeUs);
+ {
+ // We actually don't care about trying to capture the exact time video resumed, because
+ // we can just look at the historical list of rendered video timestamps
+ long postResumeVideoTimeUs;
+ long previousVideoTimeUs;
+ long startTimeMs = System.currentTimeMillis();
+ do {
+ int videoResumeTimeoutMs = 2000;
+ assertTrue(String.format("Video has not resumed after %d milliseconds",
+ videoResumeTimeoutMs),
+ System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs);
+ previousVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+ Thread.sleep(50);
+ postResumeVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs();
+ } while (postResumeVideoTimeUs == previousVideoTimeUs);
+ }
// The system time when rendering the first audio frame after the resume
long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate();
@@ -4721,52 +4751,74 @@
long resumeAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime - (long) elapsedTimeNs;
long resumeAudioSystemTimeMs = resumeAudioSystemTimeNs / 1000 / 1000;
- // The system time when rendering the first video frame after the resume
+ // The system time when rendering the first video frame after video playback resumes
long resumeVideoSystemTimeMs = mMediaCodecPlayer.getRenderedVideoFrameSystemTimeList()
- .get(underrunVideoRenderedTimestampIndex + 1) / 1000 / 1000;
+ .get(pausedVideoRenderedTimestampIndex + 1) / 1000 / 1000;
- // Verify that audio and video are in-sync after resume time
+ // Verify that video resumes in a reasonable amount of time after audio resumes
// Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later
resumeAudioSystemTimeMs += 100;
- long vsyncMs = 1000 / frameRate;
- long avSyncOffsetMs = resumeAudioSystemTimeMs - resumeVideoSystemTimeMs;
+ long resumeDeltaMs = resumeVideoSystemTimeMs - resumeAudioSystemTimeMs;
+ assertTrue(String.format("Video started %s milliseconds before audio resumed "
+ + "(video:%d audio:%d)", resumeDeltaMs * -1, resumeVideoSystemTimeMs,
+ resumeAudioSystemTimeMs),
+ resumeDeltaMs > 0); // video is expected to start after audio resumes
assertTrue(String.format(
- "Audio is %d milliseconds out of sync of video (audio:%d video:%d)",
- avSyncOffsetMs, resumeAudioSystemTimeMs, resumeVideoSystemTimeMs),
- Math.abs(avSyncOffsetMs) <= vsyncMs);
+ "Video started %d milliseconds after audio resumed (video:%d audio:%d)",
+ resumeDeltaMs, resumeVideoSystemTimeMs, resumeAudioSystemTimeMs),
+ resumeDeltaMs <= 600); // video starting 300ms after audio is barely noticeable
+
+ // Determine the system time of the audio frame that matches the presentation timestamp of
+ // the resumed video frame
+ long resumeVideoPresentationTimeUs = mMediaCodecPlayer.getRenderedVideoFrameTimestampList()
+ .get(pausedVideoRenderedTimestampIndex + 1);
+ long matchingAudioFramePosition = resumeVideoPresentationTimeUs * playbackRateFps / 1000 / 1000;
+ playedFrames = matchingAudioFramePosition - postResumeAudioTimestamp.framePosition;
+ elapsedTimeNs = playedFrames * (1000.0 * 1000.0 * 1000.0 / playbackRateFps);
+ long matchingAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime + (long) elapsedTimeNs;
+ long matchingAudioSystemTimeMs = matchingAudioSystemTimeNs / 1000 / 1000;
+
+ // Verify that video and audio are in sync at the time when video resumes
+ // Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later
+ matchingAudioSystemTimeMs += 100;
+ long avSyncOffsetMs = resumeVideoSystemTimeMs - matchingAudioSystemTimeMs;
+ assertTrue(String.format("Video is %d milliseconds out of sync of audio after resuming "
+ + "(video:%d, audio:%d)", avSyncOffsetMs, resumeVideoSystemTimeMs,
+ matchingAudioSystemTimeMs),
+ // some leniency in AV sync is required because Android TV STB/OTT OEMs often have
+ // to tune for imperfect downstream TVs (that have processing delays on the video)
+ // by knowingly producing HDMI output that has audio and video mildly out of sync
+ Math.abs(avSyncOffsetMs) <= 80);
}
/**
- * Test tunneled audio underrun with HEVC if supported
+ * Test that audio underrun pauses video and resumes in-sync for HEVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAudioUnderrunHevc() throws Exception {
tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC,
- "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv",
- 25);
+ "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv");
}
/**
- * Test tunneled audio underrun with AVC if supported
+ * Test that audio underrun pauses video and resumes in-sync for AVC in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAudioUnderrunAvc() throws Exception {
tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC,
- "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4",
- 25);
+ "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4");
}
/**
- * Test tunneled audio underrun with VP9 if supported
+ * Test that audio underrun pauses video and resumes in-sync for VP9 in tunneled mode.
*/
- @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
@Test
+ @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"})
public void testTunneledAudioUnderrunVp9() throws Exception {
tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9,
- "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm",
- 30);
+ "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm");
}
private void sleepUntil(Supplier<Boolean> supplier, Duration maxWait) throws Exception {
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
index f34f9e2..0857809 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java
@@ -81,9 +81,9 @@
AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER},
{"noise_6ch_44khz_aot5_dr_sbr_sig2_mp4.m4a", 6, AudioFormat.CHANNEL_OUT_5POINT1},
};
-
- for (Object [] sample: samples) {
- for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */)) {
+ for (Object[] sample: samples) {
+ for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */,
+ DecoderTest.CODEC_DEFAULT)) {
// verify correct number of channels is observed without downmixing
AudioParameter chanParams = new AudioParameter();
decodeUpdateFormat(codecName, (String) sample[0] /*resource*/, chanParams,
diff --git a/tests/tests/media/drmframework/AndroidTest.xml b/tests/tests/media/drmframework/AndroidTest.xml
index c6e311d..28aaadc 100644
--- a/tests/tests/media/drmframework/AndroidTest.xml
+++ b/tests/tests/media/drmframework/AndroidTest.xml
@@ -26,6 +26,11 @@
<option name="dynamic-config-name" value="CtsMediaDrmFrameworkTestCases" />
<option name="version" value="9.0_r1"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" />
+ <option name="version" value="7.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaDrmFrameworkTestCases-1.0" />
@@ -35,11 +40,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaDrmFrameworkTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" />
- <option name="version" value="7.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.drmframework.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
index 1a1a46f..2c1d9c5 100644
--- a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
+++ b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java
@@ -26,7 +26,6 @@
import android.media.UnsupportedSchemeException;
import android.media.cts.AudioManagerStub;
import android.media.cts.AudioManagerStubHelper;
-import android.media.cts.CodecState;
import android.media.cts.ConnectionStatus;
import android.media.cts.IConnectionStatus;
import android.media.cts.InputSurface;
diff --git a/tests/tests/media/encoder/AndroidTest.xml b/tests/tests/media/encoder/AndroidTest.xml
index 65d8d6d..4de5615 100644
--- a/tests/tests/media/encoder/AndroidTest.xml
+++ b/tests/tests/media/encoder/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaEncoderTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaEncoderTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaEncoderTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaEncoderTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaEncoderTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.encoder.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
index 4afc7aa..74aa214 100644
--- a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java
@@ -43,6 +43,7 @@
import android.media.cts.OutputSurface;
import android.media.cts.Preconditions;
import android.media.cts.TestArgs;
+import android.media.cts.TestUtils;
import android.net.Uri;
import android.platform.test.annotations.AppModeFull;
import android.util.Log;
@@ -1301,6 +1302,10 @@
if (TestArgs.shouldSkipCodec(encoder)) {
continue;
}
+ if (!TestUtils.isTestableCodecInCurrentMode(encoder)) {
+ Log.d(TAG, "Skipping tests for codec: " + encoder);
+ continue;
+ }
CodecCapabilities caps = getCodecCapabities(encoder, mediaType, true);
assertNotNull(caps);
EncoderSize encoderSize = new EncoderSize(encoder, mediaType, caps);
diff --git a/tests/tests/media/extractor/AndroidTest.xml b/tests/tests/media/extractor/AndroidTest.xml
index 007a65b..fbe2d48 100644
--- a/tests/tests/media/extractor/AndroidTest.xml
+++ b/tests/tests/media/extractor/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaExtractorTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaExtractorTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaExtractorTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaExtractorTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaExtractorTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.extractor.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml
index 58cea2c..4a2ffaf 100644
--- a/tests/tests/media/misc/AndroidTest.xml
+++ b/tests/tests/media/misc/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaMiscTestCases" />
<option name="version" value="9.0_r1"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaMiscTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaMiscTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaMiscTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaMiscTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.misc.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/muxer/AndroidTest.xml b/tests/tests/media/muxer/AndroidTest.xml
index 502f2ca..0c361e9 100644
--- a/tests/tests/media/muxer/AndroidTest.xml
+++ b/tests/tests/media/muxer/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaMuxerTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaMuxerTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaMuxerTestCases-1.1" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaMuxerTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaMuxerTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.muxer.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/media/player/AndroidTest.xml b/tests/tests/media/player/AndroidTest.xml
index e3e8b02..a0041ac 100644
--- a/tests/tests/media/player/AndroidTest.xml
+++ b/tests/tests/media/player/AndroidTest.xml
@@ -33,6 +33,11 @@
<option name="dynamic-config-name" value="CtsMediaPlayerTestCases" />
<option name="version" value="1.0"/>
</target_preparer>
+ <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
+ <option name="target" value="device" />
+ <option name="config-filename" value="CtsMediaPlayerTestCases" />
+ <option name="version" value="1.0"/>
+ </target_preparer>
<target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer">
<option name="push-all" value="true" />
<option name="media-folder-name" value="CtsMediaPlayerTestCases-1.0" />
@@ -42,11 +47,6 @@
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsMediaPlayerTestCases.apk" />
</target_preparer>
- <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher">
- <option name="target" value="device" />
- <option name="config-filename" value="CtsMediaPlayerTestCases" />
- <option name="version" value="1.0"/>
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.media.player.cts" />
<!-- setup can be expensive so limit the number of shards -->
diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp
index 8bc8222..c50c6a7 100644
--- a/tests/tests/os/Android.bp
+++ b/tests/tests/os/Android.bp
@@ -40,6 +40,7 @@
"hamcrest-library",
"modules-utils-build_system",
"platformprotosnano",
+ "safety-center-internal-data",
],
jni_uses_platform_apis: true,
jni_libs: [
diff --git a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt
index 4b91ee0..f49e065 100644
--- a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt
+++ b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt
@@ -50,17 +50,23 @@
import com.android.compatibility.common.util.click
import com.android.compatibility.common.util.depthFirstSearch
import com.android.compatibility.common.util.textAsString
+import java.io.InputStream
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.junit.Assert
import org.junit.Assert.assertThat
import org.junit.Assert.assertTrue
-import java.io.InputStream
-import java.util.concurrent.CountDownLatch
-import java.util.concurrent.TimeUnit
private const val BROADCAST_TIMEOUT_MS = 60000L
+const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled"
+const val HIBERNATION_BOOT_RECEIVER_CLASS_NAME =
+ "com.android.permissioncontroller.hibernation.HibernationOnBootReceiver"
+const val ACTION_SET_UP_HIBERNATION =
+ "com.android.permissioncontroller.action.SET_UP_HIBERNATION"
+
const val SYSUI_PKG_NAME = "com.android.systemui"
const val NOTIF_LIST_ID = "com.android.systemui:id/notification_stack_scroller"
const val CLEAR_ALL_BUTTON_ID = "dismiss_text"
@@ -82,35 +88,37 @@
fun runBootCompleteReceiver(context: Context, testTag: String) {
val pkgManager = context.packageManager
val permissionControllerPkg = pkgManager.permissionControllerPackageName
+ var permissionControllerSetupIntent = Intent(ACTION_SET_UP_HIBERNATION).apply {
+ setPackage(permissionControllerPkg)
+ setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ }
val receivers = pkgManager.queryBroadcastReceivers(
- Intent(Intent.ACTION_BOOT_COMPLETED), /* flags= */ 0)
- for (ri in receivers) {
- val pkg = ri.activityInfo.packageName
- if (pkg == permissionControllerPkg) {
- val permissionControllerSetupIntent = Intent()
- .setClassName(pkg, ri.activityInfo.name)
- .setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
- .setPackage(permissionControllerPkg)
- val countdownLatch = CountDownLatch(1)
- Log.d(testTag, "Sending boot complete broadcast directly to ${ri.activityInfo.name} " +
- "in package $permissionControllerPkg")
- context.sendOrderedBroadcast(
- permissionControllerSetupIntent,
- /* receiverPermission= */ null,
- object : BroadcastReceiver() {
- override fun onReceive(context: Context?, intent: Intent?) {
- countdownLatch.countDown()
- Log.d(testTag, "Broadcast received by $permissionControllerPkg")
- }
- },
- Handler.createAsync(Looper.getMainLooper()),
- Activity.RESULT_OK,
- /* initialData= */ null,
- /* initialExtras= */ null)
- assertTrue("Timed out while waiting for boot receiver broadcast to be received",
- countdownLatch.await(BROADCAST_TIMEOUT_MS, TimeUnit.MILLISECONDS))
+ permissionControllerSetupIntent, /* flags= */ 0)
+ if (receivers.size == 0) {
+ // May be on an older, pre-built PermissionController. In this case, try sending directly.
+ permissionControllerSetupIntent = Intent().apply {
+ setPackage(permissionControllerPkg)
+ setClassName(permissionControllerPkg, HIBERNATION_BOOT_RECEIVER_CLASS_NAME)
+ setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
}
}
+ val countdownLatch = CountDownLatch(1)
+ Log.d(testTag, "Sending boot complete broadcast directly to $permissionControllerPkg")
+ context.sendOrderedBroadcast(
+ permissionControllerSetupIntent,
+ /* receiverPermission= */ null,
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ countdownLatch.countDown()
+ Log.d(testTag, "Broadcast received by $permissionControllerPkg")
+ }
+ },
+ Handler.createAsync(Looper.getMainLooper()),
+ Activity.RESULT_OK,
+ /* initialData= */ null,
+ /* initialExtras= */ null)
+ assertTrue("Timed out while waiting for boot receiver broadcast to be received",
+ countdownLatch.await(BROADCAST_TIMEOUT_MS, TimeUnit.MILLISECONDS))
}
fun runAppHibernationJob(context: Context, tag: String) {
@@ -197,6 +205,12 @@
threshold.toString(), action)
}
+inline fun <T> withSafetyCenterEnabled(action: () -> T): T {
+ return withDeviceConfig(
+ DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_SAFETY_CENTER_ENABLED,
+ true.toString(), action)
+}
+
fun awaitAppState(pkg: String, stateMatcher: Matcher<Int>) {
val context: Context = InstrumentationRegistry.getTargetContext()
eventually {
diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
index b724be8..1d81e44 100644
--- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
+++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt
@@ -28,8 +28,11 @@
import android.content.res.Resources
import android.net.Uri
import android.os.Build
+import android.os.UserHandle
import android.platform.test.annotations.AppModeFull
import android.provider.DeviceConfig
+import android.safetycenter.SafetyCenterIssue
+import android.safetycenter.SafetyCenterManager
import android.support.test.uiautomator.By
import android.support.test.uiautomator.BySelector
import android.support.test.uiautomator.UiObject2
@@ -54,6 +57,13 @@
import com.android.compatibility.common.util.depthFirstSearch
import com.android.compatibility.common.util.uiDump
import com.android.modules.utils.build.SdkLevel
+import com.android.safetycenter.internaldata.SafetyCenterIds
+import com.android.safetycenter.internaldata.SafetyCenterIssueId
+import com.android.safetycenter.internaldata.SafetyCenterIssueKey
+import java.lang.reflect.Modifier
+import java.util.concurrent.TimeUnit
+import java.util.concurrent.atomic.AtomicReference
+import java.util.regex.Pattern
import org.hamcrest.CoreMatchers.containsString
import org.hamcrest.CoreMatchers.containsStringIgnoringCase
import org.hamcrest.CoreMatchers.equalTo
@@ -71,10 +81,6 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
-import java.lang.reflect.Modifier
-import java.util.concurrent.TimeUnit
-import java.util.concurrent.atomic.AtomicReference
-import java.util.regex.Pattern
private const val READ_CALENDAR = "android.permission.READ_CALENDAR"
private const val BLUETOOTH_CONNECT = "android.permission.BLUETOOTH_CONNECT"
@@ -103,6 +109,8 @@
companion object {
const val LOG_TAG = "AutoRevokeTest"
private const val STORE_EXACT_TIME_KEY = "permission_changes_store_exact_time"
+ private const val UNUSED_APPS_SOURCE_ID = "AndroidPermissionAutoRevoke"
+ private const val UNUSED_APPS_ISSUE_ID = "unused_apps_issue"
@JvmStatic
@BeforeClass
@@ -126,7 +134,11 @@
// Wake up the device
runShellCommandOrThrow("input keyevent KEYCODE_WAKEUP")
- runShellCommandOrThrow("input keyevent 82")
+ if ("false".equals(runShellCommandOrThrow("cmd lock_settings get-disabled"))) {
+ // Unlock screen only when it's lock settings enabled to prevent showing "wallpaper
+ // picker" which may cover another UI elements on freeform window configuration.
+ runShellCommandOrThrow("input keyevent 82")
+ }
if (isAutomotiveDevice()) {
supportedApkPath = APK_PATH_S_APP
@@ -434,6 +446,88 @@
}
}
+ @AppModeFull(reason = "Uses separate apps for testing")
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @Test
+ fun testAutoRevoke_showsUpInSafetyCenter() {
+ withSafetyCenterEnabled {
+ withUnusedThresholdMs(3L) {
+ withDummyApp {
+ startAppAndAcceptPermission()
+
+ killDummyApp()
+
+ // Run
+ runAppHibernationJob(context, LOG_TAG)
+
+ // Verify
+ val safetyCenterManager =
+ context.getSystemService(SafetyCenterManager::class.java)!!
+ eventually {
+ val issues = ArrayList<SafetyCenterIssue>()
+ runWithShellPermissionIdentity {
+ val safetyCenterData = safetyCenterManager!!.safetyCenterData
+ issues.addAll(safetyCenterData.issues)
+ }
+ val issueId = SafetyCenterIds.encodeToString(
+ SafetyCenterIssueId.newBuilder()
+ .setSafetyCenterIssueKey(SafetyCenterIssueKey.newBuilder()
+ .setSafetySourceId(UNUSED_APPS_SOURCE_ID)
+ .setSafetySourceIssueId(UNUSED_APPS_ISSUE_ID)
+ .setUserId(UserHandle.myUserId())
+ .build())
+ .setIssueTypeId(UNUSED_APPS_ISSUE_ID)
+ .build())
+ assertTrue(issues.any { it.id == issueId })
+ }
+ }
+ }
+ }
+ }
+
+ @AppModeFull(reason = "Uses separate apps for testing")
+ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
+ @Test
+ fun testAutoRevoke_goToUnusedAppsPage_removesSafetyCenterIssue() {
+ withSafetyCenterEnabled {
+ withUnusedThresholdMs(3L) {
+ withDummyApp {
+ startAppAndAcceptPermission()
+
+ killDummyApp()
+
+ // Run
+ runAppHibernationJob(context, LOG_TAG)
+
+ // Go to unused apps page
+ openUnusedAppsNotification()
+ waitFindObject(By.text(supportedAppPackageName))
+
+ // Verify
+ val safetyCenterManager =
+ context.getSystemService(SafetyCenterManager::class.java)!!
+ eventually {
+ val issues = ArrayList<SafetyCenterIssue>()
+ runWithShellPermissionIdentity {
+ val safetyCenterData = safetyCenterManager!!.safetyCenterData
+ issues.addAll(safetyCenterData.issues)
+ }
+ val issueId = SafetyCenterIds.encodeToString(
+ SafetyCenterIssueId.newBuilder()
+ .setSafetyCenterIssueKey(SafetyCenterIssueKey.newBuilder()
+ .setSafetySourceId(UNUSED_APPS_SOURCE_ID)
+ .setSafetySourceIssueId(UNUSED_APPS_ISSUE_ID)
+ .setUserId(UserHandle.myUserId())
+ .build())
+ .setIssueTypeId(UNUSED_APPS_ISSUE_ID)
+ .build())
+ assertFalse(issues.any { it.id == issueId })
+ }
+ }
+ }
+ }
+ }
+
private fun isAutomotiveDevice(): Boolean {
return context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
}
diff --git a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
index 86a680e..28d58d6 100644
--- a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
+++ b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt
@@ -58,7 +58,7 @@
@RunWith(AndroidJUnit4::class)
@AppModeFull(
reason = "Cannot set system settings as instant app. Also we never show an accessibility " +
- "notification for instant apps."
+ "notification for instant apps."
)
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
class AccessibilityPrivacySourceTest {
@@ -243,12 +243,27 @@
"cmd jobscheduler reset-execution-quota -u " +
"${Process.myUserHandle().identifier} $permissionControllerPackage")
- context.sendBroadcast(
- Intent().apply {
- setClassName(permissionControllerPackage, AccessibilityOnBootReceiver)
- setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ // Setup up permission controller again (simulate a reboot)
+ val permissionControllerSetupIntent =
+ Intent(ACTION_SET_UP_ACCESSIBILITY_CHECK).apply {
setPackage(permissionControllerPackage)
- })
+ setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ }
+
+ // Query for the setup broadcast receiver
+ val resolveInfos =
+ context.packageManager.queryBroadcastReceivers(permissionControllerSetupIntent, 0)
+
+ if (resolveInfos.size > 0) {
+ context.sendBroadcast(permissionControllerSetupIntent)
+ } else {
+ context.sendBroadcast(
+ Intent().apply {
+ setClassName(permissionControllerPackage, AccessibilityOnBootReceiver)
+ setFlags(Intent.FLAG_RECEIVER_FOREGROUND)
+ setPackage(permissionControllerPackage)
+ })
+ }
// Wait until jobs are set up
TestUtils.eventually(
@@ -288,6 +303,8 @@
private const val AccessibilityOnBootReceiver =
"com.android.permissioncontroller.privacysources.AccessibilityOnBootReceiver"
+ private const val ACTION_SET_UP_ACCESSIBILITY_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_ACCESSIBILITY_CHECK"
@get:ClassRule
@JvmStatic
diff --git a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
index 6ce52b1..9daeff2 100644
--- a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java
@@ -17,7 +17,6 @@
package android.permission.cts;
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
-import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.os.Process.myUserHandle;
import static android.permission.cts.PermissionUtils.clearAppState;
@@ -114,6 +113,11 @@
private static final String PROPERTY_JOB_SCHEDULER_RATE_LIMIT_WINDOW_MILLIS =
"qc_rate_limiting_window_ms";
+ private static final String ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_NOTIFICATION_LISTENER_CHECK";
+ private static final String NotificationListenerOnBootReceiver =
+ "com.android.permissioncontroller.privacysources.SetupPeriodicNotificationListenerCheck";
+
/**
* ID for notification shown by
* {@link com.android.permissioncontroller.privacysources.NotificationListenerCheck}.
@@ -374,19 +378,21 @@
}, UNEXPECTED_TIMEOUT_MILLIS);
// Setup up permission controller again (simulate a reboot)
- Intent permissionControllerSetupIntent = null;
- for (ResolveInfo ri : sContext.getPackageManager().queryBroadcastReceivers(
- new Intent(ACTION_BOOT_COMPLETED), 0)) {
- String pkg = ri.activityInfo.packageName;
+ Intent permissionControllerSetupIntent = new Intent(
+ ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK).setPackage(
+ PERMISSION_CONTROLLER_PKG).setFlags(FLAG_RECEIVER_FOREGROUND);
- if (pkg.equals(PERMISSION_CONTROLLER_PKG)) {
- permissionControllerSetupIntent = new Intent()
- .setClassName(pkg, ri.activityInfo.name)
- .setFlags(FLAG_RECEIVER_FOREGROUND)
- .setPackage(PERMISSION_CONTROLLER_PKG);
+ // Query for the setup broadcast receiver
+ List<ResolveInfo> resolveInfos = sContext.getPackageManager().queryBroadcastReceivers(
+ permissionControllerSetupIntent, 0);
- sContext.sendBroadcast(permissionControllerSetupIntent);
- }
+ if (resolveInfos.size() > 0) {
+ sContext.sendBroadcast(permissionControllerSetupIntent);
+ } else {
+ sContext.sendBroadcast(new Intent()
+ .setClassName(PERMISSION_CONTROLLER_PKG, NotificationListenerOnBootReceiver)
+ .setFlags(FLAG_RECEIVER_FOREGROUND)
+ .setPackage(PERMISSION_CONTROLLER_PKG));
}
// Wait until jobs are set up
diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
index b41fb08..ceb4ede 100644
--- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java
@@ -23,7 +23,6 @@
import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.content.Context.BIND_AUTO_CREATE;
import static android.content.Context.BIND_NOT_FOREGROUND;
-import static android.content.Intent.ACTION_BOOT_COMPLETED;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import static android.location.Criteria.ACCURACY_FINE;
import static android.os.Process.myUserHandle;
@@ -41,7 +40,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
@@ -120,6 +118,8 @@
"/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk";
private static final String TEST_APP_LOCATION_FG_ACCESS_APK =
"/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk";
+ private static final String ACTION_SET_UP_LOCATION_ACCESS_CHECK =
+ "com.android.permissioncontroller.action.SET_UP_LOCATION_ACCESS_CHECK";
private static final int LOCATION_ACCESS_CHECK_JOB_ID = 0;
private static final int LOCATION_ACCESS_CHECK_NOTIFICATION_ID = 0;
@@ -153,6 +153,10 @@
private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager()
.getPermissionControllerPackageName();
+ private static final String LocationAccessCheckOnBootReceiver =
+ "com.android.permissioncontroller.permission.service"
+ + ".LocationAccessCheck$SetupPeriodicBackgroundLocationAccessCheck";
+
/**
* The result of {@link #assumeCanGetFineLocation()}, so we don't have to run it over and over
@@ -389,6 +393,11 @@
* Force a run of the location check.
*/
private static void runLocationCheck() throws Throwable {
+ // If the job isn't setup, do it before running a location check
+ if (!isLocationAccessJobSetup(myUserHandle().getIdentifier())) {
+ setupLocationAccessCheckJob();
+ }
+
// Sleep a little bit to make sure we don't have overlap in timing
Thread.sleep(1000);
@@ -676,38 +685,47 @@
}
}, UNEXPECTED_TIMEOUT_MILLIS);
- // Setup up permission controller again (simulate a reboot)
- Intent permissionControllerSetupIntent = null;
- for (ResolveInfo ri : sContext.getPackageManager().queryBroadcastReceivers(
- new Intent(ACTION_BOOT_COMPLETED), 0)) {
- String pkg = ri.activityInfo.packageName;
-
- if (pkg.equals(PERMISSION_CONTROLLER_PKG)) {
- permissionControllerSetupIntent = new Intent()
- .setClassName(pkg, ri.activityInfo.name)
- .setFlags(FLAG_RECEIVER_FOREGROUND)
- .setPackage(PERMISSION_CONTROLLER_PKG);
-
- sContext.sendBroadcast(permissionControllerSetupIntent);
- }
- }
+ setupLocationAccessCheckJob();
// Wait until jobs are set up
eventually(() -> {
- JobSchedulerServiceDumpProto dump = getJobSchedulerDump();
-
- for (RegisteredJob job : dump.registeredJobs) {
- if (job.dump.sourceUserId == currentUserId
- && job.dump.sourcePackageName.equals(PERMISSION_CONTROLLER_PKG)
- && job.dump.jobInfo.service.className.contains("LocationAccessCheck")) {
- return;
- }
- }
-
- fail("Permission controller jobs not found");
+ assertTrue("LocationAccessCheck job not found",
+ isLocationAccessJobSetup(currentUserId));
}, UNEXPECTED_TIMEOUT_MILLIS);
}
+ private static boolean isLocationAccessJobSetup(int currentUserId) throws Exception {
+ JobSchedulerServiceDumpProto dump = getJobSchedulerDump();
+ for (RegisteredJob job : dump.registeredJobs) {
+ if (job.dump.sourceUserId == currentUserId
+ && job.dump.sourcePackageName.equals(PERMISSION_CONTROLLER_PKG)
+ && job.dump.jobInfo.service.className.contains("LocationAccessCheck")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void setupLocationAccessCheckJob() {
+ // Setup location access check
+ Intent permissionControllerSetupIntent = new Intent(
+ ACTION_SET_UP_LOCATION_ACCESS_CHECK).setPackage(
+ PERMISSION_CONTROLLER_PKG).setFlags(FLAG_RECEIVER_FOREGROUND);
+
+ // Query for the setup broadcast receiver
+ List<ResolveInfo> resolveInfos = sContext.getPackageManager().queryBroadcastReceivers(
+ permissionControllerSetupIntent, 0);
+
+ if (resolveInfos.size() > 0) {
+ sContext.sendBroadcast(permissionControllerSetupIntent);
+ } else {
+ sContext.sendBroadcast(new Intent()
+ .setClassName(PERMISSION_CONTROLLER_PKG, LocationAccessCheckOnBootReceiver)
+ .setFlags(FLAG_RECEIVER_FOREGROUND)
+ .setPackage(PERMISSION_CONTROLLER_PKG));
+ }
+ }
+
/**
* Unregister {@link NotificationListener}.
*/
diff --git a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
index f057b21..931ecd1 100644
--- a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
+++ b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt
@@ -67,7 +67,7 @@
@JvmStatic
fun assertSafetyCenterStarted() {
// CollapsingToolbar title can't be found by text, so using description instead.
- waitFindObject(By.desc("Security & Privacy"))
+ waitFindObject(By.desc("Security & privacy"))
}
@JvmStatic
diff --git a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
index 6693aa3..8bd15373 100644
--- a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt
@@ -20,6 +20,7 @@
import android.os.Build
import androidx.test.filters.SdkSuppress
import com.android.compatibility.common.util.SystemUtil
+import org.junit.Assume
import org.junit.Test
/**
@@ -96,6 +97,8 @@
@Test
fun testWhenVisualIsDeniedManuallyThenShouldDenyAllPermissions() {
+ // TODO: Re-enable after b/239249703 is fixed
+ Assume.assumeFalse("skip on TV due to flaky", isTv)
installPackage(APP_APK_PATH_23)
grantAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23)
revokeAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23)
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
index 5355fdc..7d0ba79 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt
@@ -45,6 +45,7 @@
private const val TIMELINE_7_DAYS_DESCRIPTION = "in the past 7 days"
private const val DASHBOARD_7_DAYS_DESCRIPTION = "7 days"
private const val PRIV_DASH_7_DAY_ENABLED = "privacy_dashboard_7_day_toggle"
+private const val REFRESH = "Refresh"
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.S)
class PermissionHistoryTest : BasePermissionHubTest() {
@@ -100,9 +101,22 @@
waitFindObject(By.textContains(APP_LABEL_1))
openPermissionDashboard()
- waitFindObject(By.res("android:id/title").textContains("Microphone")).click()
- waitFindObject(By.textContains(micLabel))
- waitFindObject(By.textContains(APP_LABEL_1))
+
+ SystemUtil.eventually {
+ try {
+ waitFindObject(By.hasChild(By.textContains("Microphone"))
+ .hasChild(By.textStartsWith("Used by")))
+ .click()
+ waitFindObject(By.textContains(micLabel))
+ waitFindObject(By.textContains(APP_LABEL_1))
+ } catch (e: Exception) {
+ // Sometimes the dashboard was in the state from previous failed tests.
+ // Clicking the refresh button to get the most recent access.
+ waitFindObject(By.descContains(REFRESH)).click()
+ throw e
+ }
+ }
+
pressBack()
pressBack()
}
diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
index 598a59a..872a243 100644
--- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
+++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt
@@ -78,6 +78,8 @@
private const val UNEXPECTED_TIMEOUT_MILLIS = 1000
private const val TIMEOUT_MILLIS: Long = 20000
private const val TV_MIC_INDICATOR_WINDOW_TITLE = "MicrophoneCaptureIndicator"
+private const val MIC_LABEL_NAME = "microphone_toggle_label_qs"
+private const val CAMERA_LABEL_NAME = "camera_toggle_label_qs"
class CameraMicIndicatorsPermissionTest {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -90,11 +92,9 @@
private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
private val isCar = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ private val micLabel = getPermissionControllerString(MIC_LABEL_NAME)
+ private val cameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME)
private var wasEnabled = false
- private val micLabel = packageManager.getPermissionGroupInfo(
- Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString()
- private val cameraLabel = packageManager.getPermissionGroupInfo(
- Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString()
private var isScreenOn = false
private var screenTimeoutBeforeTest: Long = 0L
@@ -520,4 +520,18 @@
automatorMethod(remainingTime)
}
}
+
+ private fun getPermissionControllerString(resourceName: String): String {
+ val permissionControllerPkg = context.packageManager.permissionControllerPackageName
+ try {
+ val permissionControllerContext =
+ context.createPackageContext(permissionControllerPkg, 0)
+ val resourceId =
+ permissionControllerContext.resources.getIdentifier(
+ resourceName, "string", "com.android.permissioncontroller")
+ return permissionControllerContext.getString(resourceId)
+ } catch (e: PackageManager.NameNotFoundException) {
+ throw RuntimeException(e)
+ }
+ }
}
\ No newline at end of file
diff --git a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
index 8cff38a..67fa1bc 100644
--- a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
+++ b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt
@@ -25,6 +25,7 @@
import android.content.ContextParams
import android.content.Intent
import android.content.pm.PackageManager.FEATURE_LEANBACK
+import android.content.pm.PackageManager.FEATURE_TELEPHONY
import android.net.Uri
import android.os.Bundle
import android.os.Process
@@ -44,6 +45,7 @@
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Assume.assumeFalse
+import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.mockito.ArgumentMatcher
@@ -105,6 +107,7 @@
@Throws(Exception::class)
fun testSelfSmsAccess() {
assumeNotTv()
+ assumeHasTelephony()
testSelfAccess(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -178,6 +181,7 @@
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnother() {
assumeNotTv()
+ assumeHasTelephony()
testUntrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -225,6 +229,7 @@
@Throws(Exception::class)
fun testUntrustedSmsAccessAttributeToAnotherThroughIntermediary() {
assumeNotTv()
+ assumeHasTelephony()
testUntrustedAccessAttributeToAnotherThroughIntermediary(
Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
@@ -323,6 +328,7 @@
@Throws(Exception::class)
fun testTrustedAccessSmsAttributeToAnother() {
assumeNotTv()
+ assumeHasTelephony()
testTrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI,
Manifest.permission.READ_SMS)
}
@@ -666,6 +672,7 @@
get() = InstrumentationRegistry.getInstrumentation()
private val isTv = context.packageManager.hasSystemFeature(FEATURE_LEANBACK)
+ private val isTel = context.packageManager.hasSystemFeature(FEATURE_TELEPHONY)
fun ensureAuxiliaryAppsNotRunningAndNoResidualProcessState() {
SystemUtil.runShellCommand("am force-stop $RECEIVER_PACKAGE_NAME")
@@ -1178,5 +1185,6 @@
}
private fun assumeNotTv() = assumeFalse(isTv)
+ private fun assumeHasTelephony() = assumeTrue(isTel)
}
}
diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
index 29cf672..782f30c 100644
--- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
+++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java
@@ -103,7 +103,8 @@
}
private void assertScreenshotsAreEqual(Bitmap before, Bitmap after) {
- assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after));
+ // TODO(b/227553681): remove the precision=0.99 arg so it does a pixel-by-pixel check
+ assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after, 0.99));
}
private void assertTextShown(String text) {
diff --git a/tests/tests/provider/OWNERS b/tests/tests/provider/OWNERS
index 1e318ea..7c9a6b2 100644
--- a/tests/tests/provider/OWNERS
+++ b/tests/tests/provider/OWNERS
@@ -1,7 +1,13 @@
-# Bug component: 655625
-
-include platform/frameworks/base:/core/java/android/os/storage/OWNERS
-
tgunn@google.com
nicksauer@google.com
nona@google.com
+
+# Storage team ownership
+
+# Bug component: 655625 = per-file *MediaStore*
+
+per-file *MediaStore* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file Android.bp = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file AndroidManifest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file AndroidTest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
+per-file OWNERS = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS
diff --git a/tests/tests/security/res/raw/cve_2022_22087.mkv b/tests/tests/security/res/raw/cve_2022_22087.mkv
new file mode 100644
index 0000000..0b25fe4
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_22087.mkv
Binary files differ
diff --git a/tests/tests/security/res/raw/cve_2022_25657.mkv b/tests/tests/security/res/raw/cve_2022_25657.mkv
new file mode 100644
index 0000000..3d5f70e
--- /dev/null
+++ b/tests/tests/security/res/raw/cve_2022_25657.mkv
Binary files differ
diff --git a/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java b/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java
new file mode 100644
index 0000000..c6b7e35
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2022 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.security.cts;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.OPSTR_FINE_LOCATION;
+
+import static com.android.compatibility.common.util.SystemUtil.eventually;
+import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity;
+
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.location.LocationManager;
+import android.os.PackageTagsList;
+import android.os.Process;
+import android.os.UserHandle;
+import android.platform.test.annotations.AsbSecurityTest;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+public class LocationDisabledAppOpsTest extends StsExtraBusinessLogicTestCase {
+
+ private final Context mContext = InstrumentationRegistry.getContext();
+ private LocationManager mLm;
+ private AppOpsManager mAom;
+
+ @Before
+ public void setUp() {
+ mLm = mContext.getSystemService(LocationManager.class);
+ mAom = mContext.getSystemService(AppOpsManager.class);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 231496105)
+ public void testLocationAppOpIsIgnoredForAppsWhenLocationIsDisabled() {
+ PackageTagsList ignoreList = mLm.getIgnoreSettingsAllowlist();
+
+ UserHandle[] userArr = {UserHandle.SYSTEM};
+ runWithShellPermissionIdentity(() -> {
+ userArr[0] = UserHandle.of(ActivityManager.getCurrentUser());
+ });
+
+ UserHandle user = userArr[0];
+
+ boolean wasEnabled = mLm.isLocationEnabledForUser(user);
+
+ try {
+ runWithShellPermissionIdentity(() -> {
+ mLm.setLocationEnabledForUser(false, user);
+ });
+ List<PackageInfo> pkgs =
+ mContext.getPackageManager().getInstalledPackagesAsUser(
+ 0, user.getIdentifier());
+
+ eventually(() -> {
+ List<String> bypassedNoteOps = new ArrayList<>();
+ List<String> bypassedCheckOps = new ArrayList<>();
+ for (PackageInfo pi : pkgs) {
+ ApplicationInfo ai = pi.applicationInfo;
+ if (ai.uid != Process.SYSTEM_UID) {
+ final int[] mode = {MODE_ALLOWED};
+ runWithShellPermissionIdentity(() -> {
+ mode[0] = mAom.noteOpNoThrow(
+ OPSTR_FINE_LOCATION, ai.uid, ai.packageName);
+ });
+ if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName)) {
+ bypassedNoteOps.add(pi.packageName);
+ }
+
+
+ mode[0] = MODE_ALLOWED;
+ runWithShellPermissionIdentity(() -> {
+ mode[0] = mAom
+ .checkOpNoThrow(OPSTR_FINE_LOCATION, ai.uid, ai.packageName);
+ });
+ if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName)) {
+ bypassedCheckOps.add(pi.packageName);
+ }
+
+ }
+ }
+
+ String msg = "";
+ if (!bypassedNoteOps.isEmpty()) {
+ msg += "Apps which still have access from noteOp " + bypassedNoteOps;
+ }
+ if (!bypassedCheckOps.isEmpty()) {
+ msg += (msg.isEmpty() ? "" : "\n\n")
+ + "Apps which still have access from checkOp " + bypassedCheckOps;
+ }
+ if (!msg.isEmpty()) {
+ Assert.fail(msg);
+ }
+ });
+ } finally {
+ runWithShellPermissionIdentity(() -> {
+ mLm.setLocationEnabledForUser(wasEnabled, user);
+ });
+ }
+ }
+
+}
+
diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java
index cdb27a8..f0d4ab3 100644
--- a/tests/tests/security/src/android/security/cts/StagefrightTest.java
+++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java
@@ -1809,6 +1809,18 @@
before any existing test methods
***********************************************************/
@Test
+ @AsbSecurityTest(cveBugId = 223209610)
+ public void testStagefright_cve_2022_22087() throws Exception {
+ doStagefrightTest(R.raw.cve_2022_22087);
+ }
+
+ @Test
+ @AsbSecurityTest(cveBugId = 228101835)
+ public void testStagefright_cve_2022_25657() throws Exception {
+ doStagefrightTest(R.raw.cve_2022_25657);
+ }
+
+ @Test
@AsbSecurityTest(cveBugId = 231156126)
public void testStagefright_cve_2022_22059() throws Exception {
doStagefrightTest(R.raw.cve_2022_22059);
diff --git a/tests/tests/systemui/Android.bp b/tests/tests/systemui/Android.bp
index e33d8fc..3a68e4d 100644
--- a/tests/tests/systemui/Android.bp
+++ b/tests/tests/systemui/Android.bp
@@ -39,6 +39,7 @@
"androidx.test.ext.junit",
"androidx.test.uiautomator",
"cts-wm-util",
+ "permission-test-util-lib",
"ub-uiautomator",
],
srcs: [
diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml
index f55ed3f..d4ba3b3 100644
--- a/tests/tests/systemui/AndroidManifest.xml
+++ b/tests/tests/systemui/AndroidManifest.xml
@@ -26,6 +26,7 @@
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/>
<!-- Required by flickerlib to dump window states -->
<uses-permission android:name="android.permission.DUMP"/>
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<application android:requestLegacyExternalStorage="true">
<activity android:name=".LightBarActivity"
diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
index ffa58ba..5079217 100644
--- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
+++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java
@@ -16,6 +16,9 @@
package android.systemui.cts;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
+import static android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL;
+import static android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS;
import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar;
import static android.server.wm.BarTestUtils.assumeHasColoredStatusBar;
import static android.server.wm.BarTestUtils.assumeStatusBarContainsCutout;
@@ -33,7 +36,10 @@
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Insets;
+import android.os.Process;
import android.os.SystemClock;
+import android.permission.PermissionManager;
+import android.permission.cts.PermissionUtils;
import android.platform.test.annotations.AppModeFull;
import android.view.Gravity;
import android.view.InputDevice;
@@ -45,6 +51,7 @@
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.ThrowingRunnable;
import org.junit.Rule;
@@ -244,22 +251,23 @@
}
private void runInNotificationSession(ThrowingRunnable task) throws Exception {
+ Context context = getInstrumentation().getContext();
+ String packageName = getInstrumentation().getTargetContext().getPackageName();
try {
- mNm = (NotificationManager) getInstrumentation().getContext()
- .getSystemService(Context.NOTIFICATION_SERVICE);
+ PermissionUtils.grantPermission(packageName, POST_NOTIFICATIONS);
+ mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
NotificationChannel channel1 = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW);
mNm.createNotificationChannel(channel1);
// post 10 notifications to ensure enough icons in the status bar
for (int i = 0; i < 10; i++) {
- Notification.Builder noti1 = new Notification.Builder(
- getInstrumentation().getContext(),
- NOTIFICATION_CHANNEL_ID)
- .setSmallIcon(R.drawable.ic_save)
- .setChannelId(NOTIFICATION_CHANNEL_ID)
- .setPriority(Notification.PRIORITY_LOW)
- .setGroup(NOTIFICATION_GROUP_KEY);
+ Notification.Builder noti1 =
+ new Notification.Builder(context, NOTIFICATION_CHANNEL_ID)
+ .setSmallIcon(R.drawable.ic_save)
+ .setChannelId(NOTIFICATION_CHANNEL_ID)
+ .setPriority(Notification.PRIORITY_LOW)
+ .setGroup(NOTIFICATION_GROUP_KEY);
mNm.notify(NOTIFICATION_TAG, i, noti1.build());
}
@@ -267,6 +275,16 @@
} finally {
mNm.cancelAll();
mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
+
+ // Use test API to prevent PermissionManager from killing the test process when revoking
+ // permission.
+ SystemUtil.runWithShellPermissionIdentity(
+ () -> context.getSystemService(PermissionManager.class)
+ .revokePostNotificationPermissionWithoutKillForTest(
+ packageName,
+ Process.myUserHandle().getIdentifier()),
+ REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL,
+ REVOKE_RUNTIME_PERMISSIONS);
}
}
diff --git a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java b/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java
deleted file mode 100644
index b74678b..0000000
--- a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2022 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.systemui.cts;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
-import android.app.ActivityManager;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.server.wm.WindowManagerStateHelper;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.Until;
-
-import androidx.test.InstrumentationRegistry;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.compatibility.common.util.SystemUtil;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Tests related MediaOutputDialog:
- *
- * atest MediaDialogTest
- */
-@RunWith(AndroidJUnit4.class)
-public class MediaOutputDialogTest {
-
- private static final int TIMEOUT = 5000;
- private static final String ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG =
- "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG";
- private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui";
- public static final String EXTRA_PACKAGE_NAME = "package_name";
- public static final String TEST_PACKAGE_NAME = "com.android.package.test";
- private static final BySelector MEDIA_DIALOG_SELECTOR = By.res(SYSTEMUI_PACKAGE_NAME,
- "media_output_dialog");
- private WindowManagerStateHelper mWmState = new WindowManagerStateHelper();
-
- private Context mContext;
- private UiDevice mDevice;
- private String mLauncherPackage;
- private boolean mHasTouchScreen;
- private ActivityManager mActivityManager;
- private static final int DURATION_SCREEN_TOGGLE = 2000;
-
- @Before
- public void setUp() {
- mContext = InstrumentationRegistry.getTargetContext();
- mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
- final PackageManager packageManager = mContext.getPackageManager();
-
- mHasTouchScreen = packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN)
- || packageManager.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH);
-
- Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
- launcherIntent.addCategory(Intent.CATEGORY_HOME);
- ResolveInfo resolveInfo = packageManager.resolveActivity(launcherIntent,
- PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY));
- assumeFalse("Skipping test: can't get resolve info", resolveInfo == null);
- assumeFalse("Skipping test: not supported on automotive yet",
- packageManager.hasSystemFeature(
- PackageManager.FEATURE_AUTOMOTIVE));
- mLauncherPackage = resolveInfo.activityInfo.packageName;
- mActivityManager = mContext.getSystemService(ActivityManager.class);
- }
-
- @Test
- public void mediaOutputDialog_correctDialog() throws Exception {
- prepareDevice();
- assumeTrue(mHasTouchScreen);
- try {
- if (mActivityManager.isLowRamDevice()) {
- SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
- "appops set " + SYSTEMUI_PACKAGE_NAME + " SYSTEM_ALERT_WINDOW allow");
- }
-
- launchMediaOutputDialog();
-
- assertThat(mDevice.wait(Until.hasObject(MEDIA_DIALOG_SELECTOR), TIMEOUT)).isTrue();
- } finally {
- if (mActivityManager.isLowRamDevice()) {
- SystemUtil.runShellCommand(InstrumentationRegistry.getInstrumentation(),
- "appops set " + SYSTEMUI_PACKAGE_NAME + " SYSTEM_ALERT_WINDOW ignore");
- }
- }
- }
-
- private void prepareDevice() throws Exception {
- mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP");
- mDevice.executeShellCommand("wm dismiss-keyguard");
- // Since the screen on/off intent is ordered, they will not be sent right now.
- mWmState.waitForKeyguardGone();
- }
-
- private void launchMediaOutputDialog() {
- mDevice.pressHome();
- mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT);
-
- Intent intent = new Intent();
- intent.setPackage(SYSTEMUI_PACKAGE_NAME)
- .setAction(ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG)
- .putExtra(EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME);
-
- mContext.sendBroadcast(intent);
- }
-
-}
diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS
index 02848c8..89e4f4d 100644
--- a/tests/tests/telephony/OWNERS
+++ b/tests/tests/telephony/OWNERS
@@ -1,2 +1,17 @@
-file:platform/frameworks/opt/telephony:/OWNERS
-
+# Bug component: 20868
+amagup@google.com
+amallampati@google.com
+amruthr@google.com
+breadley@google.com
+chinmayd@google.com
+fionaxu@google.com
+huiwang@google.com
+jackyu@google.com
+jayachandranc@google.com
+linggm@google.com
+rgreenwalt@google.com
+sarahchin@google.com
+sasindran@google.com
+tgunn@google.com
+tjstuart@google.com
+xiaotonj@google.com
diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
index f2523a4..0bed8a7 100644
--- a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java
@@ -34,6 +34,7 @@
public class MockModemService extends Service {
private static final String TAG = "MockModemService";
+ private static final String RESOURCE_PACKAGE_NAME = "android";
public static final int TEST_TIMEOUT_MS = 30000;
public static final String IRADIOCONFIG_INTERFACE = "android.telephony.mockmodem.iradioconfig";
@@ -233,9 +234,18 @@
}
public int getNumPhysicalSlots() {
- int numPhysicalSlots =
+ int numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN;
+ int resourceId =
sContext.getResources()
- .getInteger(com.android.internal.R.integer.config_num_physical_slots);
+ .getIdentifier(
+ "config_num_physical_slots", "integer", RESOURCE_PACKAGE_NAME);
+
+ if (resourceId > 0) {
+ numPhysicalSlots = sContext.getResources().getInteger(resourceId);
+ } else {
+ Log.d(TAG, "Fail to get the resource Id, using default: " + numPhysicalSlots);
+ }
+
if (numPhysicalSlots > MockSimService.MOCK_SIM_SLOT_MAX) {
Log.d(
TAG,
@@ -245,6 +255,15 @@
+ MockSimService.MOCK_SIM_SLOT_MAX
+ ").");
numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MAX;
+ } else if (numPhysicalSlots <= MockSimService.MOCK_SIM_SLOT_MIN) {
+ Log.d(
+ TAG,
+ "Number of physical Slot ("
+ + numPhysicalSlots
+ + ") < mock sim slot support. Reset to min number supported ("
+ + MockSimService.MOCK_SIM_SLOT_MIN
+ + ").");
+ numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN;
}
return numPhysicalSlots;
diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
index 8183925..026710b 100644
--- a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
+++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java
@@ -63,6 +63,7 @@
private static final int MOCK_SIM_SLOT_1 = 0;
private static final int MOCK_SIM_SLOT_2 = 1;
private static final int MOCK_SIM_SLOT_3 = 2;
+ public static final int MOCK_SIM_SLOT_MIN = 1;
public static final int MOCK_SIM_SLOT_MAX = 3;
/* Default value definition */
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
index 3757b63..d7bc9b1 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -109,6 +109,7 @@
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ApiTest;
import com.android.compatibility.common.util.CarrierPrivilegeUtils;
import com.android.compatibility.common.util.CddTest;
import com.android.compatibility.common.util.ShellIdentityUtils;
@@ -283,6 +284,7 @@
private static final int RADIO_HAL_VERSION_1_3 = makeRadioVersion(1, 3);
private static final int RADIO_HAL_VERSION_1_5 = makeRadioVersion(1, 5);
private static final int RADIO_HAL_VERSION_1_6 = makeRadioVersion(1, 6);
+ private static final int RADIO_HAL_VERSION_2_0 = makeRadioVersion(2, 0);
static {
EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>();
@@ -1250,23 +1252,49 @@
private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}";
@Test
+ @ApiTest(apis = "android.telephony.TelephonyManager#getNetworkCountryIso")
public void testGetNetworkCountryIso() {
assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
String countryCode = mTelephonyManager.getNetworkCountryIso();
- assertTrue("Country code '" + countryCode + "' did not match "
- + ISO_COUNTRY_CODE_PATTERN,
- Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+ ServiceState serviceState = mTelephonyManager.getServiceState();
+ if (serviceState != null && (serviceState.getState()
+ == ServiceState.STATE_IN_SERVICE || serviceState.getState()
+ == ServiceState.STATE_EMERGENCY_ONLY)) {
+ assertTrue("Country code '" + countryCode + "' did not match "
+ + ISO_COUNTRY_CODE_PATTERN,
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+ } else {
+ assertTrue("Country code could be empty when out of service",
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+ || TextUtils.isEmpty(countryCode));
+ }
+
+ int[] allSubs = ShellIdentityUtils.invokeMethodWithShellPermissions(
+ mSubscriptionManager, (sm) -> sm.getActiveSubscriptionIdList());
+ for (int i : allSubs) {
+ countryCode = mTelephonyManager.getNetworkCountryIso(
+ SubscriptionManager.getSlotIndex(i));
+ serviceState = mTelephonyManager.createForSubscriptionId(i).getServiceState();
+
+ if (serviceState != null && (serviceState.getState()
+ == ServiceState.STATE_IN_SERVICE || serviceState.getState()
+ == ServiceState.STATE_EMERGENCY_ONLY)) {
+ assertTrue("Country code '" + countryCode + "' did not match "
+ + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
+ } else {
+ assertTrue("Country code could be empty when out of service",
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+ || TextUtils.isEmpty(countryCode));
+ }
+ }
for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) {
- SubscriptionInfo subscriptionInfo =
- mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i);
- if (subscriptionInfo != null) {
- countryCode = mTelephonyManager.getNetworkCountryIso(i);
- assertTrue("Country code '" + countryCode + "' did not match "
- + ISO_COUNTRY_CODE_PATTERN + " for slot " + i,
- Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode));
- }
+ countryCode = mTelephonyManager.getNetworkCountryIso(i);
+ assertTrue("Country code must match " + ISO_COUNTRY_CODE_PATTERN + "or empty",
+ Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)
+ || TextUtils.isEmpty(countryCode));
}
}
@@ -1274,19 +1302,20 @@
public void testSetSystemSelectionChannels() {
assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
+ UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
List<RadioAccessSpecifier> channels;
try {
- channels = ShellIdentityUtils.invokeMethodWithShellPermissions(
- mTelephonyManager, TelephonyManager::getSystemSelectionChannels);
+ uiAutomation.adoptShellPermissionIdentity();
+ channels = mTelephonyManager.getSystemSelectionChannels();
} catch (IllegalStateException e) {
// TODO (b/189255895): Allow ISE once API is enforced in IRadio 2.1.
Log.d(TAG, "Skipping test since system selection channels are not available.");
return;
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
}
LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1);
- final UiAutomation uiAutomation =
- InstrumentationRegistry.getInstrumentation().getUiAutomation();
try {
uiAutomation.adoptShellPermissionIdentity();
// This is a oneway binder call, meaning we may return before the permission check
@@ -1303,20 +1332,29 @@
uiAutomation.dropShellPermissionIdentity();
}
+ uiAutomation.adoptShellPermissionIdentity();
+
// Try calling the API that doesn't provide feedback. We have no way of knowing if it
// succeeds, so just make sure nothing crashes.
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- tp -> tp.setSystemSelectionChannels(Collections.emptyList()));
+ mTelephonyManager.setSystemSelectionChannels(Collections.emptyList());
// Assert that we get back the value we set.
- assertEquals(Collections.emptyList(),
- ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager,
- TelephonyManager::getSystemSelectionChannels));
+ assertEquals(Collections.emptyList(), mTelephonyManager.getSystemSelectionChannels());
- // Reset the values back to the original.
- List<RadioAccessSpecifier> finalChannels = channels;
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager,
- tp -> tp.setSystemSelectionChannels(finalChannels));
+ try {
+ // Reset the values back to the original. Use callback to ensure we don't drop
+ // the shell permission until the original state is restored.
+ mTelephonyManager.setSystemSelectionChannels(channels,
+ getContext().getMainExecutor(), queue::offer);
+ Boolean result = queue.poll(1000, TimeUnit.MILLISECONDS);
+ if (result == null || !result) {
+ Log.e(TAG, "Invalid response when resetting initial system selection channels.");
+ }
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Interrupted while resetting initial system selection channels.");
+ } finally {
+ uiAutomation.dropShellPermissionIdentity();
+ }
}
@Test
@@ -1729,6 +1767,10 @@
@Test
public void testRebootRadio() throws Throwable {
assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS));
+ if (mRadioVersion <= RADIO_HAL_VERSION_2_0) {
+ Log.d(TAG, "Skipping test since rebootModem is not supported.");
+ return;
+ }
TestThread t = new TestThread(() -> {
Looper.prepare();
diff --git a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
index 1f7af46..94fc698 100644
--- a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
+++ b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
@@ -87,6 +87,11 @@
assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG2))
pkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO)
assertNull(pkgInfo.sharedUserId)
+ // Upgrading an APK with sharedUserMaxSdkVersion set should not change its UID.
+ assertTrue(installPackage(InstallTest.APK4))
+ val newPkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO)
+ assertNull(newPkgInfo.sharedUserId)
+ assertEquals(pkgInfo.applicationInfo.uid, newPkgInfo.applicationInfo.uid)
}
private fun testBestEffort(uid: Int) {
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 7d73da9..f00f0b1 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -125,6 +125,8 @@
canvas.drawColor(Color.WHITE);
p.setColor(Color.BLACK);
p.setAntiAlias(false);
+ // ensure the lines do not hit pixel edges
+ canvas.translate(0.05f, 0.05f);
float[] pts = {
0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50
};
diff --git a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
index a4ef244..e0c172a 100644
--- a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java
@@ -21,12 +21,17 @@
import android.app.Instrumentation;
import android.graphics.Color;
+import android.os.SystemClock;
+import android.platform.test.annotations.AppModeFull;
import android.support.test.uiautomator.UiDevice;
import android.view.Gravity;
+import android.view.InputEvent;
+import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.MediumTest;
@@ -40,7 +45,10 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+// @AppModeFull because GestureNavRule does not work for
+// instant mode tests (b/238975931)
@MediumTest
+@AppModeFull
@RunWith(AndroidJUnit4.class)
public class BackInvokedOnWidgetsTest {
@@ -93,7 +101,45 @@
private void doBackGesture() {
int midHeight = mUiDevice.getDisplayHeight() / 2;
int midWidth = mUiDevice.getDisplayWidth() / 2;
- mUiDevice.swipe(0, midHeight, midWidth, midHeight, 100);
+ quickSwipe(0, midHeight, midWidth, midHeight, 10);
mUiDevice.waitForIdle();
}
+
+ private void injectInputEventUnSynced(@NonNull InputEvent event) {
+ mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */,
+ false /* waitForAnimations */);
+ }
+
+ /**
+ * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction.
+ */
+ private void quickSwipe(float startX, float startY, float endX, float endY, int steps) {
+ if (steps <= 0) {
+ steps = 1;
+ }
+ final long startDownTime = SystemClock.uptimeMillis();
+ MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime,
+ MotionEvent.ACTION_DOWN, startX, startY, 0);
+ injectInputEventUnSynced(firstDown);
+
+ // inject in every 5 ms.
+ final int delayMillis = 5;
+ long nextEventTime = startDownTime + delayMillis;
+ final float stepGapX = (endX - startX) / steps;
+ final float stepGapY = (endY - startY) / steps;
+ for (int i = 0; i < steps; i++) {
+ SystemClock.sleep(delayMillis);
+ final float nextX = startX + stepGapX * i;
+ final float nextY = startY + stepGapY * i;
+ MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime,
+ MotionEvent.ACTION_MOVE, nextX, nextY, 0);
+ injectInputEventUnSynced(move);
+ nextEventTime += delayMillis;
+ }
+
+ SystemClock.sleep(delayMillis);
+ MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime,
+ MotionEvent.ACTION_UP, endX, endY, 0);
+ injectInputEventUnSynced(up);
+ }
}
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
index f7322dd..464fb4c 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java
@@ -274,6 +274,29 @@
new LinkedList<Integer>(Arrays.asList(waitSingleSync)));
}
+ private NetworkInfo.DetailedState waitForNextNetworkState() {
+ assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
+ assertNotNull(mMySync.expectedNetworkInfo);
+ return mMySync.expectedNetworkInfo.getDetailedState();
+ }
+
+ private boolean waitForConnectedNetworkState() {
+ // The possible orders of network states are:
+ // * IDLE > CONNECTING > CONNECTED for lazy initialization
+ // * DISCONNECTED > CONNECTING > CONNECTED for previous group removal
+ // * CONNECTING > CONNECTED
+ NetworkInfo.DetailedState state = waitForNextNetworkState();
+ if (state == NetworkInfo.DetailedState.IDLE
+ || state == NetworkInfo.DetailedState.DISCONNECTED) {
+ state = waitForNextNetworkState();
+ }
+ if (state != NetworkInfo.DetailedState.CONNECTING) {
+ return false;
+ }
+ state = waitForNextNetworkState();
+ return state == NetworkInfo.DetailedState.CONNECTED;
+ }
+
private boolean waitForServiceResponse(MyResponse waitResponse) {
synchronized (waitResponse) {
long timeout = System.currentTimeMillis() + TIMEOUT_MSEC;
@@ -508,21 +531,7 @@
mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
-
- // The first network state might be IDLE due to
- // lazy initialization, but not CONNECTED.
- for (int i = 0; i < 2; i++) {
- assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
- assertNotNull(mMySync.expectedNetworkInfo);
- if (NetworkInfo.DetailedState.CONNECTED ==
- mMySync.expectedNetworkInfo.getDetailedState()) {
- break;
- }
- assertEquals(NetworkInfo.DetailedState.IDLE,
- mMySync.expectedNetworkInfo.getDetailedState());
- }
- assertEquals(NetworkInfo.DetailedState.CONNECTED,
- mMySync.expectedNetworkInfo.getDetailedState());
+ assertTrue(waitForConnectedNetworkState());
resetResponse(mMyResponse);
mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel,
@@ -660,21 +669,7 @@
mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
-
- // The first network state might be IDLE due to
- // lazy initialization, but not CONNECTED.
- for (int i = 0; i < 2; i++) {
- assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
- assertNotNull(mMySync.expectedNetworkInfo);
- if (NetworkInfo.DetailedState.CONNECTED ==
- mMySync.expectedNetworkInfo.getDetailedState()) {
- break;
- }
- assertEquals(NetworkInfo.DetailedState.IDLE,
- mMySync.expectedNetworkInfo.getDetailedState());
- }
- assertEquals(NetworkInfo.DetailedState.CONNECTED,
- mMySync.expectedNetworkInfo.getDetailedState());
+ assertTrue(waitForConnectedNetworkState());
resetResponse(mMyResponse);
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
@@ -707,10 +702,7 @@
mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
- assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
- assertNotNull(mMySync.expectedNetworkInfo);
- assertEquals(NetworkInfo.DetailedState.CONNECTED,
- mMySync.expectedNetworkInfo.getDetailedState());
+ assertTrue(waitForConnectedNetworkState());
resetResponse(mMyResponse);
mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener);
@@ -812,21 +804,7 @@
mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener);
assertTrue(waitForServiceResponse(mMyResponse));
assertTrue(mMyResponse.success);
-
- // The first network state might be IDLE due to
- // lazy initialization, but not CONNECTED.
- for (int i = 0; i < 2; i++) {
- assertTrue(waitForBroadcasts(MySync.NETWORK_INFO));
- assertNotNull(mMySync.expectedNetworkInfo);
- if (NetworkInfo.DetailedState.CONNECTED
- == mMySync.expectedNetworkInfo.getDetailedState()) {
- break;
- }
- assertEquals(NetworkInfo.DetailedState.IDLE,
- mMySync.expectedNetworkInfo.getDetailedState());
- }
- assertEquals(NetworkInfo.DetailedState.CONNECTED,
- mMySync.expectedNetworkInfo.getDetailedState());
+ assertTrue(waitForConnectedNetworkState());
resetResponse(mMyResponse);
MacAddress peerMacAddress = MacAddress.fromString(mTestWifiP2pPeerConfig.deviceAddress);
diff --git a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java
index b61f11d..314d097 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java
@@ -151,6 +151,8 @@
() -> wifiManager.setScanThrottleEnabled(false));
// Enable Wifi
+ sWasWifiEnabled = ShellIdentityUtils.invokeWithShellPermissions(
+ () -> wifiManager.isWifiEnabled());
ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.setWifiEnabled(true));
// Make sure wifi is enabled
PollingCheck.check("Wifi not enabled", DURATION_MILLIS, () -> wifiManager.isWifiEnabled());
diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml
index dfc8031..d785d96 100644
--- a/tools/cts-tradefed/res/config/cts-known-failures.xml
+++ b/tools/cts-tradefed/res/config/cts-known-failures.xml
@@ -284,5 +284,6 @@
<!-- b/237035040 -->
<option name="compatibility:exclude-filter" value="CtsGraphicsTestCases android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" />
+ <option name="compatibility:exclude-filter" value="CtsGraphicsTestCases[instant] android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" />
</configuration>
diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
index bfbb35a..54d3f7e 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -117,4 +117,8 @@
<!-- b/235453601 -->
<option name="compatibility:exclude-filter" value="CtsMediaPerformanceClassTestCases android.mediapc.cts.MultiDecoderPerfTest" />
+
+ <!-- b/238155422 -->
+ <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.ConnectivityManagerTest#testSetAirplaneMode" />
+
</configuration>