diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-30 19:00:56 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2023-08-30 19:00:56 +0000 |
commit | 63f5219215bf88e8f35d5007f40a6d78759e7165 (patch) | |
tree | 3682fbf937c017315bf722b1f3a47694c1560312 | |
parent | 2253213e34c276b2f4a75a771b840ebe44dd4809 (diff) | |
parent | 13fab2ee3012aead0feb56d4ace52b74d2e22edd (diff) | |
download | cts-aml_res_341110000.tar.gz |
Snap for 10737070 from 13fab2ee3012aead0feb56d4ace52b74d2e22edd to mainline-resolv-releaseaml_res_341110000
Change-Id: Iaebe3f4f82b10aae7463387cdf3ad57f13c18035
277 files changed, 5898 insertions, 2249 deletions
diff --git a/apps/CameraITS/tests/scene6/test_low_latency_zoom.py b/apps/CameraITS/tests/scene6/test_low_latency_zoom.py index 96b4181a3f7..11b0106379c 100644 --- a/apps/CameraITS/tests/scene6/test_low_latency_zoom.py +++ b/apps/CameraITS/tests/scene6/test_low_latency_zoom.py @@ -31,6 +31,7 @@ import zoom_capture_utils _CIRCLE_COLOR = 0 # [0: black, 255: white] _CIRCLE_AR_RTOL = 0.15 # contour width vs height (aspect ratio) _CIRCLISH_RTOL = 0.05 # contour area vs ideal circle area pi*((w+h)/4)**2 +_CONTINUOUS_PICTURE_MODE = 4 # continuous picture AF mode _MIN_AREA_RATIO = 0.00015 # based on 2000/(4000x3000) pixels _MIN_CIRCLE_PTS = 25 _NAME = os.path.splitext(os.path.basename(__file__))[0] @@ -63,14 +64,15 @@ class LowLatencyZoomTest(its_base_test.ItsBaseTest): its_session_utils.load_scene( cam, props, self.scene, self.tablet, self.chart_distance) + # Determine test zoom range z_range = props['android.control.zoomRatioRange'] - logging.debug('testing zoomRatioRange: %s', str(z_range)) debug = self.debug_mode - z_min, z_max = float(z_range[0]), float(z_range[1]) camera_properties_utils.skip_unless(z_max >= z_min * _ZOOM_MIN_THRESH) - z_list = np.arange(z_min, z_max, float(z_max - z_min) / (_NUM_STEPS - 1)) + z_max = min(z_max, zoom_capture_utils.ZOOM_MAX_THRESH * z_min) + z_list = np.arange(z_min, z_max, (z_max - z_min) / (_NUM_STEPS - 1)) z_list = np.append(z_list, z_max) + logging.debug('Testing zoom range: %s', str(z_list)) # set TOLs based on camera and test rig params if camera_properties_utils.logical_multi_camera(props): @@ -90,13 +92,18 @@ class LowLatencyZoomTest(its_base_test.ItsBaseTest): # do auto captures over zoom range and find circles with cv2 img_name_stem = f'{os.path.join(self.log_path, _NAME)}' logging.debug('Using auto capture request') - cam.do_3a() + cam.do_3a(zoom_ratio=z_min) test_failed = False fmt = 'yuv' test_data = {} reqs = [] req = capture_request_utils.auto_capture_request() - req['android.control.settingsOverride'] = camera_properties_utils.SETTINGS_OVERRIDE_ZOOM + req['android.control.settingsOverride'] = ( + camera_properties_utils.SETTINGS_OVERRIDE_ZOOM + ) + req['android.control.enableZsl'] = False + if not camera_properties_utils.fixed_focus(props): + req['android.control.afMode'] = _CONTINUOUS_PICTURE_MODE for z in z_list: logging.debug('zoom ratio: %.2f', z) req_for_zoom = req.copy() @@ -110,8 +117,10 @@ class LowLatencyZoomTest(its_base_test.ItsBaseTest): # Check low latency zoom outputs match result metadata for i, cap in enumerate(caps): z_result = cap['metadata']['android.control.zoomRatio'] + af_state = cap['metadata']['android.control.afState'] scaled_zoom = min(z_list[i], z_result) - logging.debug('zoom ratio in result: %.2f', z_result) + logging.debug('Result[%d]: zoom ratio %.2f, afState %d', + i, z_result, af_state) img = image_processing_utils.convert_capture_to_rgb_image( cap, props=props) img_name = f'{img_name_stem}_{fmt}_{i}_{round(z_result, 2)}.jpg' @@ -147,6 +156,13 @@ class LowLatencyZoomTest(its_base_test.ItsBaseTest): test_data[i] = {'z': z_result, 'circle': circle, 'r_tol': radius_tol, 'o_tol': offset_tol, 'fl': cap_fl} + # Since we are zooming in, settings_override may change the minimum zoom + # value in the result metadata. + # This is because zoom values like: [1., 2., 3., ..., 10.] may be applied + # as: [4., 4., 4., .... 9., 10., 10.]. + # If we were zooming out, we would need to change the z_max. + z_min = test_data[min(test_data.keys())]['z'] + if not zoom_capture_utils.verify_zoom_results( test_data, size, z_max, z_min): test_failed = True diff --git a/apps/CameraITS/tests/scene6/test_preview_video_zoom_match.py b/apps/CameraITS/tests/scene6/test_preview_video_zoom_match.py index c1ff7e71bef..acfef156b52 100644 --- a/apps/CameraITS/tests/scene6/test_preview_video_zoom_match.py +++ b/apps/CameraITS/tests/scene6/test_preview_video_zoom_match.py @@ -277,7 +277,19 @@ class PreviewVideoZoomTest(its_base_test.ItsBaseTest): # Opencv expects a numpy array but np.flip generates a 'view' which # doesn't work with opencv. ndarray.copy forces copy instead of view if props['android.lens.facing'] == _LENS_FACING_FRONT: - preview_img = np.ndarray.copy(np.flip(preview_img, 0)) + # Preview are flipped on device's natural orientation + # so for sensor orientation 90 or 270, it is up or down + # Sensor orientation 0 or 180 is left or right + if props['android.sensor.orientation'] in (90, 270): + preview_img = np.ndarray.copy(np.flipud(preview_img)) + logging.debug( + 'Found sensor orientation %d, flipping up down', + props['android.sensor.orientation']) + else: + preview_img = np.ndarray.copy(np.fliplr(preview_img)) + logging.debug( + 'Found sensor orientation %d, flipping left right', + props['android.sensor.orientation']) # Find the center circle in preview img preview_img_name = (f'Preview_zoomRatio_{z}_{size}_circle.png') diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py index 78d555c7e2c..2e34320bc5f 100644 --- a/apps/CameraITS/tests/scene6/test_zoom.py +++ b/apps/CameraITS/tests/scene6/test_zoom.py @@ -58,13 +58,15 @@ class ZoomTest(its_base_test.ItsBaseTest): its_session_utils.load_scene( cam, props, self.scene, self.tablet, self.chart_distance) + # Determine test zoom range z_range = props['android.control.zoomRatioRange'] - logging.debug('testing zoomRatioRange: %s', str(z_range)) debug = self.debug_mode - z_min, z_max = float(z_range[0]), float(z_range[1]) - z_list = np.arange(z_min, z_max, float(z_max - z_min) / (_NUM_STEPS - 1)) + camera_properties_utils.skip_unless(z_max >= z_min * _ZOOM_MIN_THRESH) + z_max = min(z_max, zoom_capture_utils.ZOOM_MAX_THRESH * z_min) + z_list = np.arange(z_min, z_max, (z_max - z_min) / (_NUM_STEPS - 1)) z_list = np.append(z_list, z_max) + logging.debug('Testing zoom range: %s', str(z_list)) # Check media performance class media_performance_class = its_session_utils.get_media_performance_class( @@ -81,8 +83,6 @@ class ZoomTest(its_base_test.ItsBaseTest): f'Found media performance class {media_performance_class} ' f'and minimum zoom {z_min}.') - camera_properties_utils.skip_unless(z_max >= z_min * _ZOOM_MIN_THRESH) - # set TOLs based on camera and test rig params if camera_properties_utils.logical_multi_camera(props): test_tols, size = zoom_capture_utils.get_test_tols_and_cap_size( @@ -106,21 +106,14 @@ class ZoomTest(its_base_test.ItsBaseTest): # do captures over zoom range and find circles with cv2 img_name_stem = f'{os.path.join(self.log_path, _NAME)}' - if camera_properties_utils.manual_sensor(props): - logging.debug('Manual sensor, using manual capture request') - s, e, _, _, f_d = cam.do_3a(get_results=True) - req = capture_request_utils.manual_capture_request( - s, e, f_distance=f_d) - else: - logging.debug('Using auto capture request') - cam.do_3a() - req = capture_request_utils.auto_capture_request() + req = capture_request_utils.auto_capture_request() test_failed = False for fmt in test_formats: logging.debug('testing %s format', fmt) test_data = {} for i, z in enumerate(z_list): req['android.control.zoomRatio'] = z + cam.do_3a(zoom_ratio=z) cap = cam.do_capture( req, {'format': fmt, 'width': size[0], 'height': size[1]}) diff --git a/apps/CameraITS/tests/scene_extensions/scene_hdr/test_hdr_extension.py b/apps/CameraITS/tests/scene_extensions/scene_hdr/test_hdr_extension.py index 9bb99ecf278..7ff6cd8e9b9 100644 --- a/apps/CameraITS/tests/scene_extensions/scene_hdr/test_hdr_extension.py +++ b/apps/CameraITS/tests/scene_extensions/scene_hdr/test_hdr_extension.py @@ -214,7 +214,7 @@ class HdrExtensionTest(its_base_test.ItsBaseTest): its_session_utils.load_scene( cam, props, self.scene, self.tablet, self.chart_distance, - log_path=self.log_path) + lighting_check=False, log_path=self.log_path) file_stem = f'{test_name}_{_FMT_NAME}_{_WIDTH}x{_HEIGHT}' diff --git a/apps/CameraITS/tests/scene_extensions/scene_night/test_night_extension.py b/apps/CameraITS/tests/scene_extensions/scene_night/test_night_extension.py index 676679a2fd4..c883d6fdd53 100644 --- a/apps/CameraITS/tests/scene_extensions/scene_night/test_night_extension.py +++ b/apps/CameraITS/tests/scene_extensions/scene_night/test_night_extension.py @@ -30,6 +30,7 @@ import lighting_control_utils import opencv_processing_utils _NAME = os.path.splitext(os.path.basename(__file__))[0] +_DEFAULT_TABLET_BRIGHTNESS_SCALING = 0.04 # 4% of default brightness _EXTENSION_NIGHT = 4 # CameraExtensionCharacteristics.EXTENSION_NIGHT _TAP_COORDINATES = (500, 500) # Location to tap tablet screen via adb _TEST_REQUIRED_MPC = 34 @@ -41,9 +42,8 @@ _IMAGE_FORMAT_YUV_420_888_INT = 35 _DOT_INTENSITY_DIFF_TOL = 20 # Min diff between dot/circle intensities [0:255] _DURATION_DIFF_TOL = 0.5 # Night mode ON captures must take 0.5 seconds longer -_EDGE_NOISE_WIDTH = 0.1 # Edge is left 10% of image and right 10% of image -_EDGE_NOISE_IMPROVEMENT_TOL = 1.5 # Edge noise must be reduced by at least 67% _INTENSITY_IMPROVEMENT_TOL = 1.1 # Night mode ON captures must be 10% brighter +_IDEAL_INTENSITY_IMPROVEMENT = 2.5 # Skip noise check if images 2.5x brighter _R_STRING = 'r' _X_STRING = 'x' @@ -86,60 +86,85 @@ def _convert_captures(cap, file_stem=None): return y, image_processing_utils.convert_image_to_uint8(img) -def _get_left_patch(y): - """Return the left edge of a y plane according to _EDGE_NOISE_WIDTH. - - Args: - y: y_plane from a capture. - Returns: - Cropped y_plane on the left edge of the capture. - """ - return image_processing_utils.get_image_patch( - y, 0, 0, _EDGE_NOISE_WIDTH, 1) +def _check_dot_intensity_diff(night_img, night_y, no_night_img, no_night_y): + """Checks the difference between circle and dot intensities with Night ON. - -def _get_right_patch(y): - """Return the right edge of a y plane according to _EDGE_NOISE_WIDTH. - - Args: - y: y_plane from a capture. - Returns: - Cropped y_plane on the right edge of the capture. - """ - return image_processing_utils.get_image_patch( - y, 1 - _EDGE_NOISE_WIDTH, 0, _EDGE_NOISE_WIDTH, 1) - - -def _get_edge_noise(night_y, no_night_y): - """Computes the left and right edge noise in the y plane. + The performance with Night OFF is logged for debugging purposes. + This is an optional check, and a successful result can replace the + overall intensity check. Args: + night_img: numpy image from a capture with night mode ON. night_y: y_plane from a capture with night mode ON. + no_night_img: numpy image from a capture with night mode OFF. no_night_y: y_plane from a capture with night mode OFF. + Returns: - float; ratio of left edge noise, float; ratio of right edge noise. + True if diff between circle and dot intensities is significant. """ - night_left_patch_std = np.std(_get_left_patch(night_y), axis=(0, 1)) - no_night_left_patch_std = np.std(_get_left_patch(no_night_y), axis=(0, 1)) - night_right_patch_std = np.std(_get_right_patch(night_y), axis=(0, 1)) - no_night_right_patch_std = np.std(_get_right_patch(no_night_y), axis=(0, 1)) - logging.debug('Night mode ON left patch noise: %.2f', night_left_patch_std) - logging.debug('Night mode OFF left patch noise: %.2f', - no_night_left_patch_std) - logging.debug('Night mode ON right patch noise: %.2f', night_right_patch_std) - logging.debug('Night mode OFF right patch noise: %.2f', - no_night_right_patch_std) - left_patch_ratio = no_night_left_patch_std / night_left_patch_std - right_patch_ratio = no_night_right_patch_std / night_right_patch_std - return left_patch_ratio, right_patch_ratio + night_circle = opencv_processing_utils.find_circle( + night_img, + 'night_dot_intensity_check.png', + _MIN_AREA, + _WHITE, + ) + night_circle_center_mean = np.mean( + night_img[night_circle[_Y_STRING], night_circle[_X_STRING]]) + night_dots = _get_dots_from_circle(night_circle) + + no_night_circle = opencv_processing_utils.find_circle( + no_night_img, + 'no_night_dot_intensity_check.png', + _MIN_AREA, + _WHITE, + ) + no_night_circle_center_mean = np.mean( + no_night_img[no_night_circle[_Y_STRING], no_night_circle[_X_STRING]]) + no_night_dots = _get_dots_from_circle(no_night_circle) + + # Skip the first dot, which is of a different intensity + night_light_gray_dots_mean = np.mean( + [ + night_y[night_dots[i][_Y_STRING], night_dots[i][_X_STRING]] + for i in range(1, len(night_dots)) + ] + ) + no_night_light_gray_dots_mean = np.mean( + [ + no_night_y[no_night_dots[i][_Y_STRING], no_night_dots[i][_X_STRING]] + for i in range(1, len(no_night_dots)) + ] + ) + + night_dot_intensity_diff = ( + night_circle_center_mean - + night_light_gray_dots_mean + ) + no_night_dot_intensity_diff = ( + no_night_circle_center_mean - + no_night_light_gray_dots_mean + ) + logging.debug('With night extension ON, the difference between white ' + 'circle intensity and non-orientation dot intensity was %.2f.', + night_dot_intensity_diff) + logging.debug('With night extension OFF, the difference between white ' + 'circle intensity and non-orientation dot intensity was %.2f.', + no_night_dot_intensity_diff) + return night_dot_intensity_diff > _DOT_INTENSITY_DIFF_TOL def _check_overall_intensity(night_img, no_night_img): """Checks that overall intensity significantly improves with night mode ON. + All implementations must result in an increase in intensity of at least + _INTENSITY_IMPROVEMENT_TOL. _IDEAL_INTENSITY_IMPROVEMENT is the minimum + improvement to waive the edge noise check. + Args: night_img: numpy image taken with night mode ON no_night_img: numpy image taken with night mode OFF + Returns: + True if intensity has increased enough to waive the edge noise check. """ night_mean = np.mean(night_img) no_night_mean = np.mean(no_night_img) @@ -151,34 +176,7 @@ def _check_overall_intensity(night_img, no_night_img): 'intense than night mode OFF image! ' f'Ratio: {overall_intensity_ratio:.2f}, ' f'Expected: {_INTENSITY_IMPROVEMENT_TOL}') - - -def _check_edge_noise(night_y, no_night_y): - """Checks that noise at the edges significantly improves with night mode ON. - - Args: - night_y: y_plane from a capture with night mode ON. - no_night_y: y_plane from a capture with night mode OFF. - """ - # Normalize both y planes to [0, 255] - night_y *= 255 - no_night_y *= 255 - left_patch_ratio, right_patch_ratio = _get_edge_noise( - night_y, no_night_y) - logging.debug('Left edge of night mode OFF capture was %.2f times ' - 'as noisy as night mode ON', left_patch_ratio) - logging.debug('Right edge of night mode OFF capture was %.2f times ' - 'as noisy as night mode ON', right_patch_ratio) - if left_patch_ratio < _EDGE_NOISE_IMPROVEMENT_TOL: - raise AssertionError('Left edge of night mode OFF capture was only ' - f'{float(left_patch_ratio):.2f} as noisy as ' - 'night mode OFF, expected to be ' - f'at least {_EDGE_NOISE_IMPROVEMENT_TOL}') - if right_patch_ratio < _EDGE_NOISE_IMPROVEMENT_TOL: - raise AssertionError('Right edge of night mode OFF capture was only ' - f'{float(right_patch_ratio):.2f} as noisy as ' - 'night mode OFF, expected to be ' - f'at least {_EDGE_NOISE_IMPROVEMENT_TOL}') + return overall_intensity_ratio > _IDEAL_INTENSITY_IMPROVEMENT class NightExtensionTest(its_base_test.ItsBaseTest): @@ -190,8 +188,7 @@ class NightExtensionTest(its_base_test.ItsBaseTest): 3. Takes capture with night extension OFF using an auto capture request. Verifies that the capture with night mode ON: * takes longer - * is brighter - * contains less edge noise + * is brighter OR improves appearance of scene artifacts """ def find_tablet_brightness(self, cam, default_brightness, file_stem, @@ -255,8 +252,12 @@ class NightExtensionTest(its_base_test.ItsBaseTest): except AssertionError: logging.debug('Unable to find circle with brightness %d', brightness) max_brightness = brightness - if not final_brightness: - raise AssertionError('Unable to find a valid brightness for the tablet') + if final_brightness is None: + logging.debug('Unable to find orientation dot at any brightness, ' + 'defaulting to %.2f of current tablet brightness.', + _DEFAULT_TABLET_BRIGHTNESS_SCALING) + return int(_DEFAULT_TABLET_BRIGHTNESS_SCALING * + self.tablet_screen_brightness) return final_brightness def _time_and_take_captures(self, cam, req, out_surfaces, @@ -365,19 +366,7 @@ class NightExtensionTest(its_base_test.ItsBaseTest): extension_capture_sizes.reverse() logging.debug('Capture sizes: %s', capture_sizes) logging.debug('Extension capture sizes: %s', extension_capture_sizes) - - for capture_size, extension_capture_size in zip( - capture_sizes, extension_capture_sizes): - if capture_size == extension_capture_size: - width, height = capture_size - break - else: - raise AssertionError( - 'No matching sizes for non-extension and extension captures! ' - f'Camera ID {self.camera_id}, ' - f'extension {_EXTENSION_NIGHT}, and ' - f'format {_IMAGE_FORMAT_YUV_420_888_INT}' - ) + width, height = extension_capture_sizes[0] # Set tablet brightness to darken scene file_stem = f'{test_name}_{_FMT_NAME}_{width}x{height}' @@ -420,12 +409,20 @@ class NightExtensionTest(its_base_test.ItsBaseTest): f'Difference: {duration_diff:.2f}, ' f'Expected: {_DURATION_DIFF_TOL}') - logging.debug('Comparing overall intensity of capture with ' - 'night mode ON/OFF') - _check_overall_intensity(night_img, no_night_img) - - logging.debug('Comparing edge noise of capture with night mode ON/OFF') - _check_edge_noise(night_y, no_night_y) + logging.debug('Checking that dot intensities with Night ON match the ' + 'expected values from the scene') + # Normalize y planes to [0:255] + dot_intensities_acceptable = _check_dot_intensity_diff( + night_img, night_y * 255, no_night_img, no_night_y * 255) + + if not dot_intensities_acceptable: + logging.debug('Comparing overall intensity of capture with ' + 'night mode ON/OFF') + much_higher_intensity = _check_overall_intensity( + night_img, no_night_img) + if not much_higher_intensity: + logging.warning( + 'Improvement in intensity was smaller than expected.') if __name__ == '__main__': test_runner.main() diff --git a/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py b/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py index 149ad2aea2c..c27b192c8b6 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py +++ b/apps/CameraITS/tests/sensor_fusion/test_multi_camera_frame_sync.py @@ -15,8 +15,8 @@ import logging -import multiprocessing import os +import threading import time import cv2 @@ -181,7 +181,7 @@ class MultiCameraFrameSyncTest(its_base_test.ItsBaseTest): sensor_fusion_utils.ARDUINO_STRING) # send test cmd to Arduino until cmd returns properly sensor_fusion_utils.establish_serial_comm(serial_port) - p = multiprocessing.Process( + p = threading.Thread( target=sensor_fusion_utils.rotation_rig, args=( rot_rig['cntl'], diff --git a/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py b/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py index 1d99d643932..d860b37d5f1 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py +++ b/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py @@ -14,8 +14,8 @@ """Verify preview is stable during phone movement.""" import logging -import multiprocessing import os +import threading import time from mobly import test_runner @@ -74,7 +74,7 @@ def _collect_data(cam, tablet_device, video_size, rot_rig): servo_speed = _TABLET_SERVO_SPEED else: servo_speed = _ARDUINO_SERVO_SPEED - p = multiprocessing.Process( + p = threading.Thread( target=sensor_fusion_utils.rotation_rig, args=( rot_rig['cntl'], diff --git a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py index c925d5c81b9..c68789afc7e 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py +++ b/apps/CameraITS/tests/sensor_fusion/test_sensor_fusion.py @@ -17,9 +17,9 @@ import json import logging import math -import multiprocessing import os import sys +import threading import time from matplotlib import pylab @@ -105,7 +105,7 @@ def _collect_data(cam, fps, w, h, test_length, rot_rig, chart_dist, sensor_fusion_utils.ARDUINO_STRING) # send test cmd to Arduino until cmd returns properly sensor_fusion_utils.establish_serial_comm(serial_port) - p = multiprocessing.Process( + p = threading.Thread( target=sensor_fusion_utils.rotation_rig, args=( rot_rig['cntl'], diff --git a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py index ecdcc21359b..3f069073551 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py +++ b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py @@ -15,8 +15,8 @@ import logging import math -import multiprocessing import os +import threading import time from mobly import test_runner @@ -84,7 +84,7 @@ def _collect_data(cam, tablet_device, video_profile, video_quality, rot_rig): else: servo_speed = _ARDUINO_SERVO_SPEED - p = multiprocessing.Process( + p = threading.Thread( target=sensor_fusion_utils.rotation_rig, args=(rot_rig['cntl'], rot_rig['ch'], _NUM_ROTATIONS, _ARDUINO_ANGLES, servo_speed, _ARDUINO_MOVE_TIME, serial_port)) diff --git a/apps/CameraITS/utils/opencv_processing_utils.py b/apps/CameraITS/utils/opencv_processing_utils.py index 62bfda93fa9..fd3d5e0dbd7 100644 --- a/apps/CameraITS/utils/opencv_processing_utils.py +++ b/apps/CameraITS/utils/opencv_processing_utils.py @@ -20,6 +20,7 @@ import os import pathlib import cv2 import numpy +from scipy import spatial import capture_request_utils import error_util @@ -46,9 +47,12 @@ CIRCLISH_LOW_RES_ATOL = 0.15 # loosen for low res images CIRCLE_MIN_PTS = 20 CIRCLE_RADIUS_NUMPTS_THRESH = 2 # contour num_pts/radius: empirically ~3x CIRCLE_COLOR_ATOL = 0.05 # circle color fill tolerance +CIRCLE_LOCATION_VARIATION_RTOL = 0.05 # tolerance to remove similar circles CV2_LINE_THICKNESS = 3 # line thickness for drawing on images CV2_RED = (255, 0, 0) # color in cv2 to draw lines +CV2_THRESHOLD_BLOCK_SIZE = 11 +CV2_THRESHOLD_CONSTANT = 2 CV2_HOME_DIRECTORY = os.path.dirname(cv2.__file__) CV2_ALTERNATE_DIRECTORY = pathlib.Path(CV2_HOME_DIRECTORY).parents[3] @@ -466,7 +470,7 @@ def find_circle_fill_metric(shape, img_bw, color): return matching / total -def find_circle(img, img_name, min_area, color): +def find_circle(img, img_name, min_area, color, use_adaptive_threshold=False): """Find the circle in the test image. Args: @@ -474,6 +478,7 @@ def find_circle(img, img_name, min_area, color): img_name: string with image info of format and size. min_area: float of minimum area of circle to find color: int of [0 or 255] 0 is black, 255 is white + use_adaptive_threshold: True if binarization should use adaptive threshold. Returns: circle = {'x', 'y', 'r', 'w', 'h', 'x_offset', 'y_offset'} @@ -485,11 +490,15 @@ def find_circle(img, img_name, min_area, color): else: circlish_atol = CIRCLISH_LOW_RES_ATOL - # convert to gray-scale image - img_gray = convert_to_gray(img) - - # otsu threshold to binarize the image - img_bw = binarize_image(img_gray) + # convert to gray-scale image and binarize using adaptive/global threshold + if use_adaptive_threshold: + img_gray = cv2.cvtColor(img.astype(numpy.uint8), cv2.COLOR_BGR2GRAY) + img_bw = cv2.adaptiveThreshold(img_gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, + cv2.THRESH_BINARY, CV2_THRESHOLD_BLOCK_SIZE, + CV2_THRESHOLD_CONSTANT) + else: + img_gray = convert_to_gray(img) + img_bw = binarize_image(img_gray) # find contours contours = find_all_contours(255-img_bw) @@ -517,6 +526,35 @@ def find_circle(img, img_name, min_area, color): math.isclose(1.0, aspect_ratio, abs_tol=CIRCLE_AR_ATOL) and num_pts/radius >= CIRCLE_RADIUS_NUMPTS_THRESH and math.isclose(1.0, fill, abs_tol=CIRCLE_COLOR_ATOL)): + # spatial.distance.euclidean can handle nested numpy arrays + radii = [ + spatial.distance.euclidean((shape['ctx'], shape['cty']), point) + for point in contour + ] + minimum_radius, maximum_radius = min(radii), max(radii) + logging.debug('Minimum radius: %.2f, maximum radius: %.2f', + minimum_radius, maximum_radius) + if circle: + old_circle_center = (circle['x'], circle['y']) + new_circle_center = (shape['ctx'], shape['cty']) + # Based on image height + center_distance_atol = img_size[0]*CIRCLE_LOCATION_VARIATION_RTOL + if math.isclose( + spatial.distance.euclidean(old_circle_center, new_circle_center), + 0, + abs_tol=center_distance_atol + ) and maximum_radius - minimum_radius < circle['radius_spread']: + logging.debug('Replacing the previously found circle. ' + 'Circle located at %s has a smaller radius spread ' + 'than the previously found circle at %s. ' + 'Current radius spread: %.2f, ' + 'previous radius spread: %.2f', + new_circle_center, old_circle_center, + maximum_radius - minimum_radius, + circle['radius_spread']) + circle_contours.pop() + circle = {} + num_circles -= 1 circle_contours.append(contour) # Populate circle dictionary @@ -527,6 +565,7 @@ def find_circle(img, img_name, min_area, color): circle['h'] = float(shape['height']) circle['x_offset'] = (shape['ctx'] - img_size[1]//2) / circle['w'] circle['y_offset'] = (shape['cty'] - img_size[0]//2) / circle['h'] + circle['radius_spread'] = maximum_radius - minimum_radius logging.debug('Num pts: %d', num_pts) logging.debug('Aspect ratio: %.3f', aspect_ratio) logging.debug('Circlish value: %.3f', circlish) @@ -541,8 +580,12 @@ def find_circle(img, img_name, min_area, color): if num_circles == 0: image_processing_utils.write_image(img/255, img_name, True) - raise AssertionError('No black circle detected. ' - 'Please take pictures according to instructions.') + if not use_adaptive_threshold: + return find_circle( + img, img_name, min_area, color, use_adaptive_threshold=True) + else: + raise AssertionError('No circle detected. ' + 'Please take pictures according to instructions.') if num_circles > 1: image_processing_utils.write_image(img/255, img_name, True) @@ -551,7 +594,10 @@ def find_circle(img, img_name, min_area, color): img_name_parts = img_name.split('.') image_processing_utils.write_image( img/255, f'{img_name_parts[0]}_contours.{img_name_parts[1]}', True) - raise AssertionError('More than 1 black circle detected. ' + if not use_adaptive_threshold: + return find_circle( + img, img_name, min_area, color, use_adaptive_threshold=True) + raise AssertionError('More than 1 circle detected. ' 'Background of scene may be too complex.') return circle diff --git a/apps/CameraITS/utils/video_processing_utils.py b/apps/CameraITS/utils/video_processing_utils.py index 1665dbf8e57..3045d1f9811 100644 --- a/apps/CameraITS/utils/video_processing_utils.py +++ b/apps/CameraITS/utils/video_processing_utils.py @@ -247,14 +247,13 @@ def get_average_frame_rate(video_file_name_with_path): Float. average frames per second. """ - cmd = ['ffmpeg', - '-i', - video_file_name_with_path, - '-vf', - 'vfrdet', - '-f', - 'null', - '-', + cmd = ['ffprobe', + '-v', + 'quiet', + '-show_streams', + '-select_streams', + 'v:0', # first video stream + video_file_name_with_path ] logging.debug('Getting frame rate') raw_output = '' @@ -266,18 +265,16 @@ def get_average_frame_rate(video_file_name_with_path): raise AssertionError(str(e.output)) from e if raw_output: output = str(raw_output.decode('utf-8')).strip() - logging.debug('FFmpeg command %s output: %s', ' '.join(cmd), output) - fps_data = output.splitlines()[-3] # frames printed on third to last line - frames = int(re.search(r'frame= *([0-9]+)', fps_data).group(1)) - duration = re.search(r'time= *([0-9][0-9:\.]*)', fps_data).group(1) - time_parts = [float(t) for t in duration.split(':')] - seconds = time_parts[0] * HR_TO_SEC + time_parts[ - 1] * MIN_TO_SEC + time_parts[2] - logging.debug('Average FPS: %d / %d = %.4f', - frames, seconds, frames / seconds) - return frames / seconds + logging.debug('ffprobe command %s output: %s', ' '.join(cmd), output) + average_frame_rate_data = ( + re.search(r'avg_frame_rate=*([0-9]+/[0-9]+)', output).group(1) + ) + average_frame_rate = (int(average_frame_rate_data.split('/')[0]) / + int(average_frame_rate_data.split('/')[1])) + logging.debug('Average FPS: %.4f', average_frame_rate) + return average_frame_rate else: - raise AssertionError('ffmpeg failed to provide frame rate data') + raise AssertionError('ffprobe failed to provide frame rate data') def get_frame_deltas(video_file_name_with_path, timestamp_type='pts'): diff --git a/apps/CtsVerifier/jni/midi/MidiTestManager.cpp b/apps/CtsVerifier/jni/midi/MidiTestManager.cpp index 80d40b781d2..939fe39b888 100644 --- a/apps/CtsVerifier/jni/midi/MidiTestManager.cpp +++ b/apps/CtsVerifier/jni/midi/MidiTestManager.cpp @@ -257,7 +257,7 @@ int MidiTestManager::matchStream(uint8_t* bytes, int count) { return matchedByteCount; } -#define THROTTLE_PERIOD_MS 10 +#define THROTTLE_PERIOD_MS 20 #define THROTTLE_MAX_PACKET_SIZE 15 int portSend(AMidiInputPort* sendPort, uint8_t* msg, int length, bool throttle) { diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml index e38d57b22e2..17749a75145 100644 --- a/apps/CtsVerifier/res/values/strings.xml +++ b/apps/CtsVerifier/res/values/strings.xml @@ -2554,7 +2554,7 @@ \nPull down the notification shade and look for the NotifPrivacyTest notification. \nFail the test if it can be found without unlocking the device. \n\nGo back when ready to Pass/Fail the step.</string> - <string name="np_public_version_text">NotifPrivacyTefst: REDACTED</string> + <string name="np_public_version_text">NotifPrivacyTest: REDACTED</string> <string name="np_private_version_text">NotifPrivacyTest: EXPOSED</string> <string name="notif_privacy_test">Notification Privacy Test</string> <string name="notif_privacy_info">This test checks that Notification privacy is correctly @@ -4462,7 +4462,7 @@ You should be prompted to select credentials; choose the ones you just installed Set \'%s\' user restriction by turning on the switch below. </string> <string name="disallow_add_user">Disallow add user</string> - <string name="disallow_add_user_action">Adding a new user</string> + <string name="disallow_add_user_action">Accessing the multiple users settings page, or if the device allows access to the page, then attempting to add a new user </string> <string name="disallow_adjust_volume">Disallow adjust volume</string> <string name="disallow_adjust_volume_action">Adjusting the volume\n NOTE: If the device does not support volume adjustment in Settings app please skip this test and mark it as passing.\n</string> @@ -5717,7 +5717,8 @@ You should be prompted to select credentials; choose the ones you just installed <string name="audio_loopback_results_text">Results...</string> <string name="audio_loopback_test_all_paths">Please test all supported paths.</string> <string name="audio_loopback_test_not_required">Test not required for this device.</string> - <string name="audio_loopback_test_non_handheld">This device is not a handheld. Test can pass.</string> + <string name="audio_loopback_test_non_handheld">This device is not a phone. Test can pass.</string> + <string name="audio_loopback_test_internal_devices">Speaker/Mic</string> <string name="audio_loopback_speakermicpath_instructions">Place the DUT flat on a table in a quiet room. Press the Start button and allow the test to proceed.</string> diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java index bb0f87956ba..872590e9121 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/AbstractTestListActivity.java @@ -92,7 +92,7 @@ public abstract class AbstractTestListActivity extends ListActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if ((getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK) - == Configuration.UI_MODE_TYPE_TELEVISION) { + == Configuration.UI_MODE_TYPE_TELEVISION) { getWindow().requestFeature(Window.FEATURE_OPTIONS_PANEL); } setContentView(R.layout.list_content); @@ -114,10 +114,11 @@ public abstract class AbstractTestListActivity extends ListActivity { mEndTime = System.currentTimeMillis(); } TestResult testResult = TestResult.fromActivityResult(resultCode, data); - // Set the same result in both folded and unfolded mode if the test pass mode is set to - // either_mode. + // Set the same result in both folded and unfolded mode if the device is foldable + // and the test pass mode is set to either_mode. TestListItem testListItem = mAdapter.getItemByName(testResult.getName()); - if (testListItem != null && testListItem.passInEitherMode) { + if (mAdapter.isFoldableDevice() && testListItem != null + && testListItem.passInEitherMode) { setTestResult(TestResult.fromActivityResultWithDisplayMode( resultCode, data, DisplayMode.FOLDED.toString())); setTestResult(TestResult.fromActivityResultWithDisplayMode( diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java index 5857b484c59..43dd4cbf4eb 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java @@ -117,7 +117,29 @@ public class PassFailButtons { void setTestResultAndFinish(boolean passed); /** + * @return true if the test module will write a ReportLog entry + * + * Note that in order to obtain a non-null CtsVerifierReportLog object from + * the getReportLog() method, the activity must override this method to return true + * and should implement the getReportFileName(), getReportSectionName() and + * recordTestResults() methods. + * + * If this method returns true and the user did not setup up CtsVerifier correctly + * with respect to accessing local data on the DUT; + * <code> + * adb shell appops set com.android.cts.verifier android:read_device_identifiers allow + * adb shell appops set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE 0 + * </code> + * a warning dialog will be displayed when invoking that test. + */ + public boolean requiresReportLog(); + + /** * @return The name of the file to store the (suite of) ReportLog information. + * + * If a test uses the CtsVerifierReportLog, it should implement this method to provide + * a file name for the (JSON) report log data. This does not need to be unique to the + * test, perhaps specific to a set of related tests. */ public String getReportFileName(); @@ -125,16 +147,29 @@ public class PassFailButtons { * @return A unique name to serve as a section header in the CtsVerifierReportLog file. * Tests need to conform to the underscore_delineated_name standard for use with * the protobuff/json ReportLog parsing in Google3 + * + * If the test implements this method, it should also implement the requiresReportLog() + * method to return true and the getReportFileName() and recordTestResults() methods. */ public String getReportSectionName(); /** * Test subclasses can override this to record their CtsVerifierReportLogs. - * This is called when the test is exited + * This is called when the test is exited. + * + * NOTE: If the test gathers reportLog data, the test class should implement + * the requiresReportLog() to return true, and implement the getReportFileName() and + * getReportSectionName() methods. */ void recordTestResults(); - /** @return A {@link ReportLog} that is used to record test metric data. */ + /** + * @return A {@link ReportLog} that is used to record test metric data or null. + * + * If a test calls this method it must implement the requiresReportLog() to return true, + * and implement the getReportFileName() and getReportSectionName() methods, otherwise + * this method will return null. + */ CtsVerifierReportLog getReportLog(); /** @@ -222,10 +257,7 @@ public class PassFailButtons { getReportFileName(), getReportSectionName()); } - /** - * Specifies if the test module will write a ReportLog entry - * @return true if the test module will write a ReportLog entry - */ + @Override public boolean requiresReportLog() { return false; } @@ -329,6 +361,11 @@ public class PassFailButtons { } @Override + public boolean requiresReportLog() { + return false; + } + + @Override public CtsVerifierReportLog getReportLog() { return mReportLog; } @@ -422,6 +459,11 @@ public class PassFailButtons { } @Override + public boolean requiresReportLog() { + return false; + } + + @Override public CtsVerifierReportLog getReportLog() { return mReportLog; } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java index 03aec7a0beb..d4007f9edea 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/TestListAdapter.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.database.ContentObserver; import android.database.Cursor; +import android.hardware.devicestate.DeviceStateManager; import android.os.AsyncTask; import android.os.Environment; import android.os.Handler; @@ -44,10 +45,13 @@ import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.stream.Collectors; /** * {@link BaseAdapter} that handles loading, refreshing, and setting test results. What tests are @@ -442,12 +446,12 @@ public abstract class TestListAdapter extends BaseAdapter { histories.merge(null, mHistories.get(name)); new SetTestResultTask( - name, - testResult.getResult(), - testResult.getDetails(), - testResult.getReportLog(), - histories, - mScreenshotsMetadata.get(name)) + name, + testResult.getResult(), + testResult.getDetails(), + testResult.getReportLog(), + histories, + mScreenshotsMetadata.get(name)) .execute(); } @@ -522,13 +526,13 @@ public abstract class TestListAdapter extends BaseAdapter { protected abstract List<TestListItem> getRows(); static final String[] REFRESH_PROJECTION = { - TestResultsProvider._ID, - TestResultsProvider.COLUMN_TEST_NAME, - TestResultsProvider.COLUMN_TEST_RESULT, - TestResultsProvider.COLUMN_TEST_DETAILS, - TestResultsProvider.COLUMN_TEST_METRICS, - TestResultsProvider.COLUMN_TEST_RESULT_HISTORY, - TestResultsProvider.COLUMN_TEST_SCREENSHOTS_METADATA, + TestResultsProvider._ID, + TestResultsProvider.COLUMN_TEST_NAME, + TestResultsProvider.COLUMN_TEST_RESULT, + TestResultsProvider.COLUMN_TEST_DETAILS, + TestResultsProvider.COLUMN_TEST_METRICS, + TestResultsProvider.COLUMN_TEST_RESULT_HISTORY, + TestResultsProvider.COLUMN_TEST_SCREENSHOTS_METADATA, }; RefreshResult getRefreshResults(List<TestListItem> items) { @@ -635,12 +639,12 @@ public abstract class TestListAdapter extends BaseAdapter { ContentResolver resolver = mContext.getContentResolver(); try (Cursor cursor = - resolver.query( - TestResultsProvider.getTestNameUri(mContext, mTestName), - new String[] {TestResultsProvider.COLUMN_TEST_RESULT_HISTORY}, - null, - null, - null)) { + resolver.query( + TestResultsProvider.getTestNameUri(mContext, mTestName), + new String[] {TestResultsProvider.COLUMN_TEST_RESULT_HISTORY}, + null, + null, + null)) { if (cursor.moveToFirst()) { do { TestResultHistoryCollection historyCollection = @@ -848,8 +852,8 @@ public abstract class TestListAdapter extends BaseAdapter { for (TestListItem item : mRows) { if (item != null && item.isTest() && (!mTestResults.containsKey(item.testName) - || (mTestResults.get(item.testName) - != TestResult.TEST_RESULT_PASSED))) { + || (mTestResults.get(item.testName) + != TestResult.TEST_RESULT_PASSED))) { return false; } } @@ -907,6 +911,26 @@ public abstract class TestListAdapter extends BaseAdapter { return textView; } + /** + * Uses {@link DeviceStateManager} to determine if the device is foldable or not. It relies on + * the OEM exposing supported states, and setting + * com.android.internal.R.array.config_foldedDeviceStates correctly with the folded states. + * + * @return true if the device is foldable, false otherwise + */ + public boolean isFoldableDevice() { + DeviceStateManager deviceStateManager = mContext.getSystemService(DeviceStateManager.class); + if (deviceStateManager == null) { + return false; + } + Set<Integer> supportedStates = Arrays.stream( + deviceStateManager.getSupportedStates()).boxed().collect(Collectors.toSet()); + int identifier = mContext.getResources().getIdentifier( + "config_foldedDeviceStates", "array", "android"); + int[] foldedDeviceStates = mContext.getResources().getIntArray(identifier); + return Arrays.stream(foldedDeviceStates).anyMatch(supportedStates::contains); + } + private int getLayout(int position) { int viewType = getItemViewType(position); switch (viewType) { 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 00c5e6e2790..24e9ac46ba3 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java @@ -106,6 +106,8 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { private boolean mIsTV; private boolean mIsAutomobile; private boolean mIsHandheld; + private int mSpeakerDeviceId = AudioDeviceInfo.TYPE_UNKNOWN; + private int mMicDeviceId = AudioDeviceInfo.TYPE_UNKNOWN; // Peripheral(s) private static final int NUM_TEST_ROUTES = 3; @@ -371,8 +373,7 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { mTestInstructions = (TextView) findViewById(R.id.audio_loopback_instructions); mAudioManager = getSystemService(AudioManager.class); - - mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler()); + scanPeripheralList(mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL)); connectLoopbackUI(); @@ -384,6 +385,8 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { enableStartButtons(false); } + mAudioManager.registerAudioDeviceCallback(new ConnectListener(), new Handler()); + showTestInstructions(); handleTestCompletion(false); } @@ -459,19 +462,19 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { clearDeviceIds(); clearDeviceConnected(); + mSpeakerDeviceId = AudioDeviceInfo.TYPE_UNKNOWN; + mMicDeviceId = AudioDeviceInfo.TYPE_UNKNOWN; for (AudioDeviceInfo devInfo : devices) { switch (devInfo.getType()) { // TESTROUTE_DEVICE (i.e. Speaker & Mic) + // This needs to be handled differently. The other devices can be assumed + // to contain both input & output devices in the same type. + // For built-in we need to see both TYPES to be sure to have both input & output. case AudioDeviceInfo.TYPE_BUILTIN_SPEAKER: + mSpeakerDeviceId = devInfo.getId(); + break; case AudioDeviceInfo.TYPE_BUILTIN_MIC: - if (devInfo.isSink()) { - mTestSpecs[TESTROUTE_DEVICE].mOutputDeviceId = devInfo.getId(); - } else if (devInfo.isSource()) { - mTestSpecs[TESTROUTE_DEVICE].mInputDeviceId = devInfo.getId(); - } - mTestSpecs[TESTROUTE_DEVICE].mRouteAvailable = true; - mTestSpecs[TESTROUTE_DEVICE].mRouteConnected = true; - mTestSpecs[TESTROUTE_DEVICE].mDeviceName = devInfo.getProductName().toString(); + mMicDeviceId = devInfo.getId(); break; // TESTROUTE_ANALOG_JACK @@ -500,9 +503,19 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { mTestSpecs[TESTROUTE_USB].mRouteConnected = true; mTestSpecs[TESTROUTE_USB].mDeviceName = devInfo.getProductName().toString(); } + } - enableStartButtons(mustRunTest()); + // do we have BOTH a Speaker and Mic? + if (hasInternalPath()) { + mTestSpecs[TESTROUTE_DEVICE].mOutputDeviceId = mSpeakerDeviceId; + mTestSpecs[TESTROUTE_DEVICE].mInputDeviceId = mMicDeviceId; + mTestSpecs[TESTROUTE_DEVICE].mRouteAvailable = true; + mTestSpecs[TESTROUTE_DEVICE].mRouteConnected = true; + mTestSpecs[TESTROUTE_DEVICE].mDeviceName = + getResources().getString(R.string.audio_loopback_test_internal_devices); } + + enableStartButtons(mustRunTest()); } private class ConnectListener extends AudioDeviceCallback { @@ -737,7 +750,12 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { } private boolean mustRunTest() { - return mIsHandheld; + return mIsHandheld && hasInternalPath(); + } + + boolean hasInternalPath() { + return mSpeakerDeviceId != AudioDeviceInfo.TYPE_UNKNOWN + && mMicDeviceId != AudioDeviceInfo.TYPE_UNKNOWN; } private boolean calcPass(LoopbackLatencyRequirements requirements) { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/JavaMidiTestModule.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/JavaMidiTestModule.java index 7a3056c51b9..384b25f336f 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/JavaMidiTestModule.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/midilib/JavaMidiTestModule.java @@ -55,7 +55,7 @@ public abstract class JavaMidiTestModule extends MidiTestModule { // the bandwidth, resulting in lost data. In this case, slow the data stream // down. private static final int THROTTLE_MAX_PACKET_SIZE = 15; - private static final int THROTTLE_PERIOD_MS = 10; + private static final int THROTTLE_PERIOD_MS = 20; private static final int MESSAGE_MAX_BYTES = 4096; 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 11e1cfe60f4..d1c44ec4051 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 @@ -1717,15 +1717,22 @@ public class ItsService extends Service implements SensorEventListener { mSocketRunnableObj.sendResponse("camera1080pJpegCaptureMs", Double.toString(jpegCaptureMs)); } - private static long getReaderUsage(int format) { + private static long getReaderUsage(int format, boolean has10bitOutput) { // Private image format camera readers will default to ZSL usage unless // explicitly configured to use a common consumer such as display. - return (format == ImageFormat.PRIVATE) ? HardwareBuffer.USAGE_COMPOSER_OVERLAY : - HardwareBuffer.USAGE_CPU_READ_OFTEN; + // We don't support the ZSL use case within the 10-bit use case. + return (format == ImageFormat.PRIVATE && has10bitOutput) ? + HardwareBuffer.USAGE_COMPOSER_OVERLAY : HardwareBuffer.USAGE_CPU_READ_OFTEN; } private void prepareImageReaders(Size[] outputSizes, int[] outputFormats, Size inputSize, int inputFormat, int maxInputBuffers) { + prepareImageReaders(outputSizes, outputFormats, inputSize, + inputFormat, maxInputBuffers, /*has10bitOutput*/ false); + } + + private void prepareImageReaders(Size[] outputSizes, int[] outputFormats, Size inputSize, + int inputFormat, int maxInputBuffers, boolean has10bitOutput) { closeImageReaders(); mOutputImageReaders = new ImageReader[outputSizes.length]; for (int i = 0; i < outputSizes.length; i++) { @@ -1734,18 +1741,19 @@ public class ItsService extends Service implements SensorEventListener { mOutputImageReaders[i] = ImageReader.newInstance(outputSizes[i].getWidth(), outputSizes[i].getHeight(), outputFormats[i], MAX_CONCURRENT_READER_BUFFERS + maxInputBuffers, - getReaderUsage(outputFormats[i])); + getReaderUsage(outputFormats[i], has10bitOutput)); mInputImageReader = mOutputImageReaders[i]; } else { mOutputImageReaders[i] = ImageReader.newInstance(outputSizes[i].getWidth(), outputSizes[i].getHeight(), outputFormats[i], - MAX_CONCURRENT_READER_BUFFERS, getReaderUsage(outputFormats[i])); + MAX_CONCURRENT_READER_BUFFERS, getReaderUsage(outputFormats[i], + has10bitOutput)); } } if (inputSize != null && mInputImageReader == null) { mInputImageReader = ImageReader.newInstance(inputSize.getWidth(), inputSize.getHeight(), - inputFormat, maxInputBuffers, getReaderUsage(inputFormat)); + inputFormat, maxInputBuffers, getReaderUsage(inputFormat, has10bitOutput)); } } @@ -2269,7 +2277,8 @@ public class ItsService extends Service implements SensorEventListener { } } - prepareImageReaders(outputSizes, outputFormats, inputSize, inputFormat, maxInputBuffers); + prepareImageReaders(outputSizes, outputFormats, inputSize, inputFormat, maxInputBuffers, + is10bitOutputPresent); return is10bitOutputPresent; } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java index ca76b72f807..c31bc414ca2 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java @@ -71,6 +71,7 @@ public class ByodFlowTestActivity extends DialogTestListActivity { private static final int REQUEST_INTENT_FILTERS_STATUS = 2; private static final int REQUEST_CHECK_DISK_ENCRYPTION = 3; private static final int REQUEST_SET_LOCK_FOR_ENCRYPTION = 4; + private static final int REQUEST_DELETE_MANAGED_PROFILE = 5; private static final String PROVISIONING_PREFERENCES = "provisioning_preferences"; private static final String PREFERENCE_PROVISIONING_COMPLETE_STATUS = @@ -236,6 +237,10 @@ public class ByodFlowTestActivity extends DialogTestListActivity { // Called after checkIntentFilters() handleIntentFiltersStatus(resultCode); break; + case REQUEST_DELETE_MANAGED_PROFILE: + // Called during finish() + finishAfterProfileDeleted(); + break; default: super.handleActivityResult(requestCode, resultCode, data); } @@ -252,6 +257,13 @@ public class ByodFlowTestActivity extends DialogTestListActivity { public void finish() { // Pass and fail buttons are known to call finish() when clicked, and this is when we want to // clean up the provisioned profile. + Intent intent = new Intent(ByodHelperActivity.ACTION_REMOVE_MANAGED_PROFILE); + // Wait until the managed profile is deleted before returning to the previous + // activity, to ensure the deletion is not interrupted. + startActivityForResult(intent, REQUEST_DELETE_MANAGED_PROFILE); + } + + private void finishAfterProfileDeleted() { mByodFlowTestHelper.tearDown(); super.finish(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java index b0cc3ac89df..cf32cb77704 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java @@ -34,7 +34,6 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.UserHandle; import android.os.UserManager; import android.provider.MediaStore; import android.util.Log; @@ -258,6 +257,7 @@ public class ByodHelperActivity extends Activity mDevicePolicyManager.clearCrossProfileIntentFilters(mAdminReceiverComponent); mDevicePolicyManager.wipeData(0); showToast(R.string.provisioning_byod_profile_deleted); + setResult(RESULT_OK); } } else if (action.equals(ACTION_CHECK_DISK_ENCRYPTION)) { final int status = mDevicePolicyManager.getStorageEncryptionStatus(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/InteractiveVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/InteractiveVerifierActivity.java index 44f2f28d641..e33a9dfb78d 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/InteractiveVerifierActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/qstiles/InteractiveVerifierActivity.java @@ -189,8 +189,8 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi } @Override - protected void onResume() { - super.onResume(); + protected void onStart() { + super.onStart(); //To avoid NPE during onResume,before start to iterate next test order if (mCurrentTest != null && mCurrentTest.autoStart()) { mCurrentTest.status = READY; @@ -201,8 +201,8 @@ public abstract class InteractiveVerifierActivity extends PassFailButtons.Activi } @Override - protected void onPause() { - super.onPause(); + protected void onStop() { + super.onStop(); // Makes sure that the tile is removed when test is not running setTileState(false); } diff --git a/common/device-side/bedstead/btest/btest b/common/device-side/bedstead/btest/btest index 1432189f2f8..2b7b322a323 100755 --- a/common/device-side/bedstead/btest/btest +++ b/common/device-side/bedstead/btest/btest @@ -283,6 +283,7 @@ STATE_CODES = { DEFAULT_STATES = "csywal" SHORT_PACKAGE_PREFIXES = { + "a.d.c.t": "android.devicepolicy.cts.telephony", "a.d.c": "android.devicepolicy.cts", "a.d.g": "android.devicepolicy.gts", "a.m.c": "android.multiuser.cts", @@ -299,6 +300,11 @@ supported_modules = { "runner": "androidx.test.runner.AndroidJUnitRunner", "states": [RUN_ON_SYSTEM_USER, RUN_ON_WORK_PROFILE, RUN_ON_SECONDARY_USER, RUN_ON_ADDITIONAL_USER, RUN_ON_CLONE_PROFILE] }, + "CtsDevicePolicySimTestCases": { + "package": "android.devicepolicy.cts.telephony", + "runner": "androidx.test.runner.AndroidJUnitRunner", + "states": [RUN_ON_SYSTEM_USER, RUN_ON_WORK_PROFILE, RUN_ON_SECONDARY_USER, RUN_ON_ADDITIONAL_USER, RUN_ON_CLONE_PROFILE] + }, "GtsDevicePolicyTestCases": { "package": "android.devicepolicy.gts", "runner": "androidx.test.runner.AndroidJUnitRunner", diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireHasDefaultBrowser.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireHasDefaultBrowser.java new file mode 100644 index 00000000000..853776454aa --- /dev/null +++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireHasDefaultBrowser.java @@ -0,0 +1,37 @@ +/* + * 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 static com.android.bedstead.harrier.UserType.INSTRUMENTED_USER; + +import com.android.bedstead.harrier.UserType; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Requires the user has a default browser + */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(value = RetentionPolicy.RUNTIME) +public @interface RequireHasDefaultBrowser { + /** Which user type should we check the browser for. */ + UserType forUser() default INSTRUMENTED_USER; + + FailureMode failureMode() default FailureMode.SKIP; +} diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/FactoryResetProtection.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/FactoryResetProtection.java index 37786e50b99..45c6a9f930c 100644 --- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/FactoryResetProtection.java +++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/FactoryResetProtection.java @@ -30,9 +30,7 @@ import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy; * {@code DevicePolicyManager#getFactoryResetProtection}. */ @EnterprisePolicy(dpc = - APPLIED_BY_DEVICE_OWNER | APPLIED_BY_ORGANIZATION_OWNED_PROFILE_OWNER_PROFILE | - APPLIED_BY_DPM_ROLE_HOLDER | APPLIES_GLOBALLY | CANNOT_BE_APPLIED_BY_ROLE_HOLDER, - permissions = - @EnterprisePolicy.Permission(appliedWith = MANAGE_DEVICE_POLICY_FACTORY_RESET, appliesTo = APPLIES_GLOBALLY)) + APPLIED_BY_DEVICE_OWNER | APPLIED_BY_ORGANIZATION_OWNED_PROFILE_OWNER_PROFILE + | APPLIES_GLOBALLY | CANNOT_BE_APPLIED_BY_ROLE_HOLDER) public final class FactoryResetProtection { } diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedInputMethods.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedInputMethods.java index bf4f3c0b048..3b5179b73bb 100644 --- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedInputMethods.java +++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedInputMethods.java @@ -32,7 +32,7 @@ import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy; * <p>See {@code DevicePolicyManager#setPermittedInputMethods(ComponentName, List<String>)} * for more detail. */ // APPLIED_BY_DPM_ROLE_HOLDER -@EnterprisePolicy(dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIED_BY_PARENT_INSTANCE_OF_ORGANIZATIONAL_OWNED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER +@EnterprisePolicy(dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER | CANNOT_BE_APPLIED_BY_ROLE_HOLDER | INHERITABLE, permissions = @EnterprisePolicy.Permission(appliedWith = MANAGE_DEVICE_POLICY_INPUT_METHODS, appliesTo = APPLIES_TO_OWN_USER)) diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedSystemInputMethods.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedSystemInputMethods.java new file mode 100644 index 00000000000..1b372a2a5bf --- /dev/null +++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/PermittedSystemInputMethods.java @@ -0,0 +1,39 @@ +/* + * 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.bedstead.harrier.policies; + +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER; +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PARENT_INSTANCE_OF_ORGANIZATIONAL_OWNED_PROFILE_OWNER_PROFILE; +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER; +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER; +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.CANNOT_BE_APPLIED_BY_ROLE_HOLDER; +import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.INHERITABLE; +import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_DEVICE_POLICY_INPUT_METHODS; + +import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy; + +/** + * Policy for setting permitted input methods - with the restriction that it only permits setting + * either system apps enabled or all apps enabled (with no option to specify particular apps). + * + * <p>See {@code DevicePolicyManager#setPermittedInputMethods(ComponentName, List<String>)} + * for more detail. + */ +@EnterprisePolicy(dpc = APPLIED_BY_PARENT_INSTANCE_OF_ORGANIZATIONAL_OWNED_PROFILE_OWNER_PROFILE | APPLIES_TO_OWN_USER + | CANNOT_BE_APPLIED_BY_ROLE_HOLDER | INHERITABLE) +public class PermittedSystemInputMethods { +} 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 99648fbfb08..7a7990d594f 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 @@ -17,6 +17,7 @@ package com.android.bedstead.harrier; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; +import static android.app.role.RoleManager.ROLE_BROWSER; import static android.content.pm.PackageManager.FEATURE_MANAGED_USERS; import static android.os.Build.VERSION.SDK_INT; @@ -75,6 +76,7 @@ import com.android.bedstead.harrier.annotations.EnsureHasAccountAuthenticator; import com.android.bedstead.harrier.annotations.EnsureHasAccounts; import com.android.bedstead.harrier.annotations.EnsureHasAdditionalUser; import com.android.bedstead.harrier.annotations.EnsureHasAppOp; +import com.android.bedstead.harrier.annotations.RequireHasDefaultBrowser; import com.android.bedstead.harrier.annotations.EnsureHasNoAccounts; import com.android.bedstead.harrier.annotations.EnsureHasNoAdditionalUser; import com.android.bedstead.harrier.annotations.EnsureHasPermission; @@ -1290,6 +1292,16 @@ public final class DeviceState extends HarrierRule { requireQuickSettingsSupport.failureMode()); continue; } + if (annotation instanceof RequireHasDefaultBrowser) { + RequireHasDefaultBrowser requireHasDefaultBrowser = + (RequireHasDefaultBrowser) annotation; + UserReference user = + resolveUserTypeToUser(requireHasDefaultBrowser.forUser()); + checkFailOrSkip("User: " + user + " does not have a default browser", + !TestApis.roles().getRoleHoldersAsUser(ROLE_BROWSER, user).isEmpty(), + requireHasDefaultBrowser.failureMode()); + continue; + } if (annotation instanceof RequireTelephonySupport) { RequireTelephonySupport requireTelephonySupport = diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java index 1606b6e510c..5fed6b470fa 100644 --- a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java +++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java @@ -54,6 +54,7 @@ import static org.testng.Assert.assertThrows; import android.app.ActivityManager; import android.app.admin.DevicePolicyManager; import android.app.contentsuggestions.ContentSuggestionsManager; +import android.app.role.RoleManager; import android.os.Build; import android.os.Bundle; import android.os.UserManager; @@ -108,6 +109,7 @@ import com.android.bedstead.harrier.annotations.RequireFeatureFlagEnabled; import com.android.bedstead.harrier.annotations.RequireFeatureFlagNotEnabled; import com.android.bedstead.harrier.annotations.RequireFeatureFlagValue; import com.android.bedstead.harrier.annotations.RequireGmsBuild; +import com.android.bedstead.harrier.annotations.RequireHasDefaultBrowser; import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode; import com.android.bedstead.harrier.annotations.RequireInstantApp; import com.android.bedstead.harrier.annotations.RequireLowRamDevice; @@ -1636,4 +1638,9 @@ public class DeviceStateTest { assertThat(TestApis.content().suggestions().defaultServiceEnabled(sDeviceState.additionalUser())).isTrue(); } + @Test + @RequireHasDefaultBrowser + public void requireHasDefaultBrowser_onDifferentUser_defaultContentSuggestionsServiceIsEnabled() { + assertThat(TestApis.roles().getRoleHolders(RoleManager.ROLE_BROWSER)).isNotEmpty(); + } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java index 0383d3096f7..79492ec86bb 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java @@ -66,6 +66,7 @@ import com.android.bedstead.nene.permissions.Permissions; import com.android.bedstead.nene.roles.RoleContext; import com.android.bedstead.nene.users.UserReference; import com.android.bedstead.nene.utils.Poll; +import com.android.bedstead.nene.utils.Retry; import com.android.bedstead.nene.utils.ShellCommand; import com.android.bedstead.nene.utils.ShellCommandUtils; import com.android.bedstead.nene.utils.Versions; @@ -718,13 +719,15 @@ public final class Package { // In most cases this should work first time, however if a user restriction has been // recently removed we may need to retry + int previousPid = runningProcess().pid(); Poll.forValue("Application flag", () -> { userActivityManager.forceStopPackage(mPackageName); return userPackageManager.getPackageInfo(mPackageName, PackageManager.GET_META_DATA) .applicationInfo.flags; }) - .toMeet(flag -> (flag & FLAG_STOPPED) == FLAG_STOPPED) + .toMeet(flag ->(flag & FLAG_STOPPED) == FLAG_STOPPED + || previousPid != runningProcess().pid()) .errorOnFail("Expected application flags to contain FLAG_STOPPED (" + FLAG_STOPPED + ")") .await(); @@ -952,24 +955,48 @@ public final class Package { public RoleContext setAsRoleHolder(String role, UserReference user) { try (PermissionContext p = TestApis.permissions().withPermission( MANAGE_ROLE_HOLDERS, INTERACT_ACROSS_USERS_FULL)) { - DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); - - sRoleManager.addRoleHolderAsUser( - role, - mPackageName, - /* flags= */ 0, - user.userHandle(), - TestApis.context().instrumentedContext().getMainExecutor(), - blockingCallback::triggerCallback); - - boolean success = blockingCallback.await(); - if (!success) { - fail("Could not set role holder of " + role + "."); - } + + Retry.logic(() -> { + TestApis.logcat().clear(); + DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); + + sRoleManager.addRoleHolderAsUser( + role, + mPackageName, + /* flags= */ 0, + user.userHandle(), + TestApis.context().instrumentedContext().getMainExecutor(), + blockingCallback::triggerCallback); + + boolean success = blockingCallback.await(); + if (!success) { + fail("Could not set role holder of " + role + "." + " Relevant logcat: " + + TestApis.logcat().dump((line) -> line.contains(role))); + } + if (!TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) { + fail("addRoleHolderAsUser returned true but did not add role holder. " + + "Relevant logcat: " + TestApis.logcat().dump( + (line) -> line.contains(role))); + } + }).terminalException(e -> { + // Terminal unless we see logcat output indicating it might be temporary + var logcat = TestApis.logcat() + .dump(l -> l.contains("Error calling onAddRoleHolder()")); + if (!logcat.isEmpty()) { + // On low end devices - this can happen when the broadcast queue is full + try { + Thread.sleep(10_000); + } catch (InterruptedException ex) { + return true; + } + + return false; + } + + return true; + }).runAndWrapException(); return new RoleContext(role, this, user); - } catch (InterruptedException e) { - throw new NeneException("Error waiting for setting role holder callback " + role, e); } } @@ -988,23 +1015,45 @@ public final class Package { public void removeAsRoleHolder(String role, UserReference user) { try (PermissionContext p = TestApis.permissions().withPermission( MANAGE_ROLE_HOLDERS)) { - DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); - sRoleManager.removeRoleHolderAsUser( - role, - mPackageName, - /* flags= */ 0, - user.userHandle(), - TestApis.context().instrumentedContext().getMainExecutor(), - blockingCallback::triggerCallback); - TestApis.roles().setBypassingRoleQualification(false); - - boolean success = blockingCallback.await(); - if (!success) { - fail("Failed to clear the role holder of " - + role + "."); - } - } catch (InterruptedException e) { - throw new NeneException("Error while clearing role holder " + role, e); + Retry.logic(() -> { + TestApis.logcat().clear(); + DefaultBlockingCallback<Boolean> blockingCallback = new DefaultBlockingCallback<>(); + sRoleManager.removeRoleHolderAsUser( + role, + mPackageName, + /* flags= */ 0, + user.userHandle(), + TestApis.context().instrumentedContext().getMainExecutor(), + blockingCallback::triggerCallback); + TestApis.roles().setBypassingRoleQualification(false); + + boolean success = blockingCallback.await(); + if (!success) { + fail("Failed to clear the role holder of " + + role + "."); + } + if (TestApis.roles().getRoleHoldersAsUser(role, user).contains(packageName())) { + fail("removeRoleHolderAsUser returned true but did not remove role holder. " + + "Relevant logcat: " + TestApis.logcat().dump( + (line) -> line.contains(role))); + } + }).terminalException(e -> { + // Terminal unless we see logcat output indicating it might be temporary + var logcat = TestApis.logcat() + .dump(l -> l.contains("Error calling onRemoveRoleHolder()")); + if (!logcat.isEmpty()) { + // On low end devices - this can happen when the broadcast queue is full + try { + Thread.sleep(10_000); + } catch (InterruptedException ex) { + return true; + } + + return false; + } + + return true; + }).runAndWrapException(); } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/RoleContext.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/RoleContext.java index 714fc130532..98e38bd173b 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/RoleContext.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/RoleContext.java @@ -16,9 +16,15 @@ package com.android.bedstead.nene.roles; +import static com.android.bedstead.nene.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL; + +import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.packages.Package; +import com.android.bedstead.nene.permissions.PermissionContext; import com.android.bedstead.nene.users.UserReference; +import java.util.Set; + /** * A context that, when closed, will remove the package from the role. */ @@ -27,15 +33,30 @@ public final class RoleContext implements AutoCloseable { private final String mRole; private final Package mPackage; private final UserReference mUser; + private final Set<String> mPreviousRoleHolders; public RoleContext(String role, Package pkg, UserReference user) { mRole = role; mPackage = pkg; mUser = user; + mPreviousRoleHolders = TestApis.roles().getRoleHoldersAsUser(role, user); } @Override public void close() { - mPackage.removeAsRoleHolder(mRole, mUser); + try (PermissionContext p = TestApis.permissions().withPermission( + INTERACT_ACROSS_USERS_FULL)) { + mPackage.removeAsRoleHolder(mRole, mUser); + + Set<String> currentRoleHolders = TestApis.roles().getRoleHoldersAsUser(mRole, mUser); + // Re-adding previous role holder just for exclusive role as we would have overridden + // the previous role holder in this case, for non exclusive roles it's not a problem + // as we just add as one of the role holder so just removing them is fine. + if (currentRoleHolders.isEmpty() && mPreviousRoleHolders.size() == 1) { + Package roleHolderPackage = Package.of( + mPreviousRoleHolders.stream().toList().get(0)); + roleHolderPackage.setAsRoleHolder(mRole, mUser); + } + } } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java index 72c90b6ac24..a6378d38902 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java @@ -17,6 +17,7 @@ package com.android.bedstead.nene.roles; import static com.android.bedstead.nene.permissions.CommonPermissions.BYPASS_ROLE_QUALIFICATION; +import static com.android.bedstead.nene.permissions.CommonPermissions.INTERACT_ACROSS_USERS_FULL; import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS; import static com.android.bedstead.nene.utils.Versions.T; @@ -24,15 +25,16 @@ import android.annotation.TargetApi; import android.app.role.RoleManager; import android.content.Context; import android.os.Build; +import android.os.UserHandle; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.annotations.Experimental; import com.android.bedstead.nene.packages.Package; import com.android.bedstead.nene.permissions.PermissionContext; +import com.android.bedstead.nene.users.UserReference; import com.android.bedstead.nene.utils.Versions; import java.util.HashSet; -import java.util.List; import java.util.Set; /** @@ -74,4 +76,17 @@ public class Roles { return new HashSet<>(sContext.getSystemService(RoleManager.class).getRoleHolders(role)); } } + + /** + * @see RoleManager#getRoleHoldersAsUser(String, UserHandle) + */ + @Experimental + public Set<String> getRoleHoldersAsUser(String role, UserReference user) { + try (PermissionContext p = TestApis.permissions().withPermission( + MANAGE_ROLE_HOLDERS).withPermission(INTERACT_ACROSS_USERS_FULL)) { + return new HashSet<>( + sContext.getSystemService(RoleManager.class).getRoleHoldersAsUser(role, + user.userHandle())); + } + } } diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java index a2a41920115..7998334ebb1 100644 --- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java +++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java @@ -357,6 +357,13 @@ public class PackageTest { } @Test + public void forceStop_whenRestartableApp_doesNotLoopEndlessly() { + final int previousId = TestApis.packages().launcher().runningProcess().pid(); + TestApis.packages().launcher().forceStop(); + assertThat(TestApis.packages().launcher().runningProcess().pid()).isNotEqualTo(previousId); + } + + @Test @EnsureHasSecondaryUser @RequireRunNotOnSecondaryUser public void installedOnUsers_doesNotIncludeUserWithoutPackageInstalled() throws Exception { diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ConnectivityDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ConnectivityDeviceInfo.java index 45726b8690c..c99d9a250e5 100644 --- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ConnectivityDeviceInfo.java +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/ConnectivityDeviceInfo.java @@ -15,9 +15,13 @@ */ package com.android.compatibility.common.deviceinfo; +import android.content.pm.PackageManager; + import android.net.wifi.ScanResult; import android.net.wifi.WifiManager; +import android.os.Build; + import android.util.Log; import com.android.compatibility.common.util.DeviceInfoStore; @@ -63,12 +67,20 @@ public final class ConnectivityDeviceInfo extends DeviceInfo { return ""; } + private boolean hasWifi() { + return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI); + } + @Override protected void collectDeviceInfo(DeviceInfoStore store) throws Exception { - try { - collectWifiStandards(store); - } catch (IOException e) { - Log.w(LOG_TAG, "Failed to collect WiFi standards", e); + if (hasWifi()) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + try { + collectWifiStandards(store); + } catch (IOException e) { + Log.w(LOG_TAG, "Failed to collect WiFi standards", e); + } + } } } diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GnssDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GnssDeviceInfo.java index 8ab6b5e755a..4100c97fdf8 100644 --- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GnssDeviceInfo.java +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/GnssDeviceInfo.java @@ -51,7 +51,9 @@ public final class GnssDeviceInfo extends DeviceInfo { return; } collectGnssHardwareModelName(store, locationManager); - collectGnssCapabilities(store, locationManager.getGnssCapabilities()); + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { + collectGnssCapabilities(store, locationManager.getGnssCapabilities()); + } collectAccumulatedDeltaRangeMeasurements(store, locationManager); } @@ -79,9 +81,6 @@ public final class GnssDeviceInfo extends DeviceInfo { /** collect info for gnss capabilities into a group */ private void collectGnssCapabilities(DeviceInfoStore store, GnssCapabilities gnssCapabilities) throws IOException { - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - return; - } store.startGroup("gnss_capabilities"); store.addResult("has_low_power_mode", gnssCapabilities.hasLowPowerMode()); diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDrmDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDrmDeviceInfo.java new file mode 100644 index 00000000000..28d49395b5b --- /dev/null +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/MediaDrmDeviceInfo.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Google LLC. + * + * 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.compatibility.common.deviceinfo; + +import android.annotation.TargetApi; +import android.media.MediaDrm; +import android.os.Build; +import android.util.Log; + +import com.android.compatibility.common.deviceinfo.DeviceInfo; +import com.android.compatibility.common.util.DeviceInfoStore; +import com.android.compatibility.common.util.SystemUtil; + +import java.io.IOException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +/** + * Collects Media Drm related properties in WVTS tests. + */ +public class MediaDrmDeviceInfo extends DeviceInfo { + private static final String TAG = MediaDrmDeviceInfo.class.getSimpleName(); + + @Override + @TargetApi(Build.VERSION_CODES.R) + protected void collectDeviceInfo(DeviceInfoStore store) throws Exception { + List<UUID> supportedCryptoSchemes = MediaDrm.getSupportedCryptoSchemes(); + + store.startArray("media_drm_info"); + for (UUID scheme : supportedCryptoSchemes) { + Log.i(TAG, scheme.toString()); + store.startGroup(); + store.addResult("scheme_uuid ", scheme.toString()); + try (MediaDrm mediaDrm = new MediaDrm(scheme)) { + for (String key : new String[]{MediaDrm.PROPERTY_VENDOR, MediaDrm.PROPERTY_VERSION, + MediaDrm.PROPERTY_DESCRIPTION}) { + try { + String value = mediaDrm.getPropertyString(key); + Log.i(TAG, String.format("key %s, value %s", key, value)); + store.addResult(key, value); + } catch (Exception e) { + // leave value blank + } + } + } catch (Exception e) { + Log.e(TAG, "Failed to collect device info", e); + Log.e(TAG + "scheme", String.valueOf(scheme)); + } + store.endGroup(); + } + store.endArray(); + + try { + String output = SystemUtil.runShellCommand("pgrep -fl android.hardware.drm"); + ArrayList<String> list = new ArrayList<>(Arrays.asList(output.split("\n"))); + for (int i = list.size() - 1; i >= 0; i--) { + String drmHal = list.remove(i); + int n = drmHal.indexOf("/"); + list.add(drmHal.substring(n, drmHal.length())); + } + store.addListResult("media_drm_hal", list); + } catch (Exception e) { + Log.e(TAG, e.toString()); + } + } +} diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/Step.java b/common/device-side/interactive/src/main/java/com/android/interactive/Step.java index 07d0f036b82..abe25f18aac 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/Step.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/Step.java @@ -256,6 +256,21 @@ public abstract class Step<E> { } /** + * Adds small button with a single up/down arrow, used for moving the text box to the + * bottom of the screen in case it covers some critical area of the app + */ + + protected void addSwapButton() { + Button btn = new Button(TestApis.context().instrumentedContext()); + // up/down arrow + btn.setText("\u21F5"); + btn.setOnClickListener(v -> swap()); + + GridLayout layout = mInstructionView.findViewById(R.id.buttons); + layout.addView(btn); + } + + /** * Adds a button to immediately mark the test as failed and request the tester to provide the * reason for failure. */ @@ -296,6 +311,19 @@ public abstract class Step<E> { }); } + /** + * Swaps the prompt from the top to the bottom of the user screen + */ + protected void swap() { + WindowManager.LayoutParams params = (WindowManager.LayoutParams) mInstructionView.getLayoutParams(); + if (params.gravity == Gravity.TOP) { + params.gravity = Gravity.BOTTOM; + } else { + params.gravity = Gravity.TOP; + } + sWindowManager.updateViewLayout(mInstructionView, params); + } + protected void close() { if (mInstructionView != null) { TestApis.context().instrumentationContext().getMainExecutor().execute(() -> { diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndConfirmStep.java b/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndConfirmStep.java index 1333a6ea504..9ce5549a5fd 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndConfirmStep.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndConfirmStep.java @@ -36,5 +36,6 @@ public abstract class ActAndConfirmStep extends Step<Nothing> { addButton("Done", this::pass); addFailButton(); + addSwapButton(); } } diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndWaitStep.java b/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndWaitStep.java index 22873cd82d0..944ec8cd1d4 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndWaitStep.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/steps/ActAndWaitStep.java @@ -45,6 +45,7 @@ public abstract class ActAndWaitStep extends Step<Nothing> { public void interact() { show(mInstruction); addFailButton(); + addSwapButton(); } @Override diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/steps/MultipleChoiceStep.java b/common/device-side/interactive/src/main/java/com/android/interactive/steps/MultipleChoiceStep.java index 0f94ecf9081..56257810dfa 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/steps/MultipleChoiceStep.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/steps/MultipleChoiceStep.java @@ -46,5 +46,6 @@ public abstract class MultipleChoiceStep extends Step<String> { }); } addFailButton(); + addSwapButton(); } } diff --git a/common/device-side/interactive/src/main/java/com/android/interactive/steps/YesNoStep.java b/common/device-side/interactive/src/main/java/com/android/interactive/steps/YesNoStep.java index 6f7887579b2..bd5cd7252a8 100644 --- a/common/device-side/interactive/src/main/java/com/android/interactive/steps/YesNoStep.java +++ b/common/device-side/interactive/src/main/java/com/android/interactive/steps/YesNoStep.java @@ -43,5 +43,6 @@ public abstract class YesNoStep extends Step<Boolean> { close(); }); addFailButton(); + addSwapButton(); } } diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils2.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils2.java index 64b11c1ee5e..6189ff60bac 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils2.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils2.java @@ -172,7 +172,7 @@ public class UiAutomatorUtils2 { if (scrollable != null && scrollable.exists()) { final Rect scrollableBounds = scrollable.getVisibleBounds(); final int distanceToSwipe = collapsingToolbar.getVisibleBounds().height() / 2; - getUiDevice().drag(scrollableBounds.centerX(), scrollableBounds.centerY(), + getUiDevice().swipe(scrollableBounds.centerX(), scrollableBounds.centerY(), scrollableBounds.centerX(), scrollableBounds.centerY() - distanceToSwipe, steps); } else { diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UserHelper.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserHelper.java index a048fdee186..797743a3489 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UserHelper.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UserHelper.java @@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY; import android.app.ActivityOptions; import android.content.Context; +import android.os.Build; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; @@ -31,7 +32,6 @@ import android.view.MotionEvent; import androidx.annotation.Nullable; import androidx.test.InstrumentationRegistry; -import com.android.modules.utils.build.SdkLevel; import java.util.Objects; import java.util.function.Function; @@ -71,7 +71,7 @@ public final class UserHelper { mUser = Objects.requireNonNull(context).getUser(); UserManager userManager = context.getSystemService(UserManager.class); - if (!SdkLevel.isAtLeastU()) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { mVisibleBackgroundUsersSupported = false; if (DEBUG) { Log.d(TAG, "Pre-UDC constructor (mUser=" + mUser + "): setting " diff --git a/common/device-side/util/jni/android_cts_CpuFeatures.cpp b/common/device-side/util/jni/android_cts_CpuFeatures.cpp index 2f56a2fcab0..ac1f79b2069 100644 --- a/common/device-side/util/jni/android_cts_CpuFeatures.cpp +++ b/common/device-side/util/jni/android_cts_CpuFeatures.cpp @@ -17,6 +17,7 @@ #include <jni.h> #include <string.h> #include <sys/auxv.h> +#include <sys/system_properties.h> #include <sys/utsname.h> #include <string> @@ -73,6 +74,10 @@ jint android_cts_CpuFeatures_getHwCaps(JNIEnv*, jobject) jboolean android_cts_CpuFeatures_isNativeBridgedCpu(JNIEnv* env, jobject thiz) { +#if defined(__arm__) + static const prop_info* pi = __system_property_find("ro.dalvik.vm.isa.arm"); + return pi != nullptr; +#endif #if defined(__arm__) || defined(__aarch64__) // If the test is compiled for arm use uname() to check if host CPU is x86. struct utsname uname_data; diff --git a/hostsidetests/abioverride/AndroidTest.xml b/hostsidetests/abioverride/AndroidTest.xml index edb3122595b..105b9232acb 100644 --- a/hostsidetests/abioverride/AndroidTest.xml +++ b/hostsidetests/abioverride/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS AbiOverride host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="webview" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/accounts/AndroidTest.xml b/hostsidetests/accounts/AndroidTest.xml index 2d752c49f3a..7b77b610939 100644 --- a/hostsidetests/accounts/AndroidTest.xml +++ b/hostsidetests/accounts/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for the CTS accounts host tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="auth" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/hostsidetests/adbmanager/AndroidTest.xml b/hostsidetests/adbmanager/AndroidTest.xml index 2194a4c574c..bb96507ed5a 100644 --- a/hostsidetests/adbmanager/AndroidTest.xml +++ b/hostsidetests/adbmanager/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS AdbManager host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="devtools" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/AppCompatOverridesServiceTest.java b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/AppCompatOverridesServiceTest.java index de1656568d3..61b4bf2899c 100644 --- a/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/AppCompatOverridesServiceTest.java +++ b/hostsidetests/appcompat/compatchanges/src/com/android/cts/appcompat/AppCompatOverridesServiceTest.java @@ -316,6 +316,9 @@ public class AppCompatOverridesServiceTest extends CompatChangeGatingTestCase { } private Change getChange(long changeId) throws Exception { + // Data put by device_config app_compat_overrides need some time to update the data + // for dumpsys platform_compat so adding some sleep time + Thread.sleep(WAIT_TIME_MS); Change ctsChange = getOnDeviceChangeIdConfig(changeId); assertWithMessage("CTS specific change %s not found on device", changeId) .that(ctsChange).isNotNull(); diff --git a/hostsidetests/appsecurity/Android.bp b/hostsidetests/appsecurity/Android.bp index 4909cfc2377..100e5ede8b9 100644 --- a/hostsidetests/appsecurity/Android.bp +++ b/hostsidetests/appsecurity/Android.bp @@ -96,9 +96,6 @@ java_test_host { ":CtsStorageAppA", ":CtsStorageAppB", ":CtsNoAppDataStorageApp", - ":CtsDocumentClient", - ":CtsDocumentProvider", - ":CtsStubIme", ":CtsInvalidRequiredSplitTypeSplitApp", ":CtsNeedSplitApp", ":CtsNeedSplitFeatureWarm", diff --git a/hostsidetests/appsecurity/AndroidTest.xml b/hostsidetests/appsecurity/AndroidTest.xml index 5633cce65b1..d7da6eb9757 100644 --- a/hostsidetests/appsecurity/AndroidTest.xml +++ b/hostsidetests/appsecurity/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for the CTS App Security host tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/appsecurity/OWNERS b/hostsidetests/appsecurity/OWNERS index a9fea40b80e..ca951166539 100644 --- a/hostsidetests/appsecurity/OWNERS +++ b/hostsidetests/appsecurity/OWNERS @@ -21,10 +21,6 @@ # Bug component: 46626 = per-file *Documents* # Bug component: 46626 = per-file ScopedDirectoryAccessTest.java -# DocsUI bug component -# Bug component: 46626 = per-file *Documents* -# Bug component: 46626 = per-file ScopedDirectoryAccessTest.java - patb@google.com per-file AccessSerialNumberTest.java = ashfall@google.com per-file ApexSignatureVerificationTest.java = dariofreni@google.com @@ -71,11 +67,6 @@ per-file com.android.apex.cts.shim.*.apex = ioffe@google.com per-file *Adoptable* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS per-file *DirectBoot* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS per-file *Storage* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS -per-file *Documents* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS -per-file ScopedDirectoryAccessTest.java = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS - -per-file *Documents* = file:platform/packages/apps/DocumentsUI:/OWNERS -per-file ScopedDirectoryAccessTest.java = file:platform/packages/apps/DocumentsUI:/OWNERS # OTA per-file *ResumeOnReboot* = file:platform/frameworks/base:/core/java/android/service/resumeonreboot/OWNERS diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java deleted file mode 100644 index b9ef7ad5376..00000000000 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTest.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.appsecurity.cts; - -import android.platform.test.annotations.AsbSecurityTest; - -import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; -import com.android.compatibility.common.util.ApiLevelUtil; -import com.android.tradefed.device.DeviceNotAvailableException; - -import com.google.common.collect.ImmutableSet; - -/** - * Set of tests that verify behavior of - * {@link android.provider.DocumentsContract} and related intents. - */ -public class DocumentsTest extends DocumentsTestCase { - private static final String PROVIDER_PKG = "com.android.cts.documentprovider"; - private static final String STUBIME_PKG = "com.android.cts.stubime"; - private static final String PROVIDER_APK = "CtsDocumentProvider.apk"; - private static final String STUBIME_APK = "CtsStubIme.apk"; - - private static final long RESTRICT_STORAGE_ACCESS_FRAMEWORK = 141600225L; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - getDevice().uninstallPackage(PROVIDER_PKG); - CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); - assertNull(getDevice().installPackage(buildHelper.getTestFile(PROVIDER_APK), false)); - assertNull(getDevice().installPackage(buildHelper.getTestFile(STUBIME_APK), false)); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - - getDevice().uninstallPackage(PROVIDER_PKG); - getDevice().uninstallPackage(STUBIME_PKG); - } - - public void testOpenSimple() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenSimple"); - } - - public void testOpenVirtual() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenVirtual"); - } - - public void testCreateNew() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateNew"); - } - - public void testCreateExisting() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateExisting"); - } - - public void testTree() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testTree"); - } - - public void testGetContent_rootsShowing() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testGetContent_rootsShowing"); - } - - public void testGetContentWithQuery_matchingFileShowing() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testGetContentWithQuery_matchingFileShowing"); - } - - public void testGetContent_returnsResultToCallingActivity() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testGetContent_returnsResultToCallingActivity"); - } - - public void testTransferDocument() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testTransferDocument"); - } - - public void testFindDocumentPathInScopedAccess() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testFindDocumentPathInScopedAccess"); - } - - public void testOpenDocumentAtInitialLocation() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenDocumentAtInitialLocation"); - } - - public void testOpenDocumentTreeAtInitialLocation() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testOpenDocumentTreeAtInitialLocation"); - } - - public void testOpenDocumentTreeWithScopedStorage() throws Exception { - if (isAtLeastR()) { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testOpenDocumentTreeWithScopedStorage"); - } - } - - public void testOpenRootWithoutRootIdAtInitialLocation() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testOpenRootWithoutRootIdAtInitialLocation"); - } - - public void testCreateDocumentAtInitialLocation() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateDocumentAtInitialLocation"); - } - - public void testCreateWebLink() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testCreateWebLink"); - } - - public void testEject() throws Exception { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", "testEject"); - } - - public void testScopeStorageAtInitLocationRootWithDot_blockFromTree() throws Exception { - if (isAtLeastT()) { - // From BUILD.VERSION_CODES.S, scope storage is enabled in default - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testScopeStorageAtInitLocationRootWithDot_blockFromTree"); - } - } - - public void testScopeStorageAtInitLocationAndroidData_blockFromTree() throws Exception { - if (isAtLeastT()) { - // From BUILD.VERSION_CODES.S, scope storage is enabled in default - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testScopeStorageAtInitLocationAndroidData_blockFromTree"); - } - } - - public void testScopeStorageAtInitLocationAndroidObb_blockFromTree() throws Exception { - if (isAtLeastT()) { - // From BUILD.VERSION_CODES.S, scope storage is enabled in default - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testScopeStorageAtInitLocationAndroidObb_blockFromTree"); - } - } - - public void testRestrictStorageAccessFrameworkEnabled_blockFromTree() throws Exception { - if (isAtLeastR() && isSupportedHardware()) { - runDeviceCompatTestReported(CLIENT_PKG, ".DocumentsClientTest", - "testRestrictStorageAccessFrameworkEnabled_blockFromTree", - /* enabledChanges= */ ImmutableSet.of(RESTRICT_STORAGE_ACCESS_FRAMEWORK), - /* disabledChanges= */ ImmutableSet.of(), - /* reportedEnabledChanges= */ ImmutableSet.of(), - /* reportedDisabledChanges= */ ImmutableSet.of()); - } - } - - public void testRestrictStorageAccessFrameworkDisabled_notBlockFromTree() throws Exception { - // For S+, the flag will be force enabled, so we only run this test against R. - if (isAtLeastR() && !isAtLeastS() && isSupportedHardware()) { - runDeviceCompatTestReported(CLIENT_PKG, ".DocumentsClientTest", - "testRestrictStorageAccessFrameworkDisabled_notBlockFromTree", - /* enabledChanges */ ImmutableSet.of(), - /* disabledChanges */ ImmutableSet.of(RESTRICT_STORAGE_ACCESS_FRAMEWORK), - /* reportedEnabledChanges= */ ImmutableSet.of(), - /* reportedDisabledChanges= */ ImmutableSet.of()); - } - } - - @AsbSecurityTest(cveBugId = 157474195) - public void testAfterMoveDocumentInStorage_revokeUriPermission() throws Exception { - if (isAtLeastS()) { - runDeviceTests(CLIENT_PKG, ".DocumentsClientTest", - "testAfterMoveDocumentInStorage_revokeUriPermission"); - } - } - - private boolean isAtLeastR() { - try { - return ApiLevelUtil.isAfter(getDevice(), 29 /* BUILD.VERSION_CODES.Q */); - } catch (Exception e) { - return false; - } - } - - private boolean isAtLeastS() { - try { - return ApiLevelUtil.isAfter(getDevice(), 30 /* BUILD.VERSION_CODES.R */) - || ApiLevelUtil.codenameEquals(getDevice(), "S"); - } catch (Exception e) { - return false; - } - } - - private boolean isAtLeastT() { - try { - return ApiLevelUtil.isAfter(getDevice(), 32 /* BUILD.VERSION_CODES.S_V2 */); - } catch (Exception e) { - return false; - } - } - - private boolean isSupportedHardware() { - try { - if (getDevice().hasFeature("feature:android.hardware.type.television") - || getDevice().hasFeature("feature:android.hardware.type.watch") - || getDevice().hasFeature("feature:android.hardware.type.automotive")) { - return false; - } - } catch (DeviceNotAvailableException e) { - return true; - } - return true; - } -} diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java deleted file mode 100644 index d7a9ffc9f8c..00000000000 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/DocumentsTestCase.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2016 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.appsecurity.cts; - -import android.compat.cts.CompatChangeGatingTestCase; - -import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; -import com.android.tradefed.device.DeviceNotAvailableException; -import com.android.tradefed.testtype.IAbi; -import com.android.tradefed.testtype.IAbiReceiver; - -/** - * Base class for {@link android.provider.DocumentsContract} and related test cases. - */ -abstract class DocumentsTestCase extends CompatChangeGatingTestCase implements IAbiReceiver { - protected static final String CLIENT_PKG = "com.android.cts.documentclient"; - protected static final String CLIENT_APK = "CtsDocumentClient.apk"; - - protected IAbi mAbi; - - @Override - public void setAbi(IAbi abi) { - mAbi = abi; - } - - @Override - protected void setUp() throws Exception { - super.setUp(); - - Utils.prepareSingleUser(getDevice()); - assertNotNull(mAbi); - - reinstallClientPackage(); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - - getDevice().uninstallPackage(CLIENT_PKG); - } - - public void runDeviceTests(String packageName, String testClassName, String testMethodName) - throws DeviceNotAvailableException { - Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName, - getDevice().getCurrentUser()); - } - - protected void reinstallClientPackage() throws Exception { - getDevice().uninstallPackage(CLIENT_PKG); - CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mCtsBuild); - assertNull(getDevice().installPackage(buildHelper.getTestFile(CLIENT_APK), false)); - } -} diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java deleted file mode 100644 index 552b2987f3d..00000000000 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/ScopedDirectoryAccessTest.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2016 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.appsecurity.cts; - -/** - * Set of tests that verify behavior of the deprecated Scoped Directory access API. - */ -public class ScopedDirectoryAccessTest extends DocumentsTestCase { - - public void testInvalidPath() throws Exception { - runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest", "testInvalidPath"); - } - - public void testActivityFailsForAllVolumesAndDirectories() throws Exception { - runDeviceTests(CLIENT_PKG, ".ScopedDirectoryAccessClientTest", - "testActivityFailsForAllVolumesAndDirectories"); - } -} diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java deleted file mode 100644 index 8826c5bc863..00000000000 --- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/ScopedDirectoryAccessClientTest.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2016 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.documentclient; - -import static android.os.Environment.DIRECTORY_ALARMS; -import static android.os.Environment.DIRECTORY_DCIM; -import static android.os.Environment.DIRECTORY_DOCUMENTS; -import static android.os.Environment.DIRECTORY_DOWNLOADS; -import static android.os.Environment.DIRECTORY_MOVIES; -import static android.os.Environment.DIRECTORY_MUSIC; -import static android.os.Environment.DIRECTORY_NOTIFICATIONS; -import static android.os.Environment.DIRECTORY_PICTURES; -import static android.os.Environment.DIRECTORY_PODCASTS; -import static android.os.Environment.DIRECTORY_RINGTONES; - -import android.content.Context; -import android.content.Intent; -import android.os.storage.StorageManager; -import android.os.storage.StorageVolume; - -import java.util.List; - -/** - * Set of tests that verify behavior of the deprecated Scoped Directory Access API. - */ -public class ScopedDirectoryAccessClientTest extends DocumentsClientTestCase { - private static final String TAG = "ScopedDirectoryAccessClientTest"; - - private static final String DIRECTORY_ROOT = null; - - private static final String[] STANDARD_DIRECTORIES = { - DIRECTORY_MUSIC, - DIRECTORY_PODCASTS, - DIRECTORY_RINGTONES, - DIRECTORY_ALARMS, - DIRECTORY_NOTIFICATIONS, - DIRECTORY_PICTURES, - DIRECTORY_MOVIES, - DIRECTORY_DOWNLOADS, - DIRECTORY_DCIM, - DIRECTORY_DOCUMENTS - }; - - @Override - public void setUp() throws Exception { - super.setUp(); - - // DocumentsUI caches some info like whether a user rejects a request, so we need to clear - // its data before each test. - clearDocumentsUi(); - } - - public void testInvalidPath() { - if (!supportedHardwareForScopedDirectoryAccess()) return; - - for (StorageVolume volume : getVolumes()) { - openExternalDirectoryInvalidPath(volume, ""); - openExternalDirectoryInvalidPath(volume, "/dev/null"); - openExternalDirectoryInvalidPath(volume, "/../"); - openExternalDirectoryInvalidPath(volume, "/HiddenStuff"); - } - openExternalDirectoryInvalidPath(getPrimaryVolume(), DIRECTORY_ROOT); - } - - public void testActivityFailsForAllVolumesAndDirectories() { - if (!supportedHardwareForScopedDirectoryAccess()) return; - - for (StorageVolume volume : getVolumes()) { - // Tests user clicking DENY button, for all valid directories. - for (String dir : STANDARD_DIRECTORIES) { - sendOpenExternalDirectoryIntent(volume, dir); - assertActivityFailed(); - } - if (!volume.isPrimary()) { - // Also test root - sendOpenExternalDirectoryIntent(volume, DIRECTORY_ROOT); - assertActivityFailed(); - } - } - } - - private void openExternalDirectoryInvalidPath(StorageVolume volume, String directoryName) { - final Intent intent = volume.createAccessIntent(directoryName); - assertNull("should not get intent for volume '" + volume + "' and directory '" - + directoryName + "'", intent); - } - - private void sendOpenExternalDirectoryIntent(StorageVolume volume, String directoryName) { - final Intent intent = volume.createAccessIntent(directoryName); - assertNotNull("no intent for '" + volume + "' and directory " + directoryName, intent); - mActivity.startActivityForResult(intent, REQUEST_CODE); - mDevice.waitForIdle(); - } - - private List<StorageVolume> getVolumes() { - final StorageManager sm = (StorageManager) - getInstrumentation().getTargetContext().getSystemService(Context.STORAGE_SERVICE); - final List<StorageVolume> volumes = sm.getStorageVolumes(); - assertTrue("empty volumes", !volumes.isEmpty()); - return volumes; - } - - private StorageVolume getPrimaryVolume() { - final StorageManager sm = (StorageManager) - getInstrumentation().getTargetContext().getSystemService(Context.STORAGE_SERVICE); - return sm.getPrimaryStorageVolume(); - } -} diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp index 723d0632057..c29ae0553c5 100644 --- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp +++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp @@ -26,7 +26,6 @@ android_test_helper_app { "androidx.test.rules", "compatibility-device-util-axt", "ctstestrunner-axt", - "ub-uiautomator", ], srcs: ["src/**/*.java"], // tag this module as a cts test artifact diff --git a/hostsidetests/art/host/AndroidTest.xml b/hostsidetests/art/host/AndroidTest.xml index 95522880cf3..88b74d3e59e 100644 --- a/hostsidetests/art/host/AndroidTest.xml +++ b/hostsidetests/art/host/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Host test for ThreadLocalRandom"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/calllog/AndroidTest.xml b/hostsidetests/calllog/AndroidTest.xml index 3b0d124e617..fc09dc564b9 100644 --- a/hostsidetests/calllog/AndroidTest.xml +++ b/hostsidetests/calllog/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS media host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="telecom" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <!-- These tests explicitly handle multiuser switching themselves. --> diff --git a/hostsidetests/car/src/android/car/cts/CarUserManagerHostTest.java b/hostsidetests/car/src/android/car/cts/CarUserManagerHostTest.java index 45f8a766b2f..73fb795dddc 100644 --- a/hostsidetests/car/src/android/car/cts/CarUserManagerHostTest.java +++ b/hostsidetests/car/src/android/car/cts/CarUserManagerHostTest.java @@ -18,7 +18,6 @@ package android.car.cts; import static com.google.common.truth.Truth.assertWithMessage; -import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -56,18 +55,6 @@ public final class CarUserManagerHostTest extends CarHostJUnit4TestCase { } @Test - public void testSwitchUserUxRestrictionFailure() throws Exception { - executeCommand("cmd car_service emulate-driving-state drive"); - assertWithMessage("Waiting for driving state change").that( - waitForDrivingStateChanged("Current Driving State: 2", TEST_TIMEOUT_MS)).isTrue(); - - int newUserid = createFullUser("CarUserManagerHostTest_User"); - switchUser(newUserid, STATUS_UX_RESTRICTION_FAILURE); - - executeCommand("cmd car_service emulate-driving-state park"); - } - - @Test public void testRemoveUser() throws Exception { int newUserid = createFullUser("CarUserManagerHostTest_User"); @@ -99,25 +86,4 @@ public final class CarUserManagerHostTest extends CarHostJUnit4TestCase { .max() .orElse(0) + 1; } - - private boolean waitForDrivingStateChanged(String expected, long timeout) { - long start = System.currentTimeMillis(); - while (start + timeout > System.currentTimeMillis()) { - try { - String result = executeCommand( - "dumpsys car_service --services CarDrivingStateService"); - if (result.contains(expected)) { - return true; - } - Thread.sleep(TEST_WAIT_MS); - } catch (InterruptedException e) { - CLog.e(TAG, "Test interrupted: " + e); - return false; - } catch (Exception e) { - CLog.e(TAG, "executeCommand failed: " + e); - return false; - } - } - return false; - } } diff --git a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java index 14770d504d8..cb120a0744f 100644 --- a/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java +++ b/hostsidetests/car/src/android/car/cts/PowerPolicyHostTest.java @@ -131,9 +131,6 @@ public final class PowerPolicyHostTest extends CarHostJUnit4TestCase { teststep = "check the inital power policies"; testHelper = getTestHelper(testcase, stepNo++, teststep); testHelper.checkCurrentState(PowerPolicyConstants.CarPowerState.ON); - // power policy can be different from system_power_policy_all_on, save it to check - // after device restart. - String powerPolicyForOnState = testHelper.getCurrentPolicyId(); // save number of device power policies int registeredPoliciesNumber = testHelper.getNumberOfRegisteredPolicies(); int expectedTotalPolicies = registeredPoliciesNumber; @@ -210,7 +207,6 @@ public final class PowerPolicyHostTest extends CarHostJUnit4TestCase { teststep = "reboot to clear added test power policies"; testHelper = getTestHelper(testcase, stepNo++, teststep); testHelper.checkCurrentState(PowerPolicyConstants.CarPowerState.ON); - testHelper.checkCurrentPolicy(powerPolicyForOnState); testHelper.checkTotalRegisteredPolicies(registeredPoliciesNumber); } @@ -370,7 +366,6 @@ public final class PowerPolicyHostTest extends CarHostJUnit4TestCase { testHelper.checkCurrentState(PowerPolicyConstants.CarPowerState.ON); testHelper.checkRegisteredPolicy(PowerPolicyDef.PolicySet.INITIAL_ALL_ON); testHelper.checkRegisteredPolicy(PowerPolicyDef.PolicySet.DEFAULT_ALL_ON); - testHelper.checkCurrentPolicy(PowerPolicyDef.IdSet.DEFAULT_ALL_ON); } private void defineAndCheckPolicyTest1(String testcase, int stepNo, diff --git a/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java b/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java index d4835011329..b58078b01ef 100644 --- a/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java +++ b/hostsidetests/car/src/android/car/cts/PreCreateUsersHostTest.java @@ -274,6 +274,16 @@ public final class PreCreateUsersHostTest extends CarHostJUnit4TestCase { for (int userId : userIds) { getDevice().removeUser(userId); } + int retryCount = 10; + while (retryCount > 0) { + String allUsers = getDevice().executeShellCommand("cmd user list --all -v"); + if (allUsers.contains("(pre-created)")) { + retryCount--; + sleep(1000); + continue; + } + break; + } } private void convertPreCreatedUser(boolean isGuest, int expectedId) throws Exception { diff --git a/hostsidetests/classloaders/splits/AndroidTest.xml b/hostsidetests/classloaders/splits/AndroidTest.xml index 557b879b678..06b38f4e97b 100644 --- a/hostsidetests/classloaders/splits/AndroidTest.xml +++ b/hostsidetests/classloaders/splits/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for the CTS Classloader Splits host tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> diff --git a/hostsidetests/classloaders/useslibrary/AndroidTest.xml b/hostsidetests/classloaders/useslibrary/AndroidTest.xml index 0b94f35bca5..593435f3458 100644 --- a/hostsidetests/classloaders/useslibrary/AndroidTest.xml +++ b/hostsidetests/classloaders/useslibrary/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for the CTS UsesLibrary host tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> diff --git a/hostsidetests/compilation/src/android/compilation/cts/CompilationTest.java b/hostsidetests/compilation/src/android/compilation/cts/CompilationTest.java index 2b62399eb73..00eb56f2c45 100644 --- a/hostsidetests/compilation/src/android/compilation/cts/CompilationTest.java +++ b/hostsidetests/compilation/src/android/compilation/cts/CompilationTest.java @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertThat; import android.compilation.cts.annotation.CtsTestCase; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.testtype.junit4.DeviceParameterizedRunner; import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; import org.junit.After; @@ -31,10 +31,12 @@ import org.junit.runner.RunWith; import java.util.regex.Pattern; +import junitparams.Parameters; + /** * Compilation tests that don't require root access. */ -@RunWith(DeviceJUnit4ClassRunner.class) +@RunWith(DeviceParameterizedRunner.class) @CtsTestCase public class CompilationTest extends BaseHostJUnit4Test { private static final String STATUS_CHECKER_PKG = "android.compilation.cts.statuscheckerapp"; @@ -122,29 +124,48 @@ public class CompilationTest extends BaseHostJUnit4Test { } @Test - public void testCompileSecondaryDex() throws Exception { + @Parameters({"secondary.jar", "secondary"}) + public void testCompileSecondaryDex(String filename) throws Exception { var options = new DeviceTestRunOptions(STATUS_CHECKER_PKG) .setTestClassName(STATUS_CHECKER_CLASS) - .setTestMethodName("createAndLoadSecondaryDex"); + .setTestMethodName("createAndLoadSecondaryDex") + .addInstrumentationArg("secondary-dex-filename", filename); assertThat(runDeviceTests(options)).isTrue(); // Verify that the secondary dex file is recorded. String dump = mUtils.assertCommandSucceeds("dumpsys package " + STATUS_CHECKER_PKG); - checkDexoptStatus(dump, "secondary\\.jar", ".*"); + checkDexoptStatus(dump, Pattern.quote(filename), ".*?"); mUtils.assertCommandSucceeds( "pm compile --secondary-dex -m speed -f " + STATUS_CHECKER_PKG); dump = mUtils.assertCommandSucceeds("dumpsys package " + STATUS_CHECKER_PKG); - checkDexoptStatus(dump, "secondary\\.jar", "speed"); + checkDexoptStatus(dump, Pattern.quote(filename), "speed"); mUtils.assertCommandSucceeds( "pm compile --secondary-dex -m verify -f " + STATUS_CHECKER_PKG); dump = mUtils.assertCommandSucceeds("dumpsys package " + STATUS_CHECKER_PKG); - checkDexoptStatus(dump, "secondary\\.jar", "verify"); + checkDexoptStatus(dump, Pattern.quote(filename), "verify"); mUtils.assertCommandSucceeds("pm delete-dexopt " + STATUS_CHECKER_PKG); dump = mUtils.assertCommandSucceeds("dumpsys package " + STATUS_CHECKER_PKG); - checkDexoptStatus(dump, "secondary\\.jar", "run-from-apk"); + checkDexoptStatus(dump, Pattern.quote(filename), "run-from-apk"); + } + + @Test + public void testSecondaryDexReporting() throws Exception { + var options = new DeviceTestRunOptions(STATUS_CHECKER_PKG) + .setTestClassName(STATUS_CHECKER_CLASS) + .setTestMethodName("testSecondaryDexReporting") + .setDisableHiddenApiCheck(true); + assertThat(runDeviceTests(options)).isTrue(); + + // Check that ART Service doesn't crash on various operations after invalid dex paths are + // reported. + mUtils.assertCommandSucceeds("dumpsys package " + STATUS_CHECKER_PKG); + mUtils.assertCommandSucceeds( + "pm compile --secondary-dex -m verify -f " + STATUS_CHECKER_PKG); + mUtils.assertCommandSucceeds("pm art clear-app-profiles " + STATUS_CHECKER_PKG); + mUtils.assertCommandSucceeds("pm art cleanup"); } private void checkDexoptStatus(String dump, String dexfilePattern, String statusPattern) { @@ -153,7 +174,8 @@ public class CompilationTest extends BaseHostJUnit4Test { // x86_64: [status=speed] [reason=cmdline] [primary-abi] // The pattern is intentionally minimized to be as forward compatible as possible. // TODO(b/283447251): Use a machine-readable format. - assertThat(dump).containsMatch(Pattern.compile(String.format( - "\\b(%s)\\b[^\\n]*\\n[^\\n]*\\[status=(%s)\\]", dexfilePattern, statusPattern))); + assertThat(dump).containsMatch( + Pattern.compile(String.format("[\\s/](%s)(\\s[^\\n]*)?\\n[^\\n]*\\[status=(%s)\\]", + dexfilePattern, statusPattern))); } } diff --git a/hostsidetests/compilation/status_checker_app/src/android/compilation/cts/statuscheckerapp/StatusCheckerAppTest.java b/hostsidetests/compilation/status_checker_app/src/android/compilation/cts/statuscheckerapp/StatusCheckerAppTest.java index efc289aac8d..87357811e5d 100644 --- a/hostsidetests/compilation/status_checker_app/src/android/compilation/cts/statuscheckerapp/StatusCheckerAppTest.java +++ b/hostsidetests/compilation/status_checker_app/src/android/compilation/cts/statuscheckerapp/StatusCheckerAppTest.java @@ -29,6 +29,7 @@ import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import dalvik.system.ApplicationRuntime; +import dalvik.system.BaseDexClassLoader; import dalvik.system.PathClassLoader; import com.google.common.io.ByteStreams; @@ -41,6 +42,7 @@ import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.nio.file.Paths; +import java.util.Map; /** * An instrumentation test that checks optimization status. @@ -65,15 +67,46 @@ public class StatusCheckerAppTest { @Test public void createAndLoadSecondaryDex() throws Exception { + Bundle bundle = InstrumentationRegistry.getArguments(); + String secondaryDexFilename = bundle.getString("secondary-dex-filename"); + createAndLoadSecondaryDex(secondaryDexFilename); + } + + private String createAndLoadSecondaryDex(String secondaryDexFilename) throws Exception { Context context = ApplicationProvider.getApplicationContext(); String dataDir = context.getApplicationInfo().dataDir; - File secondaryDexFile = Paths.get(dataDir, "secondary.jar").toFile(); + File secondaryDexFile = Paths.get(dataDir, secondaryDexFilename).toFile(); if (secondaryDexFile.exists()) { secondaryDexFile.delete(); } copyResourceToFile(SECONDARY_DEX_RES, secondaryDexFile); assertThat(secondaryDexFile.setReadOnly()).isTrue(); new PathClassLoader(secondaryDexFile.getAbsolutePath(), this.getClass().getClassLoader()); + return secondaryDexFile.getAbsolutePath(); + } + + @Test + public void testSecondaryDexReporting() throws Exception { + String fooPath = createAndLoadSecondaryDex("foo.jar"); + createAndLoadSecondaryDex("bar.jar"); + + var reporter = + (BaseDexClassLoader.Reporter) BaseDexClassLoader.class.getMethod("getReporter") + .invoke(null); + + // Invalid dex paths. The binder calls should be rejected, though we won't see any failure + // on the client side because the calls are oneway. + reporter.report(Map.of("relative/path.apk", "PCL[]")); + reporter.report(Map.of("/non-normal/./path.apk", "PCL[]")); + + // Invalid class loader contexts. The binder calls should be rejected too. + reporter.report(Map.of(fooPath, "ABC")); + reporter.report(Map.of(fooPath, "PCL[./bar.jar]")); + + // Valid paths and class loader contexts. + reporter.report(Map.of("/absolute/path.apk", "PCL[]")); + reporter.report(Map.of(fooPath, "PCL[bar.jar]")); + reporter.report(Map.of(fooPath, "=UnsupportedClassLoaderContext=")); } public File copyResourceToFile(String resourceName, File file) throws Exception { diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java index a1ddafb0718..ea8c7dabcb1 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java @@ -429,6 +429,7 @@ public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test { int retries = 10; CLog.i("switching to user %d", userId); executeShellCommand("am switch-user " + userId); + RunUtil.getDefault().sleep(USER_SWITCH_WAIT); while (getDevice().getCurrentUser() != userId && (--retries) >= 0) { // am switch-user can be ignored if a previous user-switching operation // is still in progress. In this case, sleep a bit and then retry diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java index 3e7616c3569..4f0ce17a01a 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/OrgOwnedProfileOwnerTest.java @@ -475,7 +475,7 @@ public final class OrgOwnedProfileOwnerTest extends BaseDevicePolicyTest { // Wait until IMS service is registered by the system. waitForOutput("Failed waiting for IME to become available", String.format("ime list --user %d -s -a", userId), - s -> s.contains(imeComponent), 45 /* seconds */); + s -> s.contains(imeComponent), 100 /* seconds */); executeShellCommand("ime enable " + imeComponent); executeShellCommand("ime set " + imeComponent); diff --git a/hostsidetests/incrementalinstall/AndroidTest.xml b/hostsidetests/incrementalinstall/AndroidTest.xml index 4721e7a6aa1..3b25ebc1b30 100644 --- a/hostsidetests/incrementalinstall/AndroidTest.xml +++ b/hostsidetests/incrementalinstall/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS adb incremental installer host test cases"> <option name="test-suite-tag" value="cts"/> - <option name="config-descriptor:metadata" key="component" value="devtools"/> + <option name="config-descriptor:metadata" key="component" value="packagemanager"/> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/MultiUserTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/MultiUserTest.java index 65b75f4f667..f3662880a01 100644 --- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/MultiUserTest.java +++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/MultiUserTest.java @@ -88,6 +88,9 @@ public class MultiUserTest extends BaseHostJUnit4Test { */ private ArrayList<Integer> mOriginalUsers; + /** Current user before the test runs. */ + private int mInitialUserId; + /** * Set up the test case */ @@ -96,6 +99,7 @@ public class MultiUserTest extends BaseHostJUnit4Test { // Skip whole tests when DUT has no android.software.input_methods feature. assumeTrue(hasDeviceFeature(ShellCommandUtils.FEATURE_INPUT_METHODS)); assumeTrue(getDevice().isMultiUserSupported()); + mInitialUserId = getDevice().getCurrentUser(); mNeedsTearDown = true; mOriginalUsers = new ArrayList<>(getDevice().listUsers()); @@ -111,8 +115,9 @@ public class MultiUserTest extends BaseHostJUnit4Test { if (!mNeedsTearDown) { return; } + // Switch back to the initial user. + getDevice().switchUser(mInitialUserId); - getDevice().switchUser(getDeviceMainUserId(getDevice())); // We suspect that the optimization made for Bug 38143512 was a bit unstable. Let's see // if adding a sleep improves the stability or not. RunUtil.getDefault().sleep(WAIT_AFTER_USER_SWITCH); @@ -305,9 +310,14 @@ public class MultiUserTest extends BaseHostJUnit4Test { assertIme1NotCurrentInputMethodInfo(profileUserId); } - private static int getDeviceMainUserId(ITestDevice device) throws DeviceNotAvailableException { - return device.isHeadlessSystemUserMode() ? device.getPrimaryUserId() : - device.getMainUserId(); + private int getDeviceMainUserId(ITestDevice device) throws Exception { + Integer userId = device.getMainUserId(); + + // Some headless surfaces like auto may not necessarily define a MAIN user. + if (userId == null) { + userId = mInitialUserId; + } + return userId; } private String shell(String command) { diff --git a/hostsidetests/install/AndroidTest.xml b/hostsidetests/install/AndroidTest.xml index 1eae5f6717c..62266669946 100644 --- a/hostsidetests/install/AndroidTest.xml +++ b/hostsidetests/install/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Runs the install API tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <!-- Instant apps can't have INSTALL_PACKAGES permission. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java index 1c25b5a4537..60b671d9635 100644 --- a/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java +++ b/hostsidetests/media/src/android/media/session/cts/MediaSessionManagerHostTest.java @@ -22,6 +22,8 @@ import static android.media.cts.MediaSessionTestHelperConstants.FLAG_SET_MEDIA_S import static android.media.cts.MediaSessionTestHelperConstants.MEDIA_SESSION_TEST_HELPER_APK; import static android.media.cts.MediaSessionTestHelperConstants.MEDIA_SESSION_TEST_HELPER_PKG; +import static com.google.common.truth.Truth.assertWithMessage; + import android.media.cts.BaseMultiUserTest; import android.media.cts.MediaSessionTestHelperConstants; import android.platform.test.annotations.AppModeFull; @@ -30,6 +32,7 @@ import android.platform.test.annotations.RequiresDevice; import com.android.ddmlib.Log.LogLevel; import com.android.tradefed.device.DeviceNotAvailableException; +import com.android.tradefed.device.ITestDevice; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.util.RunUtil; @@ -59,6 +62,12 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { private static final int TIMEOUT_MS = 1000; + /** + * Returned by {@link ITestDevice#getCurrentUser()} when there is an error retrieving the + * current user id. + */ + private static final int INVALID_USER_ID = -10000; + private final List<Integer> mNotificationListeners = new ArrayList<>(); @Override @@ -96,18 +105,21 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { } private void testGetActiveSessions_primaryUser(boolean instant) throws Exception { - int mainUserId = getDevice().getMainUserId(); + int userIdForTesting = getUserIdForTesting(); - setAllowGetActiveSessionForTest(true, mainUserId); - installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, mainUserId, instant); + setAllowGetActiveSessionForTest(true, userIdForTesting); + installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, userIdForTesting, instant); runTest("testGetActiveSessions_noMediaSessionFromMediaSessionTestHelper"); installAppAsUser( - MEDIA_SESSION_TEST_HELPER_APK, MEDIA_SESSION_TEST_HELPER_PKG, mainUserId, false); - sendControlCommand(mainUserId, FLAG_CREATE_MEDIA_SESSION); + MEDIA_SESSION_TEST_HELPER_APK, + MEDIA_SESSION_TEST_HELPER_PKG, + userIdForTesting, + false); + sendControlCommand(userIdForTesting, FLAG_CREATE_MEDIA_SESSION); runTest("testGetActiveSessions_noMediaSessionFromMediaSessionTestHelper"); - sendControlCommand(mainUserId, FLAG_SET_MEDIA_SESSION_ACTIVE); + sendControlCommand(userIdForTesting, FLAG_SET_MEDIA_SESSION_ACTIVE); runTest("testGetActiveSessions_hasMediaSessionFromMediaSessionTestHelper"); } @@ -210,7 +222,7 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { // Remove the created user first not to exceed system's user number limit. // Managed profile's parent must not be the primary user (in the context of this test, we // use the main user). - int newUser = createAndStartManagedProfile(getDevice().getMainUserId()); + int newUser = createAndStartManagedProfile(getUserIdForTesting()); installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, newUser, instant); setAllowGetActiveSessionForTest(true, newUser); runTestAsUser("testGetActiveSessions_noMediaSession", newUser); @@ -220,15 +232,18 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { @AppModeFull @RequiresDevice public void testGetActiveSessions_noSession2() throws Exception { - int mainUserId = getDevice().getMainUserId(); + int userIdForTesting = getUserIdForTesting(); - setAllowGetActiveSessionForTest(true, mainUserId); - installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, mainUserId, false); + setAllowGetActiveSessionForTest(true, userIdForTesting); + installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, userIdForTesting, false); runTest("testGetActiveSessions_noMediaSessionFromMediaSessionTestHelper"); installAppAsUser( - MEDIA_SESSION_TEST_HELPER_APK, MEDIA_SESSION_TEST_HELPER_PKG, mainUserId, false); - sendControlCommand(mainUserId, FLAG_CREATE_MEDIA_SESSION2); + MEDIA_SESSION_TEST_HELPER_APK, + MEDIA_SESSION_TEST_HELPER_PKG, + userIdForTesting, + false); + sendControlCommand(userIdForTesting, FLAG_CREATE_MEDIA_SESSION2); // Wait for a second for framework to recognize media session2. RunUtil.getDefault().sleep(TIMEOUT_MS); @@ -238,16 +253,19 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { @AppModeFull @RequiresDevice public void testGetActiveSessions_withSession2() throws Exception { - int mainUserId = getDevice().getMainUserId(); + int userIdForTesting = getUserIdForTesting(); - setAllowGetActiveSessionForTest(true, mainUserId); - installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, mainUserId, false); + setAllowGetActiveSessionForTest(true, userIdForTesting); + installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, userIdForTesting, false); runTest("testGetActiveSessions_noMediaSessionFromMediaSessionTestHelper"); installAppAsUser( - MEDIA_SESSION_TEST_HELPER_APK, MEDIA_SESSION_TEST_HELPER_PKG, mainUserId, false); + MEDIA_SESSION_TEST_HELPER_APK, + MEDIA_SESSION_TEST_HELPER_PKG, + userIdForTesting, + false); sendControlCommand( - mainUserId, + userIdForTesting, FLAG_CREATE_MEDIA_SESSION | FLAG_CREATE_MEDIA_SESSION2 | FLAG_SET_MEDIA_SESSION_ACTIVE); @@ -261,20 +279,20 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { @AppModeFull @RequiresDevice public void testOnMediaKeyEventSessionChangedListener() throws Exception { - int mainUserId = getDevice().getMainUserId(); + int userIdForTesting = getUserIdForTesting(); - setAllowGetActiveSessionForTest(true, mainUserId); - installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, mainUserId, false); + setAllowGetActiveSessionForTest(true, userIdForTesting); + installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, userIdForTesting, false); runTest("testOnMediaKeyEventSessionChangedListener"); } @AppModeFull @RequiresDevice public void testOnMediaKeyEventSessionChangedListener_whenSessionIsReleased() throws Exception { - int mainUserId = getDevice().getMainUserId(); + int userIdForTesting = getUserIdForTesting(); - setAllowGetActiveSessionForTest(true, mainUserId); - installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, mainUserId, false); + setAllowGetActiveSessionForTest(true, userIdForTesting); + installAppAsUser(DEVICE_SIDE_TEST_APK, DEVICE_SIDE_TEST_PKG, userIdForTesting, false); runTest("testOnMediaKeyEventSessionChangedListener_whenSessionIsReleased"); } @@ -310,7 +328,7 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { } private void runTest(String testMethodName) throws DeviceNotAvailableException { - runTestAsUser(testMethodName, getDevice().getMainUserId()); + runTestAsUser(testMethodName, getUserIdForTesting()); } private void runTestAsUser(String testMethodName, int userId) @@ -319,6 +337,27 @@ public class MediaSessionManagerHostTest extends BaseMultiUserTest { } /** + * If running headless system user mode, returns the current user. Otherwise, returns the main + * user. + * + * <p>Historically, some tests in this class would use the main user for running device-side + * tests. Headless surfaces (like Android Auto) do not have a main user. As a result, on + * headless surfaces, we use the current user in replacement of the missing main user. + */ + private int getUserIdForTesting() throws DeviceNotAvailableException { + if (getDevice().isHeadlessSystemUserMode()) { + int currentUserId = getDevice().getCurrentUser(); + assertWithMessage( + "Unable to fetch a valid current user id in headless system user mode.") + .that(currentUserId) + .isNotEqualTo(INVALID_USER_ID); + return currentUserId; + } else { + return getDevice().getMainUserId(); + } + } + + /** * Sets to allow or disallow the {@link #DEVICE_SIDE_TEST_CLASS} * to call {@link MediaSessionManager#getActiveSessions} for testing. * <p>{@link MediaSessionManager#getActiveSessions} bypasses the permission check if the diff --git a/hostsidetests/numberblocking/AndroidTest.xml b/hostsidetests/numberblocking/AndroidTest.xml index 8a00fc3e2d7..4b028f70870 100644 --- a/hostsidetests/numberblocking/AndroidTest.xml +++ b/hostsidetests/numberblocking/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS number blocking test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="abuse" /> + <option name="config-descriptor:metadata" key="component" value="telecom" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml index 70b742eedaf..28ee4f9f278 100644 --- a/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml +++ b/hostsidetests/packagemanager/domainverification/device/multiuser/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS domain verification multi user test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="multiuser" /> <!-- Instant apps can never be device admin / profile owner / device owner so positive tests diff --git a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml index 95b86f5d0b2..752a5fee258 100644 --- a/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml +++ b/hostsidetests/packagemanager/domainverification/device/standalone/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS domain verification device standalone test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml index cad7454fc0d..f0a14e40732 100644 --- a/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml +++ b/hostsidetests/packagemanager/domainverification/host/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package manager metrics host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml index 62f681e5a72..b5e0eabac71 100644 --- a/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml +++ b/hostsidetests/packagemanager/dynamicmime/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for the CTS dynamic MIME tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml index 1b6f8b0a95f..ef22f11f7d6 100644 --- a/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml +++ b/hostsidetests/packagemanager/extractnativelibs/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS extract native libs host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml index 7c1850d2d1d..35fb6eb77e2 100644 --- a/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml +++ b/hostsidetests/packagemanager/installedloadingprogess/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config Package Manager InstalledLoadingProgress API test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/multiuser/AndroidTest.xml b/hostsidetests/packagemanager/multiuser/AndroidTest.xml index 0e538397861..8069661f7e2 100644 --- a/hostsidetests/packagemanager/multiuser/AndroidTest.xml +++ b/hostsidetests/packagemanager/multiuser/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package manager multi-user host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/packagesetting/AndroidTest.xml b/hostsidetests/packagemanager/packagesetting/AndroidTest.xml index 92188534b1a..ae6d560d04d 100644 --- a/hostsidetests/packagemanager/packagesetting/AndroidTest.xml +++ b/hostsidetests/packagemanager/packagesetting/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package installation code path host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/packagesetting/app/AndroidManifest.xml b/hostsidetests/packagemanager/packagesetting/app/AndroidManifest.xml index 0072f743ad1..4c7f5174d4f 100644 --- a/hostsidetests/packagemanager/packagesetting/app/AndroidManifest.xml +++ b/hostsidetests/packagemanager/packagesetting/app/AndroidManifest.xml @@ -17,8 +17,6 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.tests.packagesetting.app" > - <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> - <application> <uses-library android:name="android.test.runner" /> </application> diff --git a/hostsidetests/packagemanager/packagesetting/app/src/com/android/tests/packagesetting/app/PackageSettingDeviceTest.java b/hostsidetests/packagemanager/packagesetting/app/src/com/android/tests/packagesetting/app/PackageSettingDeviceTest.java index ca3c4edccbd..6b5d2c6cae0 100644 --- a/hostsidetests/packagemanager/packagesetting/app/src/com/android/tests/packagesetting/app/PackageSettingDeviceTest.java +++ b/hostsidetests/packagemanager/packagesetting/app/src/com/android/tests/packagesetting/app/PackageSettingDeviceTest.java @@ -19,7 +19,6 @@ package com.android.tests.packagesetting.app; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.os.UserHandle; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; @@ -45,15 +44,12 @@ public class PackageSettingDeviceTest { @Test public void testFirstInstallTimeMatchesExpected() throws Exception { final Context context = InstrumentationRegistry.getInstrumentation().getContext(); - String packageName = - InstrumentationRegistry.getInstrumentation().getContext().getPackageName(); - String userIdStr = InstrumentationRegistry.getArguments().getString("userId"); + String packageName = context.getPackageName(); String expectedFirstInstallTimeStr = InstrumentationRegistry.getArguments().getString( "expectedFirstInstallTime"); - final int userId = Integer.parseInt(userIdStr); final long expectedFirstInstallTime = Long.parseLong(expectedFirstInstallTimeStr); - final Context contextAsUser = context.createContextAsUser(UserHandle.of(userId), 0); - PackageInfo pi = contextAsUser.getPackageManager().getPackageInfo(packageName, + final PackageManager packageManager = context.getPackageManager(); + PackageInfo pi = packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0)); Assert.assertTrue(Math.abs(expectedFirstInstallTime - pi.firstInstallTime) < 1000 /* ms */); } diff --git a/hostsidetests/packagemanager/packagesetting/src/com/android/tests/packagesetting/host/PackageSettingTest.java b/hostsidetests/packagemanager/packagesetting/src/com/android/tests/packagesetting/host/PackageSettingTest.java index 93ab5a3bd30..6934c64a17b 100644 --- a/hostsidetests/packagemanager/packagesetting/src/com/android/tests/packagesetting/host/PackageSettingTest.java +++ b/hostsidetests/packagemanager/packagesetting/src/com/android/tests/packagesetting/host/PackageSettingTest.java @@ -25,7 +25,6 @@ import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.After; import org.junit.Assert; -import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,16 +40,11 @@ public class PackageSettingTest extends BaseHostJUnit4Test { private static final String TEST_CLASS = TEST_PACKAGE + "." + "PackageSettingDeviceTest"; private static final String CODE_PATH_ROOT = "/data/app"; private static final long DEFAULT_TIMEOUT = 10 * 60 * 1000L; - private int mSecondUser = -1; - /** Uninstall apps after tests. */ @After public void cleanUp() throws Exception { uninstallPackage(getDevice(), TEST_PACKAGE); Assert.assertFalse(isPackageInstalled(TEST_PACKAGE)); - if (mSecondUser != -1) { - stopAndRemoveUser(mSecondUser); - } } @Test @@ -84,52 +78,16 @@ public class PackageSettingTest extends BaseHostJUnit4Test { @Test @AppModeFull public void testFirstInstallTimeWithReboot() throws Exception { - Assume.assumeTrue("device does not support multi-user", - getDevice().getMaxNumberOfUsersSupported() > 1); installPackage(TEST_APK); final int currentUser = getDevice().getCurrentUser(); final String firstInstallTimeForCurrentUser = getFirstInstallTimeForUserFromDumpsys( TEST_PACKAGE, currentUser); Assert.assertNotNull(firstInstallTimeForCurrentUser); - testFirstInstallTimeMatchesDumpsys(firstInstallTimeForCurrentUser, currentUser); + testFirstInstallTimeMatchesDumpsys(firstInstallTimeForCurrentUser); // firstInstallTime should be the same after reboot getDevice().reboot(); Assert.assertEquals(firstInstallTimeForCurrentUser, getFirstInstallTimeForUserFromDumpsys(TEST_PACKAGE, currentUser)); - - mSecondUser = createAndStartSecondUser(); - installPackageOnExistingUser(TEST_PACKAGE, mSecondUser); - final String firstInstallTimeForSecondUser = getFirstInstallTimeForUserFromDumpsys( - TEST_PACKAGE, mSecondUser); - Assert.assertNotNull(firstInstallTimeForSecondUser); - Assert.assertNotEquals(firstInstallTimeForCurrentUser, firstInstallTimeForSecondUser); - testFirstInstallTimeMatchesDumpsys(firstInstallTimeForSecondUser, mSecondUser); - getDevice().reboot(); - Assert.assertEquals(firstInstallTimeForSecondUser, - getFirstInstallTimeForUserFromDumpsys(TEST_PACKAGE, mSecondUser)); - } - - private int createAndStartSecondUser() throws Exception { - String output = getDevice().executeShellCommand("pm create-user SecondUser"); - Assert.assertTrue(output.startsWith("Success")); - int userId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim()); - output = getDevice().executeShellCommand("am start-user -w " + userId); - Assert.assertFalse(output.startsWith("Error")); - output = getDevice().executeShellCommand("am get-started-user-state " + userId); - Assert.assertTrue(output.contains("RUNNING_UNLOCKED")); - return userId; - } - - private void stopAndRemoveUser(int userId) throws Exception { - getDevice().executeShellCommand("am stop-user -w -f " + userId); - getDevice().executeShellCommand("pm remove-user " + userId); - } - - private void installPackageOnExistingUser(String packageName, int userId) throws Exception { - final String output = getDevice().executeShellCommand( - String.format("pm install-existing --user %d %s", userId, packageName)); - Assert.assertEquals("Package " + packageName + " installed for user: " + userId + "\n", - output); } private String getFirstInstallTimeForUserFromDumpsys(String packageName, int userId) @@ -138,14 +96,13 @@ public class PackageSettingTest extends BaseHostJUnit4Test { return packageInfo.getFirstInstallTime(userId); } - private void testFirstInstallTimeMatchesDumpsys(String firstInstallTime, int userId) + private void testFirstInstallTimeMatchesDumpsys(String firstInstallTime) throws Exception { final Map<String, String> testArgs = new HashMap<>(); // Notice the printed timestamp in dumpsys is formatted and has lost sub-second precision final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); sdf.setTimeZone(TimeZone.getTimeZone(getDeviceTimezone())); final long firstInstallTs = sdf.parse(firstInstallTime).getTime(); - testArgs.put("userId", String.valueOf(userId)); testArgs.put("expectedFirstInstallTime", String.valueOf(firstInstallTs)); runDeviceTests(getDevice(), null, TEST_PACKAGE, TEST_CLASS, "testFirstInstallTimeMatchesExpected", null, DEFAULT_TIMEOUT, DEFAULT_TIMEOUT, @@ -159,5 +116,4 @@ public class PackageSettingTest extends BaseHostJUnit4Test { } return "GMT"; } - } diff --git a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml index 30f994f81f7..e920dcb7733 100644 --- a/hostsidetests/packagemanager/parsing/host/AndroidTest.xml +++ b/hostsidetests/packagemanager/parsing/host/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package manager metrics host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml index b978585c39b..9e7c4220c72 100644 --- a/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml +++ b/hostsidetests/packagemanager/preferredactivity/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package manager preferred activity host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/packagemanager/stats/AndroidTest.xml b/hostsidetests/packagemanager/stats/AndroidTest.xml index 8a9081b46c9..ef351656323 100644 --- a/hostsidetests/packagemanager/stats/AndroidTest.xml +++ b/hostsidetests/packagemanager/stats/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for CTS package manager metrics host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java index 9bdd2778982..4435e756274 100644 --- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java +++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java @@ -39,6 +39,7 @@ import static android.scopedstorage.cts.lib.TestUtils.IS_URI_REDACTED_VIA_FILE_D import static android.scopedstorage.cts.lib.TestUtils.OPEN_FILE_FOR_READ_QUERY; import static android.scopedstorage.cts.lib.TestUtils.OPEN_FILE_FOR_WRITE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.QUERY_MAX_ROW_ID; +import static android.scopedstorage.cts.lib.TestUtils.QUERY_MEDIA_BY_URI_QUERY; import static android.scopedstorage.cts.lib.TestUtils.QUERY_MIN_ROW_ID; import static android.scopedstorage.cts.lib.TestUtils.QUERY_OWNER_PACKAGE_NAMES; import static android.scopedstorage.cts.lib.TestUtils.QUERY_TYPE; @@ -48,6 +49,7 @@ import static android.scopedstorage.cts.lib.TestUtils.READDIR_QUERY; import static android.scopedstorage.cts.lib.TestUtils.RENAME_FILE_PARAMS_SEPARATOR; import static android.scopedstorage.cts.lib.TestUtils.RENAME_FILE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.SETATTR_QUERY; +import static android.scopedstorage.cts.lib.TestUtils.UPDATE_MEDIA_BY_URI_QUERY; import static android.scopedstorage.cts.lib.TestUtils.canOpen; import static android.scopedstorage.cts.lib.TestUtils.deleteRecursively; import static android.scopedstorage.cts.lib.TestUtils.getFileRowIdFromDatabase; @@ -131,6 +133,12 @@ public class ScopedStorageTestHelper extends Activity { case DELETE_MEDIA_BY_URI_QUERY: returnIntent = deleteMediaByUri(queryType); break; + case UPDATE_MEDIA_BY_URI_QUERY: + returnIntent = updateMediaByUri(queryType); + break; + case QUERY_MEDIA_BY_URI_QUERY: + returnIntent = queryMediaByUri(queryType); + break; case EXIF_METADATA_QUERY: returnIntent = sendMetadata(queryType); break; @@ -244,6 +252,51 @@ public class ScopedStorageTestHelper extends Activity { return intent; } + private Intent updateMediaByUri(String queryType) { + final Intent intent = new Intent(queryType); + final Uri uri = getIntent().getParcelableExtra(INTENT_EXTRA_URI); + final Bundle attributes = getIntent().getBundleExtra(INTENT_EXTRA_ARGS); + + final ContentValues values = new ContentValues(); + for (String key : attributes.keySet()) { + values.put(key, attributes.getString(key)); + } + + try { + getContentResolver().update(uri, values, null, null); + intent.putExtra(queryType, true); + } catch (Exception e) { + intent.putExtra(INTENT_EXCEPTION, e); + } + + return intent; + } + + private Intent queryMediaByUri(String queryType) { + final Intent intent = new Intent(queryType); + final Uri uri = getIntent().getParcelableExtra(INTENT_EXTRA_URI); + final Bundle projection = getIntent().getBundleExtra(INTENT_EXTRA_ARGS); + + try (Cursor c = getContentResolver() + .query(uri, projection.keySet().toArray(new String[0]), null, null)) { + final Bundle result = new Bundle(); + if (c.getCount() == 1) { + c.moveToFirst(); + for (String column : projection.keySet()) { + result.putString(column, c.getString(c.getColumnIndex(column))); + } + } else { + Log.d(TAG, String.format("Uri in QUERY_MEDIA_BY_URI_QUERY query points " + + "to %d media files", c.getCount())); + } + intent.putExtra(queryType, result); + } catch (Exception e) { + intent.putExtra(INTENT_EXCEPTION, e); + } + + return intent; + } + private Intent queryWithArgs(String queryType) { final Intent intent = new Intent(queryType); final Uri uri = getIntent().getParcelableExtra(INTENT_EXTRA_URI); diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java index b23f945ae4d..8e653a1f14f 100644 --- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java +++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/RedactUriDeviceTest.java @@ -40,6 +40,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import android.Manifest; import android.content.ContentValues; @@ -52,6 +53,7 @@ import android.os.Environment; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.provider.MediaStore; +import android.system.Os; import androidx.test.filters.SdkSuppress; @@ -91,6 +93,9 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { static final String IMAGE_FILE_NAME = "ScopedStorageDeviceTest_file_" + NONCE + ".jpg"; + static final String FUZZER_HEIC_FILE_NAME = + "ScopedStorageDeviceTest_file_fuzzer_" + NONCE + ".heic"; + // An app with no permissions private static final TestApp APP_B_NO_PERMS = new TestApp("TestAppB", "android.scopedstorage.cts.testapp.B.noperms", 1, false, @@ -465,7 +470,7 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { assertUriIsUnredacted(img); try (ParcelFileDescriptor pfd = - getContentResolver().openFileDescriptor(redactedUri, "r")) { + getContentResolver().openFileDescriptor(redactedUri, "r")) { FileDescriptor fd = pfd.getFileDescriptor(); ExifInterface redactedExifInf = new ExifInterface(fd); assertUriIsRedacted(redactedExifInf); @@ -475,6 +480,26 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { } } + @Test + public void testOpenOnRedactedUri_readFuzzer() throws Exception { + final File img = stageFuzzerImageFileWithMetadata(FUZZER_HEIC_FILE_NAME); + final Uri redactedUri = getRedactedUri(img); + try { + assertUriIsUnredacted(img); + + try (ParcelFileDescriptor pfd = + getContentResolver().openFileDescriptor(redactedUri, "r")) { + FileDescriptor fd = pfd.getFileDescriptor(); + int bufSize = 0x1000000; + byte[] data = new byte[bufSize]; + int fileSize = Os.read(fd, data, 0, bufSize); + assertUriIsRedacted(data, fileSize); + } + } finally { + img.delete(); + } + } + private void testRedactedUriCommon(Uri uri, Uri redactedUri) { assertEquals(redactedUri.getAuthority(), uri.getAuthority()); assertEquals(redactedUri.getScheme(), uri.getScheme()); @@ -517,6 +542,19 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { assertEquals(latLong[1], 0.0, 0.0); } + private void assertUriIsRedacted(byte[] data, int fileSize) { + // Data in redaction ranges should be zero. + int[] start = new int[]{50538, 712941, 712965, 712989, 713033, 713101}; + int[] end = new int[]{711958, 712943, 712967, 712990, 713100, 713125}; + + assertTrue(fileSize == 4407744); + for (int index = 0; index < start.length && index < end.length; index++) { + for (int c = start[index]; c < end[index]; c++) { + assertTrue("It should be zero!", data[c] == (byte) 0); + } + } + } + private Cursor getRedactedCursor(Uri redactedUri) { Cursor redactedUriCursor = getContentResolver().query(redactedUri, null, null, null); assertNotNull(redactedUriCursor); @@ -530,11 +568,19 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { } private File stageImageFileWithMetadata(String name) throws Exception { + return stageImageFileWithMetadata(name, R.raw.img_with_metadata); + } + + private File stageFuzzerImageFileWithMetadata(String name) throws Exception { + return stageImageFileWithMetadata(name, R.raw.fuzzer); + } + + private File stageImageFileWithMetadata(String name, int sourceId) throws Exception { final File img = new File( Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM), name); try (InputStream in = - getContext().getResources().openRawResource(R.raw.img_with_metadata); + getContext().getResources().openRawResource(sourceId); OutputStream out = new FileOutputStream(img)) { // Dump the image we have to external storage FileUtils.copy(in, out); @@ -542,4 +588,4 @@ public class RedactUriDeviceTest extends ScopedStorageBaseDeviceTest { return img; } -} +}
\ No newline at end of file diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java index 4bff21bbd54..8dd4f7a8211 100644 --- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java +++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java @@ -35,6 +35,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; +import android.net.Uri; +import android.os.Bundle; import android.provider.MediaStore; import android.scopedstorage.cts.lib.TestUtils; import android.util.Log; @@ -54,7 +56,10 @@ import org.junit.runners.Parameterized.Parameter; import java.io.File; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; @RunWith(Parameterized.class) public final class StableUrisTest extends ScopedStorageBaseDeviceTest { @@ -66,9 +71,14 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { "android.scopedstorage.cts.testapp.filemanager", 1, false, "CtsScopedStorageTestAppFileManager.apk"); + private static final TestApp APP_NO_PERMS = new TestApp("TestAppB", + "android.scopedstorage.cts.testapp.B.noperms", 1, false, + "CtsScopedStorageTestAppB.apk"); private static final String OPSTR_MANAGE_EXTERNAL_STORAGE = permissionToOp(Manifest.permission.MANAGE_EXTERNAL_STORAGE); + private static final int MAX_MEDIA_FILES_COUNT_THRESHOLD = 1000; + private Context mContext; private ContentResolver mContentResolver; private UiDevice mDevice; @@ -79,17 +89,23 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { /** Parameters data. */ @Parameterized.Parameters(name = "volume={0}") public static Iterable<?> data() { - return Arrays.asList(MediaStore.VOLUME_EXTERNAL_PRIMARY); + return Arrays.asList(MediaStore.VOLUME_EXTERNAL); } @Before public void setUp() throws Exception { super.setupExternalStorage(mVolumeName); - Log.d(TAG, "Using volume : " + mVolumeName); mContext = ApplicationProvider.getApplicationContext(); mContentResolver = mContext.getContentResolver(); final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); mDevice = UiDevice.getInstance(inst); + final int mMediaFilesCount = TestUtils.queryWithArgsAs(APP_FM, + MediaStore.Files.getContentUri(mVolumeName), null); + Log.d(TAG, "Number of media files on device: " + mMediaFilesCount); + + assumeTrue("The number of media files is too large; Skipping the test as it " + + "will take too much time to execute", + mMediaFilesCount <= MAX_MEDIA_FILES_COUNT_THRESHOLD); } @Test @@ -99,6 +115,37 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { } @Test + public void testAttributesRestoration() throws Exception { + Map<File, Uri> fileToUriMap = new HashMap<>(); + + try { + setFlag("persist.sys.fuse.backup.internal_db_backup", true); + setFlag("persist.sys.fuse.backup.external_volume_backup", true); + + fileToUriMap = createFilesAsTestApp(APP_NO_PERMS, 5); + final Map<File, Bundle> fileToAttributesMapBeforeRestore = setAttributes(fileToUriMap); + + final Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); + final ContentResolver resolver = context.getContentResolver(); + MediaStore.waitForIdle(resolver); + resolver.call(MediaStore.AUTHORITY, "idle_maintenance_for_stable_uris", + null, null); + + // Clear MediaProvider package data to trigger DB recreation. + mDevice.executeShellCommand("pm clear " + getMediaProviderPackageName()); + + // Sleeping to make sure the db recovering is completed + Thread.sleep(20000); + + verifyAttributes(fileToUriMap, fileToAttributesMapBeforeRestore); + } finally { + for (File file : fileToUriMap.keySet()) { + file.delete(); + } + } + } + + @Test public void testUrisMapToNewIds_withNextRowIdBackup() throws Exception { assumeTrue(getBoolean("persist.sys.fuse.backup.nextrowid_enabled", false)); testScenario(/* nextRowIdBackupEnabled */ true); @@ -113,7 +160,7 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { 0); allowAppOpsToUid(fmUid, OPSTR_MANAGE_EXTERNAL_STORAGE); - files = createFilesAsTestApp(APP_FM, 5); + files.addAll(createFilesAsTestApp(APP_FM, 5).keySet()); long maxRowIdOfInternalDbBeforeReset = readMaximumRowIdFromDatabaseAs(APP_FM, MediaStore.Files.getContentUri(MediaStore.VOLUME_INTERNAL)); @@ -161,19 +208,95 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { } } - private List<File> createFilesAsTestApp(TestApp app, int count) throws Exception { - List<File> files = new ArrayList<>(); + private Map<File, Uri> createFilesAsTestApp(TestApp app, int count) throws Exception { + final Map<File, Uri> files = new HashMap<>(); for (int i = 1; i <= count; i++) { final File file = new File(getPicturesDir(), "Cts_" + System.currentTimeMillis() + ".jpg"); - TestUtils.createFileAs(app, file.getAbsolutePath()); - MediaStore.scanFile(mContentResolver, file); - files.add(file); + final boolean isFileCreated = !file.exists() + && TestUtils.createFileAs(app, file.getAbsolutePath()); + + if (!isFileCreated) { + throw new RuntimeException( + "File was not created on path: " + file.getAbsolutePath()); + } + + final Uri uri = MediaStore.scanFile(mContentResolver, file); + if (uri == null) { + throw new RuntimeException("Scanning returned null uri for file " + + file.getAbsolutePath()); + } + files.put(file, uri); } return files; } + private void verifyAttributes(Map<File, Uri> fileToUriMap, + Map<File, Bundle> fileToAttributesMapBeforeRestore) throws Exception { + Log.d(TAG, "Started attributes verification after db restore"); + for (Map.Entry<File, Uri> entry : fileToUriMap.entrySet()) { + final Bundle originalAttributes = fileToAttributesMapBeforeRestore.get(entry.getKey()); + final Bundle attributesAfterRestore = TestUtils.queryMediaByUriAs(APP_NO_PERMS, + entry.getValue(), originalAttributes.keySet()); + + assertWithMessage("Uri doesn't point to a media file after db restore") + .that(attributesAfterRestore.isEmpty()).isFalse(); + + for (String attribute : originalAttributes.keySet()) { + final String afterRestore = attributesAfterRestore.getString(attribute); + final String beforeRestore = fileToAttributesMapBeforeRestore + .get(entry.getKey()).getString(attribute); + + final String assertMessage = String.format("Expected values for %s attribute to be " + + "equal before and after DB restoration", attribute); + assertWithMessage(assertMessage) + .that(afterRestore).isEqualTo(beforeRestore); + } + } + Log.d(TAG, "Finished attributes verification after db restore"); + } + + private Map<File, Bundle> setAttributes(Map<File, Uri> fileToUriMap) throws Exception { + final Map<File, Bundle> fileToAttributes = new HashMap<>(); + int seed = 0; + for (Map.Entry<File, Uri> entry : fileToUriMap.entrySet()) { + final Bundle attributes = generateAttributes(seed++); + + TestUtils.updateMediaByUriAs(APP_NO_PERMS, entry.getValue(), attributes); + + final Bundle autoGeneratedAttributes = TestUtils.queryMediaByUriAs(APP_NO_PERMS, + entry.getValue(), new HashSet<>(Arrays.asList( + MediaStore.MediaColumns._ID, + MediaStore.MediaColumns.DATE_EXPIRES, + MediaStore.MediaColumns.OWNER_PACKAGE_NAME))); + + attributes.putAll(autoGeneratedAttributes); + fileToAttributes.put(entry.getKey(), attributes); + } + return fileToAttributes; + } + + private Bundle generateAttributes(int seed) { + final Bundle attributes = new Bundle(); + attributes.putString(MediaStore.MediaColumns.IS_FAVORITE, seed % 2 == 0 ? "1" : "0"); + attributes.putString(MediaStore.MediaColumns.IS_PENDING, seed % 3 == 0 ? "1" : "0"); + // Shouldn't set both IS_PENDING and IS_TRASHED + attributes.putString(MediaStore.MediaColumns.IS_TRASHED, + seed % 4 == 0 && seed % 3 != 0 ? "1" : "0"); + + return attributes; + } + + private void setFlag(String flagName, boolean value) throws Exception { + mDevice.executeShellCommand( + "setprop " + flagName + " " + value); + final String newValue = mDevice.executeShellCommand("getprop " + flagName).trim(); + + assumeTrue("Not able to set flag: " + flagName, + String.valueOf(value).equals(newValue)); + } + private static String getMediaProviderPackageName() { final Instrumentation inst = androidx.test.InstrumentationRegistry.getInstrumentation(); final PackageManager packageManager = inst.getContext().getPackageManager(); 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 6d17f4f9c15..a824a1558aa 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 @@ -93,6 +93,7 @@ import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Optional; +import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -118,6 +119,10 @@ public class TestUtils { public static final String DELETE_FILE_QUERY = "android.scopedstorage.cts.deletefile"; public static final String DELETE_MEDIA_BY_URI_QUERY = "android.scopedstorage.cts.deletemediabyuri"; + public static final String UPDATE_MEDIA_BY_URI_QUERY = + "android.scopedstorage.cts.update_media_by_uri"; + public static final String QUERY_MEDIA_BY_URI_QUERY = + "android.scopedstorage.cts.query_media_by_uri"; public static final String DELETE_RECURSIVE_QUERY = "android.scopedstorage.cts.deleteRecursive"; public static final String CAN_OPEN_FILE_FOR_READ_QUERY = "android.scopedstorage.cts.can_openfile_read"; @@ -413,6 +418,36 @@ public class TestUtils { } /** + * Makes the given {@code testApp} update the media rows for the given {@code uri} by + * updating values for the provided {@code attributes}. + * + * <p>This method drops shell permission identity. + */ + public static boolean updateMediaByUriAs(TestApp testApp, Uri uri, Bundle attributes) + throws Exception { + final String actionName = UPDATE_MEDIA_BY_URI_QUERY; + return getFromTestApp(testApp, uri, actionName, attributes).getBoolean(actionName); + } + + /** + * Makes the given {@code testApp} query media file by the given {@code uri} + * and {@code projection}. An empty result will be returned if {@code uri} + * indicates location of multiple files or no files at all. + * + * <p>This method drops shell permission identity. + */ + public static Bundle queryMediaByUriAs(TestApp testApp, Uri uri, Set<String> projection) + throws Exception { + final String actionName = QUERY_MEDIA_BY_URI_QUERY; + final Bundle bundle = new Bundle(); + for (String columnName : projection) { + bundle.putString(columnName, ""); + } + + return getFromTestApp(testApp, uri, actionName, bundle).getBundle(actionName); + } + + /** * Makes the given {@code testApp} delete a file. * * <p>This method drops shell permission identity. diff --git a/hostsidetests/scopedstorage/res/raw/fuzzer.heic b/hostsidetests/scopedstorage/res/raw/fuzzer.heic Binary files differnew file mode 100644 index 00000000000..32069afcce0 --- /dev/null +++ b/hostsidetests/scopedstorage/res/raw/fuzzer.heic diff --git a/hostsidetests/seccomp/AndroidTest.xml b/hostsidetests/seccomp/AndroidTest.xml index 49e19c552e4..669834114f2 100644 --- a/hostsidetests/seccomp/AndroidTest.xml +++ b/hostsidetests/seccomp/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS Sseccomp host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="security" /> <!-- Run in instant_app mode as well, since application zygotes are available even in that context --> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java index a5e655737b9..603292ab624 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java @@ -17,10 +17,12 @@ package android.security.cts; import static org.junit.Assert.*; +import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.sts.common.util.KernelVersionHost; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -36,6 +38,7 @@ public class CVE_2020_29374 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 174737879) @Test public void testPocCVE_2020_29374() throws Exception { + assumeTrue(KernelVersionHost.isKernelVersionGreaterThanEqualTo(getDevice(), "5.4.0")); AdbUtils.runPocAssertExitStatusNotVulnerable("CVE-2020-29374", getDevice(),60); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20112.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20112.java index 69645d8d012..c12cf330d81 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20112.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20112.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 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. @@ -17,10 +17,10 @@ package android.security.cts; import static org.junit.Assume.assumeNoException; -import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.UserUtils; import com.android.sts.common.tradefed.testtype.RootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -38,55 +38,28 @@ public class CVE_2022_20112 extends RootSecurityTestCase { // Is play managed : No @AsbSecurityTest(cveBugId = 206987762) @Test - public void testPocCVE_2022_20112() throws Exception { - // This setting is not supported on automotive - if (isAutomotive()) return; - - final String testPkg = "android.security.cts.CVE_2022_20112"; - ITestDevice device = null; - int currentUser = -1; - int newUser = -1; + public void testPocCVE_2022_20112() { try { - device = getDevice(); - - // Device wakeup and unlock - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); - AdbUtils.runCommandLine("wm dismiss-keyguard", device); - - // Get current user - currentUser = device.getCurrentUser(); - - // Create new guest user 'CTSUser' for test - newUser = device.createUser("CTSUser", true, false); - - // Start new guest user 'CTSUser' - assumeTrue("Unable to create new guest user", device.startUser(newUser, true)); - - // Switch to new user 'CTSUser' - assumeTrue("Unable to switch to guest user", device.switchUser(newUser)); - - // Install PoC application - installPackage("CVE-2022-20112.apk"); - - runDeviceTests(testPkg, testPkg + ".DeviceTest", "testprivateDnsPreferenceController"); + // This setting is not supported on automotive + if (isAutomotive()) return; + + final ITestDevice device = getDevice(); + try (AutoCloseable asGuestUser = + new UserUtils.SecondaryUser(device) + .name("CTSUser") + .guest() + .doSwitch() + .withUser()) { + // Install PoC application in guest user + installPackageAsUser( + "CVE-2022-20112.apk", false /* grantPermission */, device.getCurrentUser()); + + final String testPkg = "android.security.cts.CVE_2022_20112"; + runDeviceTests( + testPkg, testPkg + ".DeviceTest", "testPrivateDnsPreferenceController"); + } } catch (Exception e) { assumeNoException(e); - } finally { - try { - if (currentUser != -1) { - // Switch back to previous user - device.switchUser(currentUser); - } - if (newUser != -1) { - // Stop user 'CTSUser' - device.stopUser(newUser); - - // Remove user 'CTSUser' - device.removeUser(newUser); - } - } catch (Exception e) { - // Ignore exception here - } } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_21144.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_21144.java new file mode 100644 index 00000000000..064cfbc3dcd --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2023_21144.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2023 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.NonRootSecurityTestCase; +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_2023_21144 extends NonRootSecurityTestCase { + + @AsbSecurityTest(cveBugId = 252766417) + @Test + public void testPocCVE_2023_21144() { + ITestDevice device = null; + try { + device = getDevice(); + final String testPkg = "android.security.cts.CVE_2023_21144"; + installPackage("CVE-2023-21144.apk", "-g"); + + // Allowing notification listener service for PocListenerService + device.executeShellCommand( + "cmd notification allow_listener " + testPkg + "/.PocListenerService"); + + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testPocCVE_2023_21144"); + } catch (Exception e) { + assumeNoException(e); + } finally { + // To undo the expanded notification panel + try { + device.executeShellCommand("input keyevent KEYCODE_HOME"); + } catch (Exception ignore) { + // Ignore + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/res/values/strings.xml deleted file mode 100644 index af458479c47..00000000000 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/res/values/strings.xml +++ /dev/null @@ -1,24 +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. ---> -<resources> - <string name="defaultSettingsPkg">com.android.settings</string> - <string name="getAvailabilityStatusMethodName">getAvailabilityStatus</string> - <string name="privateDnsPreferenceControllerClassName">.network.PrivateDnsPreferenceController - </string> - <string name="testFailMsg">Device is vulnerable to b/206987762!! Private DNS can be modified in - guest mode</string> -</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/src/android/security/cts/CVE_2022_20112/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/src/android/security/cts/CVE_2022_20112/DeviceTest.java index 96cb205134f..731c5949c6d 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/src/android/security/cts/CVE_2022_20112/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20112/src/android/security/cts/CVE_2022_20112/DeviceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 The Android Open Source Project + * Copyright (C) 2023 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. @@ -17,10 +17,12 @@ package android.security.cts.CVE_2022_20112; 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.app.Instrumentation; import android.app.UiAutomation; import android.content.ComponentName; import android.content.Context; @@ -40,51 +42,64 @@ import java.lang.reflect.Method; public class DeviceTest { @Test - public void testprivateDnsPreferenceController() { - UiAutomation uiAutomation = null; + public void testPrivateDnsPreferenceController() { try { - Context context = getInstrumentation().getTargetContext(); + final Instrumentation instrumentation = getInstrumentation(); + final Context context = instrumentation.getContext(); // Retrieve settings package name dynamically Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); ComponentName settingsComponent = settingsIntent.resolveActivity(context.getPackageManager()); - String settingsPkgName = settingsComponent != null ? settingsComponent.getPackageName() - : context.getString(R.string.defaultSettingsPkg); + String settingsPkgName = + settingsComponent != null + ? settingsComponent.getPackageName() + : "com.android.settings"; // Get vulnerable method 'getAvailabilityStatus' using reflection - Context settingsContext = context.createPackageContext(settingsPkgName, - Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + final Context settingsContext = + context.createPackageContext( + settingsPkgName, + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); ClassLoader settingsClassLoader = settingsContext.getClassLoader(); Class<?> privateDnsPreferenceControllerClass = - settingsClassLoader.loadClass(settingsPkgName - + context.getString(R.string.privateDnsPreferenceControllerClassName)); + settingsClassLoader.loadClass( + settingsPkgName + ".network.PrivateDnsPreferenceController"); Constructor<?> privateDnsPreferenceControllerCstr = privateDnsPreferenceControllerClass.getConstructor(Context.class); Object privateDnsPreferenceControllerObject = privateDnsPreferenceControllerCstr.newInstance(settingsContext); - Method getAvailabilityStatusMethod = privateDnsPreferenceControllerClass - .getDeclaredMethod(context.getString(R.string.getAvailabilityStatusMethodName)); + Method getAvailabilityStatusMethod = + privateDnsPreferenceControllerClass.getDeclaredMethod("getAvailabilityStatus"); getAvailabilityStatusMethod.setAccessible(true); // Check if current user is guest user - uiAutomation = getInstrumentation().getUiAutomation(); - uiAutomation.adoptShellPermissionIdentity(android.Manifest.permission.CREATE_USERS); final UserManager userManager = context.getSystemService(UserManager.class); - assumeTrue(userManager.isGuestUser()); - - // Invoke vulnerable method 'getAvailabilityStatus' - int status = - (int) getAvailabilityStatusMethod.invoke(privateDnsPreferenceControllerObject); - assertFalse(context.getString(R.string.testFailMsg), status == 0 /* AVAILABLE */); + try (AutoCloseable withAdoptShellPermissionIdentity = + withAdoptShellPermissionIdentity( + instrumentation, android.Manifest.permission.CREATE_USERS)) { + assumeTrue(userManager.isGuestUser()); + // Invoke vulnerable method 'getAvailabilityStatus' + int status = + (int) + getAvailabilityStatusMethod.invoke( + privateDnsPreferenceControllerObject); + assertFalse( + "Device is vulnerable to b/206987762!! Private DNS can be modified in" + + " guest mode", + status == 0 /* AVAILABLE */); + } } catch (Exception e) { assumeNoException(e); - } finally { - try { - uiAutomation.dropShellPermissionIdentity(); - } catch (Exception ignored) { - // Ignore exception here - } } } + + private AutoCloseable withAdoptShellPermissionIdentity( + Instrumentation instrumentation, String permission) { + final UiAutomation uiAutomation = instrumentation.getUiAutomation(); + uiAutomation.adoptShellPermissionIdentity(permission); + + // Remove permissions + return () -> uiAutomation.dropShellPermissionIdentity(); + } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/DeviceTest.java index 070a1d9f90b..d9dc035dc0f 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/DeviceTest.java @@ -16,6 +16,8 @@ package android.security.cts.CVE_2023_21129; +import static android.app.NotificationManager.BUBBLE_PREFERENCE_ALL; + import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertFalse; @@ -30,6 +32,9 @@ import android.content.IntentFilter; import androidx.test.runner.AndroidJUnit4; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; @@ -75,7 +80,7 @@ public class DeviceTest { // Check if bubble notifications are enabled or disabled on the device assumeTrue( context.getString(R.string.bubbleNotificDisabled), - notificationManager.areBubblesAllowed()); + notificationManager.getBubblePreference() == BUBBLE_PREFERENCE_ALL); // Launching BubbleActivity Intent intent = new Intent(context, BubbleActivity.class); diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/PocService.java index 9fdacad1130..1406e8372aa 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/PocService.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21129/src/android/security/cts/CVE_2023_21129/PocService.java @@ -46,8 +46,16 @@ public class PocService extends Service { PendingIntent.getActivity( this, mResources.getInteger(R.integer.requestCode), + new Intent(this, BubbleActivity.class), + PendingIntent.FLAG_MUTABLE /* flags */); + + // Create a pending intent for fullscreen intent + PendingIntent pendingIntent = + PendingIntent.getActivity( + this, + mResources.getInteger(R.integer.requestCode), new Intent(this, PocActivity.class), - 0 /* flags */); + PendingIntent.FLAG_IMMUTABLE /* flags */); // Create icon Icon icon = createNotificationIcon(); @@ -102,7 +110,7 @@ public class PocService extends Service { .setSmallIcon(icon) .setShortcutId(shortcutId) .setBubbleMetadata(bubbleData) - .setFullScreenIntent(bubbleIntent, true /* high priority */) + .setFullScreenIntent(pendingIntent, true /* high priority */) .setStyle(messagingStyle); notificationManager.notify( diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/Android.bp new file mode 100644 index 00000000000..a1132bb534c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2023 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-2023-21144", + defaults: [ + "cts_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/AndroidManifest.xml new file mode 100644 index 00000000000..c0b27b711f2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/AndroidManifest.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 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_2023_21144"> + + <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" /> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> + + <application> + <service android:name=".PocListenerService" + android:exported="true" + android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"> + <intent-filter> + <action android:name="android.service.notification.NotificationListenerService" /> + </intent-filter> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2023_21144" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/res/values/strings.xml new file mode 100644 index 00000000000..d51c9b90445 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/res/values/strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 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="broadcastAction">CVE_2023_21144_action</string> + <string name="dataMimeType">image/png</string> + <string name="failMessage">Device is vulnerable to b/252766417 !!</string> + <string name="fileNotCreated">image file is not created</string> + <string name="imageFile">cve_2023_21144.png</string> + <string name="message">CVE_2023_21144_message</string> + <string name="notificationChannelId">CVE_2023_21144_notification_channel_id</string> + <string name="notificationChannelName">CVE_2023_21144_notification_channel_name</string> + <string name="notificationListenerNotConnected">notification listener not connected!!</string> + <string name="notificationText">CVE_2023_21144_notification_text</string> + <string name="resourceId">android:id/message_text</string> + <string name="systemUiPkgName">com.android.systemui</string> + <string name="username">CVE_2023_21144_username</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/DeviceTest.java new file mode 100644 index 00000000000..e91c3ea9424 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/DeviceTest.java @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2023 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_2023_21144; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.Instrumentation; +import android.app.Notification; +import android.app.Notification.MessagingStyle; +import android.app.Notification.MessagingStyle.Message; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.app.Person; +import android.app.StatusBarManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.graphics.Bitmap; +import android.graphics.drawable.Icon; +import android.net.Uri; +import android.os.Environment; +import android.os.Handler; +import android.os.HandlerThread; +import android.widget.ImageView; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.FileOutputStream; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testPocCVE_2023_21144() { + try { + Instrumentation instrumentation = getInstrumentation(); + Context context = instrumentation.getContext(); + + try (AutoCloseable withTemporaryImage = withTemporaryImage(context)) { + // Prepare notification channel + NotificationChannel notificationChannel = + new NotificationChannel( + context.getString(R.string.notificationChannelId), + context.getString(R.string.notificationChannelName), + NotificationManager.IMPORTANCE_HIGH); + NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(notificationChannel); + + // Prepare messaging style + Person person = + new Person.Builder().setName(context.getString(R.string.username)).build(); + Message message = + new Message(context.getString(R.string.message), 0L /* timestamp */, person) + .setData( + context.getString(R.string.dataMimeType), + Uri.fromFile(getImageFile(context))); + MessagingStyle messagingStyle = new MessagingStyle(person).addMessage(message); + + // Build notification + Notification notification = + new Notification.Builder(context, notificationChannel.getId()) + .setContentText(context.getString(R.string.notificationText)) + .setSmallIcon( + Icon.createWithData( + new byte[0] /* data */, + 0 /* offset */, + 0 /* length */)) + .setStyle(messagingStyle) + .build(); + + // Register BroadcastReceiver + Semaphore broadcastReceived = new Semaphore(0); + BroadcastReceiver broadcastReceiver = + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + try { + broadcastReceived.release(); + } catch (Exception ignore) { + // Ignore + } + } + }; + IntentFilter broadcastFilter = + new IntentFilter(context.getString(R.string.broadcastAction)); + context.registerReceiver(broadcastReceiver, broadcastFilter); + + // Send notification and wait for broadcast to get received + notificationManager.notify(0 /* notification id */, notification); + final long timeout = 10_000L; + assumeTrue( + context.getString(R.string.notificationListenerNotConnected), + broadcastReceived.tryAcquire(timeout, TimeUnit.MILLISECONDS)); + + // With fix, the notification does not contain image. Hence, using uiautomator + // to detect the imageview to fail test + context.getSystemService(StatusBarManager.class).expandNotificationsPanel(); + final UiDevice uiDevice = UiDevice.getInstance(instrumentation); + UiObject2 uiObject = + uiDevice.wait( + Until.findObject( + By.pkg(context.getString(R.string.systemUiPkgName)) + .res(context.getString(R.string.resourceId)) + .desc(context.getString(R.string.message)) + .clazz(ImageView.class.getName())), + timeout); + assertNull(context.getString(R.string.failMessage), uiObject); + } + } catch (Exception e) { + assumeNoException(e); + } + } + + private AutoCloseable withTemporaryImage(Context context) throws Exception { + // Create a png file in a worker Thread. + CompletableFuture<Boolean> imageIsReady = new CompletableFuture<Boolean>(); + final HandlerThread handlerThread = new HandlerThread(context.getPackageName()); + handlerThread.start(); + new Handler(handlerThread.getLooper()) + .post( + () -> { + try { + // The dimension of 13000 x 17000 creates png image of size ~2mb + // which is sufficient to extend loading time of 100ms. + final int sufficientlyLargeWidth = 13000; + final int sufficientlyLargeHeight = 17000; + Bitmap bitmap = + Bitmap.createBitmap( + sufficientlyLargeWidth, + sufficientlyLargeHeight, + Bitmap.Config.ARGB_8888); + File imageFile = getImageFile(context); + imageFile.createNewFile(); + FileOutputStream fileOutputStream = new FileOutputStream(imageFile); + bitmap.compress( + Bitmap.CompressFormat.PNG, + 100 /* quality */, + fileOutputStream); + fileOutputStream.flush(); + fileOutputStream.close(); + imageIsReady.complete(true); + } catch (Exception ignore) { + imageIsReady.complete(false); + } + }); + + // Wait until image gets created successfully + assumeTrue( + context.getString(R.string.fileNotCreated), + imageIsReady.get() && getImageFile(context).exists()); + + return () -> { + File imageFile = getImageFile(context); + if (imageFile.exists()) { + imageFile.delete(); + } + }; + } + + private File getImageFile(Context context) { + return Environment.buildPath( + Environment.getExternalStorageDirectory(), + Environment.DIRECTORY_DOWNLOADS, + context.getString(R.string.imageFile)); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/PocListenerService.java b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/PocListenerService.java new file mode 100644 index 00000000000..77508a6a78f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2023-21144/src/android/security/cts/CVE_2023_21144/PocListenerService.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 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_2023_21144; + +import android.content.Intent; +import android.service.notification.NotificationListenerService; +import android.service.notification.StatusBarNotification; + +public class PocListenerService extends NotificationListenerService { + + @Override + public void onNotificationPosted(StatusBarNotification sbn) { + try { + // Send broadcast when notification is posted + sendBroadcast(new Intent(getString(R.string.broadcastAction))); + } catch (Exception ignore) { + // Ignore as it causes assumptionFailure in DeviceTest + } + } +} diff --git a/hostsidetests/silentupdate/AndroidTest.xml b/hostsidetests/silentupdate/AndroidTest.xml index 8cb9ca11258..2f23e0918f3 100644 --- a/hostsidetests/silentupdate/AndroidTest.xml +++ b/hostsidetests/silentupdate/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Runs the silent update install tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/stagedinstall/AndroidTest.xml b/hostsidetests/stagedinstall/AndroidTest.xml index a76052890eb..45ba6e1eb1f 100644 --- a/hostsidetests/stagedinstall/AndroidTest.xml +++ b/hostsidetests/stagedinstall/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Runs the staged install API tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <!-- Instant apps can't have INSTALL_PACKAGES permission. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/DisplayWakeReportedTests.java b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/DisplayWakeReportedTests.java index 95607c270d9..89d67c617f4 100644 --- a/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/DisplayWakeReportedTests.java +++ b/hostsidetests/statsdatom/apps/statsdapp/src/com/android/server/cts/device/statsdatom/DisplayWakeReportedTests.java @@ -93,7 +93,7 @@ public class DisplayWakeReportedTests { @Test public void testWakeWithWakeUpApi() throws Exception { ShellIdentityUtils.invokeWithShellPermissions(() -> - mPowerManager.wakeUp(SystemClock.elapsedRealtime(), + mPowerManager.wakeUp(SystemClock.uptimeMillis(), PowerManager.WAKE_REASON_UNKNOWN, TAG)); } diff --git a/hostsidetests/systemui/src/android/host/systemui/TileServiceTest.java b/hostsidetests/systemui/src/android/host/systemui/TileServiceTest.java index 7f5e74ea0fd..10d94271545 100644 --- a/hostsidetests/systemui/src/android/host/systemui/TileServiceTest.java +++ b/hostsidetests/systemui/src/android/host/systemui/TileServiceTest.java @@ -62,21 +62,6 @@ public class TileServiceTest extends BaseTileServiceTest { remTile(); assertTrue(waitFor("onTileRemoved")); } - - @ApiTest(apis = {"android.service.quicksettings.TileService#onStartListening", - "android.service.quicksettings.TileService#onStopListening"}) - public void testListeningNotifications() throws Exception { - if (!supported()) return; - addTile(); - - // Open the notification shade and make sure the tile gets a chance to listen. - openNotifications(); - assertTrue(waitFor("onStartListening")); - // Collapse the shade and make sure the listening ends. - collapse(); - assertTrue(waitFor("onStopListening")); - } - @ApiTest(apis = {"android.service.quicksettings.TileService#onStartListening", "android.service.quicksettings.TileService#onStopListening"}) public void testListeningSettings() throws Exception { diff --git a/hostsidetests/usb/AndroidTest.xml b/hostsidetests/usb/AndroidTest.xml index 14f6b95e803..c217a2fbfe0 100644 --- a/hostsidetests/usb/AndroidTest.xml +++ b/hostsidetests/usb/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS USB host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="systems" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/hostsidetests/wifibroadcasts/AndroidTest.xml b/hostsidetests/wifibroadcasts/AndroidTest.xml index 79760f7b9e2..cc32b6f7dbf 100644 --- a/hostsidetests/wifibroadcasts/AndroidTest.xml +++ b/hostsidetests/wifibroadcasts/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS WifiBroadcasts host test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="networking" /> + <option name="config-descriptor:metadata" key="component" value="wifi" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java index cfcbddaae6b..dbb3ad7b353 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java @@ -51,6 +51,7 @@ import androidx.test.uiautomator.UiSelector; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.ArrayList; @@ -149,6 +150,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { } @Test + @Ignore("Tracking this in b/295151917") public void testBrowse_multiSelect() throws Exception { final int itemCount = 3; List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(itemCount, @@ -251,7 +253,7 @@ public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { assertWithMessage("Waiting for app list to appear in DocumentsUi").that( new UiObject(appList).waitForExists(SHORT_TIMEOUT)).isTrue(); - String photoPickerAppName = "Media"; + String photoPickerAppName = "Media picker"; UiObject mediaButton = sDevice.findObject(new UiSelector().text(photoPickerAppName)); assertWithMessage("Timed out waiting for " + photoPickerAppName + " app icon to appear") diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java index d0d2595ae05..afc33e11c7d 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerBaseTest.java @@ -22,6 +22,7 @@ import android.app.Instrumentation; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.os.UserHandle; import android.util.Log; import androidx.annotation.Nullable; @@ -91,11 +92,15 @@ public class PhotoPickerBaseTest { protected static void setCloudProvider(@Nullable String authority) throws Exception { if (authority == null) { sDevice.executeShellCommand( - "content call --uri content://media/ --method set_cloud_provider --extra" + "content call" + + " --user " + UserHandle.myUserId() + + " --uri content://media/ --method set_cloud_provider --extra" + " cloud_provider:n:null"); } else { sDevice.executeShellCommand( - "content call --uri content://media/ --method set_cloud_provider --extra" + "content call" + + " --user " + UserHandle.myUserId() + + " --uri content://media/ --method set_cloud_provider --extra" + " cloud_provider:s:" + authority); } @@ -104,7 +109,9 @@ public class PhotoPickerBaseTest { protected static String getCurrentCloudProvider() throws IOException { final String out = sDevice.executeShellCommand( - "content call --uri content://media/ --method get_cloud_provider"); + "content call" + + " --user " + UserHandle.myUserId() + + " --uri content://media/ --method get_cloud_provider"); return extractCloudProvider(out); } diff --git a/tests/accessibilityservice/res/layout/accessibility_embedded_hierarchy_test_host_side.xml b/tests/accessibilityservice/res/layout/accessibility_embedded_hierarchy_test_host_side.xml index 9c0976274df..4c52f05af5e 100644 --- a/tests/accessibilityservice/res/layout/accessibility_embedded_hierarchy_test_host_side.xml +++ b/tests/accessibilityservice/res/layout/accessibility_embedded_hierarchy_test_host_side.xml @@ -16,8 +16,8 @@ --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="200dp" - android:layout_height="200dp" + android:layout_width="@dimen/embedded_hierarchy_host_layout_size" + android:layout_height="@dimen/embedded_hierarchy_host_layout_size" android:orientation="vertical"> <EditText xmlns:android="http://schemas.android.com/apk/res/android" diff --git a/tests/accessibilityservice/res/values/dimens.xml b/tests/accessibilityservice/res/values/dimens.xml index a50549c55ed..2f96ab30b89 100644 --- a/tests/accessibilityservice/res/values/dimens.xml +++ b/tests/accessibilityservice/res/values/dimens.xml @@ -18,4 +18,10 @@ <resources> <dimen name="button_touchable_width_increment_amount">48dp</dimen> + + <dimen name="embedded_hierarchy_host_layout_size">200dp</dimen> + <!-- Smaller than embedded_hierarchy_host_layout_size so it can be embedded inside. --> + <dimen name="embedded_hierarchy_embedded_layout_size">150dp</dimen> + <!-- Amount that the embedded layout can move while still remaining inside the host. --> + <dimen name="embedded_hierarchy_embedded_layout_movement_size">50dp</dimen> </resources>
\ No newline at end of file diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedHierarchyTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedHierarchyTest.java index 13aacb032c0..ba7a0ddc6ab 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedHierarchyTest.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityEmbeddedHierarchyTest.java @@ -177,8 +177,10 @@ public class AccessibilityEmbeddedHierarchyTest { final Rect oldEmbeddedViewBoundsInScreen = new Rect(); target.getBoundsInScreen(oldEmbeddedViewBoundsInScreen); - // Move Host SurfaceView from (0, 0) to (50, 50). - mActivity.requestNewLayoutForTest(50, 50); + // Move the host's SurfaceView away from (0,0). + int moveAmountPx = mActivity.getResources().getDimensionPixelSize( + R.dimen.embedded_hierarchy_embedded_layout_movement_size); + mActivity.moveSurfaceViewLayoutPosition(moveAmountPx, moveAmountPx); target.refresh(); final AccessibilityNodeInfo parent = target.getParent(); @@ -207,7 +209,7 @@ public class AccessibilityEmbeddedHierarchyTest { // Move Host SurfaceView out of screen final Point screenSize = getScreenSize(); - mActivity.requestNewLayoutForTest(screenSize.x, screenSize.y); + mActivity.moveSurfaceViewLayoutPosition(screenSize.x, screenSize.y); target.refresh(); assertWithMessage("Embedded view should be invisible after moving out of screen.").that( @@ -248,9 +250,6 @@ public class AccessibilityEmbeddedHierarchyTest { AccessibilityTestActivity implements SurfaceHolder.Callback { private final CountDownLatch mCountDownLatch = new CountDownLatch(1); - private static final int DEFAULT_WIDTH = 150; - private static final int DEFAULT_HEIGHT = 150; - private SurfaceView mSurfaceView; private View mInputFocusableView; private SurfaceControlViewHost mViewHost; @@ -273,7 +272,9 @@ public class AccessibilityEmbeddedHierarchyTest { View layout = getLayoutInflater().inflate( R.layout.accessibility_embedded_hierarchy_test_embedded_side, null); - mViewHost.setView(layout, DEFAULT_WIDTH, DEFAULT_HEIGHT); + final int viewSizePx = getResources().getDimensionPixelSize( + R.dimen.embedded_hierarchy_embedded_layout_size); + mViewHost.setView(layout, viewSizePx, viewSizePx); mCountDownLatch.countDown(); } @@ -296,7 +297,7 @@ public class AccessibilityEmbeddedHierarchyTest { } } - public void requestNewLayoutForTest(int x, int y) throws TimeoutException { + public void moveSurfaceViewLayoutPosition(int x, int y) throws TimeoutException { sUiAutomation.executeAndWaitForEvent( () -> sInstrumentation.runOnMainSync(() -> { mSurfaceView.setX(x); diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java index 9f78f556b0e..e279e746e85 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityWindowReportingTest.java @@ -35,14 +35,11 @@ import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_ADDED import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_BOUNDS; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_CHILDREN; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_FOCUSED; -import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_REMOVED; import static android.view.accessibility.AccessibilityEvent.WINDOWS_CHANGE_TITLE; -import static junit.framework.TestCase.assertTrue; +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; -import static org.junit.Assert.assertNotNull; import static org.junit.Assume.assumeTrue; import android.Manifest; @@ -51,6 +48,7 @@ import android.accessibilityservice.AccessibilityServiceInfo; import android.accessibilityservice.cts.activities.AccessibilityWindowReportingActivity; import android.accessibilityservice.cts.activities.NonDefaultDisplayActivity; import android.accessibilityservice.cts.activities.NotTouchableWindowTestActivity; +import android.accessibilityservice.cts.utils.ActivityLaunchUtils; import android.accessibilityservice.cts.utils.DisplayUtils; import android.accessibilityservice.cts.utils.WindowCreationUtils; import android.app.Activity; @@ -103,10 +101,10 @@ public class AccessibilityWindowReportingTest { private Activity mActivity; private CharSequence mActivityTitle; - private ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule = + private final ActivityTestRule<AccessibilityWindowReportingActivity> mActivityRule = new ActivityTestRule<>(AccessibilityWindowReportingActivity.class, false, false); - private AccessibilityDumpOnFailureRule mDumpOnFailureRule = + private final AccessibilityDumpOnFailureRule mDumpOnFailureRule = new AccessibilityDumpOnFailureRule(); @Rule @@ -124,7 +122,7 @@ public class AccessibilityWindowReportingTest { } @AfterClass - public static void finalTearDown() throws Exception { + public static void finalTearDown() { sUiAutomation.destroy(); } @@ -145,8 +143,8 @@ public class AccessibilityWindowReportingTest { public void testUpdatedWindowTitle_generatesEventAndIsReturnedByGetTitle() { final String updatedTitle = "Updated Title"; try { - sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( - () -> mActivity.setTitle(updatedTitle)), + sUiAutomation.executeAndWaitForEvent( + () -> sInstrumentation.runOnMainSync(() -> mActivity.setTitle(updatedTitle)), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_TITLE), TIMEOUT_ASYNC_PROCESSING); } catch (TimeoutException exception) { @@ -154,7 +152,8 @@ public class AccessibilityWindowReportingTest { "Failed to get windows changed event for title update", exception); } final AccessibilityWindowInfo window = findWindowByTitle(sUiAutomation, updatedTitle); - assertNotNull("Updated window title not reported to accessibility", window); + assertWithMessage("Updated window title not reported to accessibility") + .that(window).isNotNull(); window.recycle(); } @@ -163,8 +162,8 @@ public class AccessibilityWindowReportingTest { public void testWindowAddedMovedAndRemoved_generatesEventsForAllThree() throws Exception { final WindowManager.LayoutParams paramsForTop = WindowCreationUtils.layoutParamsForWindowOnTop( - sInstrumentation, mActivity, TOP_WINDOW_TITLE); - final WindowManager.LayoutParams paramsForBottom = layoutParmsForWindowOnBottom(); + sInstrumentation, mActivity, TOP_WINDOW_TITLE); + final WindowManager.LayoutParams paramsForBottom = layoutParamsForWindowOnBottom(); final Button button = new Button(mActivity); button.setText(R.string.button1); @@ -212,7 +211,7 @@ public class AccessibilityWindowReportingTest { numPictureInPictureWindows++; } } - assertTrue(numPictureInPictureWindows >= 1); + assertThat(numPictureInPictureWindows).isAtLeast(1); } @Test @@ -237,14 +236,15 @@ public class AccessibilityWindowReportingTest { // Windows may have changed - refresh activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); - assertFalse(activityWindow.isActive()); - assertFalse(activityWindow.isFocused()); + assertThat(activityWindow.isActive()).isFalse(); + assertThat(activityWindow.isFocused()).isFalse(); // Find a focusable view in the main activity menu final AccessibilityNodeInfo autoCompleteTextInfo = activityWindow.getRoot() .findAccessibilityNodeInfosByViewId( "android.accessibilityservice.cts:id/autoCompleteLayout") .get(0); + assertThat(autoCompleteTextInfo).isNotNull(); // Remove the top window and focus on the main activity sUiAutomation.executeAndWaitForEvent( @@ -264,15 +264,15 @@ public class AccessibilityWindowReportingTest { // Makes sure activityWindow on default display is focused AccessibilityWindowInfo activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); - assertTrue(activityWindow.isActive()); - assertTrue(activityWindow.isFocused()); + assertThat(activityWindow.isActive()).isTrue(); + assertThat(activityWindow.isFocused()).isTrue(); // Creates a virtual display. try (VirtualDisplaySession displaySession = new VirtualDisplaySession()) { final int virtualDisplayId = - displaySession.createDisplayWithDefaultDisplayMetricsAndWait( - sInstrumentation.getContext(), false).getDisplayId(); - // Launchs an activity on virtual display. + displaySession.createDisplayWithDefaultDisplayMetricsAndWait( + sInstrumentation.getContext(), false).getDisplayId(); + // Launches an activity on virtual display. final Activity activityOnVirtualDisplay = launchActivityOnSpecifiedDisplayAndWaitForItToBeOnscreen(sInstrumentation, sUiAutomation, @@ -286,44 +286,43 @@ public class AccessibilityWindowReportingTest { // at virtual display needs to be touched then it becomes to be focused one. Adding this // touch event on the activity window of the virtual display to pass this test case. sUiAutomation.executeAndWaitForEvent( - () -> { - DisplayUtils.touchDisplay(sUiAutomation, virtualDisplayId, activityTitle); - }, + () -> DisplayUtils.touchDisplay(sUiAutomation, virtualDisplayId, activityTitle), filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE), TIMEOUT_ASYNC_PROCESSING); // Make sure activityWindow on virtual display is focused. AccessibilityWindowInfo activityWindowOnVirtualDisplay = - findWindowByTitleAndDisplay(sUiAutomation, activityTitle, virtualDisplayId); + findWindowByTitleAndDisplay(sUiAutomation, activityTitle, virtualDisplayId); // Windows may have changed - refresh. activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); try { if (!perDisplayFocusEnabled()) { - assertFalse(activityWindow.isActive()); - assertFalse(activityWindow.isFocused()); + assertThat(activityWindow.isActive()).isFalse(); + assertThat(activityWindow.isFocused()).isFalse(); } else { - assertTrue(activityWindow.isActive()); - assertTrue(activityWindow.isFocused()); + assertThat(activityWindow.isActive()).isTrue(); + assertThat(activityWindow.isFocused()).isTrue(); } - assertTrue(activityWindowOnVirtualDisplay.isActive()); - assertTrue(activityWindowOnVirtualDisplay.isFocused()); + assertThat(activityWindowOnVirtualDisplay.isActive()).isTrue(); + assertThat(activityWindowOnVirtualDisplay.isFocused()).isTrue(); } finally { sUiAutomation.executeAndWaitForEvent( - () -> { - sInstrumentation.runOnMainSync( - () -> activityOnVirtualDisplay.finish()); - }, - filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_FOCUSED | - WINDOWS_CHANGE_ACTIVE), + () -> sInstrumentation.runOnMainSync(activityOnVirtualDisplay::finish), + filterWaitForAll( + filterWindowsChangedWithChangeTypes( + WINDOWS_CHANGE_FOCUSED | WINDOWS_CHANGE_ACTIVE), + event -> { + // The focused window should be returned to activity at + // default display after + // the activity at virtual display is destroyed. + AccessibilityWindowInfo window = findWindowByTitle( + sUiAutomation, mActivityTitle); + return window.isActive() && window.isFocused(); + }), TIMEOUT_ASYNC_PROCESSING); } } - // The focused window should be returned to activity at default display after - // the activity at virtual display is destroyed. - activityWindow = findWindowByTitle(sUiAutomation, mActivityTitle); - assertTrue(activityWindow.isActive()); - assertTrue(activityWindow.isFocused()); } @Test @@ -369,20 +368,20 @@ public class AccessibilityWindowReportingTest { @Test public void testGetAnchorForDropDownForAutoCompleteTextView_returnsTextViewNode() { final AutoCompleteTextView autoCompleteTextView = - (AutoCompleteTextView) mActivity.findViewById(R.id.autoCompleteLayout); + mActivity.findViewById(R.id.autoCompleteLayout); final AccessibilityNodeInfo autoCompleteTextInfo = sUiAutomation.getRootInActiveWindow() .findAccessibilityNodeInfosByViewId( "android.accessibilityservice.cts:id/autoCompleteLayout") .get(0); // For the drop-down - final String[] COUNTRIES = new String[] {"Belgium", "France", "Italy", "Germany", "Spain"}; + final String[] countries = new String[]{"Belgium", "France", "Italy", "Germany", "Spain"}; try { sUiAutomation.executeAndWaitForEvent(() -> sInstrumentation.runOnMainSync( () -> { final ArrayAdapter<String> adapter = new ArrayAdapter<>( - mActivity, android.R.layout.simple_dropdown_item_1line, COUNTRIES); + mActivity, android.R.layout.simple_dropdown_item_1line, countries); autoCompleteTextView.setAdapter(adapter); autoCompleteTextView.showDropDown(); }), @@ -401,17 +400,18 @@ public class AccessibilityWindowReportingTest { if (window.getAnchor() == null) { continue; } - assertEquals(autoCompleteTextInfo, window.getAnchor()); - assertFalse("Found multiple pop-ups anchored to one text view", foundPopup); + assertThat(window.getAnchor()).isEqualTo(autoCompleteTextInfo); + assertWithMessage("Found multiple pop-ups anchored to one text view") + .that(foundPopup).isFalse(); foundPopup = true; } - assertTrue("Failed to find accessibility window for auto-complete pop-up", foundPopup); + assertWithMessage("Failed to find accessibility window for auto-complete pop-up") + .that(foundPopup).isTrue(); } @AppModeFull @Test - public void showNotTouchableWindow_activityWindowIsNotVisible() - throws TimeoutException { + public void showNotTouchableWindow_activityWindowIsNotVisible() throws TimeoutException { try { launchNotTouchableWindowTestActivityFromShell(); @@ -419,40 +419,29 @@ public class AccessibilityWindowReportingTest { intent.setAction(NotTouchableWindowTestActivity.ADD_WINDOW); intent.setPackage(sInstrumentation.getContext().getPackageName()); - try { - // Waits for two events, whose order is nondeterministic: - // (1) the test activity is covered by the untrusted non-touchable window. - // (2) the untrusted non-touchable window is added. - sendIntentAndWaitForEvent(intent, - filterWaitForAll( - event -> { - final AccessibilityWindowInfo coveredWindow = - findWindowByTitle(sUiAutomation, - NotTouchableWindowTestActivity.TITLE); - return coveredWindow == null; - }, - filterWindowsChangeTypesAndWindowTitle(sUiAutomation, - WINDOWS_CHANGE_ADDED, - NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE) - )); - } finally { - intent.setAction(NotTouchableWindowTestActivity.REMOVE_WINDOW); - sendIntentAndWaitForEvent(intent, - filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); - } - } finally { - Intent intent = new Intent(); - intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); - intent.setPackage(sInstrumentation.getContext().getPackageName()); + // Waits for two events, whose order is nondeterministic: + // (1) the test activity is covered by the untrusted non-touchable window. + // (2) the untrusted non-touchable window is added. sendIntentAndWaitForEvent(intent, - filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); + filterWaitForAll( + event -> { + final AccessibilityWindowInfo coveredWindow = + findWindowByTitle(sUiAutomation, + NotTouchableWindowTestActivity.TITLE); + return coveredWindow == null; + }, + filterWindowsChangeTypesAndWindowTitle(sUiAutomation, + WINDOWS_CHANGE_ADDED, + NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE) + )); + } finally { + closeNotTouchableWindowTestActivity(); } } @AppModeFull @Test - public void showNotTouchableTrustedWindow_activityWindowIsVisible() - throws TimeoutException { + public void showNotTouchableTrustedWindow_activityWindowIsVisible() { try { launchNotTouchableWindowTestActivityFromShell(); @@ -460,31 +449,17 @@ public class AccessibilityWindowReportingTest { intent.setAction(NotTouchableWindowTestActivity.ADD_TRUSTED_WINDOW); intent.setPackage(sInstrumentation.getContext().getPackageName()); - try { - SystemUtil.runWithShellPermissionIdentity(sUiAutomation, () -> { - sendIntentAndWaitForEvent(intent, + SystemUtil.runWithShellPermissionIdentity(sUiAutomation, + () -> sendIntentAndWaitForEvent(intent, filterWindowsChangeTypesAndWindowTitle(sUiAutomation, WINDOWS_CHANGE_ADDED, - NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE) - ); - }, Manifest.permission.INTERNAL_SYSTEM_WINDOW); - - List<AccessibilityWindowInfo> windows = sUiAutomation.getWindows(); - assertNotNull(windows); + NotTouchableWindowTestActivity.NON_TOUCHABLE_WINDOW_TITLE)), + Manifest.permission.INTERNAL_SYSTEM_WINDOW); - assertEquals(1, windows.stream().filter( - w -> NotTouchableWindowTestActivity.TITLE.equals(w.getTitle())).count()); - } finally { - intent.setAction(NotTouchableWindowTestActivity.REMOVE_WINDOW); - sendIntentAndWaitForEvent(intent, - filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); - } + assertThat(findWindowByTitle(sUiAutomation, NotTouchableWindowTestActivity.TITLE)) + .isNotNull(); } finally { - Intent intent = new Intent(); - intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); - intent.setPackage(sInstrumentation.getContext().getPackageName()); - sendIntentAndWaitForEvent(intent, - filterWindowsChangedWithChangeTypes(WINDOWS_CHANGE_REMOVED)); + closeNotTouchableWindowTestActivity(); } } @@ -493,31 +468,43 @@ public class AccessibilityWindowReportingTest { // Use shell command instead of ActivityLaunchUtils to get INTERNAL_SYSTEM_WINDOW // permission when the Session is created. private void launchNotTouchableWindowTestActivityFromShell() { - SystemUtil.runWithShellPermissionIdentity(sUiAutomation, () -> { - sUiAutomation.executeAndWaitForEvent( - () -> { - final ComponentName componentName = new ComponentName( - sInstrumentation.getContext(), NotTouchableWindowTestActivity.class); - - String command = "am start -n " + componentName.flattenToString(); - try { - SystemUtil.runShellCommand(sInstrumentation, command); - } catch (IOException e) { - throw new RuntimeException(e); - } - }, - (event) -> { - final AccessibilityWindowInfo window = - findWindowByTitleAndDisplay(sUiAutomation, - NotTouchableWindowTestActivity.TITLE, 0); - return window != null; - }, TIMEOUT_ASYNC_PROCESSING); - }, Manifest.permission.INTERNAL_SYSTEM_WINDOW); + SystemUtil.runWithShellPermissionIdentity(sUiAutomation, + () -> sUiAutomation.executeAndWaitForEvent( + () -> { + final ComponentName componentName = new ComponentName( + sInstrumentation.getContext(), + NotTouchableWindowTestActivity.class); + + String command = "am start -n " + componentName.flattenToString(); + try { + SystemUtil.runShellCommand(sInstrumentation, command); + } catch (IOException e) { + throw new RuntimeException(e); + } + }, + (event) -> { + final AccessibilityWindowInfo window = + findWindowByTitleAndDisplay(sUiAutomation, + NotTouchableWindowTestActivity.TITLE, 0); + return window != null; + }, TIMEOUT_ASYNC_PROCESSING), Manifest.permission.INTERNAL_SYSTEM_WINDOW); + } + + private void closeNotTouchableWindowTestActivity() { + final Intent intent = new Intent(); + intent.setAction(NotTouchableWindowTestActivity.FINISH_ACTIVITY); + intent.setPackage(sInstrumentation.getContext().getPackageName()); + // Call finish() on the window. This is required to launch more activities in any subsequent + // tests from this same app process. + sInstrumentation.runOnMainSync(() -> sInstrumentation.getContext().sendBroadcast(intent)); + // Ensure we're at the home screen before continuing to other tests. + // finish() should do this, but sometimes takes longer than expected. + ActivityLaunchUtils.homeScreenOrBust(sInstrumentation.getContext(), sUiAutomation); } - /** - * Test whether we can successfully enable and disable window animations. - */ + /** + * Test whether we can successfully enable and disable window animations. + */ @Test public void testDisableWindowAnimations() { setAndAssertAnimationScale(0.0f); @@ -529,22 +516,23 @@ public class AccessibilityWindowReportingTest { private void setAndAssertAnimationScale(float value) { Context context = sInstrumentation.getContext(); sUiAutomation.setAnimationScale(value); - assertEquals(value, getGlobalFloat(context, Settings.Global.WINDOW_ANIMATION_SCALE), 0.0f); - assertEquals( - value, getGlobalFloat(context, Settings.Global.TRANSITION_ANIMATION_SCALE), 0.0f); - assertEquals(value, getGlobalFloat(context, Settings.Global.ANIMATOR_DURATION_SCALE), 0.0f); + assertThat(getGlobalFloat(context, Settings.Global.WINDOW_ANIMATION_SCALE)) + .isEqualTo(value); + assertThat(getGlobalFloat(context, Settings.Global.TRANSITION_ANIMATION_SCALE)) + .isEqualTo(value); + assertThat(getGlobalFloat(context, Settings.Global.ANIMATOR_DURATION_SCALE)) + .isEqualTo(value); } /** Returns value of constants in Settings.Global. */ private static float getGlobalFloat(Context context, String constantName) { - float value = Settings.Global.getFloat(context.getContentResolver(), constantName, -1); - return value; + return Settings.Global.getFloat(context.getContentResolver(), constantName, -1); } private View showTopWindowAndWaitForItToShowUp() throws TimeoutException { final WindowManager.LayoutParams paramsForTop = WindowCreationUtils.layoutParamsForWindowOnTop( - sInstrumentation, mActivity, TOP_WINDOW_TITLE); + sInstrumentation, mActivity, TOP_WINDOW_TITLE); final Button button = new Button(mActivity); button.setText(R.string.button1); @@ -555,7 +543,7 @@ public class AccessibilityWindowReportingTest { return button; } - private WindowManager.LayoutParams layoutParmsForWindowOnBottom() { + private WindowManager.LayoutParams layoutParamsForWindowOnBottom() { final WindowManager.LayoutParams params = WindowCreationUtils.layoutParamsForTestWindow( sInstrumentation, mActivity); params.gravity = Gravity.BOTTOM; diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java index e2bd5c2c046..039b948399a 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java @@ -44,11 +44,6 @@ public class NotTouchableWindowTestActivity extends AccessibilityTestActivity { context.getSystemService(WindowManager.class).addView(rootView, params); break; - case REMOVE_WINDOW: - context.getSystemService(WindowManager.class).removeViewImmediate(rootView); - rootView = null; - break; - case ADD_TRUSTED_WINDOW: if (rootView != null) { throw new IllegalStateException("Window already exists"); @@ -60,9 +55,6 @@ public class NotTouchableWindowTestActivity extends AccessibilityTestActivity { break; case FINISH_ACTIVITY: - if (rootView != null) { - throw new IllegalStateException("Window still exists"); - } finish(); } } @@ -78,8 +70,6 @@ public class NotTouchableWindowTestActivity extends AccessibilityTestActivity { public static final String ADD_WINDOW = "android.accessibilityservice.cts.ADD_WINDOW"; - public static final String REMOVE_WINDOW = - "android.accessibilityservice.cts.REMOVE_WINDOW"; public static final String ADD_TRUSTED_WINDOW = "android.accessibilityservice.cts.ADD_TRUSTED_WINDOW"; public static final String FINISH_ACTIVITY = @@ -94,7 +84,6 @@ public class NotTouchableWindowTestActivity extends AccessibilityTestActivity { IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION); filter.addAction(ADD_WINDOW); - filter.addAction(REMOVE_WINDOW); filter.addAction(ADD_TRUSTED_WINDOW); filter.addAction(FINISH_ACTIVITY); this.registerReceiver(mBroadcastReceiver, filter, Context.RECEIVER_NOT_EXPORTED); diff --git a/tests/app/BroadcastsTest/src/android/app/cts/broadcasts/BroadcastDeliveryGroupTest.java b/tests/app/BroadcastsTest/src/android/app/cts/broadcasts/BroadcastDeliveryGroupTest.java index dd7a45b83a6..3eca80428b4 100644 --- a/tests/app/BroadcastsTest/src/android/app/cts/broadcasts/BroadcastDeliveryGroupTest.java +++ b/tests/app/BroadcastsTest/src/android/app/cts/broadcasts/BroadcastDeliveryGroupTest.java @@ -76,6 +76,7 @@ public class BroadcastDeliveryGroupTest extends BaseBroadcastTest { // Now force delay the broadcasts to make sure delivery group policies are // applied as expected. + initializeQueue(HELPER_PKG2, cmdReceiver2); forceDelayBroadcasts(HELPER_PKG2); final Intent intent1 = new Intent(TEST_ACTION1) @@ -142,6 +143,7 @@ public class BroadcastDeliveryGroupTest extends BaseBroadcastTest { // Now force delay the broadcasts to make sure delivery group policies are // applied as expected. + initializeQueue(HELPER_PKG2, cmdReceiver2); forceDelayBroadcasts(HELPER_PKG2); final Intent intent1 = new Intent(TEST_ACTION1) @@ -163,4 +165,16 @@ public class BroadcastDeliveryGroupTest extends BaseBroadcastTest { connection2.unbind(); } } + + private void initializeQueue(String pkg, ICommandReceiver cmdReceiver) throws Exception { + // TODO: b/294884478 - We can remove this method once forceDelayBroadcastDelivery() is + // updated to work in any state. + final String testAction = "com.android.app.cts.test"; + final IntentFilter filter = new IntentFilter(testAction); + cmdReceiver.monitorBroadcasts(filter, testAction); + + final Intent testIntent = new Intent(testAction) + .setPackage(pkg); + getContext().sendBroadcast(testIntent); + } } diff --git a/tests/app/WallpaperTest/AndroidTest.xml b/tests/app/WallpaperTest/AndroidTest.xml index 096db14efb2..373f7067675 100644 --- a/tests/app/WallpaperTest/AndroidTest.xml +++ b/tests/app/WallpaperTest/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS App test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="sysui" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/app/WallpaperTest/testsdk33/AndroidTest.xml b/tests/app/WallpaperTest/testsdk33/AndroidTest.xml index 4955e5cecd5..7b37cef6c1c 100644 --- a/tests/app/WallpaperTest/testsdk33/AndroidTest.xml +++ b/tests/app/WallpaperTest/testsdk33/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS App test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="sysui" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/app/src/android/app/cts/SystemFeaturesTest.java b/tests/app/src/android/app/cts/SystemFeaturesTest.java index 182b2d65ffb..20151d17cb6 100644 --- a/tests/app/src/android/app/cts/SystemFeaturesTest.java +++ b/tests/app/src/android/app/cts/SystemFeaturesTest.java @@ -70,6 +70,8 @@ import java.util.Set; */ @RunWith(JUnit4.class) public class SystemFeaturesTest { + private static final String FEATURE_GOOGLE_BATTERYLESS_DEVICE = + "com.google.android.feature.batteryless_device"; private static final String FEATURE_GOOGLE_LARGE_DISPLAY = "com.google.android.feature.large_display"; private static final String FEATURE_GOOGLE_OTHER_FORM_FACTOR = @@ -174,6 +176,7 @@ public class SystemFeaturesTest { boolean motionTracking = false; boolean raw = false; boolean hasFlash = false; + boolean hasAutofocus = false; CameraCharacteristics[] cameraChars = new CameraCharacteristics[cameraIds.length]; for (String cameraId : cameraIds) { CameraCharacteristics chars = mCameraManager.getCameraCharacteristics(cameraId); @@ -207,6 +210,11 @@ public class SystemFeaturesTest { if (flashAvailable) { hasFlash = true; } + float minFocusDistance = + chars.get(CameraCharacteristics.LENS_INFO_MINIMUM_FOCUS_DISTANCE); + if (minFocusDistance > 0) { + hasAutofocus = true; + } } assertFeature(fullCamera, PackageManager.FEATURE_CAMERA_LEVEL_FULL); assertFeature(manualSensor, PackageManager.FEATURE_CAMERA_CAPABILITY_MANUAL_SENSOR); @@ -228,6 +236,7 @@ public class SystemFeaturesTest { assertNotAvailable(PackageManager.FEATURE_CAMERA_AR); } assertFeature(hasFlash, PackageManager.FEATURE_CAMERA_FLASH); + assertFeature(hasAutofocus, PackageManager.FEATURE_CAMERA_AUTOFOCUS); } private void checkFrontCamera() { @@ -264,19 +273,14 @@ public class SystemFeaturesTest { Camera.Parameters params = camera.getParameters(); if (params.getSupportedFocusModes().contains(Parameters.FOCUS_MODE_AUTO)) { assertAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS); - } else { - assertNotAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS); } if (params.getFlashMode() != null) { assertAvailable(PackageManager.FEATURE_CAMERA_FLASH); - } else { - assertNotAvailable(PackageManager.FEATURE_CAMERA_FLASH); } + } else { assertNotAvailable(PackageManager.FEATURE_CAMERA); - assertNotAvailable(PackageManager.FEATURE_CAMERA_AUTOFOCUS); - assertNotAvailable(PackageManager.FEATURE_CAMERA_FLASH); } } finally { if (camera != null) { @@ -336,6 +340,8 @@ public class SystemFeaturesTest { // Watches MAY support all FEATURE_NFC features when an NfcAdapter is available, but // non-watches MUST support them both. if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) + || (mPackageManager.hasSystemFeature(FEATURE_GOOGLE_BATTERYLESS_DEVICE) + && mPackageManager.hasSystemFeature(FEATURE_GOOGLE_OTHER_FORM_FACTOR)) || (mPackageManager.hasSystemFeature(FEATURE_GOOGLE_LARGE_DISPLAY) && mPackageManager.hasSystemFeature(FEATURE_GOOGLE_OTHER_FORM_FACTOR))) { assertOneAvailable(PackageManager.FEATURE_NFC, diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java index b904623d934..b8aa7ec4133 100644 --- a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java +++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSchemaCtsTest.java @@ -28,6 +28,8 @@ import android.app.appsearch.testutil.AppSearchEmail; import org.junit.Test; +import java.util.Collections; + public class AppSearchSchemaCtsTest { @Test public void testInvalidEnums() { @@ -515,4 +517,28 @@ public class AppSearchSchemaCtsTest { IllegalArgumentException.class, () -> new LongPropertyConfig.Builder("timestamp").setIndexingType(-1).build()); } + + @Test + public void testInvalidDocumentPropertyConfig_indexableNestedProperties() { + // Adding indexableNestedProperties with shouldIndexNestedProperties=true should fail. + AppSearchSchema.DocumentPropertyConfig.Builder builder = + new AppSearchSchema.DocumentPropertyConfig.Builder("prop1", "Schema1") + .setShouldIndexNestedProperties(true) + .addIndexableNestedProperties(Collections.singleton("prop1")); + IllegalArgumentException e = + assertThrows(IllegalArgumentException.class, () -> builder.build()); + assertThat(e) + .hasMessageThat() + .contains( + "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false" + + " when one or more indexableNestedProperties are provided."); + + builder.addIndexableNestedProperties(Collections.singleton("prop1.prop2")); + e = assertThrows(IllegalArgumentException.class, () -> builder.build()); + assertThat(e) + .hasMessageThat() + .contains( + "DocumentIndexingConfig#shouldIndexNestedProperties is required to be false" + + " when one or more indexableNestedProperties are provided."); + } } 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 61bbd15b366..ba76489068d 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 @@ -6409,4 +6409,1155 @@ public abstract class AppSearchSessionCtsTestBase { new SearchSuggestionResult.Builder().setSuggestedResult("bar subject:foo").build(); assertThat(suggestions).containsExactly(barSubjectFo, barSubjectFoo); } + + @Test + public void testGetSchema_parentTypes() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email").build(); + AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message").build(); + AppSearchSchema emailMessageSchema = + new AppSearchSchema.Builder("EmailMessage") + .addProperty( + new StringPropertyConfig.Builder("sender") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .addProperty( + new StringPropertyConfig.Builder("email") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .addProperty( + new StringPropertyConfig.Builder("content") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .addParentType("Email") + .addParentType("Message") + .build(); + + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchemas(emailMessageSchema) + .addSchemas(emailSchema) + .addSchemas(messageSchema) + .build(); + + mDb1.setSchemaAsync(request).get(); + + Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas(); + assertThat(actual).hasSize(3); + assertThat(actual).isEqualTo(request.getSchemas()); + } + + @Test + public void testGetSchema_parentTypes_notSupported() throws Exception { + assumeFalse(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + AppSearchSchema emailSchema = new AppSearchSchema.Builder("Email").build(); + AppSearchSchema messageSchema = new AppSearchSchema.Builder("Message").build(); + AppSearchSchema emailMessageSchema = + new AppSearchSchema.Builder("EmailMessage") + .addParentType("Email") + .addParentType("Message") + .build(); + + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchemas(emailMessageSchema) + .addSchemas(emailSchema) + .addSchemas(messageSchema) + .build(); + + UnsupportedOperationException e = + assertThrows( + UnsupportedOperationException.class, + () -> mDb1.setSchemaAsync(request).get()); + assertThat(e) + .hasMessageThat() + .contains( + Features.SCHEMA_ADD_PARENT_TYPE + + " is not available on this AppSearch implementation."); + } + + @Test + public void testSetSchema_dataTypeIncompatibleWithParentTypes() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + AppSearchSchema messageSchema = + new AppSearchSchema.Builder("Message") + .addProperty( + new AppSearchSchema.LongPropertyConfig.Builder("sender") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .build(); + AppSearchSchema emailSchema = + new AppSearchSchema.Builder("Email") + .addParentType("Message") + .addProperty( + new StringPropertyConfig.Builder("sender") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .build(); + + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchemas(messageSchema) + .addSchemas(emailSchema) + .build(); + + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(request).get()); + assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class); + AppSearchException exception = (AppSearchException) executionException.getCause(); + assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT); + assertThat(exception) + .hasMessageThat() + .containsMatch( + "Property sender from child type .*\\$/Email is not compatible" + + " to the parent type .*\\$/Message."); + } + + @Test + public void testSetSchema_documentTypeIncompatibleWithParentTypes() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + AppSearchSchema personSchema = new AppSearchSchema.Builder("Person").build(); + AppSearchSchema artistSchema = + new AppSearchSchema.Builder("Artist").addParentType("Person").build(); + AppSearchSchema messageSchema = + new AppSearchSchema.Builder("Message") + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Artist") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .build(); + AppSearchSchema emailSchema = + new AppSearchSchema.Builder("Email") + .addParentType("Message") + // "sender" is defined as an Artist in the parent type Message, which + // requires "sender"'s type here to be a subtype of Artist. Thus, this is + // incompatible because Person is not a subtype of Artist. + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .build(); + + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchemas(personSchema) + .addSchemas(artistSchema) + .addSchemas(messageSchema) + .addSchemas(emailSchema) + .build(); + + ExecutionException executionException = + assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(request).get()); + assertThat(executionException).hasCauseThat().isInstanceOf(AppSearchException.class); + AppSearchException exception = (AppSearchException) executionException.getCause(); + assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_ARGUMENT); + assertThat(exception) + .hasMessageThat() + .containsMatch( + "Property sender from child type .*\\$/Email is not compatible" + + " to the parent type .*\\$/Message."); + } + + @Test + public void testSetSchema_compatibleWithParentTypes() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + AppSearchSchema personSchema = new AppSearchSchema.Builder("Person").build(); + AppSearchSchema artistSchema = + new AppSearchSchema.Builder("Artist").addParentType("Person").build(); + AppSearchSchema messageSchema = + new AppSearchSchema.Builder("Message") + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .addProperty( + new StringPropertyConfig.Builder("note") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + AppSearchSchema emailSchema = + new AppSearchSchema.Builder("Email") + .addParentType("Message") + .addProperty( + // Artist is a subtype of Person, so compatible + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Artist") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .addProperty( + new StringPropertyConfig.Builder("note") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + // A different indexing or tokenizer type is ok. + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType( + StringPropertyConfig.TOKENIZER_TYPE_VERBATIM) + .build()) + .build(); + + SetSchemaRequest request = + new SetSchemaRequest.Builder() + .addSchemas(personSchema) + .addSchemas(artistSchema) + .addSchemas(messageSchema) + .addSchemas(emailSchema) + .build(); + + mDb1.setSchemaAsync(request).get(); + + Set<AppSearchSchema> actual = mDb1.getSchemaAsync().get().getSchemas(); + assertThat(actual).hasSize(4); + assertThat(actual).isEqualTo(request.getSchemas()); + } + + @Test + public void testQuery_typeFilterWithPolymorphism() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + + // Schema registration + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + AppSearchSchema artistSchema = + new AppSearchSchema.Builder("Artist") + .addParentType("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema) + .addSchemas(artistSchema) + .addSchemas(AppSearchEmail.SCHEMA) + .build()) + .get(); + + // Index some documents + GenericDocument personDoc = + new GenericDocument.Builder<>("namespace", "id1", "Person") + .setPropertyString("name", "Foo") + .build(); + GenericDocument artistDoc = + new GenericDocument.Builder<>("namespace", "id2", "Artist") + .setPropertyString("name", "Foo") + .build(); + AppSearchEmail emailDoc = + new AppSearchEmail.Builder("namespace", "id3") + .setFrom("from@example.com") + .setTo("to1@example.com", "to2@example.com") + .setSubject("testPut example") + .setBody("Foo") + .build(); + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(personDoc, artistDoc, emailDoc) + .build())); + + // Query for the documents + SearchResultsShim searchResults = + mDb1.search( + "Foo", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults); + assertThat(documents).hasSize(3); + assertThat(documents).containsExactly(personDoc, artistDoc, emailDoc); + + // Query with a filter for the "Person" type should also include the "Artist" type. + searchResults = + mDb1.search( + "Foo", + new SearchSpec.Builder() + .addFilterSchemas("Person") + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + documents = convertSearchResultsToDocuments(searchResults); + assertThat(documents).hasSize(2); + assertThat(documents).containsExactly(personDoc, artistDoc); + + // Query with a filters for the "Artist" type should not include the "Person" type. + searchResults = + mDb1.search( + "Foo", + new SearchSpec.Builder() + .addFilterSchemas("Artist") + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + documents = convertSearchResultsToDocuments(searchResults); + assertThat(documents).hasSize(1); + assertThat(documents).containsExactly(artistDoc); + } + + @Test + public void testQuery_projectionWithPolymorphism() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + + // Schema registration + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new StringPropertyConfig.Builder("emailAddress") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + AppSearchSchema artistSchema = + new AppSearchSchema.Builder("Artist") + .addParentType("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new StringPropertyConfig.Builder("emailAddress") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new StringPropertyConfig.Builder("company") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema) + .addSchemas(artistSchema) + .build()) + .get(); + + // Index two documents + GenericDocument personDoc = + new GenericDocument.Builder<>("namespace", "id1", "Person") + .setCreationTimestampMillis(1000) + .setPropertyString("name", "Foo Person") + .setPropertyString("emailAddress", "person@gmail.com") + .build(); + GenericDocument artistDoc = + new GenericDocument.Builder<>("namespace", "id2", "Artist") + .setCreationTimestampMillis(1000) + .setPropertyString("name", "Foo Artist") + .setPropertyString("emailAddress", "artist@gmail.com") + .setPropertyString("company", "Company") + .build(); + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(personDoc, artistDoc) + .build())); + + // Query with type property paths {"Person", ["name"]}, {"Artist", ["emailAddress"]} + // This will be expanded to paths {"Person", ["name"]}, {"Artist", ["name", "emailAddress"]} + // via polymorphism. + SearchResultsShim searchResults = + mDb1.search( + "Foo", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .addProjection("Person", ImmutableList.of("name")) + .addProjection("Artist", ImmutableList.of("emailAddress")) + .build()); + List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults); + + // The person document should have been returned with only the "name" property. The artist + // document should have been returned with all of its properties. + GenericDocument expectedPerson = + new GenericDocument.Builder<>("namespace", "id1", "Person") + .setCreationTimestampMillis(1000) + .setPropertyString("name", "Foo Person") + .build(); + GenericDocument expectedArtist = + new GenericDocument.Builder<>("namespace", "id2", "Artist") + .setCreationTimestampMillis(1000) + .setPropertyString("name", "Foo Artist") + .setPropertyString("emailAddress", "artist@gmail.com") + .build(); + assertThat(documents).containsExactly(expectedPerson, expectedArtist); + } + + @Test + public void testQuery_indexBasedOnParentTypePolymorphism() throws Exception { + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SCHEMA_ADD_PARENT_TYPE)); + + // Schema registration + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + AppSearchSchema artistSchema = + new AppSearchSchema.Builder("Artist") + .addParentType("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .addProperty( + new StringPropertyConfig.Builder("company") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .build()) + .build(); + AppSearchSchema messageSchema = + new AppSearchSchema.Builder("Message") + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setShouldIndexNestedProperties(true) + .build()) + .build(); + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema) + .addSchemas(artistSchema) + .addSchemas(messageSchema) + .build()) + .get(); + + // Index some an artistDoc and a messageDoc + GenericDocument artistDoc = + new GenericDocument.Builder<>("namespace", "id1", "Artist") + .setPropertyString("name", "Foo") + .setPropertyString("company", "Bar") + .build(); + GenericDocument messageDoc = + new GenericDocument.Builder<>("namespace", "id2", "Message") + // sender is defined as a Person, which accepts an Artist because Artist <: + // Person. + // However, indexing will be based on what's defined in Person, so the + // "company" + // property in artistDoc cannot be used to search this messageDoc. + .setPropertyDocument("sender", artistDoc) + .build(); + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(artistDoc, messageDoc) + .build())); + + // Query for the documents + SearchResultsShim searchResults = + mDb1.search( + "Foo", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + List<GenericDocument> documents = convertSearchResultsToDocuments(searchResults); + assertThat(documents).hasSize(2); + assertThat(documents).containsExactly(artistDoc, messageDoc); + + // The "company" property in artistDoc cannot be used to search messageDoc. + searchResults = + mDb1.search( + "Bar", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + documents = convertSearchResultsToDocuments(searchResults); + assertThat(documents).hasSize(1); + assertThat(documents).containsExactly(artistDoc); + } + + @Test + public void testSetSchema_indexableNestedPropsList() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)); + + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "worksFor", "Organization") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties(Collections.singleton("name")) + .build()) + .build(); + AppSearchSchema organizationSchema = + new AppSearchSchema.Builder("Organization") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("notes") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema, organizationSchema) + .build()) + .get(); + + // Test that properties in Person's indexable_nested_properties_list are indexed and + // searchable + GenericDocument org1 = + new GenericDocument.Builder<>("namespace", "org1", "Organization") + .setPropertyString("name", "Org1") + .setPropertyString("notes", "Some notes") + .build(); + GenericDocument person1 = + new GenericDocument.Builder<>("namespace", "person1", "Person") + .setPropertyString("name", "Jane") + .setPropertyDocument("worksFor", org1) + .build(); + + AppSearchBatchResult<String, Void> putResult = + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(person1, org1) + .build())); + assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null); + assertThat(putResult.getFailures()).isEmpty(); + + GetByDocumentIdRequest getByDocumentIdRequest = + new GetByDocumentIdRequest.Builder("namespace").addIds("person1", "org1").build(); + List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person1, org1); + + // Both org1 and person should be returned for query "Org1" + // For org1 this matches the 'name' property and for person1 this matches the + // 'worksFor.name' property. + SearchResultsShim searchResults = + mDb1.search( + "Org1", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person1, org1); + + // Only org1 should be returned for query "notes", since 'worksFor.notes' is not indexed + // for the Person-type. + searchResults = + mDb1.search( + "notes", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(1); + assertThat(outDocuments).containsExactly(org1); + } + + @Test + public void testSetSchema_indexableNestedPropsList_notSupported() throws Exception { + assumeFalse( + mDb1.getFeatures() + .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)); + + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "worksFor", "Organization") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties(Collections.singleton("name")) + .build()) + .build(); + AppSearchSchema organizationSchema = + new AppSearchSchema.Builder("Organization") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("notes") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + + SetSchemaRequest setSchemaRequest = + new SetSchemaRequest.Builder().addSchemas(personSchema, organizationSchema).build(); + UnsupportedOperationException e = + assertThrows( + UnsupportedOperationException.class, + () -> mDb1.setSchemaAsync(setSchemaRequest).get()); + assertThat(e) + .hasMessageThat() + .contains( + "DocumentPropertyConfig.addIndexableNestedProperties is not supported on" + + " this AppSearch implementation."); + } + + @Test + public void testSetSchema_indexableNestedPropsList_nonIndexableProp() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)); + + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "worksFor", "Organization") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties(Collections.singleton("name")) + .build()) + .build(); + AppSearchSchema organizationSchema = + new AppSearchSchema.Builder("Organization") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("notes") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType(StringPropertyConfig.INDEXING_TYPE_NONE) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_NONE) + .build()) + .build(); + + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema, organizationSchema) + .build()) + .get(); + + // Test that Person's nested properties are indexed correctly. + GenericDocument org1 = + new GenericDocument.Builder<>("namespace", "org1", "Organization") + .setPropertyString("name", "Org1") + .setPropertyString("notes", "Some notes") + .build(); + GenericDocument person1 = + new GenericDocument.Builder<>("namespace", "person1", "Person") + .setPropertyString("name", "Jane") + .setPropertyDocument("worksFor", org1) + .build(); + + AppSearchBatchResult<String, Void> putResult = + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(person1, org1) + .build())); + assertThat(putResult.getSuccesses()).containsExactly("person1", null, "org1", null); + assertThat(putResult.getFailures()).isEmpty(); + + GetByDocumentIdRequest getByDocumentIdRequest = + new GetByDocumentIdRequest.Builder("namespace").addIds("person1", "org1").build(); + List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person1, org1); + + // Both org1 and person should be returned for query "Org1" + // For org1 this matches the 'name' property and for person1 this matches the + // 'worksFor.name' property. + SearchResultsShim searchResults = + mDb1.search( + "Org1", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person1, org1); + + // No documents should match for "notes", since both 'Organization:notes' + // and 'Person:worksFor.notes' are non-indexable. + searchResults = + mDb1.search( + "notes", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(0); + } + + @Test + public void testSetSchema_indexableNestedPropsList_multipleNestedLevels() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)); + + AppSearchSchema emailSchema = + new AppSearchSchema.Builder("Email") + .addProperty( + new StringPropertyConfig.Builder("subject") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "sender", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties( + Arrays.asList( + "name", "worksFor.name", "worksFor.notes")) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "recipient", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setShouldIndexNestedProperties(true) + .build()) + .build(); + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("age") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "worksFor", "Organization") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties(Arrays.asList("name", "id")) + .build()) + .build(); + AppSearchSchema organizationSchema = + new AppSearchSchema.Builder("Organization") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("notes") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("id") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .build(); + + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(emailSchema, personSchema, organizationSchema) + .build()) + .get(); + + // Test that Email and Person's nested properties are indexed correctly. + GenericDocument org1 = + new GenericDocument.Builder<>("namespace", "org1", "Organization") + .setPropertyString("name", "Org1") + .setPropertyString("notes", "Some notes") + .setPropertyString("id", "1234") + .build(); + GenericDocument person1 = + new GenericDocument.Builder<>("namespace", "person1", "Person") + .setPropertyString("name", "Jane") + .setPropertyString("age", "20") + .setPropertyDocument("worksFor", org1) + .build(); + GenericDocument person2 = + new GenericDocument.Builder<>("namespace", "person2", "Person") + .setPropertyString("name", "John") + .setPropertyString("age", "30") + .setPropertyDocument("worksFor", org1) + .build(); + GenericDocument email1 = + new GenericDocument.Builder<>("namespace", "email1", "Email") + .setPropertyString("subject", "Greetings!") + .setPropertyDocument("sender", person1) + .setPropertyDocument("recipient", person2) + .build(); + AppSearchBatchResult<String, Void> putResult = + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(person1, org1, person2, email1) + .build())); + assertThat(putResult.getSuccesses()) + .containsExactly("person1", null, "org1", null, "person2", null, "email1", null); + assertThat(putResult.getFailures()).isEmpty(); + + GetByDocumentIdRequest getByDocumentIdRequest = + new GetByDocumentIdRequest.Builder("namespace") + .addIds("person1", "org1", "person2", "email1") + .build(); + List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest); + assertThat(outDocuments).hasSize(4); + assertThat(outDocuments).containsExactly(person1, org1, person2, email1); + + // Indexed properties: + // Email: 'subject', 'sender.name', 'sender.worksFor.name', 'sender.worksFor.notes', + // 'recipient.name', 'recipient.age', 'recipient.worksFor.name', + // 'recipient.worksFor.id' + // (Email:recipient sets index_nested_props=true, so it follows the same indexing + // configs as the next schema-type level (person)) + // Person: 'name', 'age', 'worksFor.name', 'worksFor.id' + // Organization: 'name', 'notes', 'id' + // + // All documents should be returned for query 'Org1' because all schemaTypes index the + // 'Organization:name' property. + SearchResultsShim searchResults = + mDb1.search( + "Org1", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(4); + assertThat(outDocuments).containsExactly(person1, org1, person2, email1); + + // org1 and email1 should be returned for query 'notes' + searchResults = + mDb1.search( + "notes", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(org1, email1); + + // all docs should be returned for query "1234" + searchResults = + mDb1.search( + "1234", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(4); + assertThat(outDocuments).containsExactly(person1, org1, person2, email1); + + // email1 should be returned for query "30", but not for "20" since sender.age is not + // indexed, but recipient.age is. + // For query "30", person2 should also be returned + // For query "20, person1 should be returned. + searchResults = + mDb1.search( + "30", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person2, email1); + + searchResults = + mDb1.search( + "20", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(1); + assertThat(outDocuments).containsExactly(person1); + } + + @Test + public void testSetSchema_indexableNestedPropsList_circularRefs() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported(Features.SCHEMA_ADD_INDEXABLE_NESTED_PROPERTIES)); + assumeTrue(mDb1.getFeatures().isFeatureSupported(Features.SET_SCHEMA_CIRCULAR_REFERENCES)); + + // Create schema with valid cycle: Person -> Organization -> Person... + AppSearchSchema personSchema = + new AppSearchSchema.Builder("Person") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_PREFIXES) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("address") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "worksFor", "Organization") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties( + Arrays.asList("name", "notes", "funder.name")) + .build()) + .build(); + AppSearchSchema organizationSchema = + new AppSearchSchema.Builder("Organization") + .addProperty( + new StringPropertyConfig.Builder("name") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new StringPropertyConfig.Builder("notes") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .setIndexingType( + StringPropertyConfig.INDEXING_TYPE_EXACT_TERMS) + .setTokenizerType(StringPropertyConfig.TOKENIZER_TYPE_PLAIN) + .build()) + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder( + "funder", "Person") + .setCardinality(PropertyConfig.CARDINALITY_REPEATED) + .setShouldIndexNestedProperties(false) + .addIndexableNestedProperties( + Arrays.asList( + "name", + "worksFor.name", + "worksFor.funder.address", + "worksFor.funder.worksFor.notes")) + .build()) + .build(); + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder() + .addSchemas(personSchema, organizationSchema) + .build()) + .get(); + + // Test that documents following the circular schema are indexed correctly, and that its + // sections are searchable + GenericDocument person1 = + new GenericDocument.Builder<>("namespace", "person1", "Person") + .setPropertyString("name", "Person1") + .setPropertyString("address", "someAddress") + .build(); + GenericDocument org1 = + new GenericDocument.Builder<>("namespace", "org1", "Organization") + .setPropertyString("name", "Org1") + .setPropertyString("notes", "someNote") + .setPropertyDocument("funder", person1) + .build(); + GenericDocument person2 = + new GenericDocument.Builder<>("namespace", "person2", "Person") + .setPropertyString("name", "Person2") + .setPropertyString("address", "anotherAddress") + .setPropertyDocument("worksFor", org1) + .build(); + GenericDocument org2 = + new GenericDocument.Builder<>("namespace", "org2", "Organization") + .setPropertyString("name", "Org2") + .setPropertyString("notes", "anotherNote") + .setPropertyDocument("funder", person2) + .build(); + + AppSearchBatchResult<String, Void> putResult = + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder() + .addGenericDocuments(person1, org1, person2, org2) + .build())); + assertThat(putResult.getSuccesses()) + .containsExactly("person1", null, "org1", null, "person2", null, "org2", null); + assertThat(putResult.getFailures()).isEmpty(); + + GetByDocumentIdRequest getByDocumentIdRequest = + new GetByDocumentIdRequest.Builder("namespace") + .addIds("person1", "person2", "org1", "org2") + .build(); + List<GenericDocument> outDocuments = doGet(mDb1, getByDocumentIdRequest); + assertThat(outDocuments).hasSize(4); + assertThat(outDocuments).containsExactly(person1, person2, org1, org2); + + // Indexed properties: + // Person: 'name', 'address', 'worksFor.name', 'worksFor.notes', 'worksFor.funder.name' + // Organization: 'name', 'notes', 'funder.name', 'funder.worksFor.name', + // 'funder.worksFor.funder.address', 'funder.worksFor.funder.worksFor.notes' + // + // "Person1" should match person1 (name), org1 (funder.name) and person2 + // (worksFor.funder.name) + SearchResultsShim searchResults = + mDb1.search( + "Person1", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(3); + assertThat(outDocuments).containsExactly(person1, org1, person2); + + // "someAddress" should match person1 (address) and org2 (funder.worksFor.funder.address) + searchResults = + mDb1.search( + "someAddress", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person1, org2); + + // "Org1" should match org1 (name), person2 (worksFor.name) and org2 (funder.worksFor.name) + searchResults = + mDb1.search( + "Org1", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(3); + assertThat(outDocuments).containsExactly(org1, person2, org2); + + // "someNote" should match org1 (notes) and person2 (worksFor.notes) + searchResults = + mDb1.search( + "someNote", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(org1, person2); + + // "Person2" should match person2 (name), org2 (funder.name) + searchResults = + mDb1.search( + "Person2", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(2); + assertThat(outDocuments).containsExactly(person2, org2); + + // "anotherAddress" should match only person2 (address) + searchResults = + mDb1.search( + "anotherAddress", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(1); + assertThat(outDocuments).containsExactly(person2); + + // "Org2" and "anotherNote" should both match only org2 (name, notes) + searchResults = + mDb1.search( + "Org2", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(1); + assertThat(outDocuments).containsExactly(org2); + + searchResults = + mDb1.search( + "anotherNote", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_EXACT_ONLY) + .build()); + outDocuments = convertSearchResultsToDocuments(searchResults); + assertThat(outDocuments).hasSize(1); + assertThat(outDocuments).containsExactly(org2); + } } diff --git a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java index a92dbb241fb..c9e1e6274bc 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java +++ b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java @@ -21,6 +21,7 @@ import static android.autofillservice.cts.testcore.Timeouts.CALLBACK_NOT_CALLED_ import static org.junit.Assume.assumeTrue; +import android.app.Instrumentation; import android.autofillservice.cts.activities.AbstractAutoFillActivity; import android.autofillservice.cts.activities.PreSimpleSaveActivity; import android.autofillservice.cts.activities.SimpleSaveActivity; @@ -35,6 +36,9 @@ import android.platform.test.annotations.FlakyTest; import android.platform.test.annotations.Presubmit; import android.service.autofill.FillResponse; import android.util.Log; +import android.support.test.uiautomator.UiDevice; + +import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.RetryableException; @@ -49,6 +53,7 @@ import org.junit.Test; public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityLaunch { private static final String TAG = "DisableAutofillTest"; + private Instrumentation mInstrumentation; /** * Defines what to do after the activity being tested is launched. @@ -101,6 +106,7 @@ public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityL } + final UiDevice device = UiDevice.getInstance(mInstrumentation); final long before = SystemClock.elapsedRealtime(); final SimpleSaveActivity activity = startSimpleSaveActivity(); final MyAutofillCallback callback = activity.registerCallback(); @@ -111,7 +117,9 @@ public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityL if (action == PostLaunchAction.ASSERT_DISABLING) { callback.assertUiUnavailableEvent(activity.mInput); + callback.assertNotCalled(); sReplier.getNextFillRequest(); + device.waitForIdle(); // Make sure other fields are not triggered. activity.syncRunOnUiThread(() -> activity.mPassword.requestFocus()); @@ -191,6 +199,11 @@ public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityL } @Before + public void setup() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + } + + @Before public void resetAutofillOptions() throws Exception { // Reset AutofillOptions to avoid cts package was added to augmented autofill allowlist. Helper.resetApplicationAutofillOptions(sContext); diff --git a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java index 88fd9bdb0e4..b2da36177ba 100644 --- a/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/CaptureRequestTest.java @@ -113,6 +113,8 @@ public class CaptureRequestTest extends Camera2SurfaceViewTestCase { private static final int ANTI_FLICKERING_60HZ = 2; // 5 percent error margin for resulting crop regions private static final float CROP_REGION_ERROR_PERCENT_DELTA = 0.05f; + private static final float ZOOM_RATIO_ERROR_PERCENT_DELTA = 0.05f; + // 1 percent error margin for centering the crop region private static final float CROP_REGION_ERROR_PERCENT_CENTERED = 0.01f; private static final float DYNAMIC_VS_FIXED_BLK_WH_LVL_ERROR_MARGIN = 0.25f; @@ -3206,8 +3208,9 @@ public class CaptureRequestTest extends Camera2SurfaceViewTestCase { verifyCaptureResultForKey(CaptureResult.CONTROL_EXTENDED_SCENE_MODE, mode, listener, NUM_FRAMES_VERIFIED); + float zoomRatioDelta = ZOOM_RATIO_ERROR_PERCENT_DELTA * ratio; verifyCaptureResultForKey(CaptureResult.CONTROL_ZOOM_RATIO, - ratio, listener, NUM_FRAMES_VERIFIED); + ratio, listener, NUM_FRAMES_VERIFIED, zoomRatioDelta); } } } @@ -3485,6 +3488,33 @@ public class CaptureRequestTest extends Camera2SurfaceViewTestCase { * @param requestMode The request mode for this result * @param listener The capture listener to get capture results * @param numFramesVerified The number of capture results to be verified + * @param threshold The threshold by which the request and result keys can differ + */ + private void verifyCaptureResultForKey(CaptureResult.Key<Float> key, float requestMode, + SimpleCaptureCallback listener, int numFramesVerified, float threshold) { + for (int i = 0; i < numFramesVerified; i++) { + CaptureResult result = listener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS); + validatePipelineDepth(result); + float resultMode = getValueNotNull(result, key); + if (VERBOSE) { + Log.v(TAG, "Expect value: " + requestMode + " result value: " + + resultMode + " threshold " + threshold); + } + // Check that the request and result are within the given threshold of each other. + // (expectEquals isn't the most intuitive function name.) + mCollector.expectEquals("Key " + key.getName() + " request: " + requestMode + + " result: " + resultMode + " not within threshold " + threshold + + " of each other", requestMode, resultMode, threshold); + } + } + + /** + * Basic verification for the control mode capture result. + * + * @param key The capture result key to be verified against + * @param requestMode The request mode for this result + * @param listener The capture listener to get capture results + * @param numFramesVerified The number of capture results to be verified */ private <T> void verifyCaptureResultForKey(CaptureResult.Key<T> key, T requestMode, SimpleCaptureCallback listener, int numFramesVerified) { diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java index e8194d2a21f..3ac79f928f1 100644 --- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java @@ -1709,19 +1709,49 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { boolean supportsRemosaic = arrayContains(capabilities, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING); + List<CaptureRequest.Key<?>> requestKeys = c.getAvailableCaptureRequestKeys(); + boolean doesSupportSensorPixelMode = + requestKeys.contains(CaptureRequest.SENSOR_PIXEL_MODE); + + if (!isUltraHighResolutionSensor && !doesSupportSensorPixelMode) { + Log.i(TAG, "Camera id " + cameraId + " not ultra high resolution / doesn't" + + " support sensor pixel mode. Skipping " + + "testUltraHighResolutionSensorCharacteristics"); + continue; + } + + // Test conditions applicable to both ULTRA_HIGH_RESOLUTION_SENSOR devices and those + // which support SENSOR_PIXEL_MODE. + StreamConfigurationMap configs = + c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION); + assertNotNull("Maximum resolution stream configuration map must not be null for ultra" + + " high resolution sensor camera " + cameraId, configs); + + int[] outputFormats = configs.getOutputFormats(); + boolean supportsRawOutput = + arrayContains(outputFormats, ImageFormat.RAW_SENSOR) || + arrayContains(outputFormats, ImageFormat.RAW10) || + arrayContains(outputFormats, ImageFormat.RAW_PRIVATE) || + arrayContains(outputFormats, ImageFormat.RAW12); + + if (supportsRawOutput) { + Size binningFactor = c.get(CameraCharacteristics.SENSOR_INFO_BINNING_FACTOR); + assertTrue("SENSOR_INFO_BINNING_FACTOR must be advertised by a sensor that " + + " supports ULTRA_HIGH_RESOLUTION_SENSOR / SENSOR_PIXEL_MODE with " + + " RAW outputs - camera id: " + + cameraId, binningFactor != null); + } + if (!isUltraHighResolutionSensor) { - Log.i(TAG, "Camera id " + cameraId + " not ultra high resolution. Skipping " + - "testUltraHighResolutionSensorCharacteristics"); continue; } + + // These conditions apply to ULTRA_HIGH_RESOLUTION_SENSOR devices. assertArrayContains( String.format("Ultra high resolution sensor, camera id %s" + " must also have the RAW capability", cameraId), capabilities, CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_RAW); - StreamConfigurationMap configs = - c.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION); - assertNotNull("Maximum resolution stream configuration map must not be null for ultra" + - " high resolution sensor camera " + cameraId, configs); + Size uhrPixelArraySize = CameraTestUtils.getValueNotNull( c, CameraCharacteristics.SENSOR_INFO_PIXEL_ARRAY_SIZE_MAXIMUM_RESOLUTION); long uhrSensorSize = uhrPixelArraySize.getHeight() * uhrPixelArraySize.getWidth(); @@ -1730,7 +1760,6 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { MIN_UHR_SENSOR_RESOLUTION + " pixels, is " + uhrSensorSize + ", for camera id " + cameraId, uhrSensorSize >= MIN_UHR_SENSOR_RESOLUTION); - int[] outputFormats = configs.getOutputFormats(); assertArrayContains(String.format("No max res JPEG image format for ultra high" + " resolution sensor: ID %s", cameraId), outputFormats, ImageFormat.JPEG); assertArrayContains(String.format("No max res YUV_420_88 image format for ultra high" + diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/PermitInputMethodsTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/PermitInputMethodsTest.java index 35230061292..c62b526cc65 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/PermitInputMethodsTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/PermitInputMethodsTest.java @@ -48,10 +48,12 @@ import com.android.bedstead.harrier.annotations.EnsureHasPermission; import com.android.bedstead.harrier.annotations.Postsubmit; import com.android.bedstead.harrier.annotations.enterprise.CanSetPolicyTest; import com.android.bedstead.harrier.annotations.enterprise.CannotSetPolicyTest; +import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner; import com.android.bedstead.harrier.annotations.enterprise.MostRestrictiveCoexistenceTest; import com.android.bedstead.harrier.annotations.enterprise.PolicyAppliesTest; import com.android.bedstead.harrier.annotations.enterprise.PolicyDoesNotApplyTest; import com.android.bedstead.harrier.policies.PermittedInputMethods; +import com.android.bedstead.harrier.policies.PermittedSystemInputMethods; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.inputmethods.InputMethod; import com.android.bedstead.nene.packages.Package; @@ -107,7 +109,7 @@ public final class PermitInputMethodsTest { } @Postsubmit(reason = "New test") - @PolicyAppliesTest(policy = PermittedInputMethods.class) + @PolicyAppliesTest(policy = {PermittedInputMethods.class, PermittedSystemInputMethods.class}) @EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, QUERY_ADMIN_POLICY}) public void setPermittedInputMethods_allPermitted() { assertThat(sDeviceState.dpc().devicePolicyManager().setPermittedInputMethods( @@ -120,7 +122,7 @@ public final class PermitInputMethodsTest { } @Postsubmit(reason = "New test") - @CanSetPolicyTest(policy = PermittedInputMethods.class) + @CanSetPolicyTest(policy = {PermittedInputMethods.class, PermittedSystemInputMethods.class}) @EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, QUERY_ADMIN_POLICY}) public void setPermittedInputMethods_doesNotThrowException() { sDeviceState.dpc().devicePolicyManager().setPermittedInputMethods( @@ -128,7 +130,8 @@ public final class PermitInputMethodsTest { } @Postsubmit(reason = "New test") - @CannotSetPolicyTest(policy = PermittedInputMethods.class, includeNonDeviceAdminStates = false) + @CannotSetPolicyTest(policy = {PermittedInputMethods.class, PermittedSystemInputMethods.class}, + includeNonDeviceAdminStates = false) @EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, QUERY_ADMIN_POLICY}) public void setPermittedInputMethods_canNotSet_throwsException() { assertThrows(SecurityException.class, () -> { @@ -138,6 +141,15 @@ public final class PermitInputMethodsTest { } @Postsubmit(reason = "New test") + @CanSetPolicyTest(policy = PermittedSystemInputMethods.class) + public void setPermittedInputMethods_nonEmptyList_throwsException() { + assertThrows(IllegalArgumentException.class, () -> { + sDeviceState.dpc().devicePolicyManager().setPermittedInputMethods( + sDeviceState.dpc().componentName(), /* packageNames= */ List.of("package")); + }); + } + + @Postsubmit(reason = "New test") @PolicyDoesNotApplyTest(policy = PermittedInputMethods.class) @EnsureHasPermission({INTERACT_ACROSS_USERS_FULL, QUERY_ADMIN_POLICY}) public void setPermittedInputMethods_policyDoesNotApply_isNotSet() { @@ -183,7 +195,7 @@ public final class PermitInputMethodsTest { } @Postsubmit(reason = "New test") - @CanSetPolicyTest(policy = PermittedInputMethods.class) + @CanSetPolicyTest(policy = {PermittedInputMethods.class, PermittedSystemInputMethods.class}) public void setPermittedInputMethods_packageNameTooLong_throwsException() { // Invalid package name - too long List<String> badMethods = List.of(new String(new char[1000]).replace('\0', 'A')); @@ -225,7 +237,7 @@ public final class PermitInputMethodsTest { @ApiTest(apis = {"android.app.admin.DevicePolicyManager#setPermittedInputMethods"}) // TODO: enable after adding the broadcast receiver to relevant test apps. // @PolicyAppliesTest(policy = PermittedInputMethods.class) - @com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner(isPrimary = true) + @EnsureHasDeviceOwner(isPrimary = true) public void policyUpdateReceiver_setPermittedInputMethods_receivedPolicySetBroadcast() { assumeFalse("A system input method is required", SYSTEM_INPUT_METHODS_PACKAGES.isEmpty()); @@ -448,7 +460,6 @@ public final class PermitInputMethodsTest { .containsExactlyElementsIn(permittedPlusSystem); assertThat(policyState.getCurrentResolvedPolicy()) .containsExactlyElementsIn(enabledNonSystemImes); - } finally { sDeviceState.dpc().devicePolicyManager().setPermittedInputMethods( sDeviceState.dpc().componentName(), /* packageNames= */ null); diff --git a/tests/devicepolicy/telephony/src/android/devicepolicy/cts/telephony/WorkProfileTelephonyTest.java b/tests/devicepolicy/telephony/src/android/devicepolicy/cts/telephony/WorkProfileTelephonyTest.java index 60a6cb8bcf2..075cfc5e15a 100644 --- a/tests/devicepolicy/telephony/src/android/devicepolicy/cts/telephony/WorkProfileTelephonyTest.java +++ b/tests/devicepolicy/telephony/src/android/devicepolicy/cts/telephony/WorkProfileTelephonyTest.java @@ -23,14 +23,14 @@ import static android.Manifest.permission.READ_PHONE_NUMBERS; import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_SMS; import static android.Manifest.permission.WRITE_CALL_LOG; -import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP; import static android.app.role.RoleManager.ROLE_SMS; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.pm.PackageManager.FEATURE_TELEPHONY; import static com.android.bedstead.harrier.UserType.WORK_PROFILE; import static com.android.bedstead.nene.appops.CommonAppOps.OPSTR_CALL_PHONE; -import static com.android.bedstead.nene.types.OptionalBoolean.TRUE; +import static com.android.bedstead.nene.permissions.CommonPermissions.MODIFY_PHONE_STATE; +import static com.android.bedstead.nene.permissions.CommonPermissions.READ_PRIVILEGED_PHONE_STATE; import static com.android.eventlib.truth.EventLogsSubject.assertThat; import static com.android.queryable.queries.ActivityQuery.activity; import static com.android.queryable.queries.IntentFilterQuery.intentFilter; @@ -54,7 +54,6 @@ import android.content.IntentFilter; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; -import android.os.UserHandle; import android.provider.CallLog; import android.provider.Settings; import android.provider.Telephony; @@ -70,6 +69,8 @@ import android.text.TextUtils; import com.android.activitycontext.ActivityContext; import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.bedstead.harrier.DeviceState; +import com.android.bedstead.harrier.annotations.AfterClass; +import com.android.bedstead.harrier.annotations.BeforeClass; import com.android.bedstead.harrier.annotations.EnsureGlobalSettingSet; import com.android.bedstead.harrier.annotations.EnsureHasWorkProfile; import com.android.bedstead.harrier.annotations.Postsubmit; @@ -79,7 +80,9 @@ import com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile; import com.android.bedstead.nene.DefaultDialerContext; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.packages.ComponentReference; +import com.android.bedstead.nene.permissions.CommonPermissions; import com.android.bedstead.nene.permissions.PermissionContext; +import com.android.bedstead.nene.roles.RoleContext; import com.android.bedstead.nene.users.UserReference; import com.android.bedstead.nene.utils.Poll; import com.android.bedstead.testapp.TestApp; @@ -87,10 +90,8 @@ import com.android.bedstead.testapp.TestAppActivityReference; import com.android.bedstead.testapp.TestAppInstance; import com.android.bedstead.testapp.TestInCallService; import com.android.compatibility.common.util.CddTest; -import com.android.compatibility.common.util.SystemUtil; import com.android.eventlib.events.CustomEvent; -import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; @@ -98,9 +99,7 @@ import org.junit.runner.RunWith; import java.time.Duration; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @RequireFeature(FEATURE_TELEPHONY) @@ -128,21 +127,30 @@ public final class WorkProfileTelephonyTest { "enable_work_profile_telephony"; private static final String ENABLE_SWITCH_TO_MANAGED_PROFILE_FLAG = "enable_switch_to_managed_profile_dialog"; + private static final int DEFAULT_SIM_SLOT = 0; + + private static final RoleManager sRoleManager = sContext.getSystemService(RoleManager.class); + private static final TelephonyManager sTelephonyManager = sContext.getSystemService( + TelephonyManager.class); + private static final SubscriptionManager sSubscriptionManager = sContext.getSystemService( + SubscriptionManager.class); + + private static String sDestinationNumber; + + @BeforeClass + public static void setupClass() { + try (PermissionContext p = TestApis.permissions().withPermission(READ_PHONE_NUMBERS, + MODIFY_PHONE_STATE)) { + int subId = SubscriptionManager.getSubscriptionId(DEFAULT_SIM_SLOT); + sDestinationNumber = sSubscriptionManager.getPhoneNumber(subId); + sSubscriptionManager.setDefaultSmsSubId(subId); + } + } - private RoleManager mRoleManager; - private TelephonyManager mTelephonyManager; - private String mDestinationNumber; - - @Before - public void setUp() { - mTelephonyManager = sContext.getSystemService(TelephonyManager.class); - mRoleManager = sContext.getSystemService(RoleManager.class); - - try (PermissionContext p = TestApis.permissions().withPermission(READ_PHONE_NUMBERS)) { - SubscriptionManager subscriptionManager = sContext.getSystemService( - SubscriptionManager.class); - mDestinationNumber = - subscriptionManager.getPhoneNumber(SubscriptionManager.DEFAULT_SUBSCRIPTION_ID); + @AfterClass + public static void teardownClass() { + try (PermissionContext p = TestApis.permissions().withPermission(MODIFY_PHONE_STATE)) { + sSubscriptionManager.setDefaultSmsSubId(SubscriptionManager.INVALID_SUBSCRIPTION_ID); } } @@ -156,14 +164,13 @@ public final class WorkProfileTelephonyTest { throws ExecutionException, InterruptedException, TimeoutException { assumeSmsCapableDevice(); assertValidSimCardPresent(); - String previousDefaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(sContext); RemoteDevicePolicyManager dpm = sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager(); UserReference workProfileUser = sDeviceState.workProfile(); - try (TestAppInstance smsApp = sSmsApp.install(workProfileUser)) { - dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( - ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); - setPackageAsSmsRoleHolderForUser(smsApp.packageName(), workProfileUser.userHandle()); + dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( + ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); + try (TestAppInstance smsApp = sSmsApp.install(workProfileUser); + RoleContext c = smsApp.testApp().pkg().setAsRoleHolder(ROLE_SMS, workProfileUser)) { Intent sentIntent = new Intent(SMS_SENT_INTENT_ACTION).setPackage(smsApp.packageName()); PendingIntent sentPendingIntent = PendingIntent.getBroadcast( TestApis.context().instrumentedContext(), 0, sentIntent, @@ -171,15 +178,13 @@ public final class WorkProfileTelephonyTest { IntentFilter sentIntentFilter = new IntentFilter(SMS_SENT_INTENT_ACTION); smsApp.registerReceiver(sentIntentFilter, Context.RECEIVER_EXPORTED_UNAUDITED); - smsApp.smsManager().sendTextMessage(mDestinationNumber, null, "test", sentPendingIntent, + smsApp.smsManager().sendTextMessage(sDestinationNumber, null, "test", sentPendingIntent, null); assertThat(smsApp.events().broadcastReceived().whereIntent().action().isEqualTo( SMS_SENT_INTENT_ACTION).whereResultCode().isEqualTo( Activity.RESULT_OK)).eventOccurred(); } finally { - dpm.setDefaultSmsApplication(sDeviceState.profileOwner(WORK_PROFILE).componentName(), - previousDefaultSmsPackage); sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( @@ -198,44 +203,54 @@ public final class WorkProfileTelephonyTest { throws ExecutionException, InterruptedException, TimeoutException { assumeSmsCapableDevice(); assertValidSimCardPresent(); - String previousDefaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(sContext); RemoteDevicePolicyManager dpm = sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager(); UserReference primaryUser = sDeviceState.primaryUser(); - try (TestAppInstance smsApp = sSmsApp.install(primaryUser)) { - setPackageAsSmsRoleHolderForUser(smsApp.packageName(), primaryUser.userHandle()); + UserReference workProfileUser = sDeviceState.workProfile(); + try (TestAppInstance personalSmsApp = sSmsApp.install(primaryUser); + RoleContext c1 = personalSmsApp.testApp().pkg().setAsRoleHolder(ROLE_SMS, + primaryUser)) { dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); - Intent sentIntent = new Intent(SMS_SENT_INTENT_ACTION).setPackage(smsApp.packageName()); - PendingIntent sentPendingIntent = PendingIntent.getBroadcast( - TestApis.context().instrumentedContext(), 1, sentIntent, - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED); - smsApp.registerReceiver(new IntentFilter(SMS_SENT_INTENT_ACTION), - Context.RECEIVER_EXPORTED_UNAUDITED); - TestAppActivityReference activityReference = - smsApp.activities().query().whereActivity().exported().isTrue().get(); - // Launch an activity here to bring the default sms app to foreground, we only show the - // switch to managed profile dialog for sms, when sms app is foreground. - ActivityContext.runWithContext(activity -> { - Intent intent = new Intent().addFlags(FLAG_ACTIVITY_NEW_TASK).setComponent( - activityReference.component().componentName()); - activity.startActivity(intent, new Bundle()); - }); - - smsApp.smsManager().sendTextMessage(mDestinationNumber, null, "test", sentPendingIntent, - null); - - assertThat(smsApp.events().broadcastReceived().whereIntent().action().isEqualTo( - SMS_SENT_INTENT_ACTION).whereResultCode().isEqualTo( - SmsManager.RESULT_USER_NOT_ALLOWED)).eventOccurred(); - Poll.forValue("Foreground activity", () -> TestApis.activities().foregroundActivity()) - .toBeEqualTo(INTENT_FORWARDER_COMPONENT).errorOnFail().await(); - } finally { - sDeviceState.profileOwner( - WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( - new ManagedSubscriptionsPolicy( - ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS)); - setPackageAsSmsRoleHolderForUser(previousDefaultSmsPackage, primaryUser.userHandle()); + try (TestAppInstance workSmsApp = sSmsApp.install(workProfileUser); + RoleContext c2 = workSmsApp.testApp().pkg().setAsRoleHolder(ROLE_SMS, + workProfileUser)) { + Intent sentIntent = new Intent(SMS_SENT_INTENT_ACTION).setPackage( + personalSmsApp.packageName()); + PendingIntent sentPendingIntent = PendingIntent.getBroadcast( + TestApis.context().instrumentedContext(), 1, sentIntent, + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_MUTABLE_UNAUDITED); + personalSmsApp.registerReceiver(new IntentFilter(SMS_SENT_INTENT_ACTION), + Context.RECEIVER_EXPORTED_UNAUDITED); + TestAppActivityReference activityReference = personalSmsApp.activities().query() + .whereActivity().exported().isTrue().get(); + // Launch an activity here to bring the default sms app to foreground, we only + // show the + // switch to managed profile dialog for sms, when sms app is foreground. + ActivityContext.runWithContext(activity -> { + Intent intent = new Intent().addFlags(FLAG_ACTIVITY_NEW_TASK).setComponent( + activityReference.component().componentName()); + activity.startActivity(intent, new Bundle()); + }); + + personalSmsApp.smsManager().sendTextMessage(sDestinationNumber, null, "test", + sentPendingIntent, + null); + + assertThat( + personalSmsApp.events().broadcastReceived() + .whereIntent().action().isEqualTo(SMS_SENT_INTENT_ACTION) + .whereResultCode().isEqualTo(SmsManager.RESULT_USER_NOT_ALLOWED)) + .eventOccurred(); + Poll.forValue("Foreground activity", + () -> TestApis.activities().foregroundActivity()).toBeEqualTo( + INTENT_FORWARDER_COMPONENT).errorOnFail().await(); + } finally { + sDeviceState.profileOwner( + WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( + new ManagedSubscriptionsPolicy( + ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS)); + } } } @@ -249,17 +264,15 @@ public final class WorkProfileTelephonyTest { public void allManagedSubscriptions_accessWorkMessageFromPersonalProfile_fails() { assumeSmsCapableDevice(); assertValidSimCardPresent(); - String previousDefaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(sContext); RemoteDevicePolicyManager dpm = sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager(); UserReference workProfileUser = sDeviceState.workProfile(); - try (TestAppInstance smsApp = sSmsApp.install(workProfileUser)) { - dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( - ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); - dpm.setDefaultSmsApplication(sDeviceState.profileOwner(WORK_PROFILE).componentName(), - smsApp.packageName()); + dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( + ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); + try (TestAppInstance smsApp = sSmsApp.install(workProfileUser); + RoleContext c = smsApp.testApp().pkg().setAsRoleHolder(ROLE_SMS, workProfileUser)) { ContentValues smsValues = new ContentValues(); - smsValues.put(Telephony.Sms.ADDRESS, mDestinationNumber); + smsValues.put(Telephony.Sms.ADDRESS, sDestinationNumber); smsValues.put(Telephony.Sms.BODY, "This is a test message."); Uri insertedSmsUri = null; try { @@ -280,8 +293,6 @@ public final class WorkProfileTelephonyTest { } } } finally { - dpm.setDefaultSmsApplication(sDeviceState.profileOwner(WORK_PROFILE).componentName(), - previousDefaultSmsPackage); sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( @@ -298,18 +309,17 @@ public final class WorkProfileTelephonyTest { public void allManagedSubscriptions_accessWorkMessageFromWorkProfile_works() throws ExecutionException, InterruptedException, TimeoutException { assumeSmsCapableDevice(); - String previousDefaultSmsPackage = Telephony.Sms.getDefaultSmsPackage(sContext); RemoteDevicePolicyManager dpm = sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager(); UserReference workProfileUser = sDeviceState.workProfile(); - try (TestAppInstance smsApp = sSmsApp.install(workProfileUser)) { - dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( - ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); - setPackageAsSmsRoleHolderForUser(smsApp.packageName(), workProfileUser.userHandle()); + dpm.setManagedSubscriptionsPolicy(new ManagedSubscriptionsPolicy( + ManagedSubscriptionsPolicy.TYPE_ALL_MANAGED_SUBSCRIPTIONS)); + try (TestAppInstance smsApp = sSmsApp.install(workProfileUser); + RoleContext c = smsApp.testApp().pkg().setAsRoleHolder(ROLE_SMS, workProfileUser)) { String insertMessageBody = "This is a test message with timestamp : " + System.currentTimeMillis(); ContentValues smsValues = new ContentValues(); - smsValues.put(Telephony.Sms.ADDRESS, mDestinationNumber); + smsValues.put(Telephony.Sms.ADDRESS, sDestinationNumber); smsValues.put(Telephony.Sms.BODY, insertMessageBody); Uri insertedSmsUri = null; try { @@ -333,8 +343,6 @@ public final class WorkProfileTelephonyTest { } } } finally { - dpm.setDefaultSmsApplication(sDeviceState.profileOwner(WORK_PROFILE).componentName(), - previousDefaultSmsPackage); sDeviceState.profileOwner( WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( @@ -344,7 +352,7 @@ public final class WorkProfileTelephonyTest { @EnsureGlobalSettingSet(key = Settings.Global.ALLOW_WORK_PROFILE_TELEPHONY_FOR_NON_DPM_ROLE_HOLDERS, value = "1") - @EnsureHasWorkProfile(isOrganizationOwned = true) + @RequireRunOnWorkProfile(isOrganizationOwned = true) @Postsubmit(reason = "new test") @Test public void placeCall_fromWorkProfile_allManagedSubscriptions_works() throws Exception { @@ -359,8 +367,9 @@ public final class WorkProfileTelephonyTest { dialerApp.packageName()); PermissionContext p = dialerApp.permissions().withPermission(CALL_PHONE).withAppOp( OPSTR_CALL_PHONE)) { + setDefaultSimForCallInWorkProfile(); - dialerApp.telecomManager().placeCall(Uri.fromParts("tel", mDestinationNumber, null), + dialerApp.telecomManager().placeCall(Uri.fromParts("tel", sDestinationNumber, null), null); customEvents(dialerApp.packageName(), dialerApp.user()).whereTag().isEqualTo( TestInCallService.TAG).whereData().isEqualTo( @@ -373,6 +382,7 @@ public final class WorkProfileTelephonyTest { WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS)); + unsetDefaultSimForCallInWorkProfile(); } } @@ -396,7 +406,7 @@ public final class WorkProfileTelephonyTest { DefaultDialerContext dc = TestApis.telecom().setDefaultDialerForAllUsers( dialerApp.packageName())) { - dialerApp.telecomManager().placeCall(Uri.fromParts("tel", mDestinationNumber, null), + dialerApp.telecomManager().placeCall(Uri.fromParts("tel", sDestinationNumber, null), null); Poll.forValue("Foreground activity", @@ -489,16 +499,20 @@ public final class WorkProfileTelephonyTest { dialerApp.packageName()); PermissionContext p = dialerApp.permissions().withPermission(CALL_PHONE).withAppOp( OPSTR_CALL_PHONE)) { + setDefaultSimForCallInWorkProfile(); + // This will create a call log in work profile - dialerApp.telecomManager().placeCall(Uri.fromParts("tel", mDestinationNumber, null), + dialerApp.telecomManager().placeCall(Uri.fromParts("tel", sDestinationNumber, null), null); customEvents(dialerApp.packageName(), dialerApp.user()).whereTag().isEqualTo( TestInCallService.TAG).whereData().isEqualTo( "onStateChanged:" + Call.STATE_DISCONNECTED).poll(); - try (PermissionContext pc = TestApis.permissions().withPermission(READ_CALL_LOG)) { - Poll.forValue(() -> numCallLogs()).timeout(Duration.ofSeconds(10)).toNotBeEqualTo( - 0).errorOnFail().await(); + try (PermissionContext pc = TestApis.permissions().withPermission(READ_CALL_LOG, + INTERACT_ACROSS_USERS_FULL)) { + Poll.forValue(() -> numCallLogs(sDeviceState.workProfile())) + .timeout(Duration.ofSeconds(10)) + .toNotBeEqualTo(0).errorOnFail().await(); } } finally { try (PermissionContext pc = TestApis.permissions().withPermission(WRITE_CALL_LOG)) { @@ -509,13 +523,13 @@ public final class WorkProfileTelephonyTest { WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS)); + unsetDefaultSimForCallInWorkProfile(); } } @EnsureGlobalSettingSet(key = Settings.Global.ALLOW_WORK_PROFILE_TELEPHONY_FOR_NON_DPM_ROLE_HOLDERS, value = "1") - @EnsureHasWorkProfile(isOrganizationOwned = true, installInstrumentedApp = TRUE) - @RequireRunOnInitialUser + @RequireRunOnWorkProfile(isOrganizationOwned = true) @Postsubmit(reason = "new test") @Test @CddTest(requirements = {"7.4.1.4/C-2-1"}) @@ -531,17 +545,21 @@ public final class WorkProfileTelephonyTest { dialerApp.packageName()); PermissionContext p = dialerApp.permissions().withPermission(CALL_PHONE).withAppOp( OPSTR_CALL_PHONE)) { + setDefaultSimForCallInWorkProfile(); + // This will create a call log in work profile - dialerApp.telecomManager().placeCall(Uri.fromParts("tel", mDestinationNumber, null), + dialerApp.telecomManager().placeCall(Uri.fromParts("tel", sDestinationNumber, null), null); customEvents(dialerApp.packageName(), dialerApp.user()).whereTag().isEqualTo( TestInCallService.TAG).whereData().isEqualTo( "onStateChanged:" + Call.STATE_DISCONNECTED).poll(); - try (PermissionContext pc = TestApis.permissions().withPermission(READ_CALL_LOG)) { - Poll.forValue(() -> numCallLogs()).timeout(Duration.ofSeconds(10)).toNotBeEqualTo( - 0).await(); - assertThat(numCallLogs()).isEqualTo(0); + try (PermissionContext pc = TestApis.permissions().withPermission(READ_CALL_LOG, + CommonPermissions.INTERACT_ACROSS_USERS_FULL)) { + Poll.forValue(() -> numCallLogs(sDeviceState.workProfile().parent())) + .timeout(Duration.ofSeconds(10)) + .toNotBeEqualTo(0).await(); + assertThat(numCallLogs(sDeviceState.workProfile().parent())).isEqualTo(0); } } finally { try (PermissionContext p2 = TestApis.permissions().withPermission( @@ -554,52 +572,60 @@ public final class WorkProfileTelephonyTest { WORK_PROFILE).devicePolicyManager().setManagedSubscriptionsPolicy( new ManagedSubscriptionsPolicy( ManagedSubscriptionsPolicy.TYPE_ALL_PERSONAL_SUBSCRIPTIONS)); + unsetDefaultSimForCallInWorkProfile(); } } - private void setPackageAsSmsRoleHolderForUser(String packageName, UserHandle userHandle) - throws ExecutionException, InterruptedException, TimeoutException { - CompletableFuture<Boolean> roleUpdateFuture = new CompletableFuture<>(); - SystemUtil.runWithShellPermissionIdentity(() -> { - mRoleManager.addRoleHolderAsUser(ROLE_SMS, packageName, - MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, sContext.getMainExecutor(), - roleUpdateFuture::complete); - }); - // Wait for the future to complete. - roleUpdateFuture.get(60, TimeUnit.SECONDS); - } - private String getDefaultDialerPackage() { return sContext.getSystemService(TelecomManager.class).getDefaultDialerPackage(); } private void assumeSmsCapableDevice() { - assumeTrue(mTelephonyManager.isSmsCapable() || (mRoleManager != null - && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); + assumeTrue(sTelephonyManager.isSmsCapable() || (sRoleManager != null + && sRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); } private void assumeCallCapableDevice() { - assumeTrue(mTelephonyManager.isVoiceCapable() || (mRoleManager != null - && mRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER))); + assumeTrue(sTelephonyManager.isVoiceCapable() || (sRoleManager != null + && sRoleManager.isRoleAvailable(RoleManager.ROLE_DIALER))); } private void assertValidSimCardPresent() { assertTrue("[RERUN] This test requires SIM card to be present", isSimCardPresent()); assertFalse("[RERUN] SIM card does not provide phone number. Use a suitable SIM Card.", - TextUtils.isEmpty(mDestinationNumber)); + TextUtils.isEmpty(sDestinationNumber)); } private boolean isSimCardPresent() { - return mTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; + return sTelephonyManager.getSimState() == TelephonyManager.SIM_STATE_READY; } private CustomEvent.CustomEventQuery customEvents(String packageName, UserReference user) { return CustomEvent.queryPackage(packageName).onUser(user); } - private int numCallLogs() { - Cursor cursor = TestApis.context().instrumentedContext().getContentResolver().query( + private int numCallLogs(UserReference user) { + Cursor cursor = TestApis.context().androidContextAsUser(user).getContentResolver().query( CallLog.Calls.CONTENT_URI, null, null, null, null); return cursor.getCount(); } + + private void setDefaultSimForCallInWorkProfile() { + int subId = SubscriptionManager.getSubscriptionId(DEFAULT_SIM_SLOT); + try (PermissionContext permissionContext = TestApis.permissions().withPermission( + MODIFY_PHONE_STATE, READ_PRIVILEGED_PHONE_STATE)) { + PhoneAccountHandle handle = + sTelephonyManager.getPhoneAccountHandleForSubscriptionId(subId); + TestApis.context().instrumentedContext().getSystemService( + TelecomManager.class).setUserSelectedOutgoingPhoneAccount(handle); + } + } + + private void unsetDefaultSimForCallInWorkProfile() { + try (PermissionContext permissionContext = TestApis.permissions().withPermission( + MODIFY_PHONE_STATE)) { + TestApis.context().instrumentedContext().getSystemService(TelecomManager.class) + .setUserSelectedOutgoingPhoneAccount(null); + } + } } diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java index 66d1e02fedf..c4811f6381f 100644 --- a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java +++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java @@ -30,6 +30,7 @@ import com.android.compatibility.common.util.DeviceReportLog; import static org.junit.Assert.assertTrue; +import org.junit.Before; import org.junit.After; import org.junit.Rule; import org.junit.Test; @@ -45,8 +46,14 @@ public class RandomRWTest { @Rule public final TestName mTestName = new TestName(); + @Before + public void setUp() throws Exception { + CarTestUtil.getInstance().setUp(); + } + @After public void tearDown() throws Exception { + CarTestUtil.getInstance().tearDown(); FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_WR); FileUtil.removeFileOrDir(getContext(), DIR_RANDOM_RD); } diff --git a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java index 1beb91a90bd..a4c6454c130 100644 --- a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java +++ b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java @@ -35,6 +35,7 @@ import com.android.compatibility.common.util.Stat; import static org.junit.Assert.assertTrue; +import org.junit.Before; import org.junit.After; import org.junit.Rule; import org.junit.Test; @@ -58,8 +59,14 @@ public class SequentialRWTest { @Rule public final TestName mTestName = new TestName(); + @Before + public void setUp() throws Exception { + CarTestUtil.getInstance().setUp(); + } + @After public void tearDown() throws Exception { + CarTestUtil.getInstance().tearDown(); FileUtil.removeFileOrDir(getContext(), DIR_SEQ_WR); FileUtil.removeFileOrDir(getContext(), DIR_SEQ_UPDATE); FileUtil.removeFileOrDir(getContext(), DIR_SEQ_RD); diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/HandleSplashScreenExitActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/HandleSplashScreenExitActivity.java index cdf8e85139e..c6f8c4c4920 100644 --- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/HandleSplashScreenExitActivity.java +++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/HandleSplashScreenExitActivity.java @@ -44,6 +44,7 @@ import android.os.Bundle; import android.os.SystemClock; import android.server.wm.TestJournalProvider; import android.view.SurfaceView; +import android.view.ViewTreeObserver; import android.window.SplashScreen; public class HandleSplashScreenExitActivity extends Activity { @@ -73,6 +74,7 @@ public class HandleSplashScreenExitActivity extends Activity { if (getIntent().getBooleanExtra(DELAY_RESUME, false)) { SystemClock.sleep(5000); } + deferDraw(this); } private final SplashScreen.OnExitAnimationListener mSplashScreenExitHandler = @@ -130,4 +132,17 @@ public class HandleSplashScreenExitActivity extends Activity { mUiModeManager.setApplicationNightMode(MODE_NIGHT_AUTO); } } + + /** Deferring show app window to let splash screen display for a while. */ + static void deferDraw(Activity a) { + a.getWindow().getDecorView().getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + final long mCreateTime = SystemClock.uptimeMillis(); + + @Override + public boolean onPreDraw() { + return SystemClock.uptimeMillis() - mCreateTime > 500; + } + }); + } } diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenReplaceThemeActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenReplaceThemeActivity.java index 052e8d7a4d8..1cf4c4620e0 100644 --- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenReplaceThemeActivity.java +++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenReplaceThemeActivity.java @@ -39,6 +39,7 @@ public class SplashScreenReplaceThemeActivity extends Activity { super.onCreate(savedInstanceState); getSplashScreen().setOnExitAnimationListener(mSplashScreenExitHandler); mOverrideTheme = getIntent().getBooleanExtra(OVERRIDE_THEME_ENABLED, false); + HandleSplashScreenExitActivity.deferDraw(this); } @Override diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenStyleThemeActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenStyleThemeActivity.java index 47142a32981..021ae95aa7c 100644 --- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenStyleThemeActivity.java +++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/SplashScreenStyleThemeActivity.java @@ -34,6 +34,7 @@ public class SplashScreenStyleThemeActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); getSplashScreen().setOnExitAnimationListener(mSplashScreenExitHandler); + HandleSplashScreenExitActivity.deferDraw(this); } private void onSplashScreenExit(SplashScreenView view) { diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingPlaceholderTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingPlaceholderTests.java index 03a9762c96f..96915507a7a 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingPlaceholderTests.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingPlaceholderTests.java @@ -221,6 +221,14 @@ public class ActivityEmbeddingPlaceholderTests extends ActivityEmbeddingTestBase public void testPlaceholderLaunchedWhenTaskWidthIncreased() { try (RotationSession rotationSession = new RotationSession()) { rotationSession.set(ROTATION_0); + + // Reduce display size by 50% so that display size won't exceed the maximum display + // size during the test. + final Size currentSize = mReportedDisplayMetrics.getSize(); + final Size displaySize = new Size((int) (currentSize.getWidth() * 0.5), + (int) (currentSize.getHeight() * 0.5)); + mReportedDisplayMetrics.setSize(displaySize); + final double splitTaskWidth = getTaskWidth() * 1.05; final double splitTaskHeight = getTaskHeight() * 1.05; @@ -246,9 +254,8 @@ public class ActivityEmbeddingPlaceholderTests extends ActivityEmbeddingTestBase // Increase display size by 10% so that the primary and placeholder activities are // stacked - final Size currentSize = mReportedDisplayMetrics.getSize(); - mReportedDisplayMetrics.setSize(new Size((int) (currentSize.getWidth() * 1.1), - (int) (currentSize.getHeight() * 1.1))); + mReportedDisplayMetrics.setSize(new Size((int) (displaySize.getWidth() * 1.1), + (int) (displaySize.getHeight() * 1.1))); // Verify that the placeholder activity is launched into a split with the primary // activity diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingTestBase.java index 65c87dc5001..61b4f38d8cb 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingTestBase.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/ActivityEmbeddingTestBase.java @@ -20,13 +20,16 @@ import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSuppo import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions; import static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; +import android.app.ActivityTaskManager; import android.server.wm.ActivityManagerTestBase.ReportedDisplayMetrics; import android.server.wm.UiDeviceUtils; import android.server.wm.jetpack.utils.TestValueCountConsumer; import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase; import android.view.Display; +import androidx.test.core.app.ApplicationProvider; import androidx.window.extensions.WindowExtensions; import androidx.window.extensions.embedding.ActivityEmbeddingComponent; import androidx.window.extensions.embedding.SplitInfo; @@ -51,6 +54,7 @@ public class ActivityEmbeddingTestBase extends WindowManagerJetpackTestBase { @Before public void setUp() { super.setUp(); + assumeTrue(supportsMultiWindow()); assumeExtensionSupportedDevice(); WindowExtensions windowExtensions = getWindowExtensions(); assumeNotNull(windowExtensions); @@ -64,6 +68,11 @@ public class ActivityEmbeddingTestBase extends WindowManagerJetpackTestBase { UiDeviceUtils.pressUnlockButton(); } + /** Checks whether the device supports the multi-window feature or not. */ + private static boolean supportsMultiWindow() { + return ActivityTaskManager.supportsMultiWindow(ApplicationProvider.getApplicationContext()); + } + @After public void cleanUp() { mReportedDisplayMetrics.restoreDisplayMetrics(); diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/SplitAttributesCalculatorTest.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/SplitAttributesCalculatorTest.java index f60e08ecd08..93dc436723f 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/SplitAttributesCalculatorTest.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/embedding/SplitAttributesCalculatorTest.java @@ -21,8 +21,6 @@ import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.EXPAND_SPLIT import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.HINGE_SPLIT_ATTRS; import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.createSplitPairRuleBuilder; import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.startActivityAndVerifySplitAttributes; -import static android.view.Surface.ROTATION_270; -import static android.view.Surface.ROTATION_90; import static com.android.compatibility.common.util.PollingCheck.waitFor; @@ -233,10 +231,13 @@ public class SplitAttributesCalculatorTest extends ActivityEmbeddingTestBase { } try (RotationSession rotationSession = new RotationSession()) { - for (int rotation = ROTATION_90; rotation <= ROTATION_270; rotation++) { + final int initialRotation = activityA.getDisplay().getRotation(); + for (int i = 1; i <= 3; i++) { + // Rotate the device by 90 degree clockwise. + final int rotation = (initialRotation + i) % 4; rotationSession.set(rotation); - verifier.waitAndAssertFunctionApplied("The calculator function mus be called for" + verifier.waitAndAssertFunctionApplied("The calculator function must be called for" + " rotation:" + rotation); } } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java index 866b02813d6..c42a80b5730 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/AssistantStackTests.java @@ -44,6 +44,7 @@ import static android.server.wm.app.Components.TestActivity.TEST_ACTIVITY_ACTION import static android.view.Display.DEFAULT_DISPLAY; import static org.hamcrest.Matchers.hasSize; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -172,13 +173,11 @@ public class AssistantStackTests extends ActivityManagerTestBase { mWmState.assertFocusedRootTask("Assistant stack should be focused.", WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT); } else { - final int testActivityWindowingMode = - mWmState.getTaskDisplayArea(TEST_ACTIVITY).getWindowingMode(); mWmState.assertFocusedActivity("TestActivity should be resumed", TEST_ACTIVITY); mWmState.assertFrontStack("TestActivity stack should be on top.", - testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); mWmState.assertFocusedRootTask("TestActivity stack should be focused.", - testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); } // Now, tell it to finish itself and ensure that the assistant stack is brought back forward @@ -215,12 +214,10 @@ public class AssistantStackTests extends ActivityManagerTestBase { "TestActivity should be resumed"); mWmState.assertFocusedActivity("TestActivity should be focused", TEST_ACTIVITY); - final int testActivityWindowingMode = - mWmState.getTaskDisplayArea(TEST_ACTIVITY).getWindowingMode(); mWmState.assertFrontStack("TestActivity stack should be on top.", - testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); mWmState.assertFocusedRootTask("TestActivity stack should be focused.", - testActivityWindowingMode, ACTIVITY_TYPE_STANDARD); + WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_STANDARD); } @Test @@ -344,8 +341,11 @@ public class AssistantStackTests extends ActivityManagerTestBase { mWmState.assertFocusedRootTask("Expected assistant stack focused", WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT); final WindowManagerState amState = mWmState; + // If activity is hosted by created-by-organizer root task, the root task captured by + // WindowManagerState will have 0 child task. Otherwise, root task contains 1 child + // task. assertThat(amState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT).getTasks(), - hasSize(1)); + hasSize(lessThanOrEqualTo(1))); final int taskId = mWmState.getTaskByActivity(ASSISTANT_ACTIVITY) .mTaskId; @@ -376,8 +376,11 @@ public class AssistantStackTests extends ActivityManagerTestBase { mWmState.assertVisibility(ASSISTANT_ACTIVITY, true); mWmState.assertFocusedRootTask("Expected assistant stack focused", WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_ASSISTANT); + // If activity is hosted by created-by-organizer root task, the root task captured by + // WindowManagerState will have 0 child task. Otherwise, root task contains 1 child + // task. assertThat(amState.getRootTaskByActivityType(ACTIVITY_TYPE_ASSISTANT).getTasks(), - hasSize(1)); + hasSize(lessThanOrEqualTo(1))); assertEquals(taskId, mWmState.getTaskByActivity(ASSISTANT_ACTIVITY).mTaskId); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackGestureInvokedTest.java b/tests/framework/base/windowmanager/src/android/server/wm/BackGestureInvokedTest.java index 9570fccab3f..104c07a1978 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/BackGestureInvokedTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/BackGestureInvokedTest.java @@ -152,6 +152,7 @@ public class BackGestureInvokedTest extends ActivityManagerTestBase { launchActivity(componentName); mWmState.waitForActivityState(componentName, STATE_RESUMED); mWmState.assertVisibility(componentName, true); + mWmState.waitAndAssertImeWindowShownOnDisplay(DEFAULT_DISPLAY); triggerBackEventByGesture(DEFAULT_DISPLAY); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CompatScaleTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CompatScaleTests.java index 4ba6d4e4cd1..6ae297cbb50 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/CompatScaleTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatScaleTests.java @@ -73,9 +73,9 @@ public class CompatScaleTests extends ActivityManagerTestBase { /** * If application size is 1280, then Upscaling by 0.3 will make the surface 1280/0.3 = 4267. * Some devices do not support this high resolution, so limiting Upscaling test case for - * scaling >= 0.35. + * scaling >= 0.5. */ - public static float MAX_UPSCALING_TESTED = 0.35f; + public static float MAX_UPSCALING_TESTED = 0.5f; @Parameterized.Parameters(name = "{0}") public static Iterable<Object[]> data() { diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DisplayShapeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DisplayShapeTests.java index fd8f91e5b44..492745512a7 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/DisplayShapeTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/DisplayShapeTests.java @@ -20,12 +20,11 @@ import static android.view.Display.DEFAULT_DISPLAY; import static android.view.RoundedCorner.POSITION_BOTTOM_LEFT; import static android.view.RoundedCorner.POSITION_TOP_LEFT; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; import android.graphics.Path; @@ -50,13 +49,13 @@ import org.junit.Test; @Presubmit @android.server.wm.annotation.Group3 public class DisplayShapeTests extends WindowManagerTestBase { - private static final String TAG = "DisplayShapeTests"; private Display mDisplay; @Before public void setUp() throws Exception { mDisplay = mDm.getDisplay(DEFAULT_DISPLAY); + assumeNotNull(mDisplay); } @Test @@ -76,25 +75,23 @@ public class DisplayShapeTests extends WindowManagerTestBase { assumeTrue("Test does not apply for vendor image with API level lower than U", PropertyUtil.isVendorApiLevelNewerThan(Build.VERSION_CODES.TIRAMISU)); - boolean hasRoundedCorners = false; - RoundedCorner r; + boolean hasRoundedCorner = false; for (int i = POSITION_TOP_LEFT; i <= POSITION_BOTTOM_LEFT; i++) { - r = mDisplay.getRoundedCorner(POSITION_TOP_LEFT); - if (r == null) { - continue; + final RoundedCorner r = mDisplay.getRoundedCorner(i); + if (r != null && r.getRadius() > 0) { + hasRoundedCorner = true; + break; } - hasRoundedCorners = true; - break; } - final Path path = mDisplay.getShape().getPath(); - final boolean isRect = path.isRect(null); - - // By default, if the config for display shape is not set, the returned shape will be - // rectangular. The config must be set if the display has rounded corners. - assertThat("The display has rounded corners but the returned path is a rectangular shape." - + " Please set the config(config_mainDisplayShape) for display shape.", - isRect, not(hasRoundedCorners)); + if (hasRoundedCorner) { + // If the display shape is not configured, the returned path will be rectangular if the + // display is not round. The config must be set if the display is not round or + // rectangular. + assertFalse("The display has a rounded corner but the shape specifies a rectangle." + + " Please set the config (config_mainDisplayShape) for the display shape.", + mDisplay.getShape().getPath().isRect(null)); + } } @Test diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DockConfigChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/DockConfigChangeTests.java index 3e2a292b4b3..f10fe84c432 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/DockConfigChangeTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/DockConfigChangeTests.java @@ -21,7 +21,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.content.res.Configuration.UI_MODE_TYPE_NORMAL; import static android.server.wm.app.Components.TEST_ACTIVITY; import static android.server.wm.deskresources.Components.DESK_RESOURCES_ACTIVITY; -import static android.view.Surface.ROTATION_270; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; @@ -31,6 +30,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.res.Resources; import android.platform.test.annotations.Presubmit; +import android.view.Surface; import org.junit.Test; @@ -47,10 +47,10 @@ public class DockConfigChangeTests extends ActivityManagerTestBase { // enabled. assumeTrue(getConfigSkipRelaunchOnDock()); - // Set rotation to 270 first, since docking forces rotation to 270, causing an extra config - // change. + // Set rotation to the same rotation as the device would be rotated to after docking. This + // prevents an extraneous config change from the device rotating when docked/undocked. RotationSession rotationSession = createManagedRotationSession(); - rotationSession.set(ROTATION_270, /*waitDeviceRotation=*/ true); + rotateToDockRotation(rotationSession); launchActivity(TEST_ACTIVITY); waitAndAssertResumedActivity(TEST_ACTIVITY, "Activity must be resumed"); @@ -80,10 +80,10 @@ public class DockConfigChangeTests extends ActivityManagerTestBase { // enabled. assumeTrue(getConfigSkipRelaunchOnDock()); - // Set rotation to 270 first, since docking forces rotation to 270, causing an extra config - // change. + // Set rotation to the same rotation as the device would be rotated to after docking. This + // prevents an extraneous config change from the device rotating when docked/undocked. RotationSession rotationSession = createManagedRotationSession(); - rotationSession.set(ROTATION_270, /*waitDeviceRotation=*/ true); + rotateToDockRotation(rotationSession); launchActivity(DESK_RESOURCES_ACTIVITY); waitAndAssertResumedActivity(DESK_RESOURCES_ACTIVITY, "Activity must be resumed"); @@ -115,6 +115,43 @@ public class DockConfigChangeTests extends ActivityManagerTestBase { "bool", "android")); } + /** + * Rotates the device to the same rotation as it would rotate to when docked. + * + * Dock rotation is read from config_deskDockRotation. + */ + void rotateToDockRotation(RotationSession rotationSession) { + int rotation = rotationDegreesToConst(mContext.getResources().getInteger( + Resources.getSystem().getIdentifier("config_deskDockRotation", + "integer", "android"))); + if (rotation == -1) { + // -1 could come from the const itself, which means no rotation on dock, or from + // rotationDegreesToConst, which means we got an unexpected value from the resource. + return; + } + rotationSession.set(rotation); + } + + /** + * Converts from a rotation in degrees to a {@link Surface.Rotation} constant. + * + * Returns -1 if a value that doesn't match a {@link Surface.Rotation} constant is provided. + */ + private int rotationDegreesToConst(int rotationDegrees) { + switch (rotationDegrees) { + case 0: + return Surface.ROTATION_0; + case 90: + return Surface.ROTATION_90; + case 180: + return Surface.ROTATION_180; + case 270: + return Surface.ROTATION_270; + } + return -1; + } + + private class DockTestSession implements AutoCloseable { private void dock() { runShellCommand("dumpsys DockObserver set state " diff --git a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java index ba78a0afb2f..40a11b146fd 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/DragDropTest.java @@ -51,6 +51,8 @@ import android.view.ViewGroup; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.UiAutomatorUtils; + import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -220,7 +222,7 @@ public class DragDropTest extends WindowManagerTestBase { View v = mActivity.findViewById(viewId); View release = mActivity.findViewById(releaseViewId); int [] releaseLoc = new int[2]; - release.getLocationOnScreen(releaseLoc); + release.getLocationInWindow(releaseLoc); int action = DragEvent.ACTION_DRAG_ENDED; mExpected.add(new LogEntry(v, action, releaseLoc[0] + 6 / mInvCompatScale, releaseLoc[1] + 6 / mInvCompatScale, @@ -334,6 +336,7 @@ public class DragDropTest extends WindowManagerTestBase { @Before public void setUp() throws InterruptedException { assumeFalse(isWatchDevice()); + UiAutomatorUtils.getUiDevice().waitForIdle(); mActivity = startActivityInWindowingMode(DragDropActivity.class, WINDOWING_MODE_FULLSCREEN); mStartReceived = new CountDownLatch(1); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java index 713958d94a4..3911c16cf78 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/ForceRelayoutTestBase.java @@ -88,8 +88,6 @@ public class ForceRelayoutTestBase { protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); getWindow().requestFeature(Window.FEATURE_NO_TITLE); - getWindow().setDecorFitsSystemWindows(false); - getWindow().getAttributes().layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; View view = new View(this) { @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { @@ -114,17 +112,21 @@ public class ForceRelayoutTestBase { return WindowInsets.CONSUMED; }); setContentView(view); + + getWindow().setDecorFitsSystemWindows(false); + getWindow().getAttributes().layoutInDisplayCutoutMode = + LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; } } private static boolean remoteInsetsControllerControlsSystemBars() { return InstrumentationRegistry.getInstrumentation().getTargetContext().getResources() - .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars); + .getBoolean(android.R.bool.config_remoteInsetsControllerControlsSystemBars); } private boolean isCar() { PackageManager pm = InstrumentationRegistry.getInstrumentation().getContext() - .getPackageManager(); + .getPackageManager(); return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); } } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java index ed2d6100fee..83f04b89fce 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java @@ -49,7 +49,9 @@ import static org.junit.Assert.assertNotEquals; import android.app.Activity; import android.app.ActivityOptions; import android.content.ComponentName; +import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.os.Bundle; import android.platform.test.annotations.Presubmit; import android.server.wm.CommandSession.ActivitySession; @@ -313,8 +315,15 @@ public class StartActivityTests extends ActivityManagerTestBase { if (useShellPermission) { waitAndAssertResumedActivity(BROADCAST_RECEIVER_ACTIVITY, "Activity should be started and resumed"); - mWmState.assertFrontStackActivityType("The activity type should be same as requested.", - type); + if (type == ACTIVITY_TYPE_HOME && isAutomotive(mContext) + && hasSplitscreenMultitaskingFeature(mContext)) { + // For automotive devices with splitscreen multitasking, home activity might + // not be in front of the stack, hence, check for its visibility instead. + mWmState.assertHomeActivityVisible(/* visible= */ true); + } else { + mWmState.assertFrontStackActivityType( + "The activity type should be same as requested.", type); + } mBroadcastActionTrigger.finishBroadcastReceiverActivity(); mWmState.waitAndAssertActivityRemoved(BROADCAST_RECEIVER_ACTIVITY); } else { @@ -323,6 +332,23 @@ public class StartActivityTests extends ActivityManagerTestBase { } /** + * Checks whether the device is automotive + */ + private static boolean isAutomotive(Context context) { + PackageManager pm = context.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } + + /** + * Checks whether the device has automotive splitscreen multitasking feature enabled + */ + private static boolean hasSplitscreenMultitaskingFeature(Context context) { + PackageManager pm = context.getPackageManager(); + return pm.hasSystemFeature(/* PackageManager.FEATURE_CAR_SPLITSCREEN_MULTITASKING */ + "android.software.car.splitscreen_multitasking"); + } + + /** * Assume there are 3 activities (A1, A2, B1) with default launch mode. The uid of B1 is * different from A1 and A2. After A1 called {@link Activity#startActivities} to start B1 and * A2, the result should be 3 tasks. diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java index 88e9f2c9522..8ed33a692f8 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/SurfaceControlViewHostTests.java @@ -21,6 +21,7 @@ import static android.server.wm.CtsWindowInfoUtils.tapOnWindowCenter; import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus; import static android.server.wm.CtsWindowInfoUtils.waitForWindowInfo; import static android.server.wm.CtsWindowInfoUtils.waitForWindowInfos; +import static android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop; import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible; import static android.server.wm.MockImeHelper.createManagedMockImeSession; import static android.view.SurfaceControlViewHost.SurfacePackage; @@ -180,6 +181,8 @@ public class SurfaceControlViewHostTests extends ActivityManagerTestBase impleme mCtsTouchUtils = new CtsTouchUtils(mInstrumentation.getTargetContext()); mActivity = mActivityRule.launchActivity(null); mInstrumentation.waitForIdleSync(); + // Wait for device animation that shows above the activity to leave. + waitForWindowOnTop(mActivity.getWindow()); // This is necessary to call waitForWindowInfos mInstrumentation.getUiAutomation().adoptShellPermissionIdentity( diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java index 6e8f020f820..46bca49374d 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerTest.java @@ -158,7 +158,7 @@ public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { mWmState.waitForActivityState(mLaunchingActivity, STATE_RESUMED); - Task parentTask = mWmState.getRootTask(mOwnerActivity.getTaskId()); + Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName); TaskFragment taskFragment = mWmState.getTaskFragmentByActivity(mLaunchingActivity); // Assert window hierarchy must be as follows @@ -186,8 +186,8 @@ public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { assertEmptyTaskFragment(taskFragmentInfo, taskFragmentInfo.getFragmentToken()); mWmState.computeState(mOwnerActivityName); - final int originalTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments() - .size(); + final int originalTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName) + .getTaskFragments().size(); WindowContainerTransaction wct = new WindowContainerTransaction() .deleteTaskFragment(taskFragToken); @@ -200,7 +200,8 @@ public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { taskFragToken); mWmState.computeState(mOwnerActivityName); - final int currTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments().size(); + final int currTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName) + .getTaskFragments().size(); assertWithMessage("TaskFragment with token " + taskFragToken + " must be" + " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1); } @@ -219,8 +220,8 @@ public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { assertNotEmptyTaskFragment(taskFragmentInfo, taskFragmentInfo.getFragmentToken()); mWmState.computeState(mOwnerActivityName); - final int originalTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments() - .size(); + final int originalTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName) + .getTaskFragments().size(); WindowContainerTransaction wct = new WindowContainerTransaction() .deleteTaskFragment(taskFragToken); @@ -233,7 +234,8 @@ public class TaskFragmentOrganizerTest extends TaskFragmentOrganizerTestBase { taskFragToken); mWmState.computeState(mOwnerActivityName); - final int currTaskFragCount = mWmState.getRootTask(mOwnerTaskId).getTaskFragments().size(); + final int currTaskFragCount = mWmState.getTaskByActivity(mOwnerActivityName) + .getTaskFragments().size(); assertWithMessage("TaskFragment with token " + taskFragToken + " must be" + " removed.").that(originalTaskFragCount - currTaskFragCount).isEqualTo(1); } 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 4637ac7efde..d44b8e5c78a 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java @@ -179,17 +179,18 @@ public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase { */ @Test public void testUntrustedModeTaskFragment_setRelativeBoundsOutsideOfParentBounds() { - final Task parentTask = mWmState.getRootTask(mOwnerTaskId); + final Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName); final Rect parentBounds = new Rect(parentTask.getBounds()); // Create a TaskFragment with activity embedded in untrusted mode. final TaskFragmentInfo info = createTaskFragment(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY); // Try to set relative bounds that is larger than its parent bounds. mTaskFragmentOrganizer.resetLatch(); - final Rect taskFragBounds = new Rect(parentBounds); - taskFragBounds.right++; + final Rect taskFragRelativeBounds = new Rect(parentBounds); + taskFragRelativeBounds.offsetTo(0, 0); + taskFragRelativeBounds.right++; final WindowContainerTransaction wct = new WindowContainerTransaction() - .setRelativeBounds(info.getToken(), taskFragBounds) + .setRelativeBounds(info.getToken(), taskFragRelativeBounds) .setWindowingMode(info.getToken(), WINDOWING_MODE_MULTI_WINDOW); // It is allowed to set TaskFragment bounds to outside of its parent bounds. @@ -217,7 +218,7 @@ public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase { */ @Test public void testUntrustedModeTaskFragment_startActivityInTaskFragmentOutsideOfParentBounds() { - final Task parentTask = mWmState.getRootTask(mOwnerTaskId); + final Task parentTask = mWmState.getTaskByActivity(mOwnerActivityName); final Rect parentBounds = new Rect(parentTask.getBounds()); final IBinder errorCallbackToken = new Binder(); final WindowContainerTransaction wct = new WindowContainerTransaction() diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java index e45b6ebfd29..ba9eb8ec631 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/TransitionSelectionTests.java @@ -46,6 +46,7 @@ import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import android.content.ComponentName; +import android.content.pm.PackageManager; import android.platform.test.annotations.Presubmit; import org.junit.Before; @@ -113,6 +114,7 @@ public class TransitionSelectionTests extends ActivityManagerTestBase { // Test task open/close under normal timing @Test public void testOpenTask_NeitherWallpaper() { + assumeFalse(isAutomotive() && hasSplitscreenMultitaskingFeature()); testOpenTask(false /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */, false /*slowStop*/, TRANSIT_TASK_OPEN, WINDOWING_MODE_FULLSCREEN); } @@ -126,6 +128,7 @@ public class TransitionSelectionTests extends ActivityManagerTestBase { @Test public void testCloseTask_NeitherWallpaper() { + assumeFalse(isAutomotive() && hasSplitscreenMultitaskingFeature()); testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */, false /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FULLSCREEN); } @@ -198,12 +201,14 @@ public class TransitionSelectionTests extends ActivityManagerTestBase { // before AM receives its activitiyStopped @Test public void testCloseTask_NeitherWallpaper_SlowStop() { + assumeFalse(isAutomotive() && hasSplitscreenMultitaskingFeature()); testCloseTask(false /*bottomWallpaper*/, false /*topWallpaper*/, true /* topResizable */, true /*slowStop*/, TRANSIT_TASK_CLOSE, WINDOWING_MODE_FULLSCREEN); } @Test public void testCloseTask_BottomWallpaper_TopNonResizable_SlowStop() { + assumeFalse(isAutomotive() && hasSplitscreenMultitaskingFeature()); testCloseTask(true /*bottomWallpaper*/, false /*topWallpaper*/, false /* topResizable */, true /*slowStop*/, TRANSIT_WALLPAPER_OPEN, WINDOWING_MODE_FULLSCREEN); } @@ -366,4 +371,21 @@ public class TransitionSelectionTests extends ActivityManagerTestBase { assertEquals("Picked wrong transition", expectedTransit, mWmState.getDefaultDisplayLastTransition()); } + + /** + * Checks whether the device is automotive + */ + private boolean isAutomotive() { + PackageManager pm = mContext.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } + + /** + * Checks whether the device has automotive splitscreen multitasking feature enabled + */ + private boolean hasSplitscreenMultitaskingFeature() { + PackageManager pm = mContext.getPackageManager(); + return pm.hasSystemFeature(/* PackageManager.FEATURE_CAR_SPLITSCREEN_MULTITASKING */ + "android.software.car.splitscreen_multitasking"); + } } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java index 761ec07fc51..653ee1d4647 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInputTests.java @@ -68,6 +68,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.compatibility.common.util.CtsTouchUtils; import com.android.compatibility.common.util.SystemUtil; +import com.android.compatibility.common.util.UiAutomatorUtils; import org.junit.Before; import org.junit.Test; @@ -118,6 +119,9 @@ public class WindowInputTests { mActivity = mActivityRule.launchActivity(null); mInputManager = mActivity.getSystemService(InputManager.class); mInstrumentation.waitForIdleSync(); + // TODO(b/295916860) - Replace this 'uidevice' workaround with an + // WindowInfosListener-based solution + UiAutomatorUtils.getUiDevice().waitForIdle(); mClickCount = 0; } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java index 8d49372871c..9e6d82f7aaa 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsLayoutTests.java @@ -69,13 +69,12 @@ public class WindowInsetsLayoutTests extends WindowManagerTestBase { activity.assertMatchesWindowBounds(); }); - testSetFitInsetsTypesInner(Type.statusBars(), activity, mainWindowRoot); - testSetFitInsetsTypesInner(Type.navigationBars(), activity, mainWindowRoot); - testSetFitInsetsTypesInner(Type.systemBars(), activity, mainWindowRoot); + testSetFitInsetsTypesInner(Type.statusBars(), activity); + testSetFitInsetsTypesInner(Type.navigationBars(), activity); + testSetFitInsetsTypesInner(Type.systemBars(), activity); } - private void testSetFitInsetsTypesInner( - int types, TestActivity activity, View mainWindowRoot) { + private void testSetFitInsetsTypesInner(int types, TestActivity activity) { getInstrumentation().runOnMainSync(() -> { activity.addChildWindow(types, Side.all(), false); }); @@ -85,16 +84,9 @@ public class WindowInsetsLayoutTests extends WindowManagerTestBase { PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); getInstrumentation().runOnMainSync(() -> { - final WindowInsets windowInsets = mainWindowRoot.getRootWindowInsets(); + final WindowInsets windowInsets = childWindowRoot.getRootWindowInsets(); final Insets insets = windowInsets.getInsets(types); - final int[] locationOnScreen = new int[2]; - childWindowRoot.getLocationOnScreen(locationOnScreen); - assertEquals(insets.left, locationOnScreen[0]); - assertEquals(insets.top, locationOnScreen[1]); - assertEquals(insets.right, - mainWindowRoot.getWidth() - locationOnScreen[0] - childWindowRoot.getWidth()); - assertEquals(insets.bottom, - mainWindowRoot.getHeight()- locationOnScreen[1] - childWindowRoot.getHeight()); + assertEquals(Insets.NONE, insets); activity.removeChildWindow(); }); } @@ -113,14 +105,13 @@ public class WindowInsetsLayoutTests extends WindowManagerTestBase { activity.assertMatchesWindowBounds(); }); - testSetFitInsetsSidesInner(Side.LEFT, activity, mainWindowRoot); - testSetFitInsetsSidesInner(Side.TOP, activity, mainWindowRoot); - testSetFitInsetsSidesInner(Side.RIGHT, activity, mainWindowRoot); - testSetFitInsetsSidesInner(Side.BOTTOM, activity, mainWindowRoot); + testSetFitInsetsSidesInner(Side.LEFT, activity); + testSetFitInsetsSidesInner(Side.TOP, activity); + testSetFitInsetsSidesInner(Side.RIGHT, activity); + testSetFitInsetsSidesInner(Side.BOTTOM, activity); } - private void testSetFitInsetsSidesInner( - int sides, TestActivity activity, View mainWindowRoot) { + private void testSetFitInsetsSidesInner(int sides, TestActivity activity) { final int types = Type.systemBars(); getInstrumentation().runOnMainSync(() -> { activity.addChildWindow(types, sides, false); @@ -131,16 +122,20 @@ public class WindowInsetsLayoutTests extends WindowManagerTestBase { PollingCheck.waitFor(TIMEOUT, () -> childWindowRoot.getWidth() > 0); getInstrumentation().runOnMainSync(() -> { - final WindowInsets windowInsets = mainWindowRoot.getRootWindowInsets(); + final WindowInsets windowInsets = childWindowRoot.getRootWindowInsets(); final Insets insets = windowInsets.getInsets(types); - final int[] locationOnScreen = new int[2]; - childWindowRoot.getLocationOnScreen(locationOnScreen); - assertEquals((sides & Side.LEFT) != 0 ? insets.left : 0, locationOnScreen[0]); - assertEquals((sides & Side.TOP) != 0 ? insets.top : 0, locationOnScreen[1]); - assertEquals((sides & Side.RIGHT) != 0 ? insets.right : 0, - mainWindowRoot.getWidth() - locationOnScreen[0] - childWindowRoot.getWidth()); - assertEquals((sides & Side.BOTTOM) != 0 ? insets.bottom : 0, - mainWindowRoot.getHeight()- locationOnScreen[1] - childWindowRoot.getHeight()); + if ((sides & Side.LEFT) != 0) { + assertEquals(0, insets.left); + } + if ((sides & Side.TOP) != 0) { + assertEquals(0, insets.top); + } + if ((sides & Side.RIGHT) != 0) { + assertEquals(0, insets.right); + } + if ((sides & Side.BOTTOM) != 0) { + assertEquals(0, insets.bottom); + } activity.removeChildWindow(); }); } @@ -203,6 +198,8 @@ public class WindowInsetsLayoutTests extends WindowManagerTestBase { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + final View view = new View(this); + setContentView(view); WindowManager.LayoutParams lp = getWindow().getAttributes(); lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS; getWindow().setAttributes(lp); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java index 38c5a0f0c7b..60a8017a9f4 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/lifecycle/ActivityStarterTests.java @@ -41,6 +41,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import static org.junit.Assume.assumeFalse; import android.app.Activity; import android.content.ComponentName; @@ -152,6 +153,8 @@ public class ActivityStarterTests extends ActivityLifecycleClientTestBase { */ @Test public void testLaunchNoHistoryActivityShowWhenLocked() { + // Allow TV devices to skip this test. + assumeFalse(isLeanBack()); final LockScreenSession lockScreenSession = createManagedLockScreenSession(); lockScreenSession.sleepDevice(); diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java index ee4270fce6b..f133224fc1e 100644 --- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java +++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerState.java @@ -870,8 +870,8 @@ public class WindowManagerState { } public int getRootTaskIdByActivity(ComponentName activityName) { - final Task task = getTaskByActivity(activityName); - return (task == null) ? INVALID_TASK_ID : task.mRootTaskId; + final Task rootTask = getRootTaskByActivity(activityName); + return (rootTask == null) ? INVALID_TASK_ID : rootTask.mTaskId; } public Task getTaskByActivity(ComponentName activityName) { diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java index 026c0756f8e..428c555d464 100644 --- a/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java +++ b/tests/inputmethod/src/android/view/inputmethod/cts/InputMethodInfoTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.mock; @@ -48,6 +49,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.FeatureUtil; import com.android.compatibility.common.util.PropertyUtil; import org.junit.Before; @@ -248,6 +250,8 @@ public class InputMethodInfoTest { @Test public void testAtLeastOneEncryptionAwareInputMethodIsAvailable() { + assumeFalse(FeatureUtil.isWatch()); + if (!mContext.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS)) { return; diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml index b82d166606a..afb4861c438 100644 --- a/tests/media/AndroidTest.xml +++ b/tests/media/AndroidTest.xml @@ -35,7 +35,7 @@ </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> - <option name="media-folder-name" value="CtsMediaV2TestCases-3.4" /> + <option name="media-folder-name" value="CtsMediaV2TestCases-3.5" /> <option name="dynamic-config-module" value="CtsMediaV2TestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml index 5829ad2374c..e3643d73789 100644 --- a/tests/media/DynamicConfig.xml +++ b/tests/media/DynamicConfig.xml @@ -1,5 +1,5 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://dl.google.com/android/xts/cts/tests/media/CtsMediaV2TestCases-3.4.zip</value> + <value>https://dl.google.com/android/xts/cts/tests/media/CtsMediaV2TestCases-3.5.zip</value> </entry> </dynamicConfig> diff --git a/tests/media/README.md b/tests/media/README.md index d726b1f95c4..5601ad3d379 100644 --- a/tests/media/README.md +++ b/tests/media/README.md @@ -3,7 +3,7 @@ Current folder comprises of files necessary for testing media extractor, media m The aim of these tests is not solely to verify the CDD requirements but also to test components, their plugins and their interactions with media framework. -The test vectors used by the test suite is available at [link](https://dl.google.com/android/xts/cts/tests/media/CtsMediaV2TestCases-3.4.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory. +The test vectors used by the test suite is available at [link](https://dl.google.com/android/xts/cts/tests/media/CtsMediaV2TestCases-3.5.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory. All Big Buck Bunny(bbb) test vectors are of 8-bit format. They are downloaded from [link](https://peach.blender.org/download/) and resampled according to the test requirements. All Cosmos Laundromat(cosmat) test vectors are of 10-bit format. They are downloaded from [link](https://media.xiph.org/) and resampled according to the test requirements. diff --git a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java index 80760b11385..828b6db8f39 100644 --- a/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/CodecDecoderTestBase.java @@ -94,8 +94,18 @@ public class CodecDecoderTestBase extends CodecTestBase { if (mIsVideo) { ArrayList<MediaFormat> formatList = new ArrayList<>(); formatList.add(format); - boolean selectHBD = doesAnyFormatHaveHDRProfile(mMediaType, formatList) - || srcFile.contains("10bit"); + boolean selectHBD = doesAnyFormatHaveHDRProfile(mMediaType, formatList); + if (!selectHBD && srcFile.contains("10bit")) { + selectHBD = true; + if (mMediaType.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { + // In some cases, webm extractor may not signal profile for 10-bit VP9 + // clips. In such cases, set profile to a 10-bit compatible profile. + // TODO (b/295804596) Remove the following once webm extractor signals + // profile correctly for all 10-bit clips + int[] profileArray = CodecTestBase.PROFILE_HDR_MAP.get(mMediaType); + format.setInteger(MediaFormat.KEY_PROFILE, profileArray[0]); + } + } format.setInteger(MediaFormat.KEY_COLOR_FORMAT, getColorFormat(mCodecName, mMediaType, mSurface != null, selectHBD)); if (selectHBD && (format.getInteger(MediaFormat.KEY_COLOR_FORMAT) diff --git a/tests/media/common/src/android/mediav2/common/cts/CodecTestBase.java b/tests/media/common/src/android/mediav2/common/cts/CodecTestBase.java index 2b1f007fde3..dc17b8d99f4 100644 --- a/tests/media/common/src/android/mediav2/common/cts/CodecTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/CodecTestBase.java @@ -139,8 +139,6 @@ public abstract class CodecTestBase { "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; public static final String HDR_STATIC_INCORRECT_INFO = "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; - public static final HashMap<Integer, String> HDR_DYNAMIC_INFO = new HashMap<>(); - public static final HashMap<Integer, String> HDR_DYNAMIC_INCORRECT_INFO = new HashMap<>(); public static final String CODEC_PREFIX_KEY = "codec-prefix"; public static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix"; public static final String MEDIA_TYPE_SEL_KEY = "media-type-sel"; @@ -228,6 +226,48 @@ public abstract class CodecTestBase { public static final int MAX_DISPLAY_HEIGHT_LAND = Math.min(MAX_DISPLAY_WIDTH_CURRENT, MAX_DISPLAY_HEIGHT_CURRENT); + public static final String HDR10_INFO_SCENE_A = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; + public static final String HDR10_INFO_SCENE_B = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; + public static final String HDR10_INFO_SCENE_C = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; + public static final String HDR10_INFO_SCENE_D = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; + + public static final String HDR10_INCORRECT_INFO_SCENE_A = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"; + public static final String HDR10_INCORRECT_INFO_SCENE_B = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 01"; + public static final String HDR10_INCORRECT_INFO_SCENE_C = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 02"; + public static final String HDR10_INCORRECT_INFO_SCENE_D = + "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 03"; + public static String mediaTypeSelKeys; public static String codecPrefix; public static String mediaTypePrefix; @@ -396,40 +436,6 @@ public abstract class CodecTestBase { HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AV1); HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AVC); HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_HEVC); - - HDR_DYNAMIC_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); - HDR_DYNAMIC_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); - HDR_DYNAMIC_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); - HDR_DYNAMIC_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); - - HDR_DYNAMIC_INCORRECT_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); - HDR_DYNAMIC_INCORRECT_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 01"); - HDR_DYNAMIC_INCORRECT_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 02"); - HDR_DYNAMIC_INCORRECT_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" - + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" - + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" - + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 03"); } static int[] combine(int[] first, int[] second) { @@ -998,6 +1004,20 @@ public abstract class CodecTestBase { return Arrays.copyOfRange(tempArray, 0, i); } + public static String byteArrayToHexString(byte[] bytes) { + final char[] hexArray = + {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + char[] hexChars = new char[bytes.length * 3]; + int v; + for (int j = 0; j < bytes.length; j++) { + v = bytes[j] & 0xFF; + hexChars[j * 3] = hexArray[v >>> 4]; + hexChars[j * 3 + 1] = hexArray[v & 0x0F]; + hexChars[j * 3 + 2] = ' '; + } + return new String(hexChars); + } + protected abstract void enqueueInput(int bufferIndex) throws IOException; protected abstract void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info); @@ -1295,19 +1315,17 @@ public abstract class CodecTestBase { } } - protected void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef) { - ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null); - assertNotNull("error! no " + hdrInfoKey + " present in format : " + fmt + "\n " - + mTestConfig + mTestEnv, hdrInfo); - if (!hdrInfoRef.equals(hdrInfo)) { + protected void validateHDRInfo(String hdrInfoKey, ByteBuffer hdrInfoRef, ByteBuffer hdrInfoTest, + Long framePts) { + if (!hdrInfoRef.equals(hdrInfoTest)) { StringBuilder msg = new StringBuilder( "################### Error Details #####################\n"); byte[] ref = new byte[hdrInfoRef.capacity()]; hdrInfoRef.get(ref); hdrInfoRef.rewind(); - byte[] test = new byte[hdrInfo.capacity()]; - hdrInfo.get(test); - hdrInfo.rewind(); + byte[] test = new byte[hdrInfoTest.capacity()]; + hdrInfoTest.get(test); + hdrInfoTest.rewind(); msg.append("ref info :- \n"); for (byte b : ref) { msg.append(String.format("%2x ", b)); @@ -1316,11 +1334,19 @@ public abstract class CodecTestBase { for (byte b : test) { msg.append(String.format("%2x ", b)); } - fail("error! mismatch seen between ref and test info of " + hdrInfoKey + "\n" - + mTestConfig + mTestEnv + msg); + fail("Frame pts " + framePts + ": error! mismatch seen between ref and test info of " + + hdrInfoKey + "\n" + mTestConfig + mTestEnv + msg); } } + protected void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef, + Long framePts) { + ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null); + assertNotNull("error! no " + hdrInfoKey + " present in format : " + fmt + "\n " + + mTestConfig + mTestEnv, hdrInfo); + validateHDRInfo(hdrInfoKey, hdrInfoRef, hdrInfo, framePts); + } + protected void setUpSurface(CodecTestActivity activity) throws InterruptedException { activity.waitTillSurfaceIsCreated(); mSurface = activity.getSurface(); diff --git a/tests/media/common/src/android/mediav2/common/cts/HDRDecoderTestBase.java b/tests/media/common/src/android/mediav2/common/cts/HDRDecoderTestBase.java index c158b240453..746ab862ba2 100644 --- a/tests/media/common/src/android/mediav2/common/cts/HDRDecoderTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/HDRDecoderTestBase.java @@ -41,21 +41,38 @@ public class HDRDecoderTestBase extends CodecDecoderTestBase { private ByteBuffer mHdrStaticInfoRef; private ByteBuffer mHdrStaticInfoStream; private ByteBuffer mHdrStaticInfoContainer; - private Map<Integer, String> mHdrDynamicInfoRef; - private Map<Integer, String> mHdrDynamicInfoStream; - private Map<Integer, String> mHdrDynamicInfoContainer; - private String mHdrDynamicInfoCurrent; + private Map<Long, String> mHdrDynamicInfoRef; + private Map<Long, String> mHdrDynamicInfoStream; + private Map<Long, String> mHdrDynamicInfoContainer; + private final ArrayList<Long> mTotalMetadataQueued = new ArrayList<>(); public HDRDecoderTestBase(String decoder, String mediaType, String testFile, - String allTestParams) { + String allTestParams) { super(decoder, mediaType, testFile, allTestParams); } + private String getMetadataForPts(Map<Long, String> dynamicInfoList, Long pts) { + final int sttsToleranceUs = 1000; + if (dynamicInfoList.containsKey(pts)) return dynamicInfoList.get(pts); + for (Map.Entry<Long, String> entry : dynamicInfoList.entrySet()) { + Long keyPts = entry.getKey(); + if (Math.abs(keyPts - pts) < sttsToleranceUs) return entry.getValue(); + } + return null; + } + + public void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { + mTotalMetadataQueued.clear(); + super.resetContext(isAsync, signalEOSWithLastFrame); + } + protected void enqueueInput(int bufferIndex) { - if (mHdrDynamicInfoContainer != null && mHdrDynamicInfoContainer.containsKey(mInputCount) - && mExtractor.getSampleSize() != -1) { - insertHdrDynamicInfo( - loadByteArrayFromString(mHdrDynamicInfoContainer.get(mInputCount))); + if (mHdrDynamicInfoContainer != null && mExtractor.getSampleSize() != -1) { + String info = getMetadataForPts(mHdrDynamicInfoContainer, mExtractor.getSampleTime()); + if (info != null) { + insertHdrDynamicInfo(loadByteArrayFromString(info)); + mTotalMetadataQueued.add(mExtractor.getSampleTime()); + } } super.enqueueInput(bufferIndex); } @@ -63,17 +80,18 @@ public class HDRDecoderTestBase extends CodecDecoderTestBase { protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { if (info.size > 0 && mHdrDynamicInfoRef != null) { MediaFormat format = mCodec.getOutputFormat(bufferIndex); - if (mHdrDynamicInfoRef.containsKey(mOutputCount)) { - mHdrDynamicInfoCurrent = mHdrDynamicInfoRef.get(mOutputCount); + String hdr10Info = getMetadataForPts(mHdrDynamicInfoRef, info.presentationTimeUs); + if (hdr10Info != null) { + validateHDRInfo(format, MediaFormat.KEY_HDR10_PLUS_INFO, + ByteBuffer.wrap(loadByteArrayFromString(hdr10Info)), + info.presentationTimeUs); } - validateHDRInfo(format, MediaFormat.KEY_HDR10_PLUS_INFO, - ByteBuffer.wrap(loadByteArrayFromString(mHdrDynamicInfoCurrent))); } super.dequeueOutput(bufferIndex, info); } public void validateHDRInfo(String hdrStaticInfoStream, String hdrStaticInfoContainer, - Map<Integer, String> hdrDynamicInfoStream, Map<Integer, String> hdrDynamicInfoContainer) + Map<Long, String> hdrDynamicInfoStream, Map<Long, String> hdrDynamicInfoContainer) throws IOException, InterruptedException { mHdrStaticInfoStream = hdrStaticInfoStream != null ? ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null; @@ -105,7 +123,7 @@ public class HDRDecoderTestBase extends CodecDecoderTestBase { assertEquals("Container hdr10+ info size and elementary stream SEI hdr10+ info" + " size are unequal \n" + mTestConfig + mTestEnv, mHdrDynamicInfoStream.size(), mHdrDynamicInfoContainer.size()); - for (Map.Entry<Integer, String> element : mHdrDynamicInfoStream.entrySet()) { + for (Map.Entry<Long, String> element : mHdrDynamicInfoStream.entrySet()) { assertTrue("Container hdr10+ info and elementary stream SEI hdr10+ info " + "frame positions are not in sync \n" + mTestConfig + mTestEnv, mHdrDynamicInfoContainer.containsKey(element.getKey())); @@ -134,10 +152,14 @@ public class HDRDecoderTestBase extends CodecDecoderTestBase { waitForAllOutputs(); if (mHdrStaticInfoRef != null) { validateHDRInfo(mCodec.getOutputFormat(), MediaFormat.KEY_HDR_STATIC_INFO, - mHdrStaticInfoRef); + mHdrStaticInfoRef, -1L); } mCodec.stop(); mCodec.release(); mExtractor.release(); + if (mHdrDynamicInfoContainer != null) { + assertEquals("Test did not queue metadata of all frames", + mHdrDynamicInfoContainer.size(), mTotalMetadataQueued.size()); + } } } diff --git a/tests/media/common/src/android/mediav2/common/cts/HDREncoderTestBase.java b/tests/media/common/src/android/mediav2/common/cts/HDREncoderTestBase.java index e6fa7c00521..34627bc232a 100644 --- a/tests/media/common/src/android/mediav2/common/cts/HDREncoderTestBase.java +++ b/tests/media/common/src/android/mediav2/common/cts/HDREncoderTestBase.java @@ -16,6 +16,7 @@ package android.mediav2.common.cts; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; @@ -25,11 +26,11 @@ import android.media.MediaFormat; import org.junit.Assume; -import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.Map; /** @@ -39,21 +40,61 @@ public class HDREncoderTestBase extends CodecEncoderTestBase { private static final String LOG_TAG = HDREncoderTestBase.class.getSimpleName(); private ByteBuffer mHdrStaticInfo; - private Map<Integer, String> mHdrDynamicInfo; + private Map<Long, String> mHdrDynamicInfo; + private ArrayList<Long> mTotalMetadataQueued; + private Map<Long, String> mHdrDynamicInfoReceived; public HDREncoderTestBase(String encoderName, String mediaType, EncoderConfigParams encCfgParams, String allTestParams) { super(encoderName, mediaType, new EncoderConfigParams[]{encCfgParams}, allTestParams); } + private String getMetadataForPts(Map<Long, String> dynamicInfoList, Long pts) { + final int roundToleranceUs = 10; + if (dynamicInfoList.containsKey(pts)) return dynamicInfoList.get(pts); + for (Map.Entry<Long, String> entry : dynamicInfoList.entrySet()) { + Long keyPts = entry.getKey(); + if (Math.abs(keyPts - pts) < roundToleranceUs) return entry.getValue(); + } + return null; + } + + public void resetContext(boolean isAsync, boolean signalEOSWithLastFrame) { + if (mTotalMetadataQueued != null) mTotalMetadataQueued.clear(); + if (mHdrDynamicInfoReceived != null) mHdrDynamicInfoReceived.clear(); + super.resetContext(isAsync, signalEOSWithLastFrame); + } + protected void enqueueInput(int bufferIndex) { - if (mHdrDynamicInfo != null && mHdrDynamicInfo.containsKey(mInputCount)) { - insertHdrDynamicInfo(loadByteArrayFromString(mHdrDynamicInfo.get(mInputCount))); + if (mHdrDynamicInfo != null) { + long pts = mInputOffsetPts + mInputCount * 1000000L / mActiveEncCfg.mFrameRate; + String info = getMetadataForPts(mHdrDynamicInfo, pts); + if (info != null) { + insertHdrDynamicInfo(loadByteArrayFromString(info)); + mTotalMetadataQueued.add(pts); + } } super.enqueueInput(bufferIndex); } - public void validateHDRInfo(String hdrStaticInfo, Map<Integer, String> hdrDynamicInfo) + protected void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + if (mHdrDynamicInfo != null) { + if (info.size > 0 && (info.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 0) { + MediaFormat outFormat = mCodec.getOutputFormat(bufferIndex); + ByteBuffer metadataBuff = + outFormat.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO, null); + if (metadataBuff != null) { + byte[] bytes = new byte[metadataBuff.remaining()]; + metadataBuff.get(bytes); + mHdrDynamicInfoReceived.put(info.presentationTimeUs, + byteArrayToHexString(bytes)); + } + } + } + super.dequeueOutput(bufferIndex, info); + } + + public void validateHDRInfo(String hdrStaticInfo, Map<Long, String> hdrDynamicInfo) throws IOException, InterruptedException { mHdrStaticInfo = hdrStaticInfo != null ? ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfo)) : null; @@ -74,10 +115,12 @@ public class HDREncoderTestBase extends CodecEncoderTestBase { int frameLimit = 4; if (mHdrDynamicInfo != null) { - Integer lastHdr10PlusFrame = - Collections.max(HDR_DYNAMIC_INFO.entrySet(), Map.Entry.comparingByKey()) + mTotalMetadataQueued = new ArrayList<>(); + mHdrDynamicInfoReceived = new HashMap(); + Long lastHdr10PlusFramePts = + Collections.max(mHdrDynamicInfo.entrySet(), Map.Entry.comparingByKey()) .getKey(); - frameLimit = lastHdr10PlusFrame + 10; + frameLimit = (int) (lastHdr10PlusFramePts * mActiveEncCfg.mFrameRate / 1000000L) + 10; } int maxNumFrames = mInputData.length / (mActiveRawRes.mWidth * mActiveRawRes.mHeight * mActiveRawRes.mBytesPerSample); @@ -98,12 +141,29 @@ public class HDREncoderTestBase extends CodecEncoderTestBase { mCodec.stop(); mCodec.release(); + + // verify if the out fmt contains HDR Static info as expected if (mHdrStaticInfo != null) { - // verify if the out fmt contains HDR Static info as expected - validateHDRInfo(fmt, MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo); + validateHDRInfo(fmt, MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo, -1L); + } + + // verify if the out fmt contains HDR Dynamic info as expected + if (mHdrDynamicInfo != null) { + assertEquals("Test did not queue metadata of all frames", mHdrDynamicInfo.size(), + mTotalMetadataQueued.size()); + for (Map.Entry<Long, String> entry : mHdrDynamicInfo.entrySet()) { + Long pts = entry.getKey(); + assertTrue("At timestamp : " + pts + "application queued hdr10+ metadata," + + " during dequeue application did not receive it in output format", + mHdrDynamicInfoReceived.containsKey(pts)); + ByteBuffer hdrInfoRef = ByteBuffer.wrap(loadByteArrayFromString(entry.getValue())); + ByteBuffer hdrInfoTest = + ByteBuffer.wrap(loadByteArrayFromString(mHdrDynamicInfoReceived.get(pts))); + validateHDRInfo(MediaFormat.KEY_HDR10_PLUS_INFO, hdrInfoRef, hdrInfoTest, pts); + } } - // verify if the muxed file contains HDR Dynamic info as expected + // verify if the muxed file contains HDR metadata as expected MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); String decoder = codecList.findDecoderForFormat(format); assertNotNull("Device advertises support for encoding " + format + " but not decoding it \n" @@ -111,10 +171,10 @@ public class HDREncoderTestBase extends CodecEncoderTestBase { HDRDecoderTestBase decoderTest = new HDRDecoderTestBase(decoder, mMediaType, mMuxedOutputFile, mAllTestParams); - decoderTest.validateHDRInfo(hdrStaticInfo, hdrStaticInfo, mHdrDynamicInfo, mHdrDynamicInfo); + decoderTest.validateHDRInfo(hdrStaticInfo, hdrStaticInfo, mHdrDynamicInfo, + mHdrDynamicInfo); if (HDR_INFO_IN_BITSTREAM_CODECS.contains(mMediaType)) { decoderTest.validateHDRInfo(hdrStaticInfo, null, mHdrDynamicInfo, null); } - new File(mMuxedOutputFile).delete(); } } diff --git a/tests/media/copy_media.sh b/tests/media/copy_media.sh index ddde84a1740..b0850ad64d1 100755 --- a/tests/media/copy_media.sh +++ b/tests/media/copy_media.sh @@ -17,7 +17,7 @@ ## script to install mediav2 test files manually adbOptions=" " -resLabel=CtsMediaV2TestCases-3.4 +resLabel=CtsMediaV2TestCases-3.5 srcDir="/tmp/$resLabel" tgtDir="/sdcard/test" usage="Usage: $0 [-h] [-s serial]" diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java index 6834b0c75e8..9dab4fc7bb7 100644 --- a/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java +++ b/tests/media/src/android/mediav2/cts/CodecDecoderValidationTest.java @@ -175,8 +175,9 @@ public class CodecDecoderValidationTest extends CodecDecoderTestBase { // video test vectors covering cdd requirements // @CddTest(requirement="5.3.1/C-1-1") - {MEDIA_TYPE_MPEG2, new String[]{"bbb_1920x1080_mpeg2_main_high.mp4"}, null, -1.0f, - -1L, -1, -1, 1920, 1080, MediaUtils.isTv() ? CODEC_ANY : CODEC_OPTIONAL}, + {MEDIA_TYPE_MPEG2, new String[]{"bbb_1920x1080_30fps_mpeg2_main_high.mp4"}, null, + -1.0f, -1L, -1, -1, 1920, 1080, + MediaUtils.isTv() ? CODEC_ANY : CODEC_OPTIONAL}, // @CddTest(requirement="5.3.2/C-1-1") {MEDIA_TYPE_H263, new String[]{"bbb_352x288_384kbps_30fps_h263_baseline_l3.mp4"}, diff --git a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java index 68cb56672a4..8e4100ec4c4 100644 --- a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java @@ -32,6 +32,7 @@ import org.junit.runners.Parameterized; import java.io.IOException; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -51,14 +52,47 @@ import java.util.Map; public class DecoderHDRInfoTest extends HDRDecoderTestBase { private static final String LOG_TAG = DecoderHDRInfoTest.class.getSimpleName(); private static final String MEDIA_DIR = WorkDir.getMediaDirString(); + public static final HashMap<Long, String> HDR_DYNAMIC_INFO_HEVC = new HashMap<>(); + public static final HashMap<Long, String> HDR_DYNAMIC_INCORRECT_INFO_HEVC = new HashMap<>(); + public static final HashMap<Long, String> HDR_DYNAMIC_INFO_AV1 = new HashMap<>(); + public static final HashMap<Long, String> HDR_DYNAMIC_INCORRECT_INFO_AV1 = new HashMap<>(); + public static final HashMap<Long, String> HDR_DYNAMIC_INFO_VP9 = new HashMap<>(); + private final String mHDRStaticInfoStream; private final String mHDRStaticInfoContainer; - private final Map<Integer, String> mHDRDynamicInfoStream; - private final Map<Integer, String> mHDRDynamicInfoContainer; + private final Map<Long, String> mHDRDynamicInfoStream; + private final Map<Long, String> mHDRDynamicInfoContainer; + + static { + HDR_DYNAMIC_INFO_HEVC.put(0L, HDR10_INFO_SCENE_A); + HDR_DYNAMIC_INFO_HEVC.put(133333L, HDR10_INFO_SCENE_B); + HDR_DYNAMIC_INFO_HEVC.put(400000L, HDR10_INFO_SCENE_C); + HDR_DYNAMIC_INFO_HEVC.put(733333L, HDR10_INFO_SCENE_D); + + HDR_DYNAMIC_INCORRECT_INFO_HEVC.put(0L, HDR10_INCORRECT_INFO_SCENE_A); + HDR_DYNAMIC_INCORRECT_INFO_HEVC.put(133333L, HDR10_INCORRECT_INFO_SCENE_B); + HDR_DYNAMIC_INCORRECT_INFO_HEVC.put(400000L, HDR10_INCORRECT_INFO_SCENE_C); + HDR_DYNAMIC_INCORRECT_INFO_HEVC.put(733333L, HDR10_INCORRECT_INFO_SCENE_D); + + HDR_DYNAMIC_INFO_AV1.put(0L, HDR10_INFO_SCENE_A); + HDR_DYNAMIC_INFO_AV1.put(133000L, HDR10_INFO_SCENE_B); + HDR_DYNAMIC_INFO_AV1.put(400000L, HDR10_INFO_SCENE_C); + HDR_DYNAMIC_INFO_AV1.put(733000L, HDR10_INFO_SCENE_D); + + HDR_DYNAMIC_INCORRECT_INFO_AV1.put(0L, HDR10_INCORRECT_INFO_SCENE_A); + HDR_DYNAMIC_INCORRECT_INFO_AV1.put(133000L, HDR10_INCORRECT_INFO_SCENE_B); + HDR_DYNAMIC_INCORRECT_INFO_AV1.put(400000L, HDR10_INCORRECT_INFO_SCENE_C); + HDR_DYNAMIC_INCORRECT_INFO_AV1.put(733000L, HDR10_INCORRECT_INFO_SCENE_D); + + HDR_DYNAMIC_INFO_VP9.put(0L, HDR10_INFO_SCENE_A); + HDR_DYNAMIC_INFO_VP9.put(200000L, HDR10_INFO_SCENE_B); + HDR_DYNAMIC_INFO_VP9.put(400000L, HDR10_INFO_SCENE_C); + HDR_DYNAMIC_INFO_VP9.put(640000L, HDR10_INFO_SCENE_D); + } public DecoderHDRInfoTest(String codecName, String mediaType, String testFile, String hdrStaticInfoStream, String hdrStaticInfoContainer, - Map<Integer, String> hdrDynamicInfoStream, Map<Integer, String> hdrDynamicInfoContainer, + Map<Long, String> hdrDynamicInfoStream, Map<Long, String> hdrDynamicInfoContainer, String allTestParams) { super(codecName, mediaType, MEDIA_DIR + testFile, allTestParams); mHDRStaticInfoStream = hdrStaticInfoStream; @@ -98,15 +132,15 @@ public class DecoderHDRInfoTest extends HDRDecoderTestBase { {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_container_av1.mkv", null, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", - null, null, HDR_DYNAMIC_INFO, null}, + null, null, HDR_DYNAMIC_INFO_HEVC, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", - null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + null, null, HDR_DYNAMIC_INFO_HEVC, HDR_DYNAMIC_INCORRECT_INFO_HEVC}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", - null, null, HDR_DYNAMIC_INFO, null}, + null, null, HDR_DYNAMIC_INFO_AV1, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", - null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + null, null, HDR_DYNAMIC_INFO_AV1, HDR_DYNAMIC_INCORRECT_INFO_AV1}, {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv", - null, null, null, HDR_DYNAMIC_INFO}, + null, null, null, HDR_DYNAMIC_INFO_VP9}, }); return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false); diff --git a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java index 0b4ed23bbf1..93170eaf96f 100644 --- a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java @@ -37,6 +37,7 @@ import org.junit.runners.Parameterized; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -56,13 +57,21 @@ import java.util.Objects; @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") public class EncoderHDRInfoTest extends HDREncoderTestBase { private static final String LOG_TAG = EncoderHDRInfoTest.class.getSimpleName(); + public static final HashMap<Long, String> HDR_DYNAMIC_INFO = new HashMap<>(); private final String mHDRStaticInfo; - private final Map<Integer, String> mHDRDynamicInfo; + private final Map<Long, String> mHDRDynamicInfo; + + static { + HDR_DYNAMIC_INFO.put(0L, HDR10_INFO_SCENE_A); + HDR_DYNAMIC_INFO.put(133333L, HDR10_INFO_SCENE_B); + HDR_DYNAMIC_INFO.put(400000L, HDR10_INFO_SCENE_C); + HDR_DYNAMIC_INFO.put(733333L, HDR10_INFO_SCENE_D); + } public EncoderHDRInfoTest(String encoderName, String mediaType, EncoderConfigParams encCfgParams, @SuppressWarnings("unused") String testLabel, - String hdrStaticInfo, Map<Integer, String> hdrDynamicInfo, String allTestParams) { + String hdrStaticInfo, Map<Long, String> hdrDynamicInfo, String allTestParams) { super(encoderName, mediaType, encCfgParams, allTestParams); mHDRStaticInfo = hdrStaticInfo; mHDRDynamicInfo = hdrDynamicInfo; diff --git a/tests/media/src/android/mediav2/cts/ExtractorTest.java b/tests/media/src/android/mediav2/cts/ExtractorTest.java index adafffe3814..10cd3e648b9 100644 --- a/tests/media/src/android/mediav2/cts/ExtractorTest.java +++ b/tests/media/src/android/mediav2/cts/ExtractorTest.java @@ -1452,8 +1452,8 @@ public class ExtractorTest { // profile and level constraints as per sec 2.3.2 of cdd /* TODO(b/159582475) exhaustiveArgsList.add(new Object[]{MediaFormat.MIMETYPE_VIDEO_MPEG2, new String[]{ - "bbb_1920x1080_mpeg2_main_high.mp4", - "bbb_1920x1080_mpeg2_main_high.mkv"}, + "bbb_1920x1080_30fps_mpeg2_main_high.mp4", + "bbb_1920x1080_30fps_mpeg2_main_high.mkv"}, MediaCodecInfo.CodecProfileLevel.MPEG2ProfileMain, MediaCodecInfo.CodecProfileLevel.MPEG2LevelHL, 1920, 1080});*/ } diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java index 6ee7e018ad9..de9f200d7a8 100644 --- a/tests/media/src/android/mediav2/cts/WorkDir.java +++ b/tests/media/src/android/mediav2/cts/WorkDir.java @@ -24,6 +24,6 @@ import android.mediav2.common.cts.WorkDirBase; */ class WorkDir extends WorkDirBase { static final String getMediaDirString() { - return getMediaDirString("CtsMediaV2TestCases-3.4"); + return getMediaDirString("CtsMediaV2TestCases-3.5"); } } 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 5925552965d..258b30f7cc9 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java @@ -23,23 +23,26 @@ import static org.junit.Assume.assumeTrue; import android.app.ActivityManager; import android.content.Context; import android.content.pm.PackageManager; -import android.graphics.Rect; +import android.hardware.display.DisplayManager; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint; import android.media.MediaFormat; import android.os.Build; import android.os.SystemProperties; +import android.util.DisplayMetrics; import android.util.Log; -import android.view.WindowManager; -import android.view.WindowMetrics; +import android.view.Display; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.ApiLevelUtil; import java.io.IOException; +import java.util.Arrays; +import java.util.Comparator; import java.util.List; +import java.util.stream.Stream; /** * Test utilities. @@ -97,14 +100,30 @@ public class Utils { } // When used from ItsService, context will be null if (context != null) { - WindowManager windowManager = context.getSystemService(WindowManager.class); - WindowMetrics metrics = windowManager.getMaximumWindowMetrics(); - Rect displayBounds = metrics.getBounds(); - int widthPixels = displayBounds.width(); - int heightPixels = displayBounds.height(); - DISPLAY_DPI = context.getResources().getConfiguration().densityDpi; - DISPLAY_LONG_PIXELS = Math.max(widthPixels, heightPixels); - DISPLAY_SHORT_PIXELS = Math.min(widthPixels, heightPixels); + DisplayManager displayManager = context.getSystemService(DisplayManager.class); + Display defaultDisplay = displayManager.getDisplay(Display.DEFAULT_DISPLAY); + Display.Mode maxResolutionDisplayMode = + Arrays.stream(displayManager.getDisplays()) + .map(Display::getSupportedModes) + .flatMap(Stream::of) + .max(Comparator.comparing(Display.Mode::getPhysicalHeight)) + .orElseThrow( + () -> new RuntimeException("Failed to determine max height")); + int maxWidthPixels = maxResolutionDisplayMode.getPhysicalWidth(); + int maxHeightPixels = maxResolutionDisplayMode.getPhysicalHeight(); + DISPLAY_LONG_PIXELS = Math.max(maxWidthPixels, maxHeightPixels); + DISPLAY_SHORT_PIXELS = Math.min(maxWidthPixels, maxHeightPixels); + + int widthPixels = defaultDisplay.getMode().getPhysicalWidth(); + int heightPixels = defaultDisplay.getMode().getPhysicalHeight(); + + DisplayMetrics metrics = context.getResources().getDisplayMetrics(); + final double widthInch = (double) widthPixels / (double) metrics.xdpi; + final double heightInch = (double) heightPixels / (double) metrics.ydpi; + final double diagonalInch = Math.sqrt(widthInch * widthInch + heightInch * heightInch); + final double maxDiagonalPixels = + Math.sqrt(maxWidthPixels * maxWidthPixels + maxHeightPixels * maxHeightPixels); + DISPLAY_DPI = (int) (maxDiagonalPixels / diagonalInch); ActivityManager activityManager = context.getSystemService(ActivityManager.class); ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); diff --git a/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java index afc2ad0c1d1..29ed734acb9 100644 --- a/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java +++ b/tests/mediapc/src/android/mediapc/cts/CodecTestBase.java @@ -935,7 +935,6 @@ class Encode extends CodecEncoderTestBase implements Callable<Double> { super(mime); mEncoderName = encoderName; mIsAsync = isAsync; - mSurface = MediaCodec.createPersistentInputSurface(); mFrameRate = frameRate; mBitrate = bitrate; mHeight = height; diff --git a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java index 507e3157704..0e295fcd712 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java @@ -63,11 +63,11 @@ public class MultiCodecPerfTestBase { static Map<String, String> mTestFiles = new HashMap<>(); static Map<String, String> m720pTestFiles = new HashMap<>(); static Map<String, String> m1080pTestFiles = new HashMap<>(); - static Map<String, String> m2160pTestFiles = new HashMap<>(); - static Map<String, String> m2160p10bitTestFiles = new HashMap<>(); + static Map<String, String> m2160pPc14TestFiles = new HashMap<>(); + static Map<String, String> m2160pPc1410bitTestFiles = new HashMap<>(); static Map<String, String> m1080pWidevineTestFiles = new HashMap<>(); - static Map<String, String> m2160pWidevineTestFiles = new HashMap<>(); - static Map<String, String> m2160p10bitWidevineTestFiles = new HashMap<>(); + static Map<String, String> m2160pPc14WidevineTestFiles = new HashMap<>(); + static Map<String, String> m2160pPc1410bitWidevineTestFiles = new HashMap<>(); static { mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AVC); @@ -89,17 +89,23 @@ public class MultiCodecPerfTestBase { m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1920x1080_4mbps_30fps_vp9.webm"); m1080pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1.mp4"); - m2160pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_3840x2160_18mbps_30fps_avc.mp4"); - m2160pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_3840x2160_12mbps_30fps_hevc.mp4"); - m2160pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_3840x2160_12mbps_30fps_vp9.webm"); - m2160pTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_3840x2160_12mbps_30fps_av1.mp4"); - - m2160p10bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, + m2160pPc14TestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, + "bbb_3840x2160_18mbps_30fps_avc.mp4"); + m2160pPc14TestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, + "bbb_3840x2160_12mbps_30fps_hevc.mp4"); + m2160pPc14TestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, + "bbb_3840x2160_12mbps_30fps_vp9.webm"); + // Limit AV1 4k tests to 1080p as per PC14 requirements + m2160pPc14TestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, + "bbb_1920x1080_4mbps_30fps_av1.mp4"); + + m2160pPc1410bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_3840x2160_12mbps_30fps_hevc_10bit.mp4"); - m2160p10bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, + m2160pPc1410bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_3840x2160_12mbps_30fps_vp9_10bit.webm"); - m2160p10bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, - "bbb_3840x2160_12mbps_30fps_av1_10bit.mp4"); + // Limit AV1 4k tests to 1080p as per PC14 requirements + m2160pPc1410bitTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, + "bbb_1920x1080_4mbps_30fps_av1_10bit.mp4"); m1080pWidevineTestFiles .put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1920x1080_6mbps_30fps_avc_cenc.mp4"); @@ -111,20 +117,22 @@ public class MultiCodecPerfTestBase { m1080pWidevineTestFiles .put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1_cenc.mp4"); - m2160pWidevineTestFiles + m2160pPc14WidevineTestFiles .put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_3840x2160_18mbps_30fps_avc_cenc.mp4"); - m2160pWidevineTestFiles + m2160pPc14WidevineTestFiles .put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_3840x2160_12mbps_30fps_hevc_cenc.mp4"); // TODO(b/230682028) // m2160pWidevineTestFiles // .put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_3840x2160_12mbps_30fps_vp9_cenc.webm"); - m2160pWidevineTestFiles - .put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_3840x2160_12mbps_30fps_av1_cenc.mp4"); + // Limit AV1 4k tests to 1080p as per PC14 requirements + m2160pPc14WidevineTestFiles + .put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1920x1080_4mbps_30fps_av1_cenc.mp4"); - m2160p10bitWidevineTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, + m2160pPc1410bitWidevineTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_3840x2160_12mbps_30fps_hevc_10bit_cenc.mp4"); - m2160p10bitWidevineTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, - "bbb_3840x2160_12mbps_30fps_av1_10bit_cenc.mp4"); + // Limit AV1 4k tests to 1080p as per PC14 requirements + m2160pPc1410bitWidevineTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, + "bbb_1920x1080_4mbps_30fps_av1_10bit_cenc.mp4"); } String mMime; @@ -172,9 +180,11 @@ public class MultiCodecPerfTestBase { int[] maxMacroBlockRates1080p = new int[mimeCodecPairs.size()]; int required4kInstances = isEncoder ? REQUIRED_MIN_CONCURRENT_4K_ENCODER_INSTANCES : REQUIRED_MIN_CONCURRENT_4K_DECODER_INSTANCES; - int required1080pInstances = - isEncoder ? REQUIRED_MIN_CONCURRENT_1080_ENCODER_INSTANCES : - REQUIRED_MIN_CONCURRENT_1080_DECODER_INSTANCES; + // when testing secure codecs limit 4k to 2 instances + if (requiredMinInstances < REQUIRED_MIN_CONCURRENT_INSTANCES) { + required4kInstances = REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES; + } + int required1080pInstances = requiredMinInstances - required4kInstances; int loopCount = 0; for (Pair<String, String> mimeCodecPair : mimeCodecPairs) { MediaCodec codec = MediaCodec.createByCodecName(mimeCodecPair.second); @@ -183,14 +193,21 @@ public class MultiCodecPerfTestBase { List<PerformancePoint> pps = cap.getVideoCapabilities().getSupportedPerformancePoints(); assertTrue(pps.size() > 0); - boolean hasVP9 = mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_VP9); + boolean hasAV1 = mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AV1); int requiredFrameRate = height > 1080 ? required4kInstances * 30 : requiredMinInstances * 30; int requiredFrameRate1080p = required1080pInstances * 30; maxInstances[loopCount] = cap.getMaxSupportedInstances(); - PerformancePoint PPRes = new PerformancePoint(width, height, requiredFrameRate); - PerformancePoint PPRes1080p = new PerformancePoint(1920, 1080, requiredFrameRate1080p); + PerformancePoint PPRes; + if (hasAV1 && height > 1080) { + // For 4k tests, if the codec is AV1 limit it to 1080p as per PC14 requirements + PPRes = new PerformancePoint(1920, 1080, requiredFrameRate); + } else { + PPRes = new PerformancePoint(width, height, requiredFrameRate); + } + PerformancePoint PPRes1080p = + new PerformancePoint(1920, 1080, requiredFrameRate1080p + requiredFrameRate); maxMacroBlockRates[loopCount] = 0; boolean supportsResolutionPerformance = false; @@ -225,48 +242,56 @@ public class MultiCodecPerfTestBase { } loopCount++; } - Arrays.sort(maxInstances); - Arrays.sort(maxFrameRates); - Arrays.sort(maxMacroBlockRates); - Arrays.sort(maxFrameRates1080p); - Arrays.sort(maxMacroBlockRates1080p); - int minOfMaxInstances = maxInstances[0]; - int minOfMaxFrameRates = maxFrameRates[0]; - int minOfMaxMacroBlockRates = maxMacroBlockRates[0]; - int minOfMaxFrameRates1080p = maxFrameRates1080p[0]; - int minOfMaxMacroBlockRates1080p = maxMacroBlockRates1080p[0]; // Calculate how many 30fps max instances it can support from it's mMaxFrameRate // amd maxMacroBlockRate. (assuming 16x16 macroblocks) if (height > 1080) { - int blocksIn4k = (3840 / 16) * (2160 / 16); - int blocksPerSecond4k = blocksIn4k * 30; - int instances4k = Math.min((int) (minOfMaxFrameRates / 30.0), - (int) (minOfMaxMacroBlockRates / blocksPerSecond4k)); - if (instances4k < required4kInstances) { - Log.e(LOG_TAG, "Less than required 4k instances supported."); - return 0; - } + int i = 0; + for (Pair<String, String> mimeCodecPair : mimeCodecPairs) { + boolean hasAV1 = mimeCodecPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AV1); + + // Limit AV1 4k to 1920x1080 as per PC14 requirements + int blocksIn4k = hasAV1 ? (1920 / 16) * (1088 / 16) : (3840 / 16) * (2160 / 16); + int blocksPerSecond4k = blocksIn4k * 30; + int instances4k = Math.min((int) (maxFrameRates[i] / 30.0), + (int) (maxMacroBlockRates[i] / blocksPerSecond4k)); + instances4k = Math.min(instances4k, maxInstances[i]); + if (instances4k < required4kInstances) { + Log.e(LOG_TAG, "Less than required 4k instances supported."); + return 0; + } - int blocksIn1080p = (1920 / 16) * (1080 / 16); - int blocksPerSecond1080p = blocksIn1080p * 30; - int instances1080p = Math.min((int) (minOfMaxFrameRates1080p / 30.0), - (int) (minOfMaxMacroBlockRates1080p / blocksPerSecond1080p)); - if (instances1080p < required1080pInstances) { - Log.e(LOG_TAG, "Less than required 1080p instances supported."); - return 0; - } + int blocksIn1080p = (1920 / 16) * (1088 / 16); + int blocksPerSecond1080p = blocksIn1080p * 30; + int instances1080p = Math.min((int) (maxFrameRates1080p[i] / 30.0), + (int) (maxMacroBlockRates1080p[i] / blocksPerSecond1080p)); + instances1080p = Math.min(instances1080p, maxInstances[i]); + if (instances1080p < required1080pInstances) { + Log.e(LOG_TAG, "Less than required 1080p instances supported."); + return 0; + } - int totalBlockRates = - (blocksIn4k * required4kInstances + blocksIn1080p * required1080pInstances) - * 30; - if(totalBlockRates < Math.max(minOfMaxMacroBlockRates, minOfMaxMacroBlockRates1080p)){ - return requiredMinInstances; + int totalBlockRates = + (blocksIn4k * required4kInstances + blocksIn1080p * required1080pInstances) + * 30; + if (totalBlockRates > Math.max(maxMacroBlockRates[i], maxMacroBlockRates1080p[i])) { + return 0; + } + i++; } - return 0; + return required4kInstances + required1080pInstances; } + + Arrays.sort(maxInstances); + Arrays.sort(maxFrameRates); + Arrays.sort(maxMacroBlockRates); + int minOfMaxInstances = maxInstances[0]; + int minOfMaxFrameRates = maxFrameRates[0]; + int minOfMaxMacroBlockRates = maxMacroBlockRates[0]; + return Math.min(minOfMaxInstances, Math.min((int) (minOfMaxFrameRates / 30.0), - (int) (minOfMaxMacroBlockRates / ((width / 16) * (height / 16)) / 30.0))); + (int) (minOfMaxMacroBlockRates / (((width + 15) / 16) * ((height + 15) / 16)) + / 30.0))); } public int getRequiredMinConcurrentInstances720p(boolean hasVP9) throws IOException { diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java index bec9fef4607..843cf105671 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java @@ -164,13 +164,13 @@ public class MultiDecoderPairPerfTest extends MultiCodecPerfTestBase { boolean bothSecure = isFirstSecure & isSecondSecure; if (bothSecure) { - testCodec(null, m2160pWidevineTestFiles, 2160, 3840, + testCodec(null, m2160pPc14WidevineTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES); } else if (onlyOneSecure) { - testCodec(m2160pTestFiles, m2160pWidevineTestFiles, 2160, 3840, + testCodec(m2160pPc14TestFiles, m2160pPc14WidevineTestFiles, 2160, 3840, REQUIRED_CONCURRENT_NON_SECURE_INSTANCES_WITH_SECURE + 1); } else { - testCodec(m2160pTestFiles, null, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES); + testCodec(m2160pPc14TestFiles, null, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES); } } @@ -193,11 +193,11 @@ public class MultiDecoderPairPerfTest extends MultiCodecPerfTestBase { boolean bothSecure = isFirstSecure & isSecondSecure; if (bothSecure) { - testCodec(null, m2160p10bitWidevineTestFiles, 2160, 3840, + testCodec(null, m2160pPc1410bitWidevineTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES); } else if (onlyOneSecure) { // 2 non-secure 4k HDR, 1 secure 4k SDR , 1 non-secure 1080p SDR - testCodec(m2160p10bitTestFiles, m2160pWidevineTestFiles, 2160, 3840, + testCodec(m2160pPc1410bitTestFiles, m2160pPc14WidevineTestFiles, 2160, 3840, REQUIRED_CONCURRENT_NON_SECURE_INSTANCES_WITH_SECURE + 1); } else { Assume.assumeFalse("Skipping regular performance tests for pair of non-secure decoders", diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java index e052c454c34..e037f17db19 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java @@ -131,10 +131,10 @@ public class MultiDecoderPerfTest extends MultiCodecPerfTestBase { Assume.assumeTrue(Utils.isUPerfClass() || !Utils.isPerfClass()); if (isSecureSupportedCodec(mDecoderName, mMime)) { - testCodec(m2160pWidevineTestFiles, 2160, 3840, + testCodec(m2160pPc14WidevineTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES); } else { - testCodec(m2160pTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES); + testCodec(m2160pPc14TestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES); } } @@ -150,7 +150,7 @@ public class MultiDecoderPerfTest extends MultiCodecPerfTestBase { Assume.assumeTrue(Utils.isUPerfClass() || !Utils.isPerfClass()); Assume.assumeTrue("Skipping regular performance tests for non-secure codecs", isSecureSupportedCodec(mDecoderName, mMime)); - testCodec(m2160p10bitWidevineTestFiles, 2160, 3840, + testCodec(m2160pPc1410bitWidevineTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_SECURE_INSTANCES); } diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java index b4c52cda44f..ef17cf2c3ac 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java @@ -143,6 +143,8 @@ public class MultiEncoderPairPerfTest extends MultiCodecPerfTestBase { int maxInstances = checkAndGetMaxSupportedInstancesForCodecCombinations(height, width, mimeEncoderPairs, true, requiredMinInstances); double achievedFrameRate = 0.0; + boolean firstPairAV1 = mFirstPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AV1); + boolean secondPairAV1 = mSecondPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AV1); if (maxInstances >= requiredMinInstances) { int secondPairInstances = maxInstances / 2; int firstPairInstances = maxInstances - secondPairInstances; @@ -152,27 +154,33 @@ public class MultiEncoderPairPerfTest extends MultiCodecPerfTestBase { List<Encode> testList = new ArrayList<>(); if (height > 1080) { for (int i = 0; i < firstPairInstances1080p; i++) { - testList.add( - new Encode(mFirstPair.first, mFirstPair.second, mIsAsync, 1080, 1920, - 30, 10000000)); + testList.add(new Encode(mFirstPair.first, mFirstPair.second, mIsAsync, 1080, + 1920, 30, 10000000)); } for (int i = 0; i < secondPairInstances1080p; i++) { - testList.add( - new Encode(mSecondPair.first, mSecondPair.second, mIsAsync, 1080, 1920, - 30, 10000000)); + testList.add(new Encode(mSecondPair.first, mSecondPair.second, mIsAsync, 1080, + 1920, 30, 10000000)); } firstPairInstances -= firstPairInstances1080p; secondPairInstances -= secondPairInstances1080p; } for (int i = 0; i < firstPairInstances; i++) { - testList.add( - new Encode(mFirstPair.first, mFirstPair.second, mIsAsync, height, width, 30, - bitrate)); + if (height > 1080 && firstPairAV1) { + testList.add(new Encode(mFirstPair.first, mFirstPair.second, mIsAsync, 1080, + 1920, 30, 10000000)); + } else { + testList.add(new Encode(mFirstPair.first, mFirstPair.second, mIsAsync, height, + width, 30, bitrate)); + } } for (int i = 0; i < secondPairInstances; i++) { - testList.add( - new Encode(mSecondPair.first, mSecondPair.second, mIsAsync, height, width, - 30, bitrate)); + if (height > 1080 && secondPairAV1) { + testList.add(new Encode(mSecondPair.first, mSecondPair.second, mIsAsync, 1080, + 1920, 30, 10000000)); + } else { + testList.add(new Encode(mSecondPair.first, mSecondPair.second, mIsAsync, height, + width, 30, bitrate)); + } } List<Future<Double>> resultList = pool.invokeAll(testList); for (Future<Double> result : resultList) { diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java index b5d90e3eb48..79675d5883f 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java @@ -124,6 +124,7 @@ public class MultiEncoderPerfTest extends MultiCodecPerfTestBase { int maxInstances = checkAndGetMaxSupportedInstancesForCodecCombinations(height, width, mimeEncoderPairs, true, requiredMinInstances); double achievedFrameRate = 0.0; + boolean hasAV1 = mMime.equals(MediaFormat.MIMETYPE_VIDEO_AV1); if (maxInstances >= requiredMinInstances) { ExecutorService pool = Executors.newFixedThreadPool(maxInstances); List<Encode> testList = new ArrayList<>(); @@ -131,8 +132,13 @@ public class MultiEncoderPerfTest extends MultiCodecPerfTestBase { int instances4k = maxInstances / 3; int instances1080p = maxInstances - instances4k; for (int i = 0; i < instances4k; i++) { - testList.add( - new Encode(mMime, mEncoderName, mIsAsync, height, width, 30, bitrate)); + if (hasAV1) { + testList.add(new Encode(mMime, mEncoderName, mIsAsync, 1080, 1920, 30, + 10000000)); + } else { + testList.add(new Encode(mMime, mEncoderName, mIsAsync, height, width, 30, + bitrate)); + } } for (int i = 0; i < instances1080p; i++) { testList.add( diff --git a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java index fe5669b8fcf..6fa052fec8d 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java @@ -146,7 +146,7 @@ public class MultiTranscoderPerfTest extends MultiCodecPerfTestBase { @CddTest(requirements = {"2.2.7.1/5.1/H-1-5", "2.2.7.1/5.1/H-1-6"}) public void test4k() throws Exception { Assume.assumeTrue(Utils.isUPerfClass() || !Utils.isPerfClass()); - testCodec(m2160pTestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES, false); + testCodec(m2160pPc14TestFiles, 2160, 3840, REQUIRED_MIN_CONCURRENT_INSTANCES, false); } /** @@ -164,7 +164,7 @@ public class MultiTranscoderPerfTest extends MultiCodecPerfTestBase { Assume.assumeFalse("Skip HBD tests for avc", mDecoderPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AVC) || mEncoderPair.first.equals(MediaFormat.MIMETYPE_VIDEO_AVC)); - testCodec(m2160p10bitTestFiles, 2160, 3840, 3, true); + testCodec(m2160pPc1410bitTestFiles, 2160, 3840, 3, true); } private void testCodec(Map<String, String> testFiles, int height, int width, diff --git a/tests/mediapc/src/android/mediapc/cts/PlaybackFrameDrop.java b/tests/mediapc/src/android/mediapc/cts/PlaybackFrameDrop.java index 7861d1c5d1d..393d9044b76 100644 --- a/tests/mediapc/src/android/mediapc/cts/PlaybackFrameDrop.java +++ b/tests/mediapc/src/android/mediapc/cts/PlaybackFrameDrop.java @@ -21,18 +21,23 @@ import static android.mediapc.cts.FrameDropTestBase.DECODE_31S; import android.media.MediaCodec; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.util.Pair; import android.view.Surface; import java.io.File; import java.nio.ByteBuffer; import java.util.ArrayList; - +import java.util.LinkedList; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * The following class calculates the frame drops for the given array of testFiles playback. * It will do playback for at least 30 seconds worth of input data or for utmost 31 seconds. * If input reaches eos, it will rewind the input to start position. */ public class PlaybackFrameDrop extends CodecDecoderTestBase { + private static final int AV1_INITIAL_DELAY = 8; private final String mDecoderName; private final String[] mTestFiles; private final int mEachFrameTimeIntervalUs; @@ -49,6 +54,62 @@ public class PlaybackFrameDrop extends CodecDecoderTestBase { private long mDecodeStartTimeMs; private int mSampleIndex; private int mMaxNumFrames; + private int mInitialDelay; + + private OutputHandler mOutputHandler; + private Thread mThread; + + class OutputHandler implements Runnable { + private final LinkedList<Pair<Integer, MediaCodec.BufferInfo>> mQueue = new LinkedList<>(); + private boolean mStop = false; + private final Lock mLock = new ReentrantLock(); + private final Condition mCondition = mLock.newCondition(); + + private Pair<Integer, MediaCodec.BufferInfo> getOutput() throws InterruptedException { + Pair<Integer, MediaCodec.BufferInfo> element = null; + mLock.lock(); + while (!mStop) { + if (mQueue.isEmpty()) { + mCondition.await(); + } else { + element = mQueue.remove(0); + break; + } + } + mLock.unlock(); + return element; + } + + @Override + public void run() { + try { + while (true) { + Pair<Integer, MediaCodec.BufferInfo> element = getOutput(); + if (element != null) { + releaseOutput(element.first, element.second); + } else { + break; + } + } + } catch (InterruptedException e) { + // ignore + } + } + + public void add(int bufferIndex, MediaCodec.BufferInfo info) { + mLock.lock(); + mQueue.add(new Pair<>(bufferIndex, info)); + mCondition.signal(); + mLock.unlock(); + } + + public void stop() throws Exception { + mLock.lock(); + mStop = true; + mCondition.signal(); + mLock.unlock(); + } + } PlaybackFrameDrop(String mime, String decoderName, String[] testFiles, Surface surface, int frameRate, boolean isAsync) { @@ -63,9 +124,13 @@ public class PlaybackFrameDrop extends CodecDecoderTestBase { mMaxPts = 0; mSampleIndex = 0; mFrameDropCount = 0; - // Decode for 30 seconds - mMaxNumFrames = frameRate * 30; mBufferInfos = new ArrayList<>(); + // When testing AV1, because of super frames, we allow initial few frames to be delayed. + mInitialDelay = mime.equals(MediaFormat.MIMETYPE_VIDEO_AV1) ? AV1_INITIAL_DELAY : 0; + // Decode for 30 seconds + mMaxNumFrames = frameRate * 30 + mInitialDelay + 1; + mOutputHandler = new OutputHandler(); + mThread = new Thread(mOutputHandler); } private MediaFormat createInputList(MediaFormat format, ByteBuffer buffer, @@ -149,11 +214,14 @@ public class PlaybackFrameDrop extends CodecDecoderTestBase { mCodec = MediaCodec.createByCodecName(mDecoderName); configureCodec(formats.get(0), mIsAsync, false, false); + mThread.start(); mCodec.start(); mDecodeStartTimeMs = System.currentTimeMillis(); doWork(Integer.MAX_VALUE); queueEOS(); waitForAllOutputs(); + mOutputHandler.stop(); + mThread.join(); mCodec.stop(); mCodec.release(); return mFrameDropCount; @@ -193,15 +261,21 @@ public class PlaybackFrameDrop extends CodecDecoderTestBase { @Override void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + mOutputHandler.add(bufferIndex, info); if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { mSawOutputEOS = true; } + } + void releaseOutput(int bufferIndex, MediaCodec.BufferInfo info) { // We will limit the playback to 60 fps using the system timestamps. long nowUs = System.nanoTime() / 1000; + if (mOutputCount == 0) { - mRenderStartTimeUs = nowUs; - mCodec.releaseOutputBuffer(bufferIndex, true); - } else if (nowUs > getRenderTimeUs(mOutputCount + 1)) { + // delay rendering the first frame by the specific delay + mRenderStartTimeUs = nowUs + mInitialDelay * mEachFrameTimeIntervalUs; + } + + if (nowUs > getRenderTimeUs(mOutputCount + 1)) { // If the current sample timeStamp is greater than the actual presentation timeStamp // of the next sample, we will consider it as a frame drop and don't render. mFrameDropCount++; diff --git a/tests/mocking/AndroidTest.xml b/tests/mocking/AndroidTest.xml index de923261d67..dbc66f5bef9 100644 --- a/tests/mocking/AndroidTest.xml +++ b/tests/mocking/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for Mockito mocking test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="mocking" /> + <option name="config-descriptor:metadata" key="component" value="devtools" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/mocking/debuggable/AndroidTest.xml b/tests/mocking/debuggable/AndroidTest.xml index 33240ef2bf4..b1588c70f57 100644 --- a/tests/mocking/debuggable/AndroidTest.xml +++ b/tests/mocking/debuggable/AndroidTest.xml @@ -17,7 +17,7 @@ <configuration description="Config for Mockito mocking (while debuggable) test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="mocking" /> + <option name="config-descriptor:metadata" key="component" value="devtools" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/mocking/inline/AndroidTest.xml b/tests/mocking/inline/AndroidTest.xml index d78c8cf19df..ccd1371c597 100644 --- a/tests/mocking/inline/AndroidTest.xml +++ b/tests/mocking/inline/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for Mockito inline mocking test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="mocking" /> + <option name="config-descriptor:metadata" key="component" value="devtools" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java index 136211f5229..4b7e402e164 100644 --- a/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java +++ b/tests/signature/api-check/src/java/android/signature/cts/api/SignatureTest.java @@ -89,7 +89,7 @@ public class SignatureTest extends AbstractSignatureTest { * * <p>Will check the entire API, and then report the complete list of failures</p> */ - @Test + @Test(timeout = 600000) public void testRuntimeCompatibilityWithCurrentApi() { runWithTestResultObserver(mResultObserver -> { ApiComplianceChecker complianceChecker = diff --git a/tests/signature/intent-check/AndroidTest.xml b/tests/signature/intent-check/AndroidTest.xml index 7dbf56a4042..a9cda0b3635 100644 --- a/tests/signature/intent-check/AndroidTest.xml +++ b/tests/signature/intent-check/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS Intent Signature test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="systems" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/storageaccess/Android.bp b/tests/storageaccess/Android.bp new file mode 100644 index 00000000000..f2acdc22171 --- /dev/null +++ b/tests/storageaccess/Android.bp @@ -0,0 +1,50 @@ +// Copyright (C) 2023 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 { + name: "CtsStorageAccessTestCases", + defaults: ["cts_defaults"], + + static_libs: [ + // Among other things compatibility-device-util-axt "includes": androidx.test.core, + // androidx.core_core, androidx.test.ext.junit, androidx.test.rules, + // androidx.test.uiautomator_uiautomator + "compatibility-device-util-axt", + ], + libs: [ + "android.test.runner", + "android.test.base", + ], + srcs: [ + "src/**/*.java", + "src/**/*.kt" + ], + + // Need to run this on Android R and above. + min_sdk_version: "30", + sdk_version: "test_current", + dex_preopt: { enabled: false }, + optimize: { enabled: false }, + + // Tag this module as a CTS and MTS test artifact. + test_suites: [ + "cts", + "general-tests", + "mts-documentsui", + ], +} diff --git a/tests/storageaccess/AndroidManifest.xml b/tests/storageaccess/AndroidManifest.xml new file mode 100644 index 00000000000..c06b1900793 --- /dev/null +++ b/tests/storageaccess/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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.storageaccess.cts"> + + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + + <application> + <uses-library android:name="android.test.runner"/> + + <activity android:name="android.storageaccess.cts.ClientActivity" + android:exported="true"/> + + <!-- Manifest merging is disabled for the CtsStorageAccessTestCases test module, so we + declare activities used by the androidx.test.core library "manually". --> + <activity android:name="androidx.test.core.app.InstrumentationActivityInvoker$EmptyActivity" + android:exported="true"/> + + </application> + + <!-- Self-instrumenting test package. --> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:label="Storage Access CTS Tests" + android:targetPackage="android.storageaccess.cts"> + </instrumentation> +</manifest> diff --git a/tests/storageaccess/AndroidTest.xml b/tests/storageaccess/AndroidTest.xml new file mode 100644 index 00000000000..4a5cd14a432 --- /dev/null +++ b/tests/storageaccess/AndroidTest.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2023 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. +--> +<configuration description="Config for Storage Access CTS Tests"> + <option name="test-suite-tag" value="cts" /> + + <option name="config-descriptor:metadata" key="component" value="framework" /> + + <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> + <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> + <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> + + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <!-- com.android.tradefed.config.ConfigurationException: CtsStorageAccessTestCases.config: + class com.android.tradefed.targetprep.DeviceSetup needs to be configured with...--> + <option name="force-skip-system-props" value="true" /> + <!-- ... in *TS. --> + + <option name="restore-settings" value="true" /> + <!-- settings put global verifier_engprod 1 --> + <option name="set-global-setting" key="verifier_engprod" value="1" /> + <!-- settings put global verifier_verify_adb_installs 0 --> + <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> + <!-- settings put system screen_off_timeout -1 --> + <option name="set-system-setting" key="screen_off_timeout" value="-1" /> + <!-- settings put secure sleep_timeout -1 --> + <option name="set-secure-setting" key="sleep_timeout" value="-1" /> + + <!-- setprop debug.wm.disable_deprecated_abi_dialog 1 --> + <!-- Do NOT use the "set-property" option: it prompts DeviceSetup to reboot the device --> + <option name="run-command" value="setprop debug.wm.disable_deprecated_abi_dialog 1" /> + + <!-- locksettings set-disabled true --> + <option name="run-command" value="locksettings set-disabled true" /> + </target_preparer> + + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="test-file-name" value="CtsStorageAccessTestCases.apk" /> + </target_preparer> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest" > + <option name="package" value="android.storageaccess.cts" /> + </test> + +</configuration> diff --git a/tests/storageaccess/OWNERS b/tests/storageaccess/OWNERS new file mode 100644 index 00000000000..cdda1bbcc89 --- /dev/null +++ b/tests/storageaccess/OWNERS @@ -0,0 +1,11 @@ +# "Android > Android OS & Apps > Framework (Java + Native) > File Management" bug component +# Bug component: 46626 + +# Primary. +sergeynv@google.com + +# Secondary. +tylersaunders@google.com + +# Only if Primary and Secondary are unavailable. +shikhamalhotra@google.com diff --git a/tests/storageaccess/src/android/storageaccess/cts/ActivityScenarioExt.kt b/tests/storageaccess/src/android/storageaccess/cts/ActivityScenarioExt.kt new file mode 100644 index 00000000000..7f0e39dd031 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/ActivityScenarioExt.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2023 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.storageaccess.cts + +import android.content.Intent +import androidx.test.core.app.ActivityScenario +import java.util.concurrent.CompletableFuture + +fun launchClientActivity(block: (ActivityScenario<ClientActivity>) -> Unit) = + ActivityScenario.launch(ClientActivity::class.java).use(block) + +fun ActivityScenario<ClientActivity>.startActivityForFutureResult( + intent: Intent, + requestCode: Int +): CompletableFuture<ClientActivity.Result> { + val futureResult = CompletableFuture<ClientActivity.Result>() + onActivity { activity -> + // Careful: UI thread! + activity.startActivityForFutureResult(intent, requestCode, futureResult) + } + return futureResult +}
\ No newline at end of file diff --git a/tests/storageaccess/src/android/storageaccess/cts/ClientActivity.kt b/tests/storageaccess/src/android/storageaccess/cts/ClientActivity.kt new file mode 100644 index 00000000000..4192b1c6829 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/ClientActivity.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 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.storageaccess.cts + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD +import android.view.WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON +import android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON +import java.util.concurrent.CompletableFuture + + +class ClientActivity : Activity() { + private var pendingActivityResult: Boolean = false + private var activityResultFuture: CompletableFuture<Result>? = null + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + window.addFlags(FLAG_KEEP_SCREEN_ON or FLAG_TURN_SCREEN_ON or FLAG_DISMISS_KEYGUARD) + } + + fun startActivityForFutureResult( + intent: Intent, + requestCode: Int, + future: CompletableFuture<Result> + ) { + log("ClientActivity.startActivityFor_Future_Result() " + + "requestCode=$requestCode intent=$intent") + super.startActivityForResult(intent, requestCode) + activityResultFuture = future + } + + override fun startActivityForResult(intent: Intent?, requestCode: Int, options: Bundle?) { + if (pendingActivityResult) { + // To avoid silly mistakes let's make sure we do not try to start multiple activities + // for result at the same time. + error("Cannot startActivityForResult(): already waiting for another result.") + } + pendingActivityResult = true + super.startActivityForResult(intent, requestCode, options) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + val result = Result(requestCode, resultCode, data) + log("ClientActivity.onActivityResult(): $result") + + pendingActivityResult = false + + activityResultFuture?.apply { complete(result) } + activityResultFuture = null + } + + data class Result(val requestCode: Int, val resultCode: Int, val data: Intent?) +}
\ No newline at end of file diff --git a/tests/storageaccess/src/android/storageaccess/cts/Constants.kt b/tests/storageaccess/src/android/storageaccess/cts/Constants.kt new file mode 100644 index 00000000000..efdb6d18f03 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/Constants.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2023 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. + */ + +@file:JvmName("Constants") + +package android.storageaccess.cts + +const val DEBUG = false diff --git a/tests/storageaccess/src/android/storageaccess/cts/LogUtils.kt b/tests/storageaccess/src/android/storageaccess/cts/LogUtils.kt new file mode 100644 index 00000000000..40c4aff6225 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/LogUtils.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2023 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.storageaccess.cts + +import android.util.Log + +private const val TAG = "CtsStorageAccessTestCases" + +fun log(message: String) = + Log.d(TAG, if (DEBUG) "[${Thread.currentThread()}] $message" else message) diff --git a/tests/storageaccess/src/android/storageaccess/cts/tests/ScopedDirectoryAccessClientTest.kt b/tests/storageaccess/src/android/storageaccess/cts/tests/ScopedDirectoryAccessClientTest.kt new file mode 100644 index 00000000000..f148de78cc1 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/tests/ScopedDirectoryAccessClientTest.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2023 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.storageaccess.cts.tests + +import android.app.Activity.RESULT_CANCELED +import android.content.Context +import android.os.Environment +import android.os.storage.StorageManager +import android.os.storage.StorageVolume +import android.storageaccess.cts.launchClientActivity +import android.storageaccess.cts.log +import android.storageaccess.cts.startActivityForFutureResult +import com.android.compatibility.common.util.ApiTest +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Test +import java.util.concurrent.TimeUnit + +/** Test cases for [StorageVolume.createAccessIntent]. */ +class ScopedDirectoryAccessClientTest : TestBase() { + + @Test + @ApiTest(apis = ["android.os.storage.StorageVolume#createAccessIntent"]) + fun test_createAccessIntent_invalidPaths() { + val invalidPaths = listOf( + "", + "/dev/null", + "/../", + "/HiddenStuff") + + volumes.forEach { volume -> + invalidPaths.forEach { path -> + assertNull("Should NOT be able get access intent for '$path' on $volume", + volume.createAccessIntent(path)) + } + } + + // Also test root of the primary volume. + assertNull("Should NOT be able get the Access Intent for root on the primary volume", + primaryVolume.createAccessIntent(/* root */ null)) + } + + @Test + @ApiTest(apis = ["android.os.storage.StorageVolume#createAccessIntent"]) + fun test_accessIntentActivityCancelled_ForAllVolumesAndDirectories() { + val paths = listOf( + null, // Root. Will skip for the primary volume. + Environment.DIRECTORY_MUSIC, + Environment.DIRECTORY_PODCASTS, + Environment.DIRECTORY_RINGTONES, + Environment.DIRECTORY_NOTIFICATIONS, + Environment.DIRECTORY_PICTURES, + Environment.DIRECTORY_MOVIES, + Environment.DIRECTORY_DOWNLOADS, + Environment.DIRECTORY_DCIM, + Environment.DIRECTORY_DOCUMENTS) + + // We will be incrementing this every time we startActivityForResult(). + var requestCode = 100 + + launchClientActivity { scenario -> + volumes.forEach { volume -> + paths.forEach innerLoop@ { path -> + // Skip root on the primary volume for which we don't get the access intent: + // see test_createAccessIntent_invalidPaths above. + if (volume.isPrimary && path == null) return@innerLoop + + val intent = volume.createAccessIntent(path) + assertNotNull("Could NOT get access intent for '$path' on $volume", intent) + + log("Launching Access Intent for '$path' on $volume: ${intent!!}") + // Launch the access intent "for result", with a unique (incremented) + // request code. + val futureResult = scenario.startActivityForFutureResult(intent, ++requestCode) + + // We expect the "target" activity to send RESULT_CANCELED right away, so + // waiting for 5sec should be enough. + futureResult.get(5, TimeUnit.SECONDS).let { + assertEquals(/* expected */ requestCode, /* actual */ it.requestCode) + assertEquals(/* expected */ RESULT_CANCELED, /* actual */ it.resultCode) + assertNull(it.data) + } + } + } + + // Clean up the ActivityScenario (makes sure the Activity is "finished") + scenario.close() + } + } + + private val storageManager: StorageManager + get() = context.getSystemService(Context.STORAGE_SERVICE) as StorageManager + + private val volumes: List<StorageVolume> + get() = storageManager.storageVolumes.takeUnless { it.isEmpty() } + ?: error("Could not retrieve storage volumes") + + private val primaryVolume: StorageVolume + get() = storageManager.primaryStorageVolume +}
\ No newline at end of file diff --git a/tests/storageaccess/src/android/storageaccess/cts/tests/TestBase.kt b/tests/storageaccess/src/android/storageaccess/cts/tests/TestBase.kt new file mode 100644 index 00000000000..f4e0ddce823 --- /dev/null +++ b/tests/storageaccess/src/android/storageaccess/cts/tests/TestBase.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2023 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.storageaccess.cts.tests + +import android.content.pm.PackageManager.FEATURE_AUTOMOTIVE +import android.content.pm.PackageManager.FEATURE_LEANBACK +import android.content.pm.PackageManager.FEATURE_TELEVISION +import android.content.pm.PackageManager.FEATURE_WATCH +import android.storageaccess.cts.DEBUG +import android.storageaccess.cts.log +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.uiautomator.UiDevice +import org.junit.After +import org.junit.Assume.assumeFalse +import org.junit.Before + +/** Base for the test classes. */ +abstract class TestBase { + private val instrumentation = InstrumentationRegistry.getInstrumentation()!! + private val device = UiDevice.getInstance(instrumentation) + + protected val context = instrumentation.context!! + protected val contentResolver + get() = context.contentResolver!! + + @Before + fun setUp_TestBase() { + if (DEBUG) log("setUp_TestBase") + + with(context.packageManager) { + // Make sure we are running neither on a TV... + assumeFalse(hasSystemFeature(FEATURE_TELEVISION) || hasSystemFeature(FEATURE_LEANBACK)) + // nor on a watch... + assumeFalse(hasSystemFeature(FEATURE_WATCH)) + // nor in a car. + assumeFalse(hasSystemFeature(FEATURE_AUTOMOTIVE)) + } + + device.wakeUp() + device.executeShellCommand("wm dismiss-keyguard") + } + + @After + fun tearDown_TestBase() { + if (DEBUG) log("tearDown_TestBase") + } + + protected fun executeShellCommand(cmd: String): String { + if (DEBUG) log("executeShellCommand '$cmd'") + val output = device.executeShellCommand(cmd) + if (DEBUG) log("output: '$output'") + return output + } +}
\ No newline at end of file diff --git a/tests/suspendapps/permission/AndroidTest.xml b/tests/suspendapps/permission/AndroidTest.xml index e17ccd869fe..8ad9ba94579 100644 --- a/tests/suspendapps/permission/AndroidTest.xml +++ b/tests/suspendapps/permission/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Suspend Apps test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework"/> + <option name="config-descriptor:metadata" key="component" value="packagemanager"/> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/suspendapps/tests/AndroidTest.xml b/tests/suspendapps/tests/AndroidTest.xml index 6eaf9692914..1c28140627f 100644 --- a/tests/suspendapps/tests/AndroidTest.xml +++ b/tests/suspendapps/tests/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Suspend Apps test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework"/> + <option name="config-descriptor:metadata" key="component" value="packagemanager"/> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/app/AndroidTest.xml b/tests/tests/app/AndroidTest.xml index 7e323211264..5e4e17e754f 100644 --- a/tests/tests/app/AndroidTest.xml +++ b/tests/tests/app/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Configuration for app Tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="framework" /> <option name="not-shardable" value="true" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/tests/tests/appcomponentfactory/AndroidTest.xml b/tests/tests/appcomponentfactory/AndroidTest.xml index 69f53471cf5..f2169e8487f 100644 --- a/tests/tests/appcomponentfactory/AndroidTest.xml +++ b/tests/tests/appcomponentfactory/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Configuration for app Tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/appenumeration/AndroidTest.xml b/tests/tests/appenumeration/AndroidTest.xml index 92e1358f784..bf57f213187 100644 --- a/tests/tests/appenumeration/AndroidTest.xml +++ b/tests/tests/appenumeration/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for app enumeration CTS test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/car/src/android/car/cts/CarInputTest.java b/tests/tests/car/src/android/car/cts/CarInputTest.java index 2611f98eac6..4086d46132c 100644 --- a/tests/tests/car/src/android/car/cts/CarInputTest.java +++ b/tests/tests/car/src/android/car/cts/CarInputTest.java @@ -50,6 +50,7 @@ import android.view.MotionEvent; import androidx.lifecycle.Lifecycle; import androidx.test.core.app.ActivityScenario; +import androidx.test.filters.FlakyTest; import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.CddTest; @@ -68,6 +69,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.BiConsumer; @RunWith(AndroidJUnit4.class) +@FlakyTest(bugId = 279829443) public class CarInputTest extends AbstractCarTestCase { private static final String TAG = CarInputTest.class.getSimpleName(); private static final long ACTIVITY_WAIT_TIME_OUT_MS = 10_000L; diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java index d7d28fad855..117090aac4d 100644 --- a/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java +++ b/tests/tests/carrierapi/src/android/carrierapi/cts/CarrierApiTest.java @@ -39,6 +39,7 @@ import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManager; import android.database.Cursor; import android.net.Uri; import android.os.AsyncTask; @@ -1383,6 +1384,10 @@ public class CarrierApiTest extends BaseCarrierApiTest { @Test public void testEapAkaAuthentication() { + // Wear devices do not yet support EapAkaAuthentication, so skip this test for now + if (isWear()) { + return; + } assumeTrue( "testEapAkaAuthentication requires a 2021 CTS UICC or newer", UiccUtil.uiccHasCertificate(CTS_UICC_2021)); @@ -1434,4 +1439,8 @@ public class CarrierApiTest extends BaseCarrierApiTest { mTelephonyManager.getNetworkSlicingConfiguration( AsyncTask.SERIAL_EXECUTOR, resultFuture::complete); } + + private boolean isWear() { + return getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH); + } } diff --git a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml index 64808d3ac8e..f28af37c6c8 100644 --- a/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml +++ b/tests/tests/classloaderfactory/test-memcl/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Configuration for app Tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml index b457fd46bad..9d6bc151464 100644 --- a/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml +++ b/tests/tests/classloaderfactory/test-pathcl/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Configuration for app Tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="misc" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/tests/tests/content/AndroidTest.xml b/tests/tests/content/AndroidTest.xml index 7d9727e4104..2f9bc651d6d 100644 --- a/tests/tests/content/AndroidTest.xml +++ b/tests/tests/content/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS Content test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> diff --git a/tests/tests/content/TEST_MAPPING b/tests/tests/content/TEST_MAPPING index 65b6edf08a7..fc008e1edae 100644 --- a/tests/tests/content/TEST_MAPPING +++ b/tests/tests/content/TEST_MAPPING @@ -1,5 +1,5 @@ { - "presubmit-large": [ + "postsubmit": [ { "name": "CtsContentTestCases", "options": [ diff --git a/tests/tests/content/pm/SecureFrp/AndroidTest.xml b/tests/tests/content/pm/SecureFrp/AndroidTest.xml index a5e416d6c60..35e8d84e407 100644 --- a/tests/tests/content/pm/SecureFrp/AndroidTest.xml +++ b/tests/tests/content/pm/SecureFrp/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Runs the secure FRP install tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <!-- Instant apps can't install packages. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java index ada43ae4917..dc89b8c6a7a 100644 --- a/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java +++ b/tests/tests/content/src/android/content/cts/AvailableIntentsTest.java @@ -502,9 +502,10 @@ public class AvailableIntentsTest extends AndroidTestCase { } public void testPowerUsageSummarySettings() { - if(FeatureUtil.isWatch()){ + if (FeatureUtil.isWatch() || FeatureUtil.isAutomotive()) { return; } + if (isBatteryPresent()) { assertCanBeHandled(new Intent(Intent.ACTION_POWER_USAGE_SUMMARY)); } diff --git a/tests/tests/display/src/android/display/cts/DisplayManagerTest.java b/tests/tests/display/src/android/display/cts/DisplayManagerTest.java index 670ca101e93..b85d5bbacdf 100644 --- a/tests/tests/display/src/android/display/cts/DisplayManagerTest.java +++ b/tests/tests/display/src/android/display/cts/DisplayManagerTest.java @@ -17,7 +17,7 @@ package android.display.cts; import static android.view.Display.DEFAULT_DISPLAY; -import static android.view.WindowInsets.Type.statusBars; +import static android.view.WindowInsets.Type.systemBars; import static org.junit.Assert.assertTrue; @@ -45,6 +45,7 @@ import android.view.cts.surfacevalidator.SaveBitmapHelper; import android.widget.FrameLayout; import androidx.annotation.Nullable; +import androidx.core.view.WindowCompat; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.platform.app.InstrumentationRegistry; @@ -59,7 +60,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; public class DisplayManagerTest { - private static final String TAG = "MediaProjectionGlobalTest"; + private static final String TAG = "DisplayManagerTest"; @Rule public TestName mTestName = new TestName(); @@ -74,7 +75,7 @@ public class DisplayManagerTest { private int mNumRetries; - final HandlerThread mWorkerThread = new HandlerThread("MediaProjectGlobalTest"); + final HandlerThread mWorkerThread = new HandlerThread("DisplayManagerTest"); private VirtualDisplay mVirtualDisplay; @@ -200,6 +201,9 @@ public class DisplayManagerTest { setContentView(mFrameLayout, layoutParams); + // Prevent certain devices from adding a left and right border + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + mFrameLayout.getViewTreeObserver().addOnWindowAttachListener( new ViewTreeObserver.OnWindowAttachListener() { @Override @@ -232,14 +236,14 @@ public class DisplayManagerTest { int testAreaWidth = mFrameLayout.getWidth(); int testAreaHeight = mFrameLayout.getHeight(); - Insets statusBarInsets = getWindow() + Insets systemBarInsets = getWindow() .getDecorView() .getRootWindowInsets() - .getInsets(statusBars()); + .getInsets(systemBars()); - return new Rect(statusBarInsets.left, statusBarInsets.top, - testAreaWidth - statusBarInsets.right, - testAreaHeight - statusBarInsets.bottom); + return new Rect(systemBarInsets.left, systemBarInsets.top, + testAreaWidth - systemBarInsets.right, + testAreaHeight - systemBarInsets.bottom); } } } diff --git a/tests/tests/gesture/AndroidTest.xml b/tests/tests/gesture/AndroidTest.xml index 5a0d4869de0..66d7d2dc564 100644 --- a/tests/tests/gesture/AndroidTest.xml +++ b/tests/tests/gesture/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS Gesture test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="uitoolkit" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/instantapp/AndroidTest.xml b/tests/tests/instantapp/AndroidTest.xml index 3ec0d8b84f4..507af4ab5e1 100644 --- a/tests/tests/instantapp/AndroidTest.xml +++ b/tests/tests/instantapp/AndroidTest.xml @@ -18,7 +18,7 @@ <configuration description="Test module config for CTSInstantAppTests"> <option name="test-tag" value="CTSInstantAppTests" /> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/libnativehelper/AndroidTest.xml b/tests/tests/libnativehelper/AndroidTest.xml index d6ddf785369..7482b7bc8f3 100644 --- a/tests/tests/libnativehelper/AndroidTest.xml +++ b/tests/tests/libnativehelper/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Configuration for Libnativehelper Tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="libnativehelper" /> + <option name="config-descriptor:metadata" key="component" value="art" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> diff --git a/tests/tests/match_flags/AndroidTest.xml b/tests/tests/match_flags/AndroidTest.xml index 22cc54da021..aea3d436ee0 100644 --- a/tests/tests/match_flags/AndroidTest.xml +++ b/tests/tests/match_flags/AndroidTest.xml @@ -16,7 +16,7 @@ --> <configuration description="Config for match flag CTS test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java index 12ea166a227..5eb881eb1e4 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java +++ b/tests/tests/media/audio/src/android/media/audio/cts/RingtoneManagerTest.java @@ -158,9 +158,9 @@ public class RingtoneManagerTest { Uri bogus = Uri.parse("content://a_bogus_uri"); RingtoneManager.setActualDefaultRingtoneUri(mContext, RingtoneManager.TYPE_RINGTONE, bogus); - // shouldn't be able to successfully set ringtone to bogus URI - assertNotEquals(bogus, RingtoneManager.getActualDefaultRingtoneUri(mContext, - RingtoneManager.TYPE_RINGTONE)); + // not testing the matching getter after setting a bogus URI as ringtone + //assertNotEquals(bogus, RingtoneManager.getActualDefaultRingtoneUri(mContext, + // RingtoneManager.TYPE_RINGTONE)); assertEquals(Settings.System.DEFAULT_RINGTONE_URI, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE)); diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml index 1ed72c0dd45..ed3133fe29c 100644 --- a/tests/tests/media/misc/AndroidTest.xml +++ b/tests/tests/media/misc/AndroidTest.xml @@ -41,7 +41,7 @@ </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-2.1" /> + <option name="media-folder-name" value="CtsMediaMiscTestCases-2.2" /> <option name="dynamic-config-module" value="CtsMediaMiscTestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/tests/media/misc/DynamicConfig.xml b/tests/tests/media/misc/DynamicConfig.xml index 0918a54c5e6..ed21ce3cd35 100644 --- a/tests/tests/media/misc/DynamicConfig.xml +++ b/tests/tests/media/misc/DynamicConfig.xml @@ -15,6 +15,6 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://dl.google.com/android/xts/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.1.zip</value> + <value>https://dl.google.com/android/xts/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.2.zip</value> </entry> </dynamicConfig> diff --git a/tests/tests/media/misc/copy_media.sh b/tests/tests/media/misc/copy_media.sh index ca7f3cb3091..c8381975d7b 100755 --- a/tests/tests/media/misc/copy_media.sh +++ b/tests/tests/media/misc/copy_media.sh @@ -17,4 +17,4 @@ [ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/.. source $MEDIA_ROOT_DIR/common/copy_media_utils.sh get_adb_options "$@" -copy_media "misc" "CtsMediaMiscTestCases-2.1" +copy_media "misc" "CtsMediaMiscTestCases-2.2" diff --git a/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java index 01e3caf1f2d..85e4e73ef57 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/MediaMetadataRetrieverTest.java @@ -26,6 +26,7 @@ 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.assumeTrue; import android.content.Context; import android.content.pm.PackageManager; @@ -1193,15 +1194,46 @@ public class MediaMetadataRetrieverTest { 1 /*imageCount*/, 0 /*primary*/, false /*useGrid*/, true /*checkColor*/); } + /** + * Test AVIF (1024x1024 with 2x2 grid) decoding + */ + @Test + public void testGetImageAtIndexAvif1024x1024Grid2x2() throws Exception { + testGetImageAtIndexAvifGrid("sample_grid_1024x1024_2x2.avif", 1024 /* imageWidth */, + 1024 /* imageHeight */, 2 /* gridCols */, 2 /* gridRows */); + } + + /** + * Test AVIF (1920x1080 with 2x4 grid) decoding + */ @Test - public void testGetImageAtIndexAvifGrid() throws Exception { + public void testGetImageAtIndexAvif1920x1080Grid2x4() throws Exception { + testGetImageAtIndexAvifGrid("sample_grid_1920x1080_2x4.avif", 1920 /* imageWidth */, + 1080 /* imageHeight */, 2 /* gridCols */, 4 /* gridRows */); + } + + /** + * Test AVIF (1920x1080 with 4x4 grid) decoding + */ + @Test + public void testGetImageAtIndexAvif1920x1080Grid4x4() throws Exception { + testGetImageAtIndexAvifGrid("sample_grid_1920x1080_4x4.avif", 1920 /* imageWidth */, + 1080 /* imageHeight */, 4 /* gridCols */, 4 /* gridRows */); + } + + private void testGetImageAtIndexAvifGrid(final String res, int imageWidth, int imageHeight, + int gridCols, int gridRows) throws Exception { if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) return; - if (!MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AV1, 512, 512, 30)) { - MediaUtils.skipTest("No AV1 codec for 512p"); + int gridWidth = imageWidth / gridCols; + int gridHeight = imageHeight / gridRows; + if (!MediaUtils.canDecodeVideo(MediaFormat.MIMETYPE_VIDEO_AV1, gridWidth, gridHeight, 30)) { + MediaUtils.skipTest("No AV1 codec for " + gridWidth + " x " + gridHeight); + //TODO (b/224402585) Remove the following once MediaUtils.skipTest() calls assumeTrue + assumeTrue("No AV1 codec for " + gridWidth + " x " + gridHeight, false); return; } - testGetImage("sample_grid2x4.avif", 1920, 1080, "image/avif", 0 /*rotation*/, - 1 /*imageCount*/, 0 /*primary*/, true /*useGrid*/, true /*checkColor*/); + testGetImage(res, imageWidth, imageHeight, "image/avif", 0 /*rotation*/, 1 /*imageCount*/, + 0 /*primary*/, true /*useGrid*/, true /*checkColor*/); } /** diff --git a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java index 183341e3d42..6cbab7c5376 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java @@ -20,6 +20,6 @@ import android.media.cts.WorkDirBase; class WorkDir extends WorkDirBase { public static final String getMediaDirString() { - return getMediaDirString("CtsMediaMiscTestCases-2.1"); + return getMediaDirString("CtsMediaMiscTestCases-2.2"); } } diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java index f9ee9681664..6e3e8e217c6 100644 --- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java +++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java @@ -1287,7 +1287,12 @@ public class MediaPlayerTest extends MediaPlayerTestBase { mOnSeekCompleteCalled.waitForSignal(); Thread.sleep(playTime); assertFalse("MediaPlayer should not be playing", mMediaPlayer.isPlaying()); - assertEquals("MediaPlayer position should be 0", 0, mMediaPlayer.getCurrentPosition()); + int positionAtStart = mMediaPlayer.getCurrentPosition(); + // Allow both 0 and 23 (the timestamp of the second audio sample) to avoid flaky failures + // on builds that don't include http://r.android.com/2700283. + if (positionAtStart != 0 && positionAtStart != 23) { + fail("MediaPlayer position should be 0 or 23"); + } mMediaPlayer.start(); Thread.sleep(playTime); diff --git a/tests/tests/notification/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java b/tests/tests/notification/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java index 120186bbeb0..81d10e1d2b7 100644 --- a/tests/tests/notification/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java +++ b/tests/tests/notification/NotificationTrampolineBase/src/com/android/test/notificationtrampoline/NotificationTrampolineTestService.java @@ -40,6 +40,7 @@ import androidx.annotation.Nullable; import java.lang.ref.WeakReference; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; /** @@ -133,20 +134,21 @@ public class NotificationTrampolineTestService extends Service { break; } case MESSAGE_CLICK_NOTIFICATION: { - PendingIntent intent = Stream - .of(mNotificationManager.getActiveNotifications()) + AtomicInteger counter = new AtomicInteger(); + Stream.of(mNotificationManager.getActiveNotifications()) .filter(sb -> sb.getId() == notificationId) - .map(sb -> sb.getNotification().contentIntent) - .findFirst() - .orElse(null); - if (intent != null) { - try { - intent.send(); - } catch (PendingIntent.CanceledException e) { - throw new IllegalStateException("Notification PI cancelled", e); - } - } - sendMessageToTest(mCallback, TEST_MESSAGE_NOTIFICATION_CLICKED, intent != null); + .flatMap(sb -> Stream.of(sb.getNotification().contentIntent, + sb.getNotification().publicVersion.contentIntent)) + .forEach(intent -> { + try { + intent.send(); + } catch (PendingIntent.CanceledException e) { + throw new IllegalStateException("Notification PI cancelled", e); + } + counter.getAndIncrement(); + }); + sendMessageToTest(mCallback, TEST_MESSAGE_NOTIFICATION_CLICKED, + counter.get() == 2); break; } default: @@ -164,10 +166,16 @@ public class NotificationTrampolineTestService extends Service { } private void postNotification(int notificationId, PendingIntent intent) { + Notification publicNotification = + new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(android.R.drawable.ic_info) + .setContentIntent(intent) + .build(); Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID) .setSmallIcon(android.R.drawable.ic_info) .setContentIntent(intent) + .setPublicVersion(publicNotification) .build(); NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT); diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java index 30f152a3b39..da4da9bef8c 100644 --- a/tests/tests/os/src/android/os/cts/SeccompTest.java +++ b/tests/tests/os/src/android/os/cts/SeccompTest.java @@ -58,8 +58,9 @@ public class SeccompTest extends AndroidTestCase { // will not be correct, so skip those tests. private boolean isRunningUnderEmulatedAbi() { final String primaryAbi = Build.SUPPORTED_ABIS[0]; - return (CpuFeatures.isArmCpu() || CpuFeatures.isArm64Cpu()) && - !(primaryAbi.equals("armeabi-v7a") || primaryAbi.equals("arm64-v8a")); + return ((CpuFeatures.isArmCpu() || CpuFeatures.isArm64Cpu()) && + !(primaryAbi.equals("armeabi-v7a") || primaryAbi.equals("arm64-v8a"))) || + CpuFeatures.isNativeBridgedCpu(); } public void testSeccomp() { diff --git a/tests/tests/packageinstaller/adminpackageinstaller/Android.bp b/tests/tests/packageinstaller/adminpackageinstaller/Android.bp index f829a938076..6cb9baf4e62 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/Android.bp +++ b/tests/tests/packageinstaller/adminpackageinstaller/Android.bp @@ -26,6 +26,8 @@ android_test { "androidx.test.rules", "androidx.legacy_legacy-support-v4", "cts-install-lib", + "Harrier", + "Nene", ], libs: ["android.test.base"], sdk_version: "test_current", diff --git a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml index f78d67e706e..ac018a4ff87 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml +++ b/tests/tests/packageinstaller/adminpackageinstaller/AndroidTest.xml @@ -16,12 +16,13 @@ <configuration description="Config for CTS Admin Package Installer test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <!-- Device Owner-specific tests are not applicable to instant apps. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + <option name="config-descriptor:metadata" key="parameter" value="multiuser" /> <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> <option name="run-command" value="mkdir -p /data/local/tmp/cts/packageinstaller/" /> @@ -43,12 +44,10 @@ <target_preparer class="com.android.tradefed.targetprep.RunOnSystemUserTargetPreparer"/> - <target_preparer class="com.android.tradefed.targetprep.DeviceOwnerTargetPreparer"> - <option name="device-owner-component-name" value="android.packageinstaller.admin.cts/.BasicAdminReceiver" /> - </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.packageinstaller.admin.cts" /> + <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnWorkProfile" /> + <option name="exclude-annotation" value="com.android.bedstead.harrier.annotations.RequireRunOnSecondaryUser" /> </test> </configuration> diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java index c28466400e0..1b2c46734e9 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java +++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/BasePackageInstallTest.java @@ -15,7 +15,14 @@ */ package android.packageinstaller.admin.cts; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import android.app.Instrumentation; import android.app.PendingIntent; +import android.app.UiAutomation; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -28,8 +35,15 @@ import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.os.ParcelFileDescriptor; import android.os.Process; -import android.support.test.uiautomator.UiDevice; -import android.test.InstrumentationTestCase; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.UiDevice; + +import com.android.bedstead.nene.TestApis; + +import org.junit.After; +import org.junit.Before; import java.io.BufferedReader; import java.io.File; @@ -42,7 +56,8 @@ import java.util.ArrayList; /** * Base test case for testing PackageInstaller. */ -public class BasePackageInstallTest extends InstrumentationTestCase { +public class BasePackageInstallTest { + private static final String TAG = BasePackageInstallTest.class.getSimpleName(); protected static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/CtsEmptyTestApp.apk"; protected static final String TEST_APP_PKG = "android.packageinstaller.emptytestapp.cts"; @@ -61,9 +76,13 @@ public class BasePackageInstallTest extends InstrumentationTestCase { protected boolean mCallbackReceived; protected int mCallbackStatus; protected Intent mCallbackIntent; + protected ComponentName mDeviceOwner; protected boolean mHasFeature; + protected boolean mAmIDeviceOwner; + protected Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); + protected UiAutomation mUiAutomation = mInstrumentation.getUiAutomation(); protected final Object mPackageInstallerTimeoutLock = new Object(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @@ -85,13 +104,11 @@ public class BasePackageInstallTest extends InstrumentationTestCase { } }; - @Override - protected void setUp() throws Exception { - super.setUp(); - mContext = getInstrumentation().getContext(); - mDevice = UiDevice.getInstance(getInstrumentation()); - mDevicePolicyManager = (DevicePolicyManager) - mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); + @Before + public void setUp() throws Exception { + mContext = mInstrumentation.getContext(); + mDevice = UiDevice.getInstance(mInstrumentation); + mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); mPackageManager = mContext.getPackageManager(); mPackageInstaller = mPackageManager.getPackageInstaller(); assertNotNull(mPackageInstaller); @@ -105,11 +122,23 @@ public class BasePackageInstallTest extends InstrumentationTestCase { } } - @Override - protected void tearDown() throws Exception { + @Before + public void addDeviceOwner() { + mDeviceOwner = new ComponentName(mContext, BasicAdminReceiver.class); + try { + TestApis.devicePolicy().setDeviceOwner(mDeviceOwner); + mAmIDeviceOwner = true; + } catch (Exception e) { + mAmIDeviceOwner = false; + Log.e(TAG, e.getMessage()); + } + } + + @After + public void tearDown() throws Exception { if (mDevicePolicyManager.isDeviceOwnerApp(PACKAGE_NAME) || mDevicePolicyManager.isProfileOwnerApp(PACKAGE_NAME)) { - mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false); + mDevicePolicyManager.setUninstallBlocked(mDeviceOwner, TEST_APP_PKG, false); } try { mContext.unregisterReceiver(mBroadcastReceiver); @@ -119,12 +148,17 @@ public class BasePackageInstallTest extends InstrumentationTestCase { if (mSession != null) { mSession.abandon(); } - - super.tearDown(); + forceUninstall(); } - protected static ComponentName getWho() { - return new ComponentName(PACKAGE_NAME, BasicAdminReceiver.class.getName()); + @After + public void removeDeviceOwner() { + try { + TestApis.devicePolicy().getDeviceOwner().remove(); + } catch (NullPointerException e) { + Log.e(TAG, "Could not find a device owner set by this test. " + + "Maybe an owner already exists?"); + } } protected void assertInstallPackage() throws Exception { @@ -220,8 +254,7 @@ public class BasePackageInstallTest extends InstrumentationTestCase { } public ArrayList<String> runShellCommand(String command) throws Exception { - ParcelFileDescriptor pfd = getInstrumentation().getUiAutomation() - .executeShellCommand(command); + ParcelFileDescriptor pfd = mUiAutomation.executeShellCommand(command); ArrayList<String> ret = new ArrayList<>(); // Read the input stream fully. diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/InstallReasonTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/InstallReasonTest.java index 012952033b4..26a3d27d1f8 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/InstallReasonTest.java +++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/InstallReasonTest.java @@ -16,16 +16,26 @@ package android.packageinstaller.admin.cts; +import static org.junit.Assert.assertEquals; +import static org.junit.Assume.assumeTrue; + import android.content.pm.PackageManager; +import com.android.bedstead.harrier.BedsteadJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + /** * This class tests that the install reason is correctly recorded for packages. */ +@RunWith(BedsteadJUnit4.class) public class InstallReasonTest extends BasePackageInstallTest { + @Test public void testInstallReason() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + // Verify that since the Device Owner was sideloaded, its install reason is unknown. assertEquals(PackageManager.INSTALL_REASON_UNKNOWN, getInstallReason(PACKAGE_NAME)); diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java index cbdc928d2c5..790126129a5 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java +++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SessionCommitBroadcastTest.java @@ -15,6 +15,13 @@ */ package android.packageinstaller.admin.cts; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -28,11 +35,17 @@ import android.os.UserHandle; import android.os.UserManager; import android.text.TextUtils; +import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.InstallUtils; import com.android.cts.install.lib.TestApp; import com.android.cts.install.lib.Uninstall; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -40,36 +53,32 @@ import java.util.concurrent.TimeUnit; * This class tests {@link PackageInstaller#ACTION_SESSION_COMMITTED} is properly sent to the * launcher app. */ +@RunWith(BedsteadJUnit4.class) public class SessionCommitBroadcastTest extends BasePackageInstallTest { private static final long BROADCAST_TIMEOUT_SECS = 20; - private ComponentName mDefaultLauncher; private ComponentName mThisAppLauncher; private SessionCommitReceiver mReceiver; - @Override - protected void setUp() throws Exception { - super.setUp(); - final String myPackageName = mContext.getPackageName(); - final int myUid = mPackageManager.getApplicationInfo(myPackageName, 0).uid; - assertTrue("Test package:" + myPackageName + " (uid:" + myUid + ") is not an admin", - mDevicePolicyManager.isDeviceOwnerApp(myPackageName)); + @Before + public void setUpTest() throws Exception { mDefaultLauncher = ComponentName.unflattenFromString(getDefaultLauncher()); mThisAppLauncher = new ComponentName(mContext, LauncherActivity.class); mReceiver = new SessionCommitReceiver(); } - @Override - protected void tearDown() throws Exception { + @After + public void tearDownTest() throws Exception { mContext.unregisterReceiver(mReceiver); Uninstall.packages(TestApp.A); } + @Test public void testBroadcastNotReceivedForDifferentLauncher() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + if (mDefaultLauncher.equals(mThisAppLauncher)) { // Find a different launcher Intent homeIntent = new Intent(Intent.ACTION_MAIN) @@ -101,10 +110,10 @@ public class SessionCommitBroadcastTest extends BasePackageInstallTest { assertEquals(TEST_APP_PKG, info.getAppPackageName()); } + @Test public void testBroadcastNotReceivedForUpdateInstall() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); try { setLauncher(mThisAppLauncher.flattenToString()); @@ -131,10 +140,11 @@ public class SessionCommitBroadcastTest extends BasePackageInstallTest { } } + @Test public void testBroadcastReceivedForNewInstall() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + setLauncher(mThisAppLauncher.flattenToString()); // install the app @@ -152,10 +162,12 @@ public class SessionCommitBroadcastTest extends BasePackageInstallTest { setLauncher(mDefaultLauncher.flattenToString()); } + @Test public void testBroadcastReceivedForEnablingApp() throws Exception { - if (!mHasFeature || !UserManager.supportsMultipleUsers()) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Cannot add multiple users", UserManager.supportsMultipleUsers()); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + setLauncher(mThisAppLauncher.flattenToString()); ComponentName cn = new ComponentName(mContext, BasicAdminReceiver.class); @@ -195,7 +207,7 @@ public class SessionCommitBroadcastTest extends BasePackageInstallTest { private void setLauncher(String component) throws Exception { runShellCommand("cmd package set-home-activity --user " - + getInstrumentation().getContext().getUserId() + " " + component); + + mInstrumentation.getContext().getUserId() + " " + component); } private class SessionCommitReceiver extends BroadcastReceiver { diff --git a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SilentPackageInstallTest.java b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SilentPackageInstallTest.java index 4c88b8225d1..370b9372444 100644 --- a/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SilentPackageInstallTest.java +++ b/tests/tests/packageinstaller/adminpackageinstaller/src/android/packageinstaller/admin/cts/SilentPackageInstallTest.java @@ -16,14 +16,26 @@ package android.packageinstaller.admin.cts; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import com.android.bedstead.harrier.BedsteadJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + /** * This class tests silent package install and uninstall by a device owner. */ +@RunWith(BedsteadJUnit4.class) public class SilentPackageInstallTest extends BasePackageInstallTest { + + @Test public void testSilentInstallUninstall() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + // install the app assertInstallPackage(); @@ -32,21 +44,22 @@ public class SilentPackageInstallTest extends BasePackageInstallTest { assertFalse(isPackageInstalled(TEST_APP_PKG)); } + @Test public void testUninstallBlocked() throws Exception { - if (!mHasFeature) { - return; - } + assumeTrue("FEATURE_DEVICE_ADMIN unavailable", mHasFeature); + assumeTrue("Could not set BasicAdminReceiver.class as device owner", mAmIDeviceOwner); + // install the app assertInstallPackage(); - mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, true); - assertTrue(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG)); + mDevicePolicyManager.setUninstallBlocked(mDeviceOwner, TEST_APP_PKG, true); + assertTrue(mDevicePolicyManager.isUninstallBlocked(mDeviceOwner, TEST_APP_PKG)); assertTrue(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG)); assertFalse(tryUninstallPackage()); assertTrue(isPackageInstalled(TEST_APP_PKG)); - mDevicePolicyManager.setUninstallBlocked(getWho(), TEST_APP_PKG, false); - assertFalse(mDevicePolicyManager.isUninstallBlocked(getWho(), TEST_APP_PKG)); + mDevicePolicyManager.setUninstallBlocked(mDeviceOwner, TEST_APP_PKG, false); + assertFalse(mDevicePolicyManager.isUninstallBlocked(mDeviceOwner, TEST_APP_PKG)); assertFalse(mDevicePolicyManager.isUninstallBlocked(null, TEST_APP_PKG)); assertTrue(tryUninstallPackage()); assertFalse(isPackageInstalled(TEST_APP_PKG)); diff --git a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml index 73ff59a3a7b..2cf871ad9e1 100644 --- a/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml +++ b/tests/tests/packageinstaller/atomicinstall/AndroidTest.xml @@ -15,12 +15,20 @@ --> <configuration description="Runs the atomic install API tests"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <!-- Instant apps can't have INSTALL_PACKAGES permission. --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" /> + <!-- disable GPP UI --> + <target_preparer class="com.android.tradefed.targetprep.DeviceSetup"> + <option name="force-skip-system-props" value="true" /> + <option name="set-global-setting" key="verifier_engprod" value="1" /> + <option name="set-global-setting" key="verifier_verify_adb_installs" value="0" /> + <option name="restore-settings" value="true" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsAtomicInstallTestCases.apk" /> diff --git a/tests/tests/packageinstaller/contentprovider/AndroidTestNoVisibility.xml b/tests/tests/packageinstaller/contentprovider/AndroidTestNoVisibility.xml index 6cb94624a5f..d06ec4d7ad2 100644 --- a/tests/tests/packageinstaller/contentprovider/AndroidTestNoVisibility.xml +++ b/tests/tests/packageinstaller/contentprovider/AndroidTestNoVisibility.xml @@ -17,7 +17,7 @@ <configuration description="Config for CTS Package Scheme Test Cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/contentprovider/AndroidTestVisibility.xml b/tests/tests/packageinstaller/contentprovider/AndroidTestVisibility.xml index 4dd6207dbed..e11d7fa3d07 100644 --- a/tests/tests/packageinstaller/contentprovider/AndroidTestVisibility.xml +++ b/tests/tests/packageinstaller/contentprovider/AndroidTestVisibility.xml @@ -17,7 +17,7 @@ <configuration description="Config for CTS Package Scheme Test Cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/install/AndroidTest.xml b/tests/tests/packageinstaller/install/AndroidTest.xml index 260c1123b8b..28be32e1395 100644 --- a/tests/tests/packageinstaller/install/AndroidTest.xml +++ b/tests/tests/packageinstaller/install/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Packageinstaller Session test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt index ac66ceddbda..75f079e73e7 100644 --- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt +++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PackageInstallerTestBase.kt @@ -39,6 +39,7 @@ import android.content.pm.PackageInstaller.SessionParams.MODE_FULL_INSTALL import android.content.pm.PackageManager import android.provider.DeviceConfig 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 android.util.Log @@ -161,7 +162,14 @@ open class PackageInstallerTestBase { * Wait for session's install result and return it */ protected fun getInstallSessionResult(timeout: Long = TIMEOUT): SessionResult { - return installSessionResult.poll(timeout, TimeUnit.MILLISECONDS) + return getInstallSessionResult(installSessionResult, timeout) + } + + protected fun getInstallSessionResult( + installResult: LinkedBlockingQueue<SessionResult>, + timeout: Long = TIMEOUT + ): SessionResult { + return installResult.poll(timeout, TimeUnit.MILLISECONDS) ?: SessionResult(null /* status */, null /* preapproval */, "Fail to poll result") } @@ -361,16 +369,24 @@ open class PackageInstallerTestBase { * @param resId The resource ID of the button to click */ fun clickInstallerUIButton(resId: String) { + clickInstallerUIButton(By.res(PACKAGE_INSTALLER_PACKAGE_NAME, resId)) + } + + /** + * Click a button in the UI of the installer app + * + * @param bySelector The bySelector of the button to click + */ + fun clickInstallerUIButton(bySelector: BySelector) { val startTime = System.currentTimeMillis() while (startTime + TIMEOUT > System.currentTimeMillis()) { try { - uiDevice.wait(Until.findObject(By.res(PACKAGE_INSTALLER_PACKAGE_NAME, resId)), 1000) - .click() + uiDevice.wait(Until.findObject(bySelector), 1000).click() return } catch (ignore: Throwable) { } } - Assert.fail("Failed to click the button: $resId") + Assert.fail("Failed to click the button: $bySelector") } /** diff --git a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PreapprovalInstallTest.kt b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PreapprovalInstallTest.kt index de97e273c03..6df76dcf5fb 100644 --- a/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PreapprovalInstallTest.kt +++ b/tests/tests/packageinstaller/install/src/android/packageinstaller/install/cts/PreapprovalInstallTest.kt @@ -16,7 +16,9 @@ package android.packageinstaller.install.cts +import android.Manifest import android.app.PendingIntent +import android.app.compat.CompatChanges import android.content.BroadcastReceiver import android.content.Context import android.content.Intent @@ -25,13 +27,22 @@ import android.content.pm.PackageInstaller import android.icu.util.ULocale import android.platform.test.annotations.AppModeFull import android.provider.DeviceConfig +import android.support.test.uiautomator.By +import android.util.Log +import androidx.test.platform.app.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 import com.android.compatibility.common.util.DeviceConfigStateChangerRule +import com.android.compatibility.common.util.FutureResultActivity import java.io.File +import java.util.concurrent.LinkedBlockingQueue +import java.util.regex.Pattern +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.fail import org.junit.Assume +import org.junit.Assume.assumeFalse import org.junit.Before import org.junit.Rule import org.junit.Test @@ -48,7 +59,10 @@ class PreapprovalInstallTest : PackageInstallerTestBase() { const val TEST_APP_LABEL_PL = "Empty Test App Polish" const val TEST_APP_LABEL_V2 = "Empty Test App V2" const val TEST_FAKE_APP_LABEL = "Fake Test App" + const val TEST_INSTALLER_APK_NAME = "CtsEmptyInstallerApp.apk" + const val TEST_INSTALLER_APK_PACKAGE_NAME = "android.packageinstaller.emptyinstaller.cts" const val PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE = "is_preapproval_available" + const val CHANGE_ID_PRE_APPROVAL_WITH_UPDATE_OWNERSHIP_FIX = 293644536L } private val apkFile_pl = File(context.filesDir, TEST_APK_NAME_PL) @@ -103,6 +117,130 @@ class PreapprovalInstallTest : PackageInstallerTestBase() { } /** + * Check that we can request a user pre-approval with updating ownership case, the action of + * the EXTRA_INTENT is PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL. + */ + @Test + fun requestUserPreapprovalWithUpdateOwnership_userAgree_statusSuccess() { + // If the build includes the fix, the feature is disabled. Otherwise, it is enabled. + assumeFalse( + "The rom doesn't include the fix for b/293644536", + CompatChanges.isChangeEnabled(CHANGE_ID_PRE_APPROVAL_WITH_UPDATE_OWNERSHIP_FIX) + ) + + // Get the value of updateOwnership property to restore it finally + var isUpdateOwnershipEnforcementAvailable: String? = + getDeviceProperty(PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE) + setDeviceProperty(PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, "true") + + // Install the test installer to request update ownership + installPackage(TEST_INSTALLER_APK_NAME) + // Install the test app and enable update ownership enforcement with the test installer + installTestPackage("--update-ownership -i $TEST_INSTALLER_APK_PACKAGE_NAME") + assertInstalled() + + var isStatusPendingUserActionCalled = false + var installResult = LinkedBlockingQueue<SessionResult>() + + // Create the receiver to handle the install result. + // 1. If the Success status comes before STATUS_PENDING_USER_ACTION, we can assert it first + // without waiting the timeout. + // 2. Assert the action of the EXTRA_INTENT is PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL + val receiver = object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + val status = intent.getIntExtra( + PackageInstaller.EXTRA_STATUS, + PackageInstaller.STATUS_FAILURE_INVALID + ) + + val preapproval = intent.getBooleanExtra( + PackageInstaller.EXTRA_PRE_APPROVAL, + false /* defaultValue */ + ) + val msg = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + Log.d(TAG, "status: $status, msg: $msg preapproval: $preapproval") + + if (status == PackageInstaller.STATUS_SUCCESS) { + // Make sure the PendingUserAction is called before success + assertEquals(true, isStatusPendingUserActionCalled) + } else if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) { + isStatusPendingUserActionCalled = true + val activityIntent = + intent.getParcelableExtra(Intent.EXTRA_INTENT, Intent::class.java) + assertEquals( + PackageInstaller.ACTION_CONFIRM_PRE_APPROVAL, + activityIntent!!.action + ) + assertEquals(activityIntent.extras!!.keySet().size, 1) + activityIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or + Intent.FLAG_ACTIVITY_NEW_TASK) + installDialogStarter.activity.startActivityForResult(activityIntent) + } + + installResult.offer(SessionResult(status, preapproval, msg)) + } + } + + var uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation() + + try { + // Adopt the INSTALL_PACKAGES permission + uiAutomation.adoptShellPermissionIdentity(Manifest.permission.INSTALL_PACKAGES) + + val session = createSession( + 0 /* flags */, false /* isMultiPackage */, + null /* packageSource */ + ).second + + // Register the receiver for the install result + val action = "PreapprovalInstallTest.install_cb" + context.registerReceiver(receiver, IntentFilter(action), Context.RECEIVER_EXPORTED) + + // Request user preapproval + FutureResultActivity.doAndAwaitStart { + val pendingIntent = PendingIntent.getBroadcast( + context, 0 /* requestCode */, + Intent(action).setPackage(context.packageName) + .addFlags(Intent.FLAG_RECEIVER_FOREGROUND), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE + ) + session.requestUserPreapproval( + preparePreapprovalDetails(), + pendingIntent.intentSender + ) + } + + // The system should have asked us to launch the installer with pre-approval true + var result = getInstallSessionResult(installResult) + assertEquals(PackageInstaller.STATUS_PENDING_USER_ACTION, result.status) + assertEquals(true, result.preapproval) + + // Click the "Update anyway" button on the update ownership dialog + clickInstallerUIButton( + By.text( + Pattern.compile( + "UPDATE ANYWAY", + Pattern.CASE_INSENSITIVE + ) + ) + ) + + // request should have succeeded + result = getInstallSessionResult(installResult) + assertEquals(PackageInstaller.STATUS_SUCCESS, result.status) + assertEquals(true, result.preapproval) + } finally { + context.unregisterReceiver(receiver) + uninstallPackage(TEST_INSTALLER_APK_PACKAGE_NAME) + uiAutomation.dropShellPermissionIdentity() + setDeviceProperty( + PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, + isUpdateOwnershipEnforcementAvailable + ) + } + } + + /** * Request a user pre-approval, but then cancel it when it prompts. */ @Test @@ -166,9 +304,15 @@ class PreapprovalInstallTest : PackageInstallerTestBase() { val (sessionId, session) = createSession(0 /* flags */, false /* isMultiPackage */, null /* packageSource */) + val latch = CountDownLatch(1) val dummyReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { - // Do nothing + // This is to make sure we receive the pre-approval intent before + // committing the session + val preapproval = intent.getBooleanExtra(PackageInstaller.EXTRA_PRE_APPROVAL, false) + if (preapproval) { + latch.countDown() + } } } @@ -182,6 +326,7 @@ class PreapprovalInstallTest : PackageInstallerTestBase() { PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE) session.requestUserPreapproval(preparePreapprovalDetails(), pendingIntent.intentSender) + latch.await(2000, TimeUnit.MILLISECONDS) writeAndCommitSession(TEST_APK_NAME, session) clickInstallerUIButton(INSTALL_BUTTON_ID) diff --git a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml index de65129c403..7e48a7d3eb3 100644 --- a/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml +++ b/tests/tests/packageinstaller/install_appop_default/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Packageinstaller Session test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml index b1d19f80d4e..0f480af1a4d 100644 --- a/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml +++ b/tests/tests/packageinstaller/install_appop_denied/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Packageinstaller Session test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/packagescheme/AndroidTestNoVisibility.xml b/tests/tests/packageinstaller/packagescheme/AndroidTestNoVisibility.xml index 88fc9391c75..0acefa08906 100644 --- a/tests/tests/packageinstaller/packagescheme/AndroidTestNoVisibility.xml +++ b/tests/tests/packageinstaller/packagescheme/AndroidTestNoVisibility.xml @@ -17,7 +17,7 @@ <configuration description="Config for CTS Package Scheme Test Cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/packagescheme/AndroidTestVisibility.xml b/tests/tests/packageinstaller/packagescheme/AndroidTestVisibility.xml index 96385c4ed69..ea57d5f7bd4 100644 --- a/tests/tests/packageinstaller/packagescheme/AndroidTestVisibility.xml +++ b/tests/tests/packageinstaller/packagescheme/AndroidTestVisibility.xml @@ -17,7 +17,7 @@ <configuration description="Config for CTS Package Scheme Test Cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml index 78a60e95a1b..4b11f120bea 100644 --- a/tests/tests/packageinstaller/tapjacking/AndroidTest.xml +++ b/tests/tests/packageinstaller/tapjacking/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Packageinstaller Tapjacking test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/packageinstaller/uninstall/AndroidTest.xml b/tests/tests/packageinstaller/uninstall/AndroidTest.xml index cabcb22f79b..021ec19ab72 100644 --- a/tests/tests/packageinstaller/uninstall/AndroidTest.xml +++ b/tests/tests/packageinstaller/uninstall/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Packageinstaller Uninstall test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt index df6110cd2d6..571ba479d82 100644 --- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt @@ -799,7 +799,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { clearTargetSdkWarning() } - val useLegacyNavigation = isWatch || isTv || isAutomotive || manuallyNavigate + val useLegacyNavigation = isWatch || isAutomotive || manuallyNavigate if (useLegacyNavigation) { navigateToAppPermissionSettings() val permissionLabel = getPermissionLabel(permission) @@ -843,7 +843,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { if (targetSdk <= MAX_SDK_FOR_SDK_WARNING) { clearTargetSdkWarning(QUICK_CHECK_TIMEOUT_MILLIS) } - val useLegacyNavigation = isWatch || isAutomotive || isTv || manuallyNavigate + val useLegacyNavigation = isWatch || isAutomotive || manuallyNavigate if (useLegacyNavigation) { navigateToAppPermissionSettings() } diff --git a/tests/tests/permission3/src/android/permission3/cts/LocationProviderInterceptDialogTest.kt b/tests/tests/permission3/src/android/permission3/cts/LocationProviderInterceptDialogTest.kt index 7966d2f3ec2..b092546d280 100644 --- a/tests/tests/permission3/src/android/permission3/cts/LocationProviderInterceptDialogTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/LocationProviderInterceptDialogTest.kt @@ -23,6 +23,7 @@ import android.content.Intent import android.location.LocationManager import android.os.Build import android.permission.cts.PermissionUtils +import android.platform.test.annotations.FlakyTest import androidx.test.filters.SdkSuppress import androidx.test.uiautomator.By import com.android.compatibility.common.util.AppOpsUtils @@ -32,6 +33,7 @@ import java.util.concurrent.TimeUnit import org.junit.Assert import org.junit.Assume.assumeFalse import org.junit.Before +import org.junit.Ignore import org.junit.Test private const val EXTRA_PACKAGE_NAME = "android.intent.extra.PACKAGE_NAME" @@ -43,6 +45,7 @@ private const val ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_ * app in this test). */ @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) +@FlakyTest @CddTest(requirement = "9.1/C-0-1") class LocationProviderInterceptDialogTest : BaseUsePermissionTest() { @Before @@ -59,6 +62,7 @@ class LocationProviderInterceptDialogTest : BaseUsePermissionTest() { } @Test + @Ignore("b/288471744") fun clickLocationPermission_showDialog_clickOk() { openPermissionScreenForApp() click(By.text("Location")) @@ -69,6 +73,7 @@ class LocationProviderInterceptDialogTest : BaseUsePermissionTest() { } @Test + @Ignore("b/288471744") fun clickLocationPermission_showDialog_clickLocationAccess() { openPermissionScreenForApp() click(By.text("Location")) @@ -80,6 +85,7 @@ class LocationProviderInterceptDialogTest : BaseUsePermissionTest() { } @Test + @Ignore("b/288471744") fun checkRestrictedPermissions() { context.sendBroadcast(Intent(PermissionTapjackingTest.ACTION_SHOW_OVERLAY) .putExtra("package", MIC_LOCATION_PROVIDER_APP_PACKAGE_NAME) diff --git a/tests/tests/provider/src/android/provider/cts/settings/Settings_MemoryUsageTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_MemoryUsageTest.java index 0132e2b2e2b..ef5c67b9f4e 100644 --- a/tests/tests/provider/src/android/provider/cts/settings/Settings_MemoryUsageTest.java +++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_MemoryUsageTest.java @@ -37,7 +37,7 @@ import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) public class Settings_MemoryUsageTest { - private static final String STRING_SETTING = Settings.System.RINGTONE; + private static final String STRING_SETTING = Settings.System.SCREEN_BRIGHTNESS; private static final int sUserId = Process.myUserHandle().getIdentifier(); private ContentResolver mContentResolver; diff --git a/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java index aed73d9857a..24440fb7ebb 100644 --- a/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java +++ b/tests/tests/provider/src/android/provider/cts/settings/Settings_SystemTest.java @@ -16,6 +16,8 @@ package android.provider.cts.settings; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -185,6 +187,17 @@ public class Settings_SystemTest extends StsExtraBusinessLogicTestCase { } @Test + @AsbSecurityTest(cveBugId = 227201030) + public void testInvalidRingtoneUriIsRejected() { + final ContentResolver cr = InstrumentationRegistry.getTargetContext().getContentResolver(); + final String originalValue = System.getString(cr, System.RINGTONE); + final String invalidUri = "content://10@media/external/audio/media/1000000019"; + System.putString(cr, System.RINGTONE, invalidUri); + // Assert that the insertion didn't take effect + assertThat(System.getString(cr, System.RINGTONE)).isEqualTo(originalValue); + } + + @Test public void testGetDefaultValues() { final ContentResolver cr = InstrumentationRegistry.getTargetContext().getContentResolver(); diff --git a/tests/tests/resolverservice/AndroidTest.xml b/tests/tests/resolverservice/AndroidTest.xml index fbd041daef8..99811f591e2 100644 --- a/tests/tests/resolverservice/AndroidTest.xml +++ b/tests/tests/resolverservice/AndroidTest.xml @@ -19,7 +19,7 @@ <configuration description="Config for CTS resolver service test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="packagemanager" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml index ebf0a42411d..e7168c2568a 100644 --- a/tests/tests/security/AndroidManifest.xml +++ b/tests/tests/security/AndroidManifest.xml @@ -38,6 +38,9 @@ <!-- For FileIntegrityManager --> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> + <!-- CVE-2023-21143 --> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> + <application android:usesCleartextTraffic="true"> <uses-library android:name="android.test.runner"/> <uses-library android:name="org.apache.http.legacy" diff --git a/tests/tests/security/res/layout/cve_2023_21143.xml b/tests/tests/security/res/layout/cve_2023_21143.xml new file mode 100644 index 00000000000..d8c68451295 --- /dev/null +++ b/tests/tests/security/res/layout/cve_2023_21143.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:src="@raw/cve_2023_21143" /> + +</LinearLayout> diff --git a/tests/tests/security/res/raw/cve_2023_21143.png b/tests/tests/security/res/raw/cve_2023_21143.png Binary files differnew file mode 100644 index 00000000000..ce0fc57eaf6 --- /dev/null +++ b/tests/tests/security/res/raw/cve_2023_21143.png diff --git a/tests/tests/security/res/values/strings_CVE_2023_21143.xml b/tests/tests/security/res/values/strings_CVE_2023_21143.xml new file mode 100644 index 00000000000..cef10c77e23 --- /dev/null +++ b/tests/tests/security/res/values/strings_CVE_2023_21143.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2023 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="channelId">notification_channel_id</string> + <string name="channelName">notification_channel_name</string> + <string name="failMsg">Device is vulnerable to b/268193777 !!</string> + <string name="noPidFound">No pid found for com.android.systemui</string> + <string name="notificationSendingFailed">notification has not been sent successfully</string> + <string name="notificationText">notification_text</string> + <string name="shellCommandForHome">input keyevent KEYCODE_HOME</string> + <string name="systemuiPidCommand">pidof com.android.systemui</string> +</resources> diff --git a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java index fa99eba0815..880c5c612fa 100644 --- a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java +++ b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java @@ -299,6 +299,41 @@ public class ActivityManagerTest extends StsExtraBusinessLogicTestCase { assertTrue(securityException); } + @AsbSecurityTest(cveBugId = 289549315) + @Test + public void testActivityManager_backupAgentCreated_rejectIfCallerUidNotEqualsPackageUid() + throws Exception { + SecurityException securityException = null; + Exception unexpectedException = null; + try { + final Object iam = ActivityManager.class.getDeclaredMethod("getService").invoke(null); + Class.forName("android.app.IActivityManager").getDeclaredMethod("backupAgentCreated", + String.class, IBinder.class, int.class) + .invoke(iam, /* agentPackageName*/ "android", /* agent */ null, /* userId */ 0); + } catch (SecurityException e) { + securityException = e; + } catch (InvocationTargetException e) { + if (e.getCause() instanceof SecurityException) { + securityException = (SecurityException) e.getCause(); + } else { + unexpectedException = e; + } + } catch (Exception e) { + unexpectedException = e; + } + if (unexpectedException != null) { + Log.w("ActivityManagerTest", "Unexpected exception", unexpectedException); + fail("ActivityManagerNative.backupAgentCreated() API should have thrown " + + "SecurityException when invoked from process with uid not matching target " + + "package uid."); + } + + assertNotNull("Expected SecurityException when caller's uid doesn't match package uid", + securityException); + assertEquals("android does not belong to uid " + Process.myUid(), + securityException.getMessage()); + } + /** * Run ActivityManager.getHistoricalProcessExitReasons once, return the time spent on it. */ diff --git a/tests/tests/security/src/android/security/cts/CVE_2023_21143.java b/tests/tests/security/src/android/security/cts/CVE_2023_21143.java new file mode 100644 index 00000000000..756782b3a85 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2023_21143.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2023 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 androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.sts.common.SystemUtil.poll; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.Instrumentation; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.platform.test.annotations.AsbSecurityTest; +import android.service.notification.StatusBarNotification; +import android.widget.RemoteViews; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.SystemUtil; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2023_21143 extends StsExtraBusinessLogicTestCase { + private Instrumentation mInstrumentation = null; + + @Test + @AsbSecurityTest(cveBugId = 268193777) + public void testPocCVE_2023_21143() { + Context context = null; + try { + mInstrumentation = getInstrumentation(); + context = mInstrumentation.getContext(); + + // Getting pid of com.android.systemui + final String systemuiPidCommand = context.getString(R.string.systemuiPidCommand); + final int initialPidOfSystemUI = getPid(systemuiPidCommand); + assumeTrue(context.getString(R.string.noPidFound), initialPidOfSystemUI != -1); + + // Adding a remoteview with large size image to notification in order to reproduce the + // vulnerability + final String packageName = context.getPackageName(); + RemoteViews remoteView = new RemoteViews(packageName, R.layout.cve_2023_21143); + NotificationChannel channel = + new NotificationChannel( + context.getString(R.string.channelId), + context.getString(R.string.channelName), + NotificationManager.IMPORTANCE_HIGH); + NotificationManager notificationManager = + context.getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + Notification notification = + new Notification.Builder(context, context.getString(R.string.channelId)) + .setContentText(context.getString(R.string.notificationText)) + .setSmallIcon(Icon.createWithData(new byte[0], 0, 0)) + .setCustomContentView(remoteView) + .build(); + notificationManager.notify(1, notification); + + // Assumption failure if notification has not been posted successfully. + assumeTrue( + context.getString(R.string.notificationSendingFailed), + poll( + () -> { + for (StatusBarNotification sbn : + notificationManager.getActiveNotifications()) { + if (sbn.getPackageName().equals(packageName)) { + return true; + } + } + return false; + })); + + // Without fix, the systemui crashes and it's pid changes. + // Fail test only if pid has changed and notification is still visible. + boolean isDeviceVulnerable = false; + if (poll(() -> getPid(systemuiPidCommand) != initialPidOfSystemUI)) { + for (StatusBarNotification sbn : notificationManager.getActiveNotifications()) { + isDeviceVulnerable = sbn.getPackageName().equals(context.getPackageName()); + if (isDeviceVulnerable) { + break; + } + } + } + assertFalse(context.getString(R.string.failMsg), isDeviceVulnerable); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // In case of without fix, a crash window stays. + SystemUtil.runShellCommand( + mInstrumentation, context.getString(R.string.shellCommandForHome)); + } catch (Exception ignore) { + // ignore + } + } + } + + private int getPid(String commandToFindPid) { + try { + // Getting pid of com.android.systemui + return Integer.parseInt( + SystemUtil.runShellCommand(mInstrumentation, commandToFindPid).trim()); + } catch (Exception e) { + // ignore and return -1 + return -1; + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2023_40119.java b/tests/tests/security/src/android/security/cts/CVE_2023_40119.java new file mode 100644 index 00000000000..96a7f29ce61 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2023_40119.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 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.Assert.assertEquals; +import static org.junit.Assume.assumeNoException; + +import android.net.Uri; +import android.os.Parcel; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2023_40119 extends StsExtraBusinessLogicTestCase { + + @AsbSecurityTest(cveBugId = 231476072) + @Test + public void testPocCVE_2023_40119() { + try { + Parcel parcel = Parcel.obtain(); + + // Create an evil URI passing a scheme (that hides an unsafe authority and the ssp both) + // , a fake ssp and a fragment to Uri.fromParts() and write the evil URI to a parcel. + Uri.fromParts( + "scheme://notAllowedAuthorityAndSsp" /* scheme */, + "allowedAuthorityAndSsp" /* scheme-specific-part */, + "fragment" /* fragment */) + .writeToParcel(parcel, 0 /* No additional flags or options */); + parcel.setDataPosition(0); + + Uri uriFromParcel = Uri.CREATOR.createFromParcel(parcel); + + // Without fix, the scheme from parsing the string representation of uriFromParcel will + // return "scheme" as Uri parser checks for the delimiter ':' but the scheme from + // uriFromParcel.getScheme() will return "scheme://notAllowedAuthorityAndSsp" which + // hides the "not allowed" authority ("notAllowedAuthorityAndSsp") in the scheme + // enabling bypass of authority checks. + assertEquals( + "Vulnerable to b/231476072 !!, URIs are not canonicalized across AIDL" + + " boundaries", + Uri.parse(uriFromParcel.toString()).getScheme(), + uriFromParcel.getScheme()); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CertificateData.java b/tests/tests/security/src/android/security/cts/CertificateData.java index d088d0a77bc..9c8cd1e961a 100644 --- a/tests/tests/security/src/android/security/cts/CertificateData.java +++ b/tests/tests/security/src/android/security/cts/CertificateData.java @@ -24,21 +24,74 @@ package android.security.cts; * to generate this file. */ class CertificateData { + static final String[] OPTIONAL_CERTIFICATE_DATA = { + "B8:BE:6D:CB:56:F1:55:B9:63:D4:12:CA:4E:06:34:C7:94:B2:1C:C0", + "FF:BD:CD:E7:82:C8:43:5E:3C:6F:26:86:5C:CA:A8:3A:45:5B:C3:0A", + "51:C6:E7:08:49:06:6E:F3:92:D4:5C:A0:0D:6D:A3:62:8F:C3:52:39", + "58:D1:DF:95:95:67:6B:63:C0:F0:5B:1C:17:4D:8B:84:0B:C8:78:BD", + "6B:A0:B0:98:E1:71:EF:5A:AD:FE:48:15:80:77:10:F4:BD:6F:0B:28", + "53:A2:B0:4B:CA:6B:D6:45:E6:39:8A:8E:C4:0D:D2:BF:77:C3:A2:90", + "D0:67:C1:13:51:01:0C:AA:D0:C7:6A:65:37:31:16:26:4F:53:71:A2", + "17:F3:DE:5E:9F:0F:19:E9:8E:F6:1F:32:26:6E:20:C4:07:AE:30:EE", + "F3:3E:78:3C:AC:DF:F4:A2:CC:AC:67:55:69:56:D7:E5:16:3C:E1:ED", + "BC:B0:C1:9D:E9:98:92:70:19:38:57:E9:8D:A7:B4:5D:6E:EE:01:48", + "6A:92:E4:A8:EE:1B:EC:96:45:37:E3:29:57:49:CD:96:E3:E5:D2:60", + "77:D3:03:67:B5:E0:0C:15:F6:0C:38:61:DF:7C:E1:3B:92:46:4D:47", + "62:FF:D9:9E:C0:65:0D:03:CE:75:93:D2:ED:3F:2D:32:C9:E3:E5:4A", + "F9:E1:6D:DC:01:89:CF:D5:82:45:63:3E:C5:37:7D:C2:EB:93:6F:2B", + "ED:E5:71:80:2B:C8:92:B9:5B:83:3C:D2:32:68:3F:09:CD:A0:1E:46", + "C3:03:C8:22:74:92:E5:61:A2:9C:5F:79:91:2B:1E:44:13:91:30:3A", + "BD:B1:B9:3C:D5:97:8D:45:C6:26:14:55:F8:DB:95:C7:5A:D1:53:AF", + "5B:6E:68:D0:CC:15:B6:A0:5F:1E:C1:5F:AE:02:FC:6B:2F:5D:6F:74", + "84:1A:69:FB:F5:CD:1A:25:34:13:3D:E3:F8:FC:B8:99:D0:C9:14:B7", + "61:DB:8C:21:59:69:03:90:D8:7C:9C:12:86:54:CF:9D:3D:F4:DD:07", + "1F:5B:98:F0:E3:B5:F7:74:3C:ED:E6:B0:36:7D:32:CD:F4:09:41:67", + "02:2D:05:82:FA:88:CE:14:0C:06:79:DE:7F:14:10:E9:45:D7:A5:6D", + "A7:88:49:DC:5D:7C:75:8C:8C:DE:39:98:56:B3:AA:D0:B2:A5:71:35", + "9A:44:49:76:32:DB:DE:FA:D0:BC:FB:5A:7B:17:BD:9E:56:09:24:94", + "B8:0E:26:A9:BF:D2:B2:3B:C0:EF:46:C9:BA:C7:BB:F6:1D:0D:41:41", + "0B:BE:C2:27:22:49:CB:39:AA:DB:35:5C:53:E3:8C:AE:78:FF:B6:FE", + "A0:50:EE:0F:28:71:F4:27:B2:12:6D:6F:50:96:25:BA:CC:86:42:AF", + "CF:E9:70:84:0F:E0:73:0F:9D:F6:0C:7F:2C:4B:EE:20:46:34:9C:BB", + "39:B4:6C:D5:FE:80:06:EB:E2:2F:4A:BB:08:33:A0:AF:DB:B9:DD:84", + "F6:9C:DB:B0:FC:F6:02:13:B6:52:32:A6:A3:91:3F:16:70:DA:C3:E1", + "E5:8C:1C:C4:91:3B:38:63:4B:E9:10:6E:E3:AD:8E:6B:9D:D9:81:4A", + "B9:99:CD:D1:73:50:8A:C4:47:05:08:9C:8C:88:FB:BE:A0:2B:40:CD", + "C8:83:44:C0:18:AE:9F:CC:F1:87:B7:8F:22:D1:C5:D7:45:84:BA:E5", + "69:69:56:2E:40:80:F4:24:A1:E7:19:9F:14:BA:F3:EE:58:AB:6A:BB", + "75:E0:AB:B6:13:85:12:27:1C:04:F8:5F:DD:DE:38:E4:B7:24:2E:FE", + "DA:C9:02:4F:54:D8:F6:DF:94:93:5F:B1:73:26:38:CA:6A:D7:7C:13", + "78:6A:74:AC:76:AB:14:7F:9C:6A:30:50:BA:9E:A8:7E:FE:9A:CE:3C", + "8D:17:84:D5:37:F3:03:7D:EC:70:FE:57:8B:51:9A:99:E6:10:D7:B0", + "74:F8:A3:C3:EF:E7:B3:90:06:4B:83:90:3C:21:64:60:20:E5:DF:CE", + "5F:43:E5:B1:BF:F8:78:8C:AC:1C:C7:CA:4A:9A:C6:22:2B:CC:34:C6", + "76:E2:7E:C1:4F:DB:82:C1:C0:A6:75:B5:05:BE:3D:29:B4:ED:DB:BB", + "FE:45:65:9B:79:03:5B:98:A1:61:B5:51:2E:AC:DA:58:09:48:22:4D", + "36:79:CA:35:66:87:72:30:4D:30:A5:FB:87:3B:0F:A7:7B:B7:0D:54", + "D8:EB:6B:41:51:92:59:E0:F3:E7:85:00:C0:3D:B6:88:97:C9:EE:FC", + "2A:1D:60:27:D9:4A:B1:0A:1C:4D:91:5C:CD:33:A0:CB:3E:2D:54:CB", + "DE:3F:40:BD:50:93:D3:9B:6C:60:F6:DA:BC:07:62:01:00:89:76:C9", + "30:D4:24:6F:07:FF:DB:91:89:8A:0B:E9:49:66:11:EB:8C:5E:46:E5", + "4A:BD:EE:EC:95:0D:35:9C:89:AE:C7:52:A1:2C:5B:29:F6:D6:AA:0C", + "37:F7:6D:E6:07:7C:90:C5:B1:3E:93:1A:B7:41:10:B4:F2:E4:9A:27", + "28:90:3A:63:5B:52:80:FA:E6:77:4C:0B:6D:A7:D6:BA:A6:4A:F2:E8", + "3B:C0:38:0B:33:C3:F6:A6:0C:86:15:22:93:D9:DF:F5:4B:81:C0:04", + "D2:73:96:2A:2A:5E:39:9F:73:3F:E1:C7:1E:64:3F:03:38:34:FC:4D", + "E1:C9:50:E6:EF:22:F8:4C:56:45:72:8B:92:20:60:D7:D5:A7:A3:E8" + }; + static final String[] CERTIFICATE_DATA = { "99:9A:64:C3:7F:F4:7D:9F:AB:95:F1:47:69:89:14:60:EE:C4:C3:C5", "D1:CB:CA:5D:B2:D5:2A:7F:69:3B:67:4D:E5:F0:5A:1D:0C:95:7D:F0", - "6B:A0:B0:98:E1:71:EF:5A:AD:FE:48:15:80:77:10:F4:BD:6F:0B:28", "92:5A:8F:8D:2C:6D:04:E0:66:5F:59:6A:FF:22:D8:63:E8:25:6F:3F", "B4:90:82:DD:45:0C:BE:8B:5B:B1:66:D3:E2:A4:08:26:CD:ED:42:CF", "53:A2:B0:4B:CA:6B:D6:45:E6:39:8A:8E:C4:0D:D2:BF:77:C3:A2:90", "58:E8:AB:B0:36:15:33:FB:80:F7:9B:1B:6D:29:D3:FF:8D:5F:00:F0", "55:A6:72:3E:CB:F2:EC:CD:C3:23:74:70:19:9D:2A:BE:11:E3:81:D1", "D6:9B:56:11:48:F0:1C:77:C5:45:78:C1:09:26:DF:5B:85:69:76:AD", - "D0:67:C1:13:51:01:0C:AA:D0:C7:6A:65:37:31:16:26:4F:53:71:A2", "09:3C:61:F3:8B:8B:DC:7D:55:DF:75:38:02:05:00:E1:25:F5:C8:36", "27:96:BA:E6:3F:18:01:E2:77:26:1B:A0:D7:77:70:02:8F:20:EE:E4", "AD:7E:1C:28:B0:64:EF:8F:60:03:40:20:14:C3:D0:E3:37:0E:B5:8A", - "17:F3:DE:5E:9F:0F:19:E9:8E:F6:1F:32:26:6E:20:C4:07:AE:30:EE", "1F:24:C6:30:CD:A4:18:EF:20:69:FF:AD:4F:DD:5F:46:3A:1B:69:AA", "DA:FA:F7:FA:66:84:EC:06:8F:14:50:BD:C7:C2:81:A5:BC:A9:64:57", "2D:0D:52:14:FF:9E:AD:99:24:01:74:20:47:6E:6C:85:27:27:F5:43", @@ -52,7 +105,6 @@ class CertificateData { "E0:11:84:5E:34:DE:BE:88:81:B9:9C:F6:16:26:D1:96:1F:C3:B9:31", "93:05:7A:88:15:C6:4F:CE:88:2F:FA:91:16:52:28:78:BC:53:64:17", "50:30:06:09:1D:97:D4:F5:AE:39:F7:CB:E7:92:7D:7D:65:2D:34:31", - "F3:3E:78:3C:AC:DF:F4:A2:CC:AC:67:55:69:56:D7:E5:16:3C:E1:ED", "8C:F4:27:FD:79:0C:3A:D1:66:06:8D:E8:1E:57:EF:BB:93:22:72:D4", "2F:78:3D:25:52:18:A7:4A:65:39:71:B5:2C:A2:9C:45:15:6F:E9:19", "BA:29:41:60:77:98:3F:F4:F3:EF:F2:31:05:3B:2E:EA:6D:4D:45:FD", @@ -62,7 +114,6 @@ class CertificateData { "6A:92:E4:A8:EE:1B:EC:96:45:37:E3:29:57:49:CD:96:E3:E5:D2:60", "74:3A:F0:52:9B:D0:32:A0:F4:4A:83:CD:D4:BA:A9:7B:7C:2E:C4:9A", "66:31:BF:9E:F7:4F:9E:B6:C9:D5:A6:0C:BA:6A:BE:D1:F7:BD:EF:7B", - "77:D3:03:67:B5:E0:0C:15:F6:0C:38:61:DF:7C:E1:3B:92:46:4D:47", "F3:73:B3:87:06:5A:28:84:8A:F2:F3:4A:CE:19:2B:DD:C7:8E:9C:AC", "62:FF:D9:9E:C0:65:0D:03:CE:75:93:D2:ED:3F:2D:32:C9:E3:E5:4A", "F9:E1:6D:DC:01:89:CF:D5:82:45:63:3E:C5:37:7D:C2:EB:93:6F:2B", @@ -70,8 +121,6 @@ class CertificateData { "CA:BD:2A:79:A1:07:6A:31:F2:1D:25:36:35:CB:03:9D:43:29:A5:E8", "43:13:BB:96:F1:D5:86:9B:C1:4E:6A:92:F6:CF:F6:34:69:87:82:37", "05:63:B8:63:0D:62:D7:5A:BB:C8:AB:1E:4B:DF:B5:A8:99:B2:4D:43", - "ED:E5:71:80:2B:C8:92:B9:5B:83:3C:D2:32:68:3F:09:CD:A0:1E:46", - "C3:03:C8:22:74:92:E5:61:A2:9C:5F:79:91:2B:1E:44:13:91:30:3A", "D1:EB:23:A4:6D:17:D6:8F:D9:25:64:C2:F1:F1:60:17:64:D8:E3:49", "B8:01:86:D1:EB:9C:86:A5:41:04:CF:30:54:F3:4C:52:B7:E5:58:C6", "4C:DD:51:A3:D1:F5:20:32:14:B0:C6:C5:32:23:03:91:C7:46:42:6D", @@ -102,7 +151,6 @@ class CertificateData { "67:65:0D:F1:7E:8E:7E:5B:82:40:A4:F4:56:4B:CF:E2:3D:69:C6:F0", "DD:FB:16:CD:49:31:C9:73:A2:03:7D:3F:C8:3A:4D:7D:77:5D:05:E4", "36:B1:2B:49:F9:81:9E:D7:4C:9E:BC:38:0F:C6:56:8F:5D:AC:B2:F7", - "61:DB:8C:21:59:69:03:90:D8:7C:9C:12:86:54:CF:9D:3D:F4:DD:07", "E2:52:FA:95:3F:ED:DB:24:60:BD:6E:28:F3:9C:CC:CF:5E:B3:3F:DE", "26:F9:93:B4:ED:3D:28:27:B0:B9:4B:A7:E9:15:1D:A3:8D:92:E5:32", "3B:C4:9F:48:F8:F3:73:A0:9C:1E:BD:F8:5B:B1:C3:65:C7:D8:11:B3", @@ -122,8 +170,6 @@ class CertificateData { "B3:1E:B1:B7:40:E3:6C:84:02:DA:DC:37:D4:4D:F5:D4:67:49:52:F9", "A7:88:49:DC:5D:7C:75:8C:8C:DE:39:98:56:B3:AA:D0:B2:A5:71:35", "F5:17:A2:4F:9A:48:C6:C9:F8:A2:00:26:9F:DC:0F:48:2C:AB:30:89", - "9A:44:49:76:32:DB:DE:FA:D0:BC:FB:5A:7B:17:BD:9E:56:09:24:94", - "B8:0E:26:A9:BF:D2:B2:3B:C0:EF:46:C9:BA:C7:BB:F6:1D:0D:41:41", "DF:3C:24:F9:BF:D6:66:76:1B:26:80:73:FE:06:D1:CC:8D:4F:82:A4", "D3:DD:48:3E:2B:BF:4C:05:E8:AF:10:F5:FA:76:26:CF:D3:DC:30:92", "B8:23:6B:00:2F:1D:16:86:53:01:55:6C:11:A4:37:CA:EB:FF:C3:BB", @@ -150,9 +196,6 @@ class CertificateData { "89:DF:74:FE:5C:F4:0F:4A:80:F9:E3:37:7D:54:DA:91:E1:01:31:8E", "7E:04:DE:89:6A:3E:66:6D:00:E6:87:D3:3F:FA:D9:3B:E8:3D:34:9E", "2F:8F:36:4F:E1:58:97:44:21:59:87:A5:2A:9A:D0:69:95:26:7F:B5", - "F6:9C:DB:B0:FC:F6:02:13:B6:52:32:A6:A3:91:3F:16:70:DA:C3:E1", - "E5:8C:1C:C4:91:3B:38:63:4B:E9:10:6E:E3:AD:8E:6B:9D:D9:81:4A", - "B9:99:CD:D1:73:50:8A:C4:47:05:08:9C:8C:88:FB:BE:A0:2B:40:CD", "14:88:4E:86:26:37:B0:26:AF:59:62:5C:40:77:EC:35:29:BA:96:01", "8A:C7:AD:8F:73:AC:4E:C1:B5:75:4D:A5:40:F4:FC:CF:7C:B5:8E:8C", "C8:83:44:C0:18:AE:9F:CC:F1:87:B7:8F:22:D1:C5:D7:45:84:BA:E5", diff --git a/tests/tests/security/src/android/security/cts/CertificateTest.java b/tests/tests/security/src/android/security/cts/CertificateTest.java index 75693383995..875d0aae2ab 100644 --- a/tests/tests/security/src/android/security/cts/CertificateTest.java +++ b/tests/tests/security/src/android/security/cts/CertificateTest.java @@ -97,8 +97,11 @@ public class CertificateTest { System.setProperty("system.certs.enabled", mApexCertsEnabled); Set<String> expectedCertificates = new HashSet<String>( Arrays.asList(CertificateData.CERTIFICATE_DATA)); + Set<String> optionalCertificates = new HashSet<String>( + Arrays.asList(CertificateData.OPTIONAL_CERTIFICATE_DATA)); Set<String> deviceCertificates = getDeviceCertificates(); deviceCertificates.removeAll(expectedCertificates); + deviceCertificates.removeAll(optionalCertificates); assertEquals("Unknown CA certificates", Collections.EMPTY_SET, deviceCertificates); } diff --git a/tests/tests/sharesheet/AndroidTest.xml b/tests/tests/sharesheet/AndroidTest.xml index 5a3c8354f0b..f198341a740 100644 --- a/tests/tests/sharesheet/AndroidTest.xml +++ b/tests/tests/sharesheet/AndroidTest.xml @@ -16,7 +16,7 @@ <configuration description="Config for CTS Sharesheet test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="sysui" /> <!-- Instant apps can't access ShortcutManager --> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> diff --git a/tests/tests/soundtrigger/AndroidTest.xml b/tests/tests/soundtrigger/AndroidTest.xml index f6df62c4ca0..f9aa79ccff9 100644 --- a/tests/tests/soundtrigger/AndroidTest.xml +++ b/tests/tests/soundtrigger/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS Voice Interaction test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="media" /> <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/telecom/AndroidManifest.xml b/tests/tests/telecom/AndroidManifest.xml index 7b8b8654396..9c2b522d52d 100644 --- a/tests/tests/telecom/AndroidManifest.xml +++ b/tests/tests/telecom/AndroidManifest.xml @@ -78,14 +78,6 @@ </intent-filter> </service> - <service android:name="android.telecom.cts.CtsSimCallManagerConnectionService" - android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" - android:exported="true"> - <intent-filter> - <action android:name="android.telecom.ConnectionService"/> - </intent-filter> - </service> - <service android:name="android.telecom.cts.CtsSelfManagedConnectionService" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE" android:exported="true"> diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java index b0eca398b99..d2deeaf508a 100644 --- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java @@ -140,7 +140,7 @@ public class CallDetailsTest extends BaseTelecomTestWithMockServices { getInstrumentation().getTargetContext().getContentResolver(); try { mContactUri = insertContactWithPhoto( - resolver, getTestNumber().toString()); + resolver, getTestNumber().getSchemeSpecificPart()); } catch (Exception e) { assertTrue("Failed to insert test contact into ContactsProvider", false); } @@ -414,7 +414,7 @@ public class CallDetailsTest extends BaseTelecomTestWithMockServices { if (!mShouldTestTelecom) { return; } - String phoneNumber = getTestNumber().toString(); + String phoneNumber = getTestNumber().getSchemeSpecificPart(); Uri contactRef = PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.buildUpon() .appendPath(phoneNumber) diff --git a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java index 883d8448000..06b9e12cf0a 100644 --- a/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/CarModeInCallServiceTest.java @@ -45,6 +45,8 @@ public class CarModeInCallServiceTest extends BaseTelecomTestWithMockServices { private static final int ASYNC_TIMEOUT = 10000; private static final String CARMODE_APP1_PACKAGE = "android.telecom.cts.carmodetestapp"; private static final String CARMODE_APP2_PACKAGE = "android.telecom.cts.carmodetestapptwo"; + // Flag to temp disable (flaky) test + private static final boolean SHOULD_IGNORE_TEST = true; private ICtsCarModeInCallServiceControl mCarModeIncallServiceControlOne; private ICtsCarModeInCallServiceControl mCarModeIncallServiceControlTwo; @@ -577,7 +579,7 @@ public class CarModeInCallServiceTest extends BaseTelecomTestWithMockServices { } public void testSwitchToCarModeWhenEnableCarModeApp() throws Exception { - if (!mShouldTestTelecom) { + if (!mShouldTestTelecom || SHOULD_IGNORE_TEST) { return; } if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH) diff --git a/tests/tests/telecom/src/android/telecom/cts/CtsSimCallManagerConnectionService.java b/tests/tests/telecom/src/android/telecom/cts/CtsSimCallManagerConnectionService.java deleted file mode 100644 index 731f09f9499..00000000000 --- a/tests/tests/telecom/src/android/telecom/cts/CtsSimCallManagerConnectionService.java +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.telecom.cts; - -import android.content.Intent; -import android.telecom.Conference; -import android.telecom.Connection; -import android.telecom.ConnectionRequest; -import android.telecom.ConnectionService; -import android.telecom.PhoneAccountHandle; -import android.telecom.RemoteConference; -import android.telecom.RemoteConnection; -import android.util.Log; - -import java.util.Collection; -import java.util.Collections; -import java.util.concurrent.CountDownLatch; - -/** - * This is the official ConnectionService for Telecom's CTS App. Since telecom requires that a - * CS be registered in the AndroidManifest.xml file, we have to have a single implementation - * of a CS and this is it. To test specific CS behavior, tests will implement their own CS and - * tell CtsConnectionService to forward any method invocations to that test's implementation. - * This is set up using {@link #setUp} and should be cleaned up before the end of the test using - * {@link #tearDown}. - * - * sConnectionService: Contains the connection service object provided by the current test in - * progress. We use this object to forward any communication received from the - * Telecom framework to the test connection service. - * sTelecomConnectionService: Contains the connection service object registered to the Telecom - * framework. We use this object to forward any communication from the - * test connection service to the Telecom framework. After Telecom - * binds to CtsConnectionService, this is set to be the instance of - * CtsConnectionService created by the framework after Telecom binds. - */ -public class CtsSimCallManagerConnectionService extends ConnectionService { - private static final String LOG_TAG = "CtsSimCallManagerConnectionService"; - // This is the connection service implemented by the test - private static ConnectionService sConnectionService; - // This is the connection service registered with Telecom - private static ConnectionService sTelecomConnectionService; - private static boolean sIsBound = false; - private static CountDownLatch sServiceUnBoundLatch = new CountDownLatch(1); - - @Override - public void onBindClient(Intent intent) { - sTelecomConnectionService = this; - Log.i("TelecomCTS", "CS (sim call mgr) bound"); - sIsBound = true; - } - - private static Object sLock = new Object(); - - public static void setUp(ConnectionService connectionService) throws Exception { - synchronized (sLock) { - if (sConnectionService != null) { - throw new Exception("Mock ConnectionService exists. Failed to call setUp()."); - } - sConnectionService = connectionService; - } - } - - public static void tearDown() { - synchronized (sLock) { - sConnectionService = null; - sTelecomConnectionService = null; - } - } - - @Override - public Connection onCreateOutgoingConnection(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - return sConnectionService.onCreateOutgoingConnection( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, - "Tried to create outgoing connection when sConnectionService null!"); - return null; - } - } - } - - @Override - public Connection onCreateIncomingConnection(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - return sConnectionService.onCreateIncomingConnection( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, - "Tried to create incoming connection when sConnectionService null!"); - return null; - } - } - } - - @Override - public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - if (sConnectionService != null) { - sConnectionService.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, - request); - } else { - Log.e(LOG_TAG, - "onCreateIncomingConnectionFailed called when sConnectionService null!"); - } - } - - @Override - public Conference onCreateOutgoingConference(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - return sConnectionService.onCreateOutgoingConference(connectionManagerPhoneAccount, - request); - } else { - Log.e(LOG_TAG, - "onCreateOutgoingConference called when sConnectionService null!"); - return null; - } - } - } - - @Override - public void onCreateOutgoingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onCreateOutgoingConferenceFailed(connectionManagerPhoneAccount, - request); - } else { - Log.e(LOG_TAG, - "onCreateOutgoingConferenceFailed called when sConnectionService null!"); - } - } - } - - @Override - public Conference onCreateIncomingConference(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - return sConnectionService.onCreateIncomingConference(connectionManagerPhoneAccount, - request); - } else { - Log.e(LOG_TAG, - "onCreateIncomingConference called when sConnectionService null!"); - return null; - } - } - } - - @Override - public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onCreateIncomingConferenceFailed(connectionManagerPhoneAccount, - request); - } else { - Log.e(LOG_TAG, - "onCreateIncomingConferenceFailed called when sConnectionService null!"); - } - } - } - - @Override - public void onConference(Connection connection1, Connection connection2) { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onConference(connection1, connection2); - } else { - Log.e(LOG_TAG, - "onConference called when sConnectionService null!"); - } - } - } - - @Override - public void onRemoteExistingConnectionAdded(RemoteConnection connection) { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onRemoteExistingConnectionAdded(connection); - } else { - Log.e(LOG_TAG, - "onRemoteExistingConnectionAdded called when sConnectionService null!"); - } - } - } - - public static void addConferenceToTelecom(Conference conference) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - sTelecomConnectionService.addConference(conference); - } else { - Log.e(LOG_TAG, "addConferenceToTelecom called when" - + " sTelecomConnectionService null!"); - } - } - } - - public static void addExistingConnectionToTelecom( - PhoneAccountHandle phoneAccountHandle, Connection connection) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - sTelecomConnectionService.addExistingConnection(phoneAccountHandle, connection); - } else { - Log.e(LOG_TAG, "addExistingConnectionToTelecom called when" - + " sTelecomConnectionService null!"); - } - } - } - - public static Collection<Connection> getAllConnectionsFromTelecom() { - synchronized (sLock) { - if (sTelecomConnectionService == null) { - return Collections.EMPTY_LIST; - } - return sTelecomConnectionService.getAllConnections(); - } - } - - public static RemoteConnection createRemoteOutgoingConnectionToTelecom( - PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - return sTelecomConnectionService.createRemoteOutgoingConnection( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, "createRemoteOutgoingConnectionToTelecom called when" - + " sTelecomConnectionService null!"); - return null; - } - } - } - - public static RemoteConnection createRemoteIncomingConnectionToTelecom( - PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - return sTelecomConnectionService.createRemoteIncomingConnection( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, "createRemoteIncomingConnectionToTelecom called when" - + " sTelecomConnectionService null!"); - return null; - } - } - } - - public static RemoteConference createRemoteIncomingConferenceToTelecom( - PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - return sTelecomConnectionService.createRemoteIncomingConference( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, "createRemoteIncomingConferenceToTelecom called when" - + " sTelecomConnectionService null!"); - return null; - } - } - } - - - public static RemoteConference createRemoteOutgoingConferenceToTelecom( - PhoneAccountHandle connectionManagerPhoneAccount, - ConnectionRequest request) { - synchronized (sLock) { - if (sTelecomConnectionService != null) { - return sTelecomConnectionService.createRemoteOutgoingConference( - connectionManagerPhoneAccount, request); - } else { - Log.e(LOG_TAG, "createRemoteOutgoingConferenceToTelecom called when" - + " sTelecomConnectionService null!"); - return null; - } - } - } - - @Override - public void onRemoteConferenceAdded(RemoteConference conference) { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onRemoteConferenceAdded(conference); - } else { - Log.e(LOG_TAG, - "onRemoteConferenceAdded called when sConnectionService null!"); - } - } - } - - @Override - public void onConnectionServiceFocusGained() { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onConnectionServiceFocusGained(); - } else { - Log.e(LOG_TAG, - "onConnectionServiceFocusGained called when sConnectionService null!"); - } - } - } - - @Override - public void onConnectionServiceFocusLost() { - synchronized (sLock) { - if (sConnectionService != null) { - sConnectionService.onConnectionServiceFocusLost(); - } else { - Log.e(LOG_TAG, - "onConnectionServiceFocusLost called when sConnectionService null!"); - } - } - } - - @Override - public boolean onUnbind(Intent intent) { - synchronized (sLock) { - Log.i(LOG_TAG, "Service has been unbound"); - sIsBound = false; - sServiceUnBoundLatch.countDown(); - sConnectionService = null; - sTelecomConnectionService = null; - return super.onUnbind(intent); - } - } - - public static boolean isServiceRegisteredToTelecom() { - return sTelecomConnectionService != null; - } - - public static boolean isBound() { - return sIsBound; - } - - public static boolean waitForUnBinding() { - return TestUtils.waitForLatchCountDown(sServiceUnBoundLatch); - } -} diff --git a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallOnSimCallManagerTest.java b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallOnSimCallManagerTest.java index 69d564f80c4..5cc1f70c80a 100644 --- a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallOnSimCallManagerTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallOnSimCallManagerTest.java @@ -17,6 +17,7 @@ package android.telecom.cts; import static android.telecom.cts.TestUtils.ACCOUNT_ID_1; +import static android.telecom.cts.TestUtils.ACCOUNT_ID_EMERGENCY; import static android.telecom.cts.TestUtils.ACCOUNT_LABEL; import static android.telecom.cts.TestUtils.PACKAGE; @@ -45,11 +46,14 @@ import java.util.concurrent.TimeUnit; public class EmergencyCallOnSimCallManagerTest extends BaseTelecomTestWithMockServices { private static final String TAG = "EmergencyCallOnSimCallManagerTest"; public static final String SIM_CALL_MANAGER_COMPONENT = - "android.telecom.cts.CtsSimCallManagerConnectionService"; + CtsConnectionService.class.getCanonicalName(); public static final PhoneAccountHandle TEST_SIM_CALL_MANAGER_PHONE_ACCOUNT_HANDLE = new PhoneAccountHandle(new ComponentName(PACKAGE, SIM_CALL_MANAGER_COMPONENT), ACCOUNT_ID_1); + public static final PhoneAccountHandle TEST_SIM_EMERGENCY_PHONE_ACCOUNT_HANDLE = + new PhoneAccountHandle(new ComponentName(PACKAGE, SIM_CALL_MANAGER_COMPONENT), + ACCOUNT_ID_EMERGENCY); public static final PhoneAccount TEST_SIM_CALL_MANAGER_ACCOUNT = PhoneAccount.builder( TEST_SIM_CALL_MANAGER_PHONE_ACCOUNT_HANDLE, ACCOUNT_LABEL) .setAddress(Uri.parse("tel:555-TEST")) @@ -61,7 +65,6 @@ public class EmergencyCallOnSimCallManagerTest extends BaseTelecomTestWithMockSe .build(); private static final String TEST_PROVIDER = "test_provider"; - private static final Uri TEST_ADDRESS_1 = Uri.fromParts("sip", "call1@test.com", null); @Override public void setUp() throws Exception { @@ -71,8 +74,7 @@ public class EmergencyCallOnSimCallManagerTest extends BaseTelecomTestWithMockSe if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; try { - setupConnectionService(null, FLAG_REGISTER | FLAG_ENABLE); - + setupConnectionService(null, 0); mTelecomManager.registerPhoneAccount(TEST_SIM_CALL_MANAGER_ACCOUNT); TestUtils.enablePhoneAccount(getInstrumentation(), TEST_SIM_CALL_MANAGER_PHONE_ACCOUNT_HANDLE); @@ -295,7 +297,7 @@ public class EmergencyCallOnSimCallManagerTest extends BaseTelecomTestWithMockSe TestUtils.setTestEmergencyPhoneAccountPackageFilter(getInstrumentation(), mContext); // Emergency calls require special capabilities. TestUtils.registerEmergencyPhoneAccount(getInstrumentation(), - TEST_SIM_CALL_MANAGER_PHONE_ACCOUNT_HANDLE, + TEST_SIM_EMERGENCY_PHONE_ACCOUNT_HANDLE, TestUtils.ACCOUNT_LABEL + "E", "tel:555-EMER"); mIsEmergencyCallingSetup = true; } @@ -306,6 +308,6 @@ public class EmergencyCallOnSimCallManagerTest extends BaseTelecomTestWithMockSe TestUtils.clearSystemDialerOverride(getInstrumentation()); TestUtils.clearTestEmergencyNumbers(getInstrumentation()); TestUtils.clearTestEmergencyPhoneAccountPackageFilter(getInstrumentation()); - mTelecomManager.unregisterPhoneAccount(TEST_SIM_CALL_MANAGER_PHONE_ACCOUNT_HANDLE); + mTelecomManager.unregisterPhoneAccount(TEST_SIM_EMERGENCY_PHONE_ACCOUNT_HANDLE); } } diff --git a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java index 783200e7da0..117ab1a16a3 100644 --- a/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java +++ b/tests/tests/telecom/src/android/telecom/cts/EmergencyCallTests.java @@ -45,6 +45,8 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { private static final String DUMPSYS_COMMAND = "dumpsys telecom"; private static final int MIN_LINES_PER_DROPBOX_ENTRY = 15; private static final int MAX_READ_BYTES_PER_DROP_BOX_ENTRY = 5000; + private static final long DAYS_BACK_TO_SEARCH_EMERGENCY_DROP_BOX_ENTRIES_IN_MS = + 30L * 24L * 60L * 60L * 1000L; private static final String DIAG_ERROR_MSG = "DiagnosticDataCollector error executing cmd"; private CountDownLatch mLatchForDropBox; private IntentFilter dropBoxIntentFilter; @@ -170,11 +172,10 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { for (String line : content) { assertFalse(line.contains(DIAG_ERROR_MSG)); //verify that telecom dumpsys output also has this data - if (lineCount < MAX_LINES_TO_VERIFY_IN_DUMPSYS_OUTPUT) { + if (lineCount++ < MAX_LINES_TO_VERIFY_IN_DUMPSYS_OUTPUT) { //we only check top x lines to verify presence in dumpsys output assertTrue("line not found: " + line, dumpOutput.contains(line)); } - } entry = dm.getNextEntry(DROPBOX_TAG, entryTime); if(totalEntries >= 3) @@ -187,7 +188,8 @@ public class EmergencyCallTests extends BaseTelecomTestWithMockServices { */ public void testEmergencyCallFailureCreatesDropboxEntries() throws Exception { if (!mShouldTestTelecom || !TestUtils.hasTelephonyFeature(mContext)) return; - long startTime = System.currentTimeMillis(); + long startTime = System.currentTimeMillis() + - DAYS_BACK_TO_SEARCH_EMERGENCY_DROP_BOX_ENTRIES_IN_MS; mContext.registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/tests/tests/telephony/current/src/android/telephony/cts/ConnectivityManagerTestOnMockModem.java b/tests/tests/telephony/current/src/android/telephony/cts/ConnectivityManagerTestOnMockModem.java index 04b30f3b232..48ca465d5f6 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/ConnectivityManagerTestOnMockModem.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/ConnectivityManagerTestOnMockModem.java @@ -43,11 +43,13 @@ import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.ApiTest; +import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** Test MockModemService interfaces. */ @@ -55,12 +57,13 @@ public class ConnectivityManagerTestOnMockModem { private static final String TAG = "ConnectivityManagerTestOnMockModem"; private static final int TIMEOUT_NETWORK_VALIDATION = 20000; private static final int WAIT_MSEC = 500; + private static final int NETWORK_AVAILABLE_SEC = 60; private static boolean sIsValidate; private static boolean sIsOnAvailable; private static Network sDefaultNetwork; private static Object sIsValidateLock = new Object(); private static Object sIsOnAvailableLock = new Object(); - private static NetworkCallback sNetworkCallback; + private static CMNetworkCallback sNetworkCallback; private static MockModemManager sMockModemManager; private static TelephonyManager sTelephonyManager; private static ConnectivityManager sConnectivityManager; @@ -69,6 +72,69 @@ public class ConnectivityManagerTestOnMockModem { private static final boolean DEBUG = !"user".equals(Build.TYPE); private static boolean sIsMultiSimDevice; + private static class CMNetworkCallback extends NetworkCallback { + final CountDownLatch mNetworkLatch = new CountDownLatch(1); + + @Override + public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { + sDefaultNetwork = network; + Log.d( + TAG, + "Network capabilities changed. network: " + + network + + ", NetworkCapabilities: " + + nc); + + if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { + Log.d( + TAG, + "Network capabilities changed. network: " + + network + + " ,validation: Pass!"); + synchronized (sIsValidateLock) { + sIsValidate = true; + sIsValidateLock.notify(); + } + } else { + Log.d( + TAG, + "Network capabilities changed. network: " + + network + + " ,validation: Fail!"); + synchronized (sIsValidateLock) { + sIsValidate = false; + } + } + } + + @Override + public void onLost(Network network) { + sDefaultNetwork = network; + Log.d(TAG, "onLost(): network: " + network); + synchronized (sIsOnAvailableLock) { + sIsOnAvailable = false; + } + } + + @Override + public void onAvailable(Network network) { + sDefaultNetwork = network; + Log.d(TAG, "onAvailable(): network: " + network); + synchronized (sIsOnAvailableLock) { + sIsOnAvailable = true; + mNetworkLatch.countDown(); + } + } + + public void awaitNetwork() throws InterruptedException { + Log.d(TAG, "awaitNetwork(): " + NETWORK_AVAILABLE_SEC + " sec"); + mNetworkLatch.await(NETWORK_AVAILABLE_SEC, TimeUnit.SECONDS); + } + + @Override + public void onLinkPropertiesChanged(Network network, LinkProperties linkProperties) {} + } + @BeforeClass public static void beforeAllTests() throws Exception { TimeUnit.SECONDS.sleep(10); @@ -117,8 +183,6 @@ public class ConnectivityManagerTestOnMockModem { sMockModemManager = new MockModemManager(); assertNotNull(sMockModemManager); assertTrue(sMockModemManager.connectMockModemService()); - // register the network call back - registerNetworkCallback(); } @AfterClass @@ -128,8 +192,6 @@ public class ConnectivityManagerTestOnMockModem { if (!hasTelephonyFeature()) { return; } - // unregister the network call back - unregisterNetworkCallback(); // Rebind all interfaces which is binding to MockModemService to default. assertNotNull(sMockModemManager); assertTrue(sMockModemManager.disconnectMockModemService()); @@ -139,6 +201,15 @@ public class ConnectivityManagerTestOnMockModem { @Before public void beforeTest() throws Exception { assumeTrue(hasTelephonyFeature()); + registerNetworkCallback(); + } + + @After + public void afterTest() { + // unregister the network call back + if (sNetworkCallback != null) { + unregisterNetworkCallback(); + } } private static boolean isMultiSim(TelephonyManager tm) { @@ -200,63 +271,13 @@ public class ConnectivityManagerTestOnMockModem { return sIsOnAvailable; } + private static synchronized Network getDefaultNetwork() { + Log.d(TAG, "getDefaultNetwork: enter "); + return sDefaultNetwork; + } + private static void registerNetworkCallback() { - sNetworkCallback = - new NetworkCallback() { - @Override - public void onCapabilitiesChanged(Network network, NetworkCapabilities nc) { - sDefaultNetwork = network; - Log.d( - TAG, - "Network capabilities changed. network: " - + network - + ", NetworkCapabilities: " - + nc); - - if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)) { - Log.d( - TAG, - "Network capabilities changed. network: " - + network - + " ,validation: Pass!"); - synchronized (sIsValidateLock) { - sIsValidate = true; - sIsValidateLock.notify(); - } - } else { - Log.d( - TAG, - "Network capabilities changed. network: " - + network - + " ,validation: Fail!"); - synchronized (sIsValidateLock) { - sIsValidate = false; - } - } - } - - @Override - public void onLost(Network network) { - sDefaultNetwork = network; - Log.d(TAG, "onLost(): network: " + network); - synchronized (sIsOnAvailableLock) { - sIsOnAvailable = false; - } - } - - @Override - public void onAvailable(Network network) { - sDefaultNetwork = network; - Log.d(TAG, "onAvailable(): network: " + network); - synchronized (sIsOnAvailableLock) { - sIsOnAvailable = true; - } - } - - @Override - public void onLinkPropertiesChanged( - Network network, LinkProperties linkProperties) {} - }; + sNetworkCallback = new CMNetworkCallback(); try { sConnectivityManager.registerNetworkCallback( new NetworkRequest.Builder() @@ -273,8 +294,11 @@ public class ConnectivityManagerTestOnMockModem { private static void unregisterNetworkCallback() { try { sConnectivityManager.unregisterNetworkCallback(sNetworkCallback); + Log.d(TAG, "unregisterNetworkCallback"); } catch (IllegalArgumentException e) { Log.e(TAG, "IllegalArgumentException during unregisterNetworkCallback(): ", e); + } finally { + sNetworkCallback = null; } } @@ -297,13 +321,12 @@ public class ConnectivityManagerTestOnMockModem { sMockModemManager.changeNetworkService(slotId, MOCK_SIM_PROFILE_ID_TWN_CHT, true); // make sure the network is available - TimeUnit.SECONDS.sleep(5); + sNetworkCallback.awaitNetwork(); assertTrue(getNetworkOnAvailable()); // make sure the network is validated - sConnectivityManager.reportNetworkConnectivity(sDefaultNetwork, false); + sConnectivityManager.reportNetworkConnectivity(getDefaultNetwork(), false); waitForExpectedValidationState(true, TIMEOUT_NETWORK_VALIDATION); - TimeUnit.SECONDS.sleep(2); assertTrue(getNetworkValidated()); // Leave Service diff --git a/tests/tests/toastlegacy/OWNERS b/tests/tests/toastlegacy/OWNERS index d21846d5234..4065d860f4f 100644 --- a/tests/tests/toastlegacy/OWNERS +++ b/tests/tests/toastlegacy/OWNERS @@ -1,2 +1,2 @@ -# Bug component: 137825 +# Bug component: 1012119 include ../toast/OWNERS diff --git a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java index f63dc376a6d..14dca76e0e6 100644 --- a/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java +++ b/tests/tests/toastlegacy/src/android/widget/toast/cts/legacy/ToastTest.java @@ -17,6 +17,7 @@ package android.widget.toast.cts.legacy; import android.content.Intent; +import android.os.UserManager; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; import android.widget.toast.cts.BaseToastTest; @@ -25,6 +26,7 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import org.junit.Assert; +import org.junit.Assume; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -129,6 +131,8 @@ public class ToastTest extends BaseToastTest { @Test public void testAddTwoToastsViaAddingWindowApisWhenUidFocusedQuickly() throws Exception { + // Not supporting this test on Headless devices + Assume.assumeFalse(UserManager.isHeadlessSystemUserMode()); // dismiss deprecated app warnings dialog mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)); Thread.sleep(WAIT_LEGACY_DIALOG_CLOSE_MS); diff --git a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java index c160744e110..353c5a4263e 100644 --- a/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java +++ b/tests/tests/tv/src/android/media/tv/tuner/cts/TunerTest.java @@ -658,7 +658,8 @@ public class TunerTest { status.getInnerFec(); break; case FrontendStatus.FRONTEND_STATUS_TYPE_MODULATION: - if (info.getType() != FrontendSettings.TYPE_DVBT) + if (info.getType() != FrontendSettings.TYPE_DVBT && + info.getType() != FrontendSettings.TYPE_ANALOG) status.getModulation(); break; case FrontendStatus.FRONTEND_STATUS_TYPE_SPECTRAL: diff --git a/tests/tests/usb/AndroidTest.xml b/tests/tests/usb/AndroidTest.xml index ef712366cc6..1cda028118d 100644 --- a/tests/tests/usb/AndroidTest.xml +++ b/tests/tests/usb/AndroidTest.xml @@ -15,7 +15,7 @@ --> <configuration description="Config for CTS USB test cases"> <option name="test-suite-tag" value="cts" /> - <option name="config-descriptor:metadata" key="component" value="framework" /> + <option name="config-descriptor:metadata" key="component" value="systems" /> <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" /> <option name="config-descriptor:metadata" key="parameter" value="multi_abi" /> <option name="config-descriptor:metadata" key="parameter" value="secondary_user" /> diff --git a/tests/tests/view/res/layout-round/using_views_layout.xml b/tests/tests/view/res/layout-round/using_views_layout.xml new file mode 100644 index 00000000000..6e548ea28fa --- /dev/null +++ b/tests/tests/view/res/layout-round/using_views_layout.xml @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 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"> + + <TextView + android:id="@+id/country" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/country"/> + + <EditText + android:id="@+id/entry" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="@android:drawable/editbox_background" + android:layout_below="@id/country"/> + + <Button + android:id="@+id/cancel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/entry" + android:layout_alignParentRight="true" + android:layout_marginLeft="10dip" + android:text="@string/id_cancel"/> + + <Button + android:id="@+id/ok" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/entry" + android:layout_toLeftOf="@id/cancel" + android:layout_marginLeft="10dip" + android:text="@string/id_ok"/> + + <TextView + android:id="@+id/symbol" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/ok" + android:text="@string/symbol"/> + + <TextView + android:id="@+id/symbolball" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_below="@id/symbol" + android:layout_marginLeft="20dip"/> + + <TextView + android:id="@+id/warning" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:background="#aa0000" + android:layout_below="@id/symbolball" + android:text="@string/country_warning" + android:visibility="invisible" + android:layout_weight="2"/> + +</RelativeLayout> + diff --git a/tests/tests/view/src/android/view/cts/AutoHandwritingTest.java b/tests/tests/view/src/android/view/cts/AutoHandwritingTest.java index 81d72342ef0..a8818880097 100644 --- a/tests/tests/view/src/android/view/cts/AutoHandwritingTest.java +++ b/tests/tests/view/src/android/view/cts/AutoHandwritingTest.java @@ -40,14 +40,6 @@ public class AutoHandwritingTest { new ActivityTestRule<>(HandwritingActivity.class); @Test - public void autoHandwriting_defaultValueIsTrue() { - Activity activity = mActivityRule.getActivity(); - View view = activity.findViewById(R.id.default_view); - - assertTrue(view.isAutoHandwritingEnabled()); - } - - @Test public void autoHandwriting_setToTrueInXml() { Activity activity = mActivityRule.getActivity(); View view = activity.findViewById(R.id.auto_handwriting_enabled); diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java index f4cedb795aa..c3b8fb9be69 100644 --- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java +++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java @@ -1131,7 +1131,7 @@ public class ViewGroupTest implements CTSResult { } private void onResolvePointerIcon_scrollabilityAffectsPointerIcon(boolean vertical, - boolean canScroll, boolean pointerIsSystemArrow) { + boolean canScroll, boolean pointerIsArrayOrNull) { // Arrange @@ -1166,8 +1166,14 @@ public class ViewGroupTest implements CTSResult { // Assert - if (pointerIsSystemArrow) { - assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW), actualResult); + if (pointerIsArrayOrNull) { + // When the returned PointerIcon is null it will fallback to the system default for the + // given source devices. For mouse devices, the default is TYPE_ARROW. + // We also allow it to return TYPE_ARROW so that we don't break CTS test compatibility. + if (actualResult != null) { + assertEquals(PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_ARROW), + actualResult); + } } else { assertEquals(expectedPointerIcon, actualResult); } diff --git a/tests/tests/voiceinteraction/Android.bp b/tests/tests/voiceinteraction/Android.bp index 0ea0bf2c299..0105227a1cf 100644 --- a/tests/tests/voiceinteraction/Android.bp +++ b/tests/tests/voiceinteraction/Android.bp @@ -51,6 +51,7 @@ android_test { // Tag this module as a cts test artifact test_suites: [ "cts", + "gts", "general-tests", ], sdk_version: "test_current", diff --git a/tests/tests/voiceinteraction/AndroidTest.xml b/tests/tests/voiceinteraction/AndroidTest.xml index 2bd858b6d94..6191ac9a462 100644 --- a/tests/tests/voiceinteraction/AndroidTest.xml +++ b/tests/tests/voiceinteraction/AndroidTest.xml @@ -15,6 +15,8 @@ --> <configuration description="Config for CTS Voice Interaction test cases"> <option name="test-suite-tag" value="cts" /> + <!-- For downstream tests --> + <option name="test-suite-tag" value="gts" /> <option name="config-descriptor:metadata" key="component" value="framework" /> <option name="config-descriptor:metadata" key="parameter" value="all_foldable_states" /> <option name="config-descriptor:metadata" key="parameter" value="instant_app" /> diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AlwaysOnHotwordDetectorNoHdsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AlwaysOnHotwordDetectorNoHdsTest.java new file mode 100644 index 00000000000..e384f632e2d --- /dev/null +++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/AlwaysOnHotwordDetectorNoHdsTest.java @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2023 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.voiceinteraction.cts; + +import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; +import static android.Manifest.permission.MANAGE_HOTWORD_DETECTION; +import static android.Manifest.permission.RECORD_AUDIO; +import static android.content.pm.PackageManager.FEATURE_MICROPHONE; +import static android.voiceinteraction.cts.testcore.Helper.CTS_SERVICE_PACKAGE; +import static android.voiceinteraction.cts.testcore.Helper.MANAGE_VOICE_KEYPHRASES; +import static android.voiceinteraction.cts.testcore.Helper.createKeyphraseArray; +import static android.voiceinteraction.cts.testcore.Helper.createKeyphraseRecognitionExtraList; +import static android.voiceinteraction.cts.testcore.Helper.waitForFutureDoneAndAssertSuccessful; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; + +import static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.app.AppOpsManager; +import android.content.Context; +import android.hardware.soundtrigger.SoundTrigger; +import android.media.soundtrigger.SoundTriggerInstrumentation.RecognitionSession; +import android.os.Build; +import android.os.Process; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.platform.test.annotations.AppModeFull; +import android.service.voice.AlwaysOnHotwordDetector; +import android.soundtrigger.cts.instrumentation.SoundTriggerInstrumentationObserver; +import android.util.Log; +import android.voiceinteraction.cts.services.CtsBasicVoiceInteractionService; +import android.voiceinteraction.cts.testcore.VoiceInteractionServiceConnectedClassRule; +import android.voiceinteraction.cts.testcore.VoiceInteractionServiceOverrideEnrollmentRule; + +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import com.android.compatibility.common.util.ApiLevelUtil; +import com.android.compatibility.common.util.CddTest; +import com.android.compatibility.common.util.CtsDownstreamingTest; +import com.android.compatibility.common.util.RequiredFeatureRule; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runner.RunWith; +import org.junit.runners.model.Statement; + +import java.util.UUID; +import java.util.concurrent.atomic.AtomicBoolean; + +/** Tests for {@link AlwaysOnHotwordDetector} APIs. */ +@RunWith(AndroidJUnit4.class) +@AppModeFull(reason = "No real use case for instant mode hotword detector") +public class AlwaysOnHotwordDetectorNoHdsTest { + + private static final String TAG = "AlwaysOnHotwordDetectorNoHdsTest"; + // The VoiceInteractionService used by this test + private static final String SERVICE_COMPONENT = + "android.voiceinteraction.cts.services.CtsBasicVoiceInteractionService"; + + private static final int WAIT_EXPECTED_NO_CALL_TIMEOUT_IN_MS = 750; + + private static final Context sContext = getInstrumentation().getTargetContext(); + private static final SoundTrigger.Keyphrase[] KEYPHRASE_ARRAY = createKeyphraseArray(sContext); + + private final SoundTriggerInstrumentationObserver mInstrumentationObserver = + new SoundTriggerInstrumentationObserver(); + + private AtomicBoolean mOpNoted; + + private static final boolean SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED = + SystemProperties.getBoolean("ro.hotword.detection_service_required", false); + + private final AppOpsManager.OnOpNotedListener mOnOpNotedListener = + (op, uid, pkgName, attributionTag, flags, result) -> { + Log.d(TAG, "Get OnOpNotedListener callback op = " + op + ", uid = " + uid); + // We adopt ShellPermissionIdentity to pass the permission check, so the uid should + // be the shell uid. + if (Process.SHELL_UID == uid) { + mOpNoted.set(true); + } + }; + + public static final class RequiredApiLevelRule implements TestRule { + private final int mRequiredApiLevel; + private final boolean mIsRequiredApiLevel; + + RequiredApiLevelRule(int requiredApiLevel) { + mRequiredApiLevel = requiredApiLevel; + mIsRequiredApiLevel = isRequiredApiLevel(mRequiredApiLevel); + } + + @Override + public Statement apply(Statement base, Description description) { + return new Statement() { + @Override + public void evaluate() throws Throwable { + if (!mIsRequiredApiLevel) { + Log.d(TAG, "skipping " + + description.getClassName() + "#" + description.getMethodName() + + " because it requires API level " + mRequiredApiLevel); + assumeTrue("Device is not API level'" + mRequiredApiLevel, + mIsRequiredApiLevel); + return; + } + base.evaluate(); + } + }; + } + + @Override + public String toString() { + return "RequiredApiLevelRule[" + mRequiredApiLevel + ", " + mIsRequiredApiLevel + "]"; + } + + static boolean isRequiredApiLevel(int requiredApiLevel) { + return ApiLevelUtil.isAtLeast(requiredApiLevel); + } + } + + // For destroying in teardown + private AlwaysOnHotwordDetector mAlwaysOnHotwordDetector = null; + + @Rule(order = 1) + public RequiredApiLevelRule REQUIRES_API_RULE = new RequiredApiLevelRule( + Build.VERSION_CODES.UPSIDE_DOWN_CAKE); + + @Rule(order = 2) + public RequiredFeatureRule REQUIRES_MIC_RULE = new RequiredFeatureRule(FEATURE_MICROPHONE); + + @Rule(order = 3) + public VoiceInteractionServiceOverrideEnrollmentRule mEnrollOverrideRule = + new VoiceInteractionServiceOverrideEnrollmentRule(getService()); + + @ClassRule + public static final VoiceInteractionServiceConnectedClassRule sServiceRule = + new VoiceInteractionServiceConnectedClassRule( + sContext, getTestVoiceInteractionServiceName()); + + + @BeforeClass + public static void setupClass() { + // TODO(b/276393203) delete this + SystemClock.sleep(8_000); + } + + @Before + public void setup() { + // Hook up SoundTriggerInstrumentation to inject/observe STHAL operations. + // Requires MANAGE_SOUND_TRIGGER + runWithShellPermissionIdentity(mInstrumentationObserver::attachInstrumentation); + runWithShellPermissionIdentity( + () -> { + sContext.getSystemService(AppOpsManager.class) + .startWatchingNoted( + new String[] { + AppOpsManager.OPSTR_RECORD_AUDIO, + AppOpsManager.OPSTR_PHONE_CALL_MICROPHONE, + AppOpsManager.OPSTR_RECEIVE_AMBIENT_TRIGGER_AUDIO, + }, + mOnOpNotedListener); + }); + mOpNoted = new AtomicBoolean(false); + } + + @After + public void tearDown() { + runWithShellPermissionIdentity( + () -> + sContext.getSystemService(AppOpsManager.class) + .stopWatchingNoted(mOnOpNotedListener)); + // Destroy the framework session + if (mAlwaysOnHotwordDetector != null) { + mAlwaysOnHotwordDetector.destroy(); + } + + // Clean up any unexpected HAL state + try { + mInstrumentationObserver.close(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Clear the service state + getService().resetState(); + // Drop any permissions we may still have + getInstrumentation().getUiAutomation().dropShellPermissionIdentity(); + } + + @CddTest(requirements = {"9.8.2/H-4-1"}) + @Test + @CtsDownstreamingTest + public void testStartRecognition_success() throws Exception { + createAndEnrollAlwaysOnHotwordDetector(); + // Grab permissions for more than a single call since we get callbacks + adoptSoundTriggerPermissions(); + + startAndTriggerRecognition(); + } + + @CddTest(requirements = {"9.8.2/H-4-1"}) + @Test + @CtsDownstreamingTest + public void ifExemptionEnabled_startRecognition_noRecordOpsNoted() throws Exception { + assumeFalse(SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED); + createAndEnrollAlwaysOnHotwordDetector(); + // Grab permissions for more than a single call since we get callbacks + adoptSoundTriggerPermissions(); + + startAndTriggerRecognition(); + + // in case of any late arriving callbacks + SystemClock.sleep(WAIT_EXPECTED_NO_CALL_TIMEOUT_IN_MS); + assertThat(mOpNoted.get()).isFalse(); + } + + @CddTest(requirements = {"9.8.2/H-4-1"}) + @Test + @CtsDownstreamingTest + public void ifExemptionDisabled_startRecognition_RecordOpsNoted() throws Exception { + assumeTrue(SYSPROP_HOTWORD_DETECTION_SERVICE_REQUIRED); + createAndEnrollAlwaysOnHotwordDetector(); + // Grab permissions for more than a single call since we get callbacks + adoptSoundTriggerPermissions(); + + startAndTriggerRecognition(); + + // in case of any late arriving callbacks + SystemClock.sleep(WAIT_EXPECTED_NO_CALL_TIMEOUT_IN_MS); + assertThat(mOpNoted.get()).isTrue(); + } + + private static String getTestVoiceInteractionServiceName() { + Log.d(TAG, "getTestVoiceInteractionServiceName()"); + return CTS_SERVICE_PACKAGE + "/" + SERVICE_COMPONENT; + } + + private static CtsBasicVoiceInteractionService getService() { + return (CtsBasicVoiceInteractionService) sServiceRule.getService(); + } + + private void adoptSoundTriggerPermissions() { + getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity( + RECORD_AUDIO, + CAPTURE_AUDIO_HOTWORD, + MANAGE_HOTWORD_DETECTION, + MANAGE_VOICE_KEYPHRASES); + } + + private void createAndEnrollAlwaysOnHotwordDetector() throws InterruptedException { + mAlwaysOnHotwordDetector = null; + // Wait onAvailabilityChanged() callback called following AOHD creation. + getService().initAvailabilityChangeLatch(); + + // Load appropriate keyphrase model + // Required for the model to enter the enrolled state + runWithShellPermissionIdentity( + () -> + mEnrollOverrideRule + .getModelManager() + .updateKeyphraseSoundModel( + new SoundTrigger.KeyphraseSoundModel( + new UUID(5, 7), + new UUID(7, 5), + /* data= */ null, + KEYPHRASE_ARRAY)), + MANAGE_VOICE_KEYPHRASES); + + // Create alwaysOnHotwordDetector + getService() + .createAlwaysOnHotwordDetectorNoHotwordDetectionService( + /* useExecutor= */ true, /* runOnMainThread= */ true); + try { + // Bad naming, this waits for AOHD creation + getService().waitSandboxedDetectionServiceInitializedCalledOrException(); + } finally { + // Get the AlwaysOnHotwordDetector instance even if there is an error happened to avoid + // that we don't destroy the detector in tearDown method. It may be null here. We will + // check the status below. + mAlwaysOnHotwordDetector = getService().getAlwaysOnHotwordDetector(); + } + + // Verify that detector creation didn't throw + assertThat(getService().isCreateDetectorIllegalStateExceptionThrow()).isFalse(); + assertThat(getService().isCreateDetectorSecurityExceptionThrow()).isFalse(); + + assertThat(mAlwaysOnHotwordDetector).isNotNull(); + + // verify we have entered the ENROLLED state + getService().waitAvailabilityChangedCalled(); + assertThat(getService().getHotwordDetectionServiceAvailabilityResult()) + .isEqualTo(AlwaysOnHotwordDetector.STATE_KEYPHRASE_ENROLLED); + } + + private void startAndTriggerRecognition() throws InterruptedException { + // Start recognition + mAlwaysOnHotwordDetector.startRecognition(0, new byte[] {1, 2, 3, 4, 5}); + RecognitionSession recognitionSession = + waitForFutureDoneAndAssertSuccessful( + mInstrumentationObserver.getOnRecognitionStartedFuture()); + assertThat(recognitionSession).isNotNull(); + + // Trigger recognition + getService().initDetectRejectLatch(); + recognitionSession.triggerRecognitionEvent( + new byte[] {0x11, 0x22}, createKeyphraseRecognitionExtraList()); + getService().waitOnDetectOrRejectCalled(); + + // Validate that we got a result + AlwaysOnHotwordDetector.EventPayload detectResult = + getService().getHotwordServiceOnDetectedResult(); + assertThat(detectResult).isNotNull(); + } +} diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java index 92902e106ec..52d190ed340 100644 --- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java +++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java @@ -159,7 +159,7 @@ public class VoiceInteractionRoleTest { runWithShellPermissionIdentity( () -> sRoleManager.removeRoleHolderAsUser(RoleManager.ROLE_ASSISTANT, packageName, 0, Process.myUserHandle(), sContext.getMainExecutor(), future)); - assertThat(future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)).isTrue(); + future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); } private static class CallbackFuture extends CompletableFuture<Boolean> diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java index 0e02542ba72..53c93694fa0 100644 --- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java +++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/services/CtsBasicVoiceInteractionService.java @@ -145,53 +145,8 @@ public class CtsBasicVoiceInteractionService extends BaseVoiceInteractionService Log.i(TAG, "createAlwaysOnHotwordDetectorNoHotwordDetectionService"); mDetectorInitializedLatch = new CountDownLatch(1); - AlwaysOnHotwordDetector.Callback callback = new AlwaysOnHotwordDetector.Callback() { - @Override - public void onAvailabilityChanged(int status) { - Log.i(TAG, "onAvailabilityChanged(" + status + ")"); - mAvailabilityStatus = status; - setIsDetectorCallbackRunningOnMainThread(isRunningOnMainThread()); - if (mAvailabilityChangeLatch != null) { - mAvailabilityChangeLatch.countDown(); - } - } - - @Override - public void onDetected(AlwaysOnHotwordDetector.EventPayload eventPayload) { - // no-op - } - - @Override - public void onRejected(@NonNull HotwordRejectedResult result) { - // no-op - } - - @Override - public void onError() { - // no-op - } - - @Override - public void onRecognitionPaused() { - // no-op - } - - @Override - public void onRecognitionResumed() { - // no-op - } - - @Override - public void onHotwordDetectionServiceInitialized(int status) { - // no-op - } - - @Override - public void onHotwordDetectionServiceRestarted() { - // no-op - } - }; - + AlwaysOnHotwordDetector.Callback callback = + createAlwaysOnHotwordDetectorCallbackWithListeners(); final Handler handler = runOnMainThread ? new Handler(Looper.getMainLooper()) : mHandler; handler.post(() -> runWithShellPermissionIdentity(() -> { mAlwaysOnHotwordDetector = callCreateAlwaysOnHotwordDetectorNoHotwordDetectionService( diff --git a/tests/tests/widget/src/android/widget/cts/TextViewHandwritingGestureTest.java b/tests/tests/widget/src/android/widget/cts/TextViewHandwritingGestureTest.java index f06111139b5..10196b03022 100644 --- a/tests/tests/widget/src/android/widget/cts/TextViewHandwritingGestureTest.java +++ b/tests/tests/widget/src/android/widget/cts/TextViewHandwritingGestureTest.java @@ -1981,7 +1981,7 @@ public class TextViewHandwritingGestureTest { final int expectedColor = ColorUtils.setAlphaComponent(colorPrimary, (int) (0.12f * Color.alpha(colorPrimary))); - assertGestureHighlightRange(start, end, expectedColor); + assertGestureHighlightRange(start, end); } private void assertCursorOffset(int offset) { @@ -2024,6 +2024,19 @@ public class TextViewHandwritingGestureTest { assertGestureHighlightRange(start, end, color); } + private void assertGestureHighlightRange(int start, int end) { + Canvas canvas = prepareMockCanvas(); + mEditText.draw(canvas); + + ArgumentCaptor<Path> pathCaptor = ArgumentCaptor.forClass(Path.class); + ArgumentCaptor<Paint> paintCaptor = ArgumentCaptor.forClass(Paint.class); + verify(canvas).drawPath(pathCaptor.capture(), paintCaptor.capture()); + + Path expectedPath = new Path(); + mEditText.getLayout().getSelectionPath(start, end, expectedPath); + assertPathEquals(expectedPath, pathCaptor.getValue()); + } + private void assertGestureHighlightRange(int start, int end, int color) { Canvas canvas = prepareMockCanvas(); mEditText.draw(canvas); diff --git a/tests/tests/wifi/AndroidManifest.xml b/tests/tests/wifi/AndroidManifest.xml index e758789f185..b3fbee0a26a 100644 --- a/tests/tests/wifi/AndroidManifest.xml +++ b/tests/tests/wifi/AndroidManifest.xml @@ -42,7 +42,7 @@ (as opposed to HTTPS). --> <application android:usesCleartextTraffic="true"> <uses-library android:name="android.test.runner"/> - <activity android:name=".WaitForResultActivity"/> + <activity android:name=".WaitForResultActivity" android:screenOrientation="portrait"/> <service android:name="android.net.wifi.sharedconnectivity.service.cts.TestSharedConnectivityService" android:exported="true"> diff --git a/tests/videocodec/src/android/videocodec/cts/VideoEncoderMaxBFrameTest.java b/tests/videocodec/src/android/videocodec/cts/VideoEncoderMaxBFrameTest.java index 311d2e068f2..7c13a72ee56 100644 --- a/tests/videocodec/src/android/videocodec/cts/VideoEncoderMaxBFrameTest.java +++ b/tests/videocodec/src/android/videocodec/cts/VideoEncoderMaxBFrameTest.java @@ -19,6 +19,7 @@ package android.videocodec.cts; import static android.media.MediaFormat.PICTURE_TYPE_B; import static android.media.MediaFormat.PICTURE_TYPE_I; import static android.media.MediaFormat.PICTURE_TYPE_P; +import static android.mediav2.common.cts.CodecTestBase.ComponentClass.HARDWARE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -113,7 +114,7 @@ public class VideoEncoderMaxBFrameTest extends VideoEncoderValidationTestBase { maxBFrames), BIRTHDAY_FULLHD_LANDSCAPE, label}); } } - return prepareParamList(exhaustiveArgsList, true, false, true, false); + return prepareParamList(exhaustiveArgsList, true, false, true, false, HARDWARE); } public VideoEncoderMaxBFrameTest(String encoder, String mediaType, EncoderConfigParams cfg, diff --git a/tools/cts-device-info/Android.bp b/tools/cts-device-info/Android.bp index ad33ac29fec..e7cf2f95ec3 100644 --- a/tools/cts-device-info/Android.bp +++ b/tools/cts-device-info/Android.bp @@ -83,6 +83,7 @@ genrule { " -a com.android.compatibility.common.deviceinfo.KeystoreAttestationDeviceInfo " + " -a com.android.compatibility.common.deviceinfo.LocaleDeviceInfo " + " -a com.android.compatibility.common.deviceinfo.MediaDeviceInfo " + + " -a com.android.compatibility.common.deviceinfo.MediaDrmDeviceInfo " + " -a com.android.compatibility.common.deviceinfo.MediaOutputDeviceInfo " + " -a com.android.compatibility.common.deviceinfo.MemoryDeviceInfo " + " -a com.android.compatibility.common.deviceinfo.PackageDeviceInfo " + 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 ffb38a75956..a1f5efd0b62 100644 --- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml @@ -154,4 +154,12 @@ <option name="compatibility:exclude-filter" value="CtsUwbMultiDeviceTestCase_UwbManagerTests" /> <option name="compatibility:exclude-filter" value="CtsUwbMultiDeviceTestCase_FiraRangingTests" /> + <!-- b/290651182 --> + <option name="compatibility:exclude-filter" value="CtsSecurityTestCases android.security.cts.CertificateTest#testNoAddedCertificates" /> + <option name="compatibility:exclude-filter" value="CtsSecurityTestCases android.security.cts.CertificateTest#testNoRemovedCertificates" /> + + <!-- b/296896687 --> + <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.ASurfaceControlTest#testSurfaceTransaction_setDesiredPresentTime_30ms" /> + <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.ASurfaceControlTest#testSurfaceTransaction_setDesiredPresentTime_100ms" /> + </configuration> diff --git a/tools/cts-tradefed/res/config/cts-validation-exclude.xml b/tools/cts-tradefed/res/config/cts-validation-exclude.xml index a13ab72d3eb..edc29af288d 100644 --- a/tools/cts-tradefed/res/config/cts-validation-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-validation-exclude.xml @@ -498,4 +498,16 @@ <!-- b/292213604 --> <option name="compatibility:exclude-filter" value="CtsDomainVerificationDeviceMultiUserTestCases" /> + + <!-- b/292536082 --> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_policyNotAllowedToBeSet_throwsSecurityException[DelegateWithoutValidScope]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_policyNotAllowedToBeSet_throwsSecurityException[IncludeRunOnFinancedDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_nullPolicy_allowsAllProviders[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_allowlistAndSystemPolicy_allowsAllowlistedAndSystemProviders[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_null_setsPolicy[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_blocklistPolicy_allowsNotBlocklistedProviders[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_allowlistPolicy_allowsAllowlistedProviders[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_policyIsSet[IncludeRunOnDeviceOwnerUser]" /> + <option name="compatibility:exclude-filter" value="CtsCredentialManagerTestCases android.credentials.cts.CtsDevicePolicyTest#setCredentialManagerPolicy_allowlistAndSystemPolicy_allowsAllowlistedAndSystemProviders_SDK[IncludeRunOnDeviceOwnerUser]" /> + </configuration> diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java index 3b370f49a2a..364123949a1 100644 --- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java +++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/loading/CtsConfigLoadingTest.java @@ -60,6 +60,7 @@ public class CtsConfigLoadingTest { Arrays.asList( // modifications to the list below must be reviewed "abuse", + "adservices", "art", "auth", "auto", @@ -85,6 +86,8 @@ public class CtsConfigLoadingTest { "mocking", "networking", "neuralnetworks", + "packagemanager", + "permissions", "print", "renderscript", "security", |