Merge "Wait for alert dialog" into tm-dev
diff --git a/apps/CameraITS/tests/scene1_2/test_raw_exposure.py b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
index 9b4afe0..5bd44e8 100644
--- a/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
+++ b/apps/CameraITS/tests/scene1_2/test_raw_exposure.py
@@ -129,11 +129,14 @@
allow_under_saturated = False
# Check pixel means are increasing (with small tolerance)
for ch, color in enumerate(COLORS):
- e_msg = 'ISO=%d, %s, exp %3fms mean: %.2f, %s mean: %.2f, TOL=%.f%%' % (
- sens, color, exps[i-1], mean[ch],
- 'black level' if i == 1 else 'exp_time %.3fms'%exps[i-2],
- prev_mean[ch], IMG_DELTA_THRESH*100)
if mean[ch] <= prev_mean[ch] * IMG_DELTA_THRESH:
+ e_msg = f'{color} not increasing with increased exp time! ISO: {sens}, '
+ if i == 1:
+ e_msg += f'black_level: {black_levels[ch]}, '
+ else:
+ e_msg += f'exp[i-1]: {exps[i-2]:.3f}ms, mean[i-1]: {prev_mean[ch]:.2f}, '
+ e_msg += (f'exp[i]: {exps[i-1]:.3f}ms, mean[i]: {mean[ch]}, '
+ f'TOL: {IMG_DELTA_THRESH}')
raise AssertionError(e_msg)
diff --git a/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py b/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
index bb982d9..59ead98 100644
--- a/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
+++ b/apps/CameraITS/tests/scene1_2/test_yuv_jpeg_all.py
@@ -51,6 +51,9 @@
"""
out_surface = {'width': size[0], 'height': size[1], 'format': img_type}
cap = cam.do_capture(req, out_surface)
+ logging.debug('e_cap: %d, s_cap: %d',
+ cap['metadata']['android.sensor.exposureTime'],
+ cap['metadata']['android.sensor.sensitivity'])
if img_type == 'jpg':
if cap['format'] != 'jpeg':
raise AssertionError(f"{cap['format']} != jpeg")
@@ -67,6 +70,7 @@
if debug:
image_processing_utils.write_image(img, '%s_%s_w%d_h%d.jpg'%(
os.path.join(log_path, NAME), img_type, size[0], size[1]))
+
if img_type == 'jpg':
if img.shape[0] != size[1]:
raise AssertionError(f'{img.shape[0]} != {size[1]}')
@@ -107,6 +111,7 @@
# should look the same (once converted by the image_processing_utils).
e, s = target_exposure_utils.get_target_exposure_combos(
log_path, cam)['midExposureTime']
+ logging.debug('e_req: %d, s_req: %d', e, s)
req = capture_request_utils.manual_capture_request(s, e, 0.0, True, props)
rgbs = []
diff --git a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
index 35cd382..5f59a49 100644
--- a/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
+++ b/apps/CameraITS/tests/scene4/test_aspect_ratio_and_crop.py
@@ -28,7 +28,6 @@
import opencv_processing_utils
_ANDROID11_API_LEVEL = 30
-_FOV_PERCENT_RTOL = 0.15 # Relative tolerance on circle FoV % to expected.
_NAME = os.path.splitext(os.path.basename(__file__))[0]
_PREVIEW_SIZE = (1920, 1080)
@@ -140,21 +139,6 @@
return False
-def _check_fov(circle, ref_fov, w, h, first_api_level):
- """Check the FoV for correct size."""
- fov_percent = image_fov_utils.calc_circle_image_ratio(
- circle['r'], w, h)
- chk_percent = image_fov_utils.calc_expected_circle_image_ratio(
- ref_fov, w, h)
- chk_enabled = _is_checked_aspect_ratio(first_api_level, w, h)
- if chk_enabled and not np.isclose(fov_percent, chk_percent,
- rtol=_FOV_PERCENT_RTOL):
- e_msg = 'FoV %%: %.2f, Ref FoV %%: %.2f, ' % (fov_percent, chk_percent)
- e_msg += 'TOL=%.f%%, img: %dx%d, ref: %dx%d' % (
- _FOV_PERCENT_RTOL*100, w, h, ref_fov['w'], ref_fov['h'])
- return e_msg
-
-
class AspectRatioAndCropTest(its_base_test.ItsBaseTest):
"""Test aspect ratio/field of view/cropping for each tested fmt combinations.
@@ -305,11 +289,12 @@
# Check pass/fail for fov coverage for all fmts in AR_CHECKED
img /= 255 # image_processing_utils uses [0, 1].
- fov_chk_msg = _check_fov(circle, ref_fov, w_iter, h_iter,
- first_api_level)
- if fov_chk_msg:
- failed_fov.append(fov_chk_msg)
- image_processing_utils.write_image(img, img_name, True)
+ if _is_checked_aspect_ratio(first_api_level, w_iter, h_iter):
+ fov_chk_msg = image_fov_utils.check_fov(
+ circle, ref_fov, w_iter, h_iter)
+ if fov_chk_msg:
+ failed_fov.append(fov_chk_msg)
+ image_processing_utils.write_image(img, img_name, True)
# Check pass/fail for aspect ratio.
ar_chk_msg = image_fov_utils.check_ar(
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 af7e5d5..399aa05 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
@@ -21,10 +21,41 @@
import camera_properties_utils
import its_session_utils
import video_processing_utils
+import capture_request_utils
+import image_processing_utils
+import opencv_processing_utils
+import image_fov_utils
_ANDROID13_API_LEVEL = 32
_NAME = os.path.splitext(os.path.basename(__file__))[0]
-_VIDEO_RECORDING_DURATION_SECONDS = 2
+_VIDEO_RECORDING_DURATION_SECONDS = 3
+_FOV_PERCENT_RTOL = 0.15 # Relative tolerance on circle FoV % to expected.
+_AR_CHECKED_PRE_API_30 = ('4:3', '16:9', '18:9')
+_AR_DIFF_ATOL = 0.01
+
+
+def _print_failed_test_results(failed_ar, failed_fov, failed_crop,
+ quality):
+ """Print failed test results."""
+ if failed_ar:
+ logging.error('Aspect ratio test summary for %s', quality)
+ logging.error('Images failed in the aspect ratio test:')
+ logging.error('Aspect ratio value: width / height')
+ for fa in failed_ar:
+ logging.error('%s', fa)
+
+ if failed_fov:
+ logging.error('FoV test summary for %s', quality)
+ logging.error('Images failed in the FoV test:')
+ for fov in failed_fov:
+ logging.error('%s', str(fov))
+
+ if failed_crop:
+ logging.error('Crop test summary for %s', quality)
+ logging.error('Images failed in the crop test:')
+ logging.error('Circle center (H x V) relative to the image center.')
+ for fc in failed_crop:
+ logging.error('%s', fc)
class VideoAspectRatioAndCropTest(its_base_test.ItsBaseTest):
@@ -86,6 +117,10 @@
def test_video_aspect_ratio_and_crop(self):
logging.debug('Starting %s', _NAME)
+ failed_ar = [] # Streams failed the aspect ratio test.
+ failed_crop = [] # Streams failed the crop test.
+ failed_fov = [] # Streams that fail FoV test.
+
with its_session_utils.ItsSession(
device_id=self.dut.serial,
camera_id=self.camera_id,
@@ -106,15 +141,127 @@
self.camera_id)
logging.debug('Supported video qualities: %s', supported_video_qualities)
+ # Determine camera capabilities.
+ full_or_better = camera_properties_utils.full_or_better(props)
+ raw_avlb = camera_properties_utils.raw16(props)
+ fls_logical = props['android.lens.info.availableFocalLengths']
+ logging.debug('logical available focal lengths: %s', str(fls_logical))
+ fls_physical = props['android.lens.info.availableFocalLengths']
+ logging.debug('physical available focal lengths: %s',
+ str(fls_physical))
+
+ req = capture_request_utils.auto_capture_request()
+ ref_img_name_stem = f'{os.path.join(self.log_path, _NAME)}'
+
+ if raw_avlb and (fls_physical == fls_logical):
+ logging.debug('RAW')
+ ref_fov, cc_ct_gt, aspect_ratio_gt = (
+ image_fov_utils.find_fov_reference(
+ cam, req, props, 'RAW', ref_img_name_stem))
+ else:
+ logging.debug('JPEG')
+ ref_fov, cc_ct_gt, aspect_ratio_gt = (
+ image_fov_utils.find_fov_reference(
+ cam, req, props, 'JPEG', ref_img_name_stem))
+
+ run_crop_test = full_or_better and raw_avlb
+
for quality_profile_id_pair in supported_video_qualities:
quality = quality_profile_id_pair.split(':')[0]
profile_id = quality_profile_id_pair.split(':')[-1]
# Check if we support testing this quality.
- if quality in video_processing_utils._ITS_SUPPORTED_QUALITIES:
- logging.debug("Testing video recording for quality: %s" % quality)
- cam.do_basic_recording(profile_id, quality, _VIDEO_RECORDING_DURATION_SECONDS)
- # TODO(ruchamk): Add processing of video recordings, aspect ratio
- # and crop checks.
+ if quality in video_processing_utils.ITS_SUPPORTED_QUALITIES:
+ logging.debug('Testing video recording for quality: %s', quality)
+ video_recording_obj = cam.do_basic_recording(
+ profile_id, quality, _VIDEO_RECORDING_DURATION_SECONDS)
+ logging.debug('video_recording_obj: %s', video_recording_obj)
+ # TODO(ruchamk): Modify video recording object to send videoFrame
+ # width and height instead of videoSize to avoid string operation
+ # here.
+ video_size = video_recording_obj['videoSize']
+ width = int(video_size.split('x')[0])
+ height = int(video_size.split('x')[-1])
+
+ # Pull the video recording file from the device.
+ self.dut.adb.pull([video_recording_obj['recordedOutputPath'],
+ self.log_path])
+ logging.debug('Recorded video is available at: %s',
+ self.log_path)
+ mp4_file_name = video_recording_obj['recordedOutputPath'].split('/')[-1]
+ logging.debug('mp4_file_name: %s', mp4_file_name)
+
+ key_frame_files = []
+ key_frame_files = video_processing_utils.extract_key_frames_from_video(
+ self.log_path, mp4_file_name)
+ logging.debug('key_frame_files:%s', key_frame_files)
+
+ # Get the key frame file to process.
+ last_key_frame_file = video_processing_utils.get_key_frame_to_process(
+ key_frame_files)
+ logging.debug('last_key_frame: %s', last_key_frame_file)
+ last_key_frame_path = os.path.join(self.log_path, last_key_frame_file)
+
+ # Convert lastKeyFrame to numpy array
+ np_image = image_processing_utils.convert_image_to_numpy_array(
+ last_key_frame_path)
+ logging.debug('numpy image shape: %s', np_image.shape)
+
+ # Check fov
+ circle = opencv_processing_utils.find_circle(
+ np_image, ref_img_name_stem, image_fov_utils.CIRCLE_MIN_AREA,
+ image_fov_utils.CIRCLE_COLOR)
+
+ # Check pass/fail for fov coverage for all fmts in AR_CHECKED
+ fov_chk_msg = image_fov_utils.check_fov(
+ circle, ref_fov, width, height)
+ if fov_chk_msg:
+ img_name = '%s_%s_w%d_h%d_fov.png' % (
+ os.path.join(self.log_path, _NAME), quality, width, height)
+ fov_chk_quality_msg = f'Quality: {quality} {fov_chk_msg}'
+ failed_fov.append(fov_chk_quality_msg)
+ image_processing_utils.write_image(np_image/255, img_name, True)
+
+ # Check pass/fail for aspect ratio.
+ ar_chk_msg = image_fov_utils.check_ar(
+ circle, aspect_ratio_gt, width, height,
+ f'{quality}')
+ if ar_chk_msg:
+ img_name = '%s_%s_w%d_h%d_ar.png' % (
+ os.path.join(self.log_path, _NAME), quality, width, height)
+ failed_ar.append(ar_chk_msg)
+ image_processing_utils.write_image(np_image/255, img_name, True)
+
+ # Check pass/fail for crop.
+ if run_crop_test:
+ # Normalize the circle size to 1/4 of the image size, so that
+ # circle size won't affect the crop test result
+ crop_thresh_factor = ((min(ref_fov['w'], ref_fov['h']) / 4.0) /
+ max(ref_fov['circle_w'], ref_fov['circle_h']))
+ crop_chk_msg = image_fov_utils.check_crop(
+ circle, cc_ct_gt, width, height,
+ f'{quality}', crop_thresh_factor)
+ if crop_chk_msg:
+ crop_img_name = '%s_%s_w%d_h%d_crop.png' % (
+ os.path.join(self.log_path, _NAME), quality, width, height)
+ opencv_processing_utils.append_circle_center_to_img(
+ circle, np_image*255, crop_img_name)
+ failed_crop.append(crop_chk_msg)
+ image_processing_utils.write_image(np_image/255,
+ crop_img_name, True)
+ else:
+ logging.debug('Crop test skipped')
+
+ # Print any failed test results.
+ _print_failed_test_results(failed_ar, failed_fov, failed_crop, quality)
+ e_msg = ''
+ if failed_ar:
+ e_msg = 'Aspect ratio '
+ if failed_fov:
+ e_msg += 'FoV '
+ if failed_crop:
+ e_msg += 'Crop '
+ if e_msg:
+ raise AssertionError(f'{e_msg}check failed.')
if __name__ == '__main__':
test_runner.main()
diff --git a/apps/CameraITS/utils/image_fov_utils.py b/apps/CameraITS/utils/image_fov_utils.py
index b9caf33..8ac4ece 100644
--- a/apps/CameraITS/utils/image_fov_utils.py
+++ b/apps/CameraITS/utils/image_fov_utils.py
@@ -26,7 +26,7 @@
CIRCLE_COLOR = 0 # [0: black, 255: white]
CIRCLE_MIN_AREA = 0.01 # 1% of image size
-
+FOV_PERCENT_RTOL = 0.15 # Relative tolerance on circle FoV % to expected.
LARGE_SIZE_IMAGE = 2000 # Size of a large image (compared against max(w, h))
THRESH_AR_L = 0.02 # Aspect ratio test threshold of large images
THRESH_AR_S = 0.075 # Aspect ratio test threshold of mini images
@@ -35,6 +35,17 @@
THRESH_MIN_PIXEL = 4 # Crop test allowed offset
+def check_fov(circle, ref_fov, w, h):
+ """Check the FoV for correct size."""
+ fov_percent = calc_circle_image_ratio(circle['r'], w, h)
+ chk_percent = calc_expected_circle_image_ratio(ref_fov, w, h)
+ if not math.isclose(fov_percent, chk_percent, rel_tol=FOV_PERCENT_RTOL):
+ e_msg = (f'FoV %: {fov_percent:.2f}, Ref FoV %: {chk_percent:.2f}, '
+ f'TOL={FOV_PERCENT_RTOL*100}%, img: {w}x{h}, ref: '
+ f"{ref_fov['w']}x{ref_fov['h']}")
+ return e_msg
+
+
def check_ar(circle, ar_gt, w, h, e_msg_stem):
"""Check the aspect ratio of the circle.
diff --git a/apps/CameraITS/utils/image_processing_utils.py b/apps/CameraITS/utils/image_processing_utils.py
index 6da2d92..3ebee72 100644
--- a/apps/CameraITS/utils/image_processing_utils.py
+++ b/apps/CameraITS/utils/image_processing_utils.py
@@ -245,6 +245,19 @@
h = img.size[1]
return numpy.array(img).reshape(h, w, 3) / 255.0
+def convert_image_to_numpy_array(image_path):
+ """
+ Converts an image at the path image_path to numpy array and returns the array.
+
+ Args:
+ image_path: file path
+ Returns:
+ numpy array
+ """
+ if not os.path.exists(image_path):
+ raise assertionError(f'{image_path} does not exist.')
+ image = Image.open(image_path)
+ return numpy.array(image)
def convert_capture_to_planes(cap, props=None):
"""Convert a captured image object to separate image planes.
diff --git a/apps/CameraITS/utils/sensor_fusion_utils.py b/apps/CameraITS/utils/sensor_fusion_utils.py
index 8c7881b..35c21c4 100644
--- a/apps/CameraITS/utils/sensor_fusion_utils.py
+++ b/apps/CameraITS/utils/sensor_fusion_utils.py
@@ -615,7 +615,8 @@
pylab.title(f'{plot_name}(mean of {_NUM_GYRO_PTS_TO_AVG} pts)')
pylab.plot(times, x, 'r', label='x')
pylab.plot(times, y, 'g', label='y')
- pylab.ylabel('gyro x & y movement (rads/s)')
+ pylab.ylim([np.amin(z), np.amax(z)])
+ pylab.ylabel('gyro x,y movement (rads/s)')
pylab.legend()
# z on separate axes
diff --git a/apps/CameraITS/utils/target_exposure_utils.py b/apps/CameraITS/utils/target_exposure_utils.py
index 279b5ea..65e53ec 100644
--- a/apps/CameraITS/utils/target_exposure_utils.py
+++ b/apps/CameraITS/utils/target_exposure_utils.py
@@ -69,71 +69,71 @@
# Combo 1: smallest legal exposure time.
e1_expt = exp_time_range[0]
- e1_sens = exposure / e1_expt
+ e1_sens = int(exposure / e1_expt)
if e1_sens > sens_range[1]:
e1_sens = sens_range[1]
- e1_expt = exposure / e1_sens
- e1_logging = (f'e1 exp: {e1_expt}, sens: {e1_sens}')
+ e1_expt = int(exposure / e1_sens)
+ e1_logging = (f'MinExp exp: {e1_expt}, sens: {e1_sens}')
logging.debug('%s', e1_logging)
# Combo 2: largest legal exposure time.
e2_expt = exp_time_range[1]
- e2_sens = exposure / e2_expt
+ e2_sens = int(exposure / e2_expt)
if e2_sens < sens_range[0]:
e2_sens = sens_range[0]
- e2_expt = exposure / e2_sens
- e2_logging = (f'e2 exp: {e2_expt}, sens: {e2_sens}')
+ e2_expt = int(exposure / e2_sens)
+ e2_logging = (f'MaxExp exp: {e2_expt}, sens: {e2_sens}')
logging.debug('%s', e2_logging)
# Combo 3: smallest legal sensitivity.
e3_sens = sens_range[0]
- e3_expt = exposure / e3_sens
+ e3_expt = int(exposure / e3_sens)
if e3_expt > exp_time_range[1]:
e3_expt = exp_time_range[1]
- e3_sens = exposure / e3_expt
- e3_logging = (f'e3 exp: {e3_expt}, sens: {e3_sens}')
+ e3_sens = int(exposure / e3_expt)
+ e3_logging = (f'MinISO exp: {e3_expt}, sens: {e3_sens}')
logging.debug('%s', e3_logging)
# Combo 4: largest legal sensitivity.
e4_sens = sens_range[1]
- e4_expt = exposure / e4_sens
+ e4_expt = int(exposure / e4_sens)
if e4_expt < exp_time_range[0]:
e4_expt = exp_time_range[0]
- e4_sens = exposure / e4_expt
- e4_logging = (f'e4 exp: {e4_expt}, sens: {e4_sens}')
+ e4_sens = int(exposure / e4_expt)
+ e4_logging = (f'MaxISO exp: {e4_expt}, sens: {e4_sens}')
logging.debug('%s', e4_logging)
# Combo 5: middle exposure time.
- e5_expt = (exp_time_range[0] + exp_time_range[1]) / 2.0
- e5_sens = exposure / e5_expt
+ e5_expt = int((exp_time_range[0] + exp_time_range[1]) / 2.0)
+ e5_sens = int(exposure / e5_expt)
if e5_sens > sens_range[1]:
e5_sens = sens_range[1]
- e5_expt = exposure / e5_sens
+ e5_expt = int(exposure / e5_sens)
if e5_sens < sens_range[0]:
e5_sens = sens_range[0]
- e5_expt = exposure / e5_sens
- e5_logging = (f'e5 exp: {e5_expt}, sens: {e5_sens}')
+ e5_expt = int(exposure / e5_sens)
+ e5_logging = (f'MidExp exp: {e5_expt}, sens: {e5_sens}')
logging.debug('%s', e5_logging)
# Combo 6: middle sensitivity.
- e6_sens = (sens_range[0] + sens_range[1]) / 2.0
- e6_expt = exposure / e6_sens
+ e6_sens = int((sens_range[0] + sens_range[1]) / 2.0)
+ e6_expt = int(exposure / e6_sens)
if e6_expt > exp_time_range[1]:
e6_expt = exp_time_range[1]
- e6_sens = exposure / e6_expt
+ e6_sens = int(exposure / e6_expt)
if e6_expt < exp_time_range[0]:
e6_expt = exp_time_range[0]
- e6_sens = exposure / e6_expt
- e6_logging = (f'e6 exp: {e6_expt}, sens: {e6_sens}')
+ e6_sens = int(exposure / e6_expt)
+ e6_logging = (f'MidISO exp: {e6_expt}, sens: {e6_sens}')
logging.debug('%s', e6_logging)
return {
- 'minExposureTime': (int(e1_expt), int(e1_sens)),
- 'maxExposureTime': (int(e2_expt), int(e2_sens)),
- 'minSensitivity': (int(e3_expt), int(e3_sens)),
- 'maxSensitivity': (int(e4_expt), int(e4_sens)),
- 'midExposureTime': (int(e5_expt), int(e5_sens)),
- 'midSensitivity': (int(e6_expt), int(e6_sens))
+ 'minExposureTime': (e1_expt, e1_sens),
+ 'maxExposureTime': (e2_expt, e2_sens),
+ 'minSensitivity': (e3_expt, e3_sens),
+ 'maxSensitivity': (e4_expt, e4_sens),
+ 'midExposureTime': (e5_expt, e5_sens),
+ 'midSensitivity': (e6_expt, e6_sens)
}
diff --git a/apps/CameraITS/utils/video_processing_utils.py b/apps/CameraITS/utils/video_processing_utils.py
index 967bb9e..f862c59 100644
--- a/apps/CameraITS/utils/video_processing_utils.py
+++ b/apps/CameraITS/utils/video_processing_utils.py
@@ -16,7 +16,13 @@
# Each item in this list corresponds to quality levels defined per
# CamcorderProfile. For Video ITS, we will currently test below qualities
# only if supported by the camera device.
-_ITS_SUPPORTED_QUALITIES = (
+import logging
+import os.path
+import subprocess
+import time
+
+
+ITS_SUPPORTED_QUALITIES = (
'HIGH',
'2160P',
'1080P',
@@ -28,3 +34,62 @@
'LOW',
'VGA'
)
+
+
+def extract_key_frames_from_video(log_path, mp4_file_name):
+ """
+ Returns a list of extracted key frames.
+
+ Ffmpeg tool is used to extract key frames from the video at path
+ os.path.join(log_path, mp4_file_name).
+ The extracted key frames will have the name mp4_file_name with "_key_frame"
+ suffix to identify the frames for video of each quality.Since there can be
+ multiple key frames, each key frame image will be differentiated with it's
+ frame index.All the extracted key frames will be available in jpeg format
+ at the same path as the video file.
+
+ Args:
+ log_path: path for video file directory
+ mp4_file_name: name of the file in mp4 format.
+ Ex: VID_20220325_050918_0_CIF_352x288.mp4
+ Returns:
+ key_frame_files: A list of paths for each key frame extracted from the
+ video.
+ """
+ ffmpeg_image_name = f"{mp4_file_name.split('.')[0]}_key_frame"
+ ffmpeg_image_file_path = os.path.join(log_path, ffmpeg_image_name + '_%02d.png')
+ cmd = ['ffmpeg',
+ '-skip_frame',
+ 'nokey',
+ '-i',
+ os.path.join(log_path, mp4_file_name),
+ '-vsync',
+ 'vfr',
+ '-frame_pts',
+ 'true' ,
+ ffmpeg_image_file_path,
+ ]
+ logging.debug('Extracting key frames for: %s' % mp4_file_name)
+ output = subprocess.call(cmd)
+ arr = os.listdir(os.path.join(log_path))
+ key_frame_files = []
+ for file in arr:
+ if '.png' in file and not os.path.isdir(file) and ffmpeg_image_name in file:
+ key_frame_files.append(file)
+ return key_frame_files
+
+
+def get_key_frame_to_process(key_frame_files):
+ """
+ Returns the key frame file from the list of key_frame_files.
+
+ If the size of the list is 1 then the file in the list will be returned else
+ the file with highest frame_index will be returned for further processing.
+
+ Args:
+ key_frame_files: A list of key frame files.
+ Returns:
+ key_frame_file to be used for further processing.
+ """
+ key_frame_files.sort()
+ return key_frame_files[-1]
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 9e7dab9..66c1d2f 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -51,6 +51,7 @@
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_PASSWORD_COMPLEXITY" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
<uses-feature android:name="android.hardware.camera.flash" android:required="false"/>
<uses-feature android:name="android.hardware.sensor.accelerometer" android:required="false" />
@@ -2948,6 +2949,7 @@
android:value="android.hardware.type.watch" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest" android:value="7.7.1/H-1-1" />
</activity>
<activity android:name=".usb.accessory.AccessoryAttachmentHandler"
@@ -2977,6 +2979,9 @@
android:value="android.hardware.type.watch" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest" android:value="7.7.2/C-1-1" />
+ <meta-data android:name="ApiTest"
+ android:value="android.hardware.usb.UsbDeviceConnection#controlTransfer|android.hardware.usb.UsbDeviceConnection#bulkTransfer" />
</activity>
<activity android:name=".usb.mtp.MtpHostTestActivity" android:label="@string/mtp_host_test"
@@ -2991,6 +2996,7 @@
android:value="android.hardware.type.automotive:android.hardware.type.television" />
<meta-data android:name="display_mode"
android:value="multi_display_mode" />
+ <meta-data android:name="CddTest" android:value="7.7.2/C-3-1" />
</activity>
<!-- Turned off Sensor Power Test in initial L release
@@ -3085,6 +3091,7 @@
<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_required_features" android:value="android.software.secure_lock_screen" />
<meta-data android:name="test_excluded_features"
android:value="android.hardware.type.automotive" />
<meta-data android:name="display_mode" android:value="multi_display_mode" />
@@ -3222,7 +3229,7 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
<meta-data android:name="android.service.notification.default_filter_types"
- android:value="alerting|silent" />
+ android:value="conversations|alerting|silent" />
<meta-data android:name="android.service.notification.disabled_filter_types"
android:value="ongoing" />
</service>
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index c2d1897..dc3bed4 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -3307,6 +3307,8 @@
<string name="provisioning_byod_work_notification_instruction">
Please press the Go button to trigger a notification.\n
\n
+ If a permission is requested, grant it and press the Go button again.\n
+ \n
Verify that the notification is badged (see sample badge below). Then mark this test accordingly.
</string>
<string name="provisioning_byod_notification_title">This is a notification</string>
@@ -3610,7 +3612,7 @@
<string name="provisioning_byod_turn_off_work_prepare_notifications">Prepare a work notification</string>
<string name="provisioning_byod_turn_off_work_prepare_notifications_instruction">
This is a test setup step.\n
- 1. Press the go button to send a work notification.\n
+ 1. Press the go button to send a work notification. (if a permission is requested, grant it and press the go button again). \n
2. Verify that the notification is displayed and mark this test as passed.\n
(Note: in the following test, you will be asked to verify the notification disappears after work profile is turned off.)
</string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
index ac451e4..ddadfff 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/audio/AudioFrequencyUnprocessedActivity.java
@@ -226,7 +226,7 @@
//Init bands for Mic test
mBandSpecsMic[0] = new AudioBandSpecs(
- 5, 100, /* frequency start,stop */
+ 30, 100, /* frequency start,stop */
20.0, -20.0, /* start top,bottom value */
20.0, -20.0 /* stop top,bottom value */);
@@ -242,7 +242,7 @@
//Init bands for Tone test
mBandSpecsTone[0] = new AudioBandSpecs(
- 5, 900, /* frequency start,stop */
+ 30, 900, /* frequency start,stop */
-10.0, -100.0, /* start top,bottom value */
-10.0, -100.0 /* stop top,bottom value */);
@@ -258,7 +258,7 @@
//Init bands for Background test
mBandSpecsBack[0] = new AudioBandSpecs(
- 5, 100, /* frequency start,stop */
+ 30, 100, /* frequency start,stop */
10.0, -120.0, /* start top,bottom value */
-10.0, -120.0 /* stop top,bottom value */);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/bokeh/CameraBokehActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/bokeh/CameraBokehActivity.java
index 3f27a2d..55791c6 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/bokeh/CameraBokehActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/bokeh/CameraBokehActivity.java
@@ -59,6 +59,7 @@
import android.view.View;
import android.view.Surface;
import android.view.TextureView;
+import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
@@ -98,6 +99,7 @@
1f, 1.772f, 0f, 0f, -226.816f,
0f, 0f, 0f, 1f, 0f
});
+ private static final String CAMERA_ID_PREFIX = "Camera ";
private TextureView mPreviewView;
private SurfaceTexture mPreviewTexture;
@@ -262,7 +264,7 @@
String[] cameraNames = new String[cameraIdSet.size()];
int i = 0;
for (String id : cameraIdSet) {
- cameraNames[i++] = "Camera " + id;
+ cameraNames[i++] = CAMERA_ID_PREFIX + id;
}
mCameraSpinner = (Spinner) findViewById(R.id.cameras_selection);
mCameraSpinner.setAdapter(
@@ -592,11 +594,10 @@
shutdownCamera();
mCurrentCameraIndex = index;
+ Adapter adapter = mCameraSpinner.getAdapter();
+ String idDisplayed = (String) adapter.getItem(index);
+ mCameraId = idDisplayed.substring(CAMERA_ID_PREFIX.length());
- Set<String> cameraIdSet = mTestCases.keySet();
- List<String> stringsList = new ArrayList<>(cameraIdSet);
-
- mCameraId = stringsList.get(index);
try {
mCameraCharacteristics = mCameraManager.getCameraCharacteristics(mCameraId);
mCameraDevice = mBlockingCameraManager.openCamera(mCameraId,
@@ -761,7 +762,8 @@
private void startPreview() {
try {
- if (mPreviewSize == null || !mPreviewSize.equals(mNextCombination.mPreviewSize)) {
+ if (mPreviewSize == null || !mPreviewSize.equals(mNextCombination.mPreviewSize) ||
+ mYuvImageReader == null) {
mPreviewSize = mNextCombination.mPreviewSize;
mYuvImageReader = ImageReader.newInstance(mPreviewSize.getWidth(),
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 520d95f..caeec91 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
@@ -1703,12 +1703,12 @@
private void doGetSupportedVideoQualities(String id) throws ItsException {
int cameraId = Integer.parseInt(id);
StringBuilder profiles = new StringBuilder();
- appendSupportProfile(profiles, "480", CamcorderProfile.QUALITY_480P, cameraId);
- appendSupportProfile(profiles, "1080", CamcorderProfile.QUALITY_1080P, cameraId);
- appendSupportProfile(profiles, "2160", CamcorderProfile.QUALITY_2160P, cameraId);
+ appendSupportProfile(profiles, "480P", CamcorderProfile.QUALITY_480P, cameraId);
+ appendSupportProfile(profiles, "1080P", CamcorderProfile.QUALITY_1080P, cameraId);
+ appendSupportProfile(profiles, "2160P", CamcorderProfile.QUALITY_2160P, cameraId);
appendSupportProfile(profiles, "2k", CamcorderProfile.QUALITY_2K, cameraId);
appendSupportProfile(profiles, "4KDCI", CamcorderProfile.QUALITY_4KDCI, cameraId);
- appendSupportProfile(profiles, "720", CamcorderProfile.QUALITY_720P, cameraId);
+ appendSupportProfile(profiles, "720P", CamcorderProfile.QUALITY_720P, cameraId);
appendSupportProfile(profiles, "8KUHD", CamcorderProfile.QUALITY_8KUHD, cameraId);
appendSupportProfile(profiles, "CIF", CamcorderProfile.QUALITY_CIF, cameraId);
appendSupportProfile(profiles, "HIGH", CamcorderProfile.QUALITY_HIGH, cameraId);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
index e67fd85..10010cb 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/ByodHelperActivity.java
@@ -16,6 +16,7 @@
package com.android.cts.verifier.managedprovisioning;
+import static android.Manifest.permission.POST_NOTIFICATIONS;
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES;
import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY;
@@ -165,6 +166,7 @@
private static final int REQUEST_VIDEO_CAPTURE_WITH_EXTRA_OUTPUT = 4;
private static final int REQUEST_VIDEO_CAPTURE_WITHOUT_EXTRA_OUTPUT = 5;
private static final int REQUEST_AUDIO_CAPTURE = 6;
+ private static final int REQUEST_POST_NOTIFICATIONS = 7;
private static final String ORIGINAL_RESTRICTIONS_NAME = "original restrictions";
@@ -188,13 +190,24 @@
private ArrayList<File> mTempFiles = new ArrayList<File>();
private Handler mMainThreadHandler;
+ private int mNextNotificationVisibility;
private void showNotification(int visibility) {
+ mNextNotificationVisibility = visibility;
+
+ if (hasPostNotificationsPermission()) {
+ showNotificationInner();
+ } else {
+ requestPostNotificationsPermission(REQUEST_POST_NOTIFICATIONS);
+ }
+ }
+
+ private void showNotificationInner() {
final Notification notification = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.icon)
.setContentTitle(getString(R.string.provisioning_byod_notification_title))
.setContentText(getString(R.string.provisioning_byod_notification_title))
- .setVisibility(visibility)
+ .setVisibility(mNextNotificationVisibility)
.setAutoCancel(true)
.setPublicVersion(createPublicVersionNotification())
.build();
@@ -597,10 +610,22 @@
requestCode);
}
+ private boolean hasPostNotificationsPermission() {
+ return ContextCompat.checkSelfPermission(this, POST_NOTIFICATIONS)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
+ private void requestPostNotificationsPermission(int requestCode) {
+ ActivityCompat.requestPermissions(this,
+ new String[]{Manifest.permission.POST_NOTIFICATIONS},
+ requestCode);
+ }
+
/**
* Launch the right test based on the request code, after validating the right permission
* has been granted.
*/
+ @Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grants) {
// Test that the right permission was granted.
@@ -616,6 +641,14 @@
return;
}
break;
+ case REQUEST_POST_NOTIFICATIONS:
+ if (!permissions[0].equals(POST_NOTIFICATIONS)
+ || grants[0] != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "The test needs notifications permission.");
+ finish();
+ return;
+ }
+ break;
}
// Execute the right test.
@@ -627,6 +660,9 @@
case EXECUTE_VIDEO_CAPTURE_WITHOUT_EXTRA_TEST:
startCaptureVideoActivity(requestCode);
break;
+ case REQUEST_POST_NOTIFICATIONS:
+ showNotificationInner();
+ break;
default:
Log.e(TAG, "Unknown action.");
finish();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
index e91f92b..5909757 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/NotificationListenerVerifierActivity.java
@@ -123,12 +123,14 @@
tests.add(new IsEnabledTest());
tests.add(new ServiceStartedTest());
tests.add(new NotificationReceivedTest());
+ /*
+ // TODO (b/200701618): re-enable tests if conditions in 3.8.3.1 change to MUST
if (!isAutomotive) {
tests.add(new SendUserToChangeFilter());
tests.add(new AskIfFilterChanged());
tests.add(new NotificationTypeFilterTest());
tests.add(new ResetChangeFilter());
- }
+ }*/
tests.add(new LongMessageTest());
tests.add(new DataIntactTest());
tests.add(new AudiblyAlertedTest());
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OWNERS b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OWNERS
index 5627ff5..5940ede4 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OWNERS
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/tv/display/OWNERS
@@ -1,2 +1,2 @@
blindahl@google.com
-shalamanov@google.com
+kritidang@google.com
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/Policy.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/Policy.java
index ada4af5..133bf8a 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/Policy.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/Policy.java
@@ -50,18 +50,19 @@
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDelegate;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.AppOp;
+import com.android.bedstead.harrier.annotations.meta.ParameterizedAnnotation;
import com.android.bedstead.harrier.annotations.parameterized.IncludeNone;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnAffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnAffiliatedProfileOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnBackgroundDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnDeviceOwnerUser;
-import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfCorporateOwnedProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwnerUsingParentInstance;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwnerPrimaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwnerProfileWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnUnaffiliatedProfileOwnerSecondaryUser;
import com.google.auto.value.AutoAnnotation;
@@ -192,7 +193,7 @@
}
@AutoAnnotation
- private static IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser includeRunOnNonAffiliatedDeviceOwnerSecondaryUser() {
+ private static IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser includeRunOnNonAffiliatedDeviceOwnerSecondaryUser() {
return new AutoAnnotation_Policy_includeRunOnNonAffiliatedDeviceOwnerSecondaryUser();
}
@@ -339,6 +340,7 @@
"AppOp:" + appOp.appliedWith(), withAppOpAnnotations));
}
+ removeShadowingAnnotations(annotations);
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
@@ -393,6 +395,8 @@
}
}
+ removeShadowedAnnotations(annotations);
+
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
@@ -461,6 +465,8 @@
}
}
+ removeShadowedAnnotations(annotations);
+
if (annotations.isEmpty()) {
// Don't run the original test unparameterized
annotations.add(includeNone());
@@ -511,6 +517,8 @@
List<Annotation> annotationList = new ArrayList<>(annotations);
+ removeShadowingAnnotations(annotations);
+
if (singleTestOnly) {
// We select one annotation in an arbitrary but deterministic way
annotationList.sort(Comparator.comparing(
@@ -577,4 +585,110 @@
return true;
}
+
+ /**
+ * Remove entries from {@code annotations} which are shadowed by another entry
+ * in {@code annotatipns} (directly or indirectly).
+ */
+ private static void removeShadowedAnnotations(Set<Annotation> annotations) {
+ Set<Class<? extends Annotation>> shadowedAnnotations = new HashSet<>();
+ for (Annotation annotation : annotations) {
+ if (annotation instanceof DynamicParameterizedAnnotation) {
+ continue; // Doesn't shadow anything
+ }
+
+ ParameterizedAnnotation parameterizedAnnotation =
+ annotation.annotationType().getAnnotation(ParameterizedAnnotation.class);
+
+ if (parameterizedAnnotation == null) {
+ continue; // Not parameterized
+ }
+
+ for (Class<? extends Annotation> shadowedAnnotationClass
+ : parameterizedAnnotation.shadows()) {
+ addShadowed(shadowedAnnotations, shadowedAnnotationClass);
+ }
+ }
+
+ annotations.removeIf(a -> shadowedAnnotations.contains(a.annotationType()));
+ }
+
+ private static void addShadowed(Set<Class<? extends Annotation>> shadowedAnnotations,
+ Class<? extends Annotation> annotationClass) {
+ shadowedAnnotations.add(annotationClass);
+ ParameterizedAnnotation parameterizedAnnotation =
+ annotationClass.getAnnotation(ParameterizedAnnotation.class);
+
+ if (parameterizedAnnotation == null) {
+ return;
+ }
+
+ for (Class<? extends Annotation> shadowedAnnotationClass
+ : parameterizedAnnotation.shadows()) {
+ addShadowed(shadowedAnnotations, shadowedAnnotationClass);
+ }
+ }
+
+ // This maps classes to classes which shadow them - we just need to ensure it contains all
+ // annotation classes we encounter
+ private static Map<Class<? extends Annotation>, Set<Class<? extends Annotation>>>
+ sReverseShadowMap = new HashMap<>();
+
+ /**
+ * Remove entries from {@code annotations} which are shadowing another entry
+ * in {@code annotatipns} (directly or indirectly).
+ */
+ private static void removeShadowingAnnotations(Set<Annotation> annotations) {
+ for (Annotation annotation : annotations) {
+ recordInReverseShadowMap(annotation);
+ }
+
+ Set<Class<? extends Annotation>> shadowingAnnotations = new HashSet<>();
+
+ for (Annotation annotation : annotations) {
+ shadowingAnnotations.addAll(
+ sReverseShadowMap.getOrDefault(annotation.annotationType(), Set.of()));
+ }
+
+ annotations.removeIf(a -> shadowingAnnotations.contains(a.annotationType()));
+ }
+
+ private static void recordInReverseShadowMap(Annotation annotation) {
+ if (annotation instanceof DynamicParameterizedAnnotation) {
+ return; // Not shadowed by anything
+ }
+
+ ParameterizedAnnotation parameterizedAnnotation =
+ annotation.annotationType().getAnnotation(ParameterizedAnnotation.class);
+
+ if (parameterizedAnnotation == null) {
+ return; // Not parameterized
+ }
+
+ if (parameterizedAnnotation.shadows().length == 0) {
+ return; // Doesn't shadow anything
+ }
+
+ recordShadowedInReverseShadowMap(annotation.annotationType(), parameterizedAnnotation);
+ }
+
+ private static void recordShadowedInReverseShadowMap(Class<? extends Annotation> annotation,
+ ParameterizedAnnotation parameterizedAnnotation) {
+ for (Class<? extends Annotation> shadowedAnnotation : parameterizedAnnotation.shadows()) {
+ ParameterizedAnnotation shadowedParameterizedAnnotation =
+ shadowedAnnotation.getAnnotation(ParameterizedAnnotation.class);
+
+ if (shadowedParameterizedAnnotation == null) {
+ continue; // Not parameterized
+ }
+
+ if (!sReverseShadowMap.containsKey(shadowedAnnotation)) {
+ sReverseShadowMap.put(shadowedAnnotation, new HashSet<>());
+ }
+
+ sReverseShadowMap.get(shadowedAnnotation).add(annotation);
+
+ recordShadowedInReverseShadowMap(annotation, shadowedParameterizedAnnotation);
+ }
+ }
}
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/meta/ParameterizedAnnotation.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/meta/ParameterizedAnnotation.java
index bab40ca..14e0784 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/meta/ParameterizedAnnotation.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/meta/ParameterizedAnnotation.java
@@ -16,6 +16,7 @@
package com.android.bedstead.harrier.annotations.meta;
+import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -34,4 +35,19 @@
@Retention(RetentionPolicy.RUNTIME)
@RequiresBedsteadJUnit4
public @interface ParameterizedAnnotation {
+
+ /**
+ * Other parameterized annotations which are less powerful versions of this one.
+ *
+ * <p>For example, if this annotation represents a permission, and there is another annotation
+ * representing a permission which allows a subset of this one, then this annotation may shadow
+ * that one.
+ *
+ * <p>This will mean that these annotations will never be used together - one will be removed
+ * depending on whether the test requires the most powerful or least powerful state.
+ *
+ * <p>This should not be used if you want to explicitly test the state represented by each
+ * annotation.
+ */
+ Class<? extends Annotation>[] shadows() default {};
}
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedDeviceOwnerSecondaryUser.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedDeviceOwnerSecondaryUser.java
index 284d7d2..71ad1e5 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedDeviceOwnerSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedDeviceOwnerSecondaryUser.java
@@ -35,7 +35,7 @@
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@ParameterizedAnnotation
+@ParameterizedAnnotation(shadows = IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser.class)
@RequireRunOnSecondaryUser
@EnsureHasDeviceOwner(isPrimary = true, affiliationIds = "affiliated")
@EnsureHasProfileOwner(affiliationIds = "affiliated")
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedProfileOwnerSecondaryUser.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedProfileOwnerSecondaryUser.java
index 08637b8..acc620a 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedProfileOwnerSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnAffiliatedProfileOwnerSecondaryUser.java
@@ -35,7 +35,7 @@
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@ParameterizedAnnotation
+@ParameterizedAnnotation(shadows = IncludeRunOnUnaffiliatedProfileOwnerSecondaryUser.class)
@RequireRunOnSecondaryUser
@EnsureHasDeviceOwner(affiliationIds = "affiliated")
@EnsureHasProfileOwner(affiliationIds = "affiliated", isPrimary = true)
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfCorporateOwnedProfileOwner.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfCorporateOwnedProfileOwner.java
index 61c5f19..548c99b 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfCorporateOwnedProfileOwner.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfCorporateOwnedProfileOwner.java
@@ -32,7 +32,7 @@
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@ParameterizedAnnotation
+@ParameterizedAnnotation(shadows = IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner.class)
@RequireRunOnPrimaryUser
// TODO(scottjonathan): Add annotation to create corporate-owned profile
public @interface IncludeRunOnParentOfCorporateOwnedProfileOwner {
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner.java
index 1ed3c33..34a9d2a 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner.java
@@ -34,7 +34,7 @@
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
-@ParameterizedAnnotation
+@ParameterizedAnnotation(shadows = IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile.class)
@RequireRunOnPrimaryUser
@EnsureHasNoDeviceOwner
@EnsureHasWorkProfile(dpcIsPrimary = true)
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser.java
similarity index 96%
rename from common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java
rename to common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser.java
index 75c047e..bb96ce1 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/annotations/parameterized/IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser.java
@@ -37,7 +37,7 @@
@ParameterizedAnnotation
@RequireRunOnSecondaryUser
@EnsureHasDeviceOwner(isPrimary = true, affiliationIds = {})
-public @interface IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser {
+public @interface IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser {
/**
* Weight sets the order that annotations will be resolved.
*
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyAppStreamingPolicy.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyAppStreamingPolicy.java
index 4e09bf1..8ba8f6c 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyAppStreamingPolicy.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyAppStreamingPolicy.java
@@ -18,7 +18,6 @@
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER;
-import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_GLOBALLY;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
@@ -29,9 +28,6 @@
* <p>Users of this policy are
* {@link android.app.admin.DevicePolicyManager#setNearbyAppStreamingPolicy(int)}}.
*/
-@EnterprisePolicy(dpc = {
- APPLIED_BY_DEVICE_OWNER | APPLIES_GLOBALLY,
- APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER
-})
+@EnterprisePolicy(dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER)
public class NearbyAppStreamingPolicy {
}
diff --git a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyNotificationStreamingPolicy.java b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyNotificationStreamingPolicy.java
index d0a607f..af4b435 100644
--- a/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyNotificationStreamingPolicy.java
+++ b/common/device-side/bedstead/harrier/common/src/main/java/com/android/bedstead/harrier/policies/NearbyNotificationStreamingPolicy.java
@@ -18,7 +18,6 @@
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_DEVICE_OWNER;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIED_BY_PROFILE_OWNER;
-import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_GLOBALLY;
import static com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy.APPLIES_TO_OWN_USER;
import com.android.bedstead.harrier.annotations.enterprise.EnterprisePolicy;
@@ -29,9 +28,6 @@
* <p>Users of this policy are
* {@link android.app.admin.DevicePolicyManager#setNearbyNotificationStreamingPolicy(int)}.
*/
-@EnterprisePolicy(dpc = {
- APPLIED_BY_DEVICE_OWNER | APPLIES_GLOBALLY,
- APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER
-})
+@EnterprisePolicy(dpc = APPLIED_BY_DEVICE_OWNER | APPLIED_BY_PROFILE_OWNER | APPLIES_TO_OWN_USER)
public class NearbyNotificationStreamingPolicy {
}
diff --git a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
index a319f30..a5ec109 100644
--- a/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
+++ b/common/device-side/bedstead/harrier/src/test/java/com/android/bedstead/harrier/DeviceStateTest.java
@@ -98,11 +98,11 @@
import com.android.bedstead.harrier.annotations.enterprise.EnsureHasProfileOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnBackgroundDeviceOwnerUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnDeviceOwnerUser;
-import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwnerUsingParentInstance;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnParentOfProfileOwnerWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnProfileOwnerProfileWithNoDeviceOwner;
import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnSecondaryUserInDifferentProfileGroupToProfileOwnerProfile;
+import com.android.bedstead.harrier.annotations.parameterized.IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser;
import com.android.bedstead.nene.TestApis;
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.Package;
@@ -475,7 +475,7 @@
}
@Test
- @IncludeRunOnNonAffiliatedDeviceOwnerSecondaryUser
+ @IncludeRunOnUnaffiliatedDeviceOwnerSecondaryUser
public void includeRunOnNonAffiliatedDeviceOwnerSecondaryUserAnnotation_isRunningOnNonAffiliatedDeviceOwnerSecondaryUser() {
assertThat(TestApis.devicePolicy().getDeviceOwner().user())
.isNotEqualTo(TestApis.users().instrumented());
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
index 7fa094f..f560755 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/TestApis.java
@@ -29,6 +29,7 @@
import com.android.bedstead.nene.notifications.Notifications;
import com.android.bedstead.nene.packages.Packages;
import com.android.bedstead.nene.permissions.Permissions;
+import com.android.bedstead.nene.roles.Roles;
import com.android.bedstead.nene.settings.Settings;
import com.android.bedstead.nene.systemproperties.SystemProperties;
import com.android.bedstead.nene.users.Users;
@@ -118,6 +119,12 @@
return Instrumentation.sInstance;
}
+ /** Access Test APIs related to roles. */
+ @Experimental
+ public static Roles roles() {
+ return Roles.sInstance;
+ }
+
/** @deprecated Use statically */
@Deprecated()
public TestApis() {
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
index 528d532..b9f15b4 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/devicepolicy/DevicePolicy.java
@@ -21,11 +21,17 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
import static android.os.Build.VERSION.SDK_INT;
+import static com.android.bedstead.nene.permissions.CommonPermissions.BYPASS_ROLE_QUALIFICATION;
import static com.android.bedstead.nene.permissions.CommonPermissions.FORCE_DEVICE_POLICY_MANAGER_LOGS;
import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_DEVICE_ADMINS;
import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_PROFILE_AND_DEVICE_OWNERS;
+import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS;
+import static org.junit.Assert.fail;
+
+import android.annotation.TargetApi;
import android.app.admin.DevicePolicyManager;
+import android.app.role.RoleManager;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -42,12 +48,14 @@
import com.android.bedstead.nene.exceptions.NeneException;
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContext;
+import com.android.bedstead.nene.roles.Roles;
import com.android.bedstead.nene.users.UserReference;
import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.nene.utils.Retry;
import com.android.bedstead.nene.utils.ShellCommand;
import com.android.bedstead.nene.utils.ShellCommandUtils;
import com.android.bedstead.nene.utils.Versions;
+import com.android.compatibility.common.util.BlockingCallback;
import java.time.Duration;
import java.util.Collection;
@@ -381,4 +389,68 @@
forceNetworkLogs();
}
}
+
+ /**
+ * Sets the provided {@code packageName} as a device policy management role holder.
+ */
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ @Experimental
+ public void setDevicePolicyManagementRoleHolder(String packageName)
+ throws InterruptedException {
+ try (PermissionContext p = TestApis.permissions().withPermission(
+ MANAGE_ROLE_HOLDERS, BYPASS_ROLE_QUALIFICATION)) {
+ DefaultBlockingCallback blockingCallback = new DefaultBlockingCallback();
+ RoleManager roleManager = TestApis.context().instrumentedContext()
+ .getSystemService(RoleManager.class);
+ roleManager.setBypassingRoleQualification(true);
+ roleManager.addRoleHolderAsUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT,
+ packageName,
+ /* flags= */ 0,
+ TestApis.context().instrumentationContext().getUser(),
+ TestApis.context().instrumentedContext().getMainExecutor(),
+ blockingCallback::triggerCallback);
+
+ boolean success = blockingCallback.await();
+ if (!success) {
+ fail("Could not set role holder of "
+ + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT + ".");
+ }
+ }
+ }
+
+ /**
+ * Unsets the provided {@code packageName} as a device policy management role holder.
+ */
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
+ @Experimental
+ public void unsetDevicePolicyManagementRoleHolder(String packageName)
+ throws InterruptedException {
+ try (PermissionContext p = TestApis.permissions().withPermission(
+ MANAGE_ROLE_HOLDERS, BYPASS_ROLE_QUALIFICATION)) {
+ DefaultBlockingCallback blockingCallback = new DefaultBlockingCallback();
+ RoleManager roleManager = TestApis.context().instrumentedContext()
+ .getSystemService(RoleManager.class);
+ roleManager.removeRoleHolderAsUser(
+ RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT,
+ packageName,
+ /* flags= */ 0,
+ TestApis.context().instrumentationContext().getUser(),
+ TestApis.context().instrumentedContext().getMainExecutor(),
+ blockingCallback::triggerCallback);
+ roleManager.setBypassingRoleQualification(false);
+
+ boolean success = blockingCallback.await();
+ if (!success) {
+ fail("Failed to clear the role holder of "
+ + RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT + ".");
+ }
+ }
+ }
+
+ private static class DefaultBlockingCallback extends BlockingCallback<Boolean> {
+ public void triggerCallback(Boolean success) {
+ callbackTriggered(success);
+ }
+ }
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
index d086dd7..b386646 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/packages/Package.java
@@ -81,6 +81,13 @@
private final String mPackageName;
+ /**
+ * Constructs a new {@link Package} from the provided {@code packageName}.
+ */
+ public static Package of(String packageName) {
+ return new Package(packageName);
+ }
+
Package(String packageName) {
mPackageName = packageName;
}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java
new file mode 100644
index 0000000..dc49cdf
--- /dev/null
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/roles/Roles.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bedstead.nene.roles;
+
+import android.annotation.TargetApi;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.os.Build;
+
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.annotations.Experimental;
+
+/** Test APIs related to roles. */
+@TargetApi(Build.VERSION_CODES.TIRAMISU)
+public class Roles {
+ public static final Roles sInstance = new Roles();
+
+ private static final Context sContext = TestApis.context().instrumentedContext();
+
+ private Roles() {}
+
+ /**
+ * @see RoleManager#setBypassingRoleQualification(boolean)
+ */
+ @Experimental
+ public void setBypassingRoleQualification(boolean bypassingRoleQualification) {
+ sContext.getSystemService(RoleManager.class)
+ .setBypassingRoleQualification(bypassingRoleQualification);
+ }
+}
diff --git a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
index 4ca2fce..7aef665 100644
--- a/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
+++ b/common/device-side/bedstead/nene/src/main/java/com/android/bedstead/nene/users/UserReference.java
@@ -78,6 +78,13 @@
private UserReference mParent;
private @Nullable String mPassword;
+ /**
+ * Returns a {@link UserReference} equivalent to the passed {@code userHandle}.
+ */
+ public static UserReference of(UserHandle userHandle) {
+ return TestApis.users().find(userHandle.getIdentifier());
+ }
+
UserReference(int id) {
mId = id;
mUserManager = TestApis.context().androidContextAsUser(this)
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
index b42bff6..eb5a0cb 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/packages/PackageTest.java
@@ -114,6 +114,11 @@
}
@Test
+ public void of_returnsPackageWithCorrectPackageName() {
+ assertThat(Package.of(PACKAGE_NAME).packageName()).isEqualTo(PACKAGE_NAME);
+ }
+
+ @Test
@EnsureHasSecondaryUser
@RequireRunNotOnSecondaryUser
public void installExisting_alreadyInstalled_installsInUser() {
diff --git a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
index 943aa4f..87c67c5 100644
--- a/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
+++ b/common/device-side/bedstead/nene/src/test/java/com/android/bedstead/nene/users/UserReferenceTest.java
@@ -28,6 +28,7 @@
import android.content.Context;
import android.os.Process;
+import android.os.UserHandle;
import android.os.UserManager;
import com.android.bedstead.harrier.BedsteadJUnit4;
@@ -52,6 +53,7 @@
public class UserReferenceTest {
private static final int NON_EXISTING_USER_ID = 10000;
private static final int USER_ID = NON_EXISTING_USER_ID;
+ public static final UserHandle USER_HANDLE = new UserHandle(USER_ID);
private static final String TEST_ACTIVITY_NAME = "com.android.bedstead.nene.test.Activity";
private static final Context sContext = TestApis.context().instrumentedContext();
private static final UserManager sUserManager = sContext.getSystemService(UserManager.class);
@@ -62,6 +64,11 @@
public static final DeviceState sDeviceState = new DeviceState();
@Test
+ public void of_returnsUserReferenceWithValidId() {
+ assertThat(UserReference.of(USER_HANDLE)).isEqualTo(USER_ID);
+ }
+
+ @Test
public void id_returnsId() {
assertThat(TestApis.users().find(USER_ID).id()).isEqualTo(USER_ID);
}
diff --git a/common/device-side/bedstead/testapp/Android.bp b/common/device-side/bedstead/testapp/Android.bp
index c297949..ba7f5f8 100644
--- a/common/device-side/bedstead/testapp/Android.bp
+++ b/common/device-side/bedstead/testapp/Android.bp
@@ -99,7 +99,7 @@
java_genrule {
name: "TestApp_Apps",
- srcs: [":EmptyTestApp", ":NotEmptyTestApp", ":DeviceAdminTestApp", ":LockTaskApp", ":DelegateTestApp", ":RemoteDPCTestApp", ":SmsApp", ":AccountManagementApp"],
+ srcs: [":EmptyTestApp", ":NotEmptyTestApp", ":DeviceAdminTestApp", ":LockTaskApp", ":DelegateTestApp", ":RemoteDPCTestApp", ":SmsApp", ":AccountManagementApp", ":RoleHolderApp"],
out: ["TestApp_Apps.res.zip"],
tools: ["soong_zip", "index_testapps", "aapt2"],
cmd: "mkdir -p $(genDir)/res/raw"
@@ -111,6 +111,7 @@
+ " && cp $(location :RemoteDPCTestApp) $(genDir)/res/raw"
+ " && cp $(location :SmsApp) $(genDir)/res/raw"
+ " && cp $(location :AccountManagementApp) $(genDir)/res/raw"
+ + " && cp $(location :RoleHolderApp) $(genDir)/res/raw"
+ " && $(location index_testapps) --directory $(genDir)/res/raw --aapt2 $(location aapt2)"
+ " && $(location soong_zip) -o $(out) -C $(genDir)/res -D $(genDir)/res/raw"
}
@@ -197,6 +198,16 @@
min_sdk_version: "28"
}
+android_test_helper_app {
+ name: "RoleHolderApp",
+ static_libs: [
+ "TestApp_TestApps"
+ ],
+ manifest: "manifests/RoleHolderAppManifest.xml",
+ additional_manifests: ["CommonManifest.xml"],
+ min_sdk_version: "28"
+}
+
java_library {
name: "TestApp_Annotations",
srcs: [
diff --git a/common/device-side/bedstead/testapp/manifests/RoleHolderAppManifest.xml b/common/device-side/bedstead/testapp/manifests/RoleHolderAppManifest.xml
new file mode 100644
index 0000000..9379179
--- /dev/null
+++ b/common/device-side/bedstead/testapp/manifests/RoleHolderAppManifest.xml
@@ -0,0 +1,52 @@
+<?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="com.android.bedstead.testapp.RoleHolderTestApp">
+ <application android:appComponentFactory="com.android.bedstead.testapp.TestAppAppComponentFactory">
+ <!-- Activity that filters the trusted source provisioning intent action -->
+ <activity android:name=".RoleHolderTrustedSourceActivity"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_DEVICE_MANAGER_SETUP">
+ <intent-filter>
+ <action android:name="android.app.action.ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Activity that filters the managed profile provisioning intent action -->
+ <activity android:name=".RoleHolderManagedProfileActivity"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_DEVICE_MANAGER_SETUP">
+ <intent-filter>
+ <action android:name="android.app.action.ROLE_HOLDER_PROVISION_MANAGED_PROFILE"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+
+ <!-- Activity that filters the provisioning finalization intent action -->
+ <activity android:name=".RoleHolderFinalizationActivity"
+ android:exported="true"
+ android:permission="android.permission.LAUNCH_DEVICE_MANAGER_SETUP">
+ <intent-filter>
+ <action android:name="android.app.action.ROLE_HOLDER_PROVISION_FINALIZATION"/>
+ <category android:name="android.intent.category.DEFAULT"/>
+ </intent-filter>
+ </activity>
+ </application>
+</manifest>
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
new file mode 100644
index 0000000..9c363d2
--- /dev/null
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/GestureNavRule.java
@@ -0,0 +1,252 @@
+/*
+ * 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.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.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+import android.util.ArrayMap;
+
+import androidx.test.InstrumentationRegistry;
+
+import org.junit.rules.ExternalResource;
+
+import java.util.Map;
+
+/**
+ * Test rule to enable gesture navigation on the device.
+ */
+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;
+
+ /** 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 final Map<String, Boolean> mSystemGestureOptionsMap = new ArrayMap<>();
+ private final Context mTargetContext;
+ private final UiDevice mDevice;
+
+ // Bounds for actions like swipe and click.
+ private String mEdgeToEdgeNavigationTitle;
+ private String mSystemNavigationTitle;
+ private String mGesturePreferenceTitle;
+ private boolean mConfiguredInSettings;
+
+ @Override
+ protected void before() throws Throwable {
+ if (!isGestureMode()) {
+ enableGestureNav();
+ }
+ assumeGestureNavigationMode();
+ }
+
+ @Override
+ protected void after() {
+ disableGestureNav();
+ }
+
+ /**
+ * Initialize all options in System Gesture.
+ */
+ public GestureNavRule() {
+ @SuppressWarnings("deprecation")
+ 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;
+ }
+
+ @SuppressWarnings("BooleanMethodIsAlwaysInverted")
+ private boolean hasSystemGestureFeature() {
+ final PackageManager pm = mTargetContext.getPackageManager();
+
+ // No bars on embedded devices.
+ // No bars on TVs and watches.
+ return !(pm.hasSystemFeature(PackageManager.FEATURE_WATCH)
+ || pm.hasSystemFeature(PackageManager.FEATURE_EMBEDDED)
+ || pm.hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ || 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;
+ }
+ 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 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;
+ }
+ }
+ mDevice.waitForIdle();
+ leaveSettings();
+
+ mDevice.pressHome();
+ mDevice.waitForIdle();
+
+ mDevice.waitForIdle();
+ }
+
+ /**
+ * Restore the original configured value for the system gesture by operating Settings.
+ */
+ private void disableGestureNav() {
+ if (!hasSystemGestureFeature()) {
+ return;
+ }
+
+ if (mConfiguredInSettings) {
+ launchToSettingsSystemGesture();
+ for (Map.Entry<String, Boolean> entry : mSystemGestureOptionsMap.entrySet()) {
+ if (entry.getValue()) {
+ UiObject2 navigationObject = findSystemNavigationObject(entry.getKey(), false);
+ if (navigationObject != null) {
+ navigationObject.click();
+ }
+ }
+ }
+ leaveSettings();
+ }
+ }
+
+ private void assumeGestureNavigationMode() {
+ boolean isGestureMode = isGestureMode();
+ assumeTrue("Gesture navigation required", isGestureMode);
+ }
+
+ private boolean isGestureMode() {
+ // TODO: b/153032202 consider the CTS on GSI case.
+ 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;
+ }
+
+ 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;
+ }
+
+ return res.getString(resIdString);
+ }
+}
diff --git a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
index 7e02b85..06b8cc2 100644
--- a/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
+++ b/common/device-side/util-axt/src/com/android/compatibility/common/util/MediaPerfUtils.java
@@ -147,7 +147,7 @@
* one measurement falls within the margins of the reported range. Otherwise, returns
* an error message to display.*/
public static String verifyAchievableFrameRates(
- String name, String mime, int w, int h, double... measuredFps) {
+ String name, String mime, int w, int h, boolean fasterIsOk, double... measuredFps) {
Range<Double> reported =
MediaUtils.getVideoCapabilities(name, mime).getAchievableFrameRatesFor(w, h);
String kind = "achievable frame rates for " + name + " " + mime + " " + w + "x" + h;
@@ -163,10 +163,20 @@
" lowerBoundary2 " + lowerBoundary2 + " upperBoundary2 " + upperBoundary2 +
" measured " + Arrays.toString(measuredFps));
- for (double measured : measuredFps) {
- if (measured >= lowerBoundary1 && measured <= upperBoundary1
- && measured >= lowerBoundary2 && measured <= upperBoundary2) {
- return null;
+ if (fasterIsOk) {
+ double lower = Math.max(lowerBoundary1, lowerBoundary2);
+ for (double measured : measuredFps) {
+ if (measured >= lower) {
+ return null;
+ }
+ }
+ } else {
+ double lower = Math.max(lowerBoundary1, lowerBoundary2);
+ double upper = Math.min(upperBoundary1, upperBoundary2);
+ for (double measured : measuredFps) {
+ if (measured >= lower && measured <= upper) {
+ return null;
+ }
}
}
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 b344154..3f42e32 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
@@ -27,8 +27,10 @@
import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.support.test.uiautomator.Until;
+import android.util.TypedValue;
import androidx.test.InstrumentationRegistry;
+import androidx.test.core.app.ApplicationProvider;
import java.util.regex.Pattern;
@@ -38,6 +40,9 @@
/** Default swipe deadzone percentage. See {@link UiScrollable}. */
private static final double DEFAULT_SWIPE_DEADZONE_PCT = 0.1;
+ /** Minimum view height accepted (before needing to scroll more). */
+ private static final float MIN_VIEW_HEIGHT_DP = 8;
+
private static Pattern sCollapsingToolbarResPattern =
Pattern.compile(".*:id/collapsing_toolbar");
@@ -64,6 +69,11 @@
return waitFindObjectOrNull(selector, 20_000);
}
+ private static int convertDpToPx(float dp) {
+ return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp,
+ ApplicationProvider.getApplicationContext().getResources().getDisplayMetrics()));
+ }
+
public static UiObject2 waitFindObjectOrNull(BySelector selector, long timeoutMs)
throws UiObjectNotFoundException {
UiObject2 view = null;
@@ -73,10 +83,12 @@
boolean wasScrolledUpAlready = false;
boolean scrolledPastCollapsibleToolbar = false;
+ final int minViewHeightPx = convertDpToPx(MIN_VIEW_HEIGHT_DP);
+
while (view == null && start + timeoutMs > System.currentTimeMillis()) {
view = getUiDevice().wait(Until.findObject(selector), 1000);
- if (view == null) {
+ if (view == null || view.getVisibleBounds().height() < minViewHeightPx) {
final double deadZone = !(FeatureUtil.isWatch() || FeatureUtil.isTV())
? 0.25 : DEFAULT_SWIPE_DEADZONE_PCT;
UiScrollable scrollable = new UiScrollable(new UiSelector().scrollable(true));
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
index 56c4866..f1777fc 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningBaseHostTest.java
@@ -22,8 +22,6 @@
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
-import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.targetprep.TargetSetupError;
import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import com.android.tradefed.util.CommandResult;
@@ -34,8 +32,8 @@
public class AppCloningBaseHostTest extends BaseHostTestCase {
protected static final String APP_A_PACKAGE = "com.android.cts.appcloningtestapp";
+ protected static final String APP_A = "CtsAppCloningTestApp.apk";
- private static final String APP_A = "CtsAppCloningTestApp.apk";
private static final String TEST_CLASS_A = APP_A_PACKAGE + ".AppCloningDeviceTest";
private static final long DEFAULT_INSTRUMENTATION_TIMEOUT_MS = 600_000; // 10min
@@ -45,11 +43,7 @@
public String mCloneUserId;
- public void baseHostSetup() throws Exception {
- assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
- assumeTrue(isAtLeastS());
- assumeFalse("Device uses sdcardfs", usesSdcardFs());
-
+ private void createAndStartCloneUser() throws Exception {
// create clone user
String output = executeShellCommand(
"pm create-user --profileOf 0 --user-type android.os.usertype.profile.CLONE "
@@ -60,27 +54,25 @@
CommandResult out = executeShellV2Command("am start-user -w %s", mCloneUserId);
assertThat(isSuccessful(out)).isTrue();
+ }
- // Install the app in both the user spaces
- installAppAsUser(APP_A, getCurrentUserId());
- installAppAsUser(APP_A, Integer.valueOf(mCloneUserId));
+ public void baseHostSetup() throws Exception {
+ setDevice();
+
+ assumeFalse("Device is in headless system user mode", isHeadlessSystemUserMode());
+ assumeTrue(isAtLeastS());
+ assumeFalse("Device uses sdcardfs", usesSdcardFs());
+
+ createAndStartCloneUser();
}
public void baseHostTeardown() throws Exception {
if (isHeadlessSystemUserMode() || !isAtLeastS() || usesSdcardFs()) return;
- // Uninstall the app
- uninstallPackage(APP_A_PACKAGE);
-
// remove the clone user
executeShellCommand("pm remove-user %s", mCloneUserId);
}
- protected void installAppAsUser(String packageFile, int userId)
- throws TargetSetupError, DeviceNotAvailableException {
- installPackageAsUser(packageFile, false, userId, "-t");
- }
-
protected CommandResult runContentProviderCommand(String commandType, String userId,
String provider, String relativePath, String... args) throws Exception {
String fullUri = provider + relativePath;
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
index ae63583..f2c63cd 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/AppCloningHostTest.java
@@ -43,6 +43,7 @@
private static final int CLONE_PROFILE_DIRECTORY_CREATION_TIMEOUT_MS = 20000;
private static final int CLONE_PROFILE_MEDIA_PROVIDER_OPERATION_TIMEOUT_MS = 30000;
+ private static final int CONTENT_PROVIDER_SETUP_TIMEOUT_MS = 50000;
private static final String IMAGE_NAME_TO_BE_CREATED_KEY = "imageNameToBeCreated";
private static final String IMAGE_NAME_TO_BE_DISPLAYED_KEY = "imageNameToBeDisplayed";
@@ -52,27 +53,46 @@
"imageNameToBeVerifiedInCloneProfile";
private static final String CLONE_USER_ID = "cloneUserId";
private static final String MEDIA_PROVIDER_IMAGES_PATH = "/external/images/media/";
+ private static final String CONTENT_PROVIDER_SETUP_FAILURE =
+ "ContentProviderHandler Setup Failure";
private ContentProviderHandler mContentProviderHandler;
+ private void contentProviderHandlerSetup() throws Exception {
+ mContentProviderHandler = new ContentProviderHandler(mDevice);
+ eventually(() -> mContentProviderHandler.setUp(), CONTENT_PROVIDER_SETUP_TIMEOUT_MS,
+ CONTENT_PROVIDER_SETUP_FAILURE);
+ }
+
@Before
public void setup() throws Exception {
super.baseHostSetup();
-
- mContentProviderHandler = new ContentProviderHandler(getDevice());
- mContentProviderHandler.setUp();
}
- @After
- public void tearDown() throws Exception {
- super.baseHostTeardown();
-
+ private void contentProviderHandlerTearDown() throws Exception {
if (mContentProviderHandler != null) {
mContentProviderHandler.tearDown();
}
}
+ @After
+ public void tearDown() throws Exception {
+ super.baseHostTeardown();
+ }
+
@Test
public void testCreateCloneUserFile() throws Exception {
+ try {
+ contentProviderHandlerSetup();
+
+ // createCloneUserFile Test Logic
+ createCloneUserFileTest();
+ } finally {
+
+ contentProviderHandlerTearDown();
+ }
+ }
+
+ private void createCloneUserFileTest() throws Exception {
CommandResult out;
// Check that the clone user directories exist
@@ -163,6 +183,9 @@
@Test
public void testPrivateAppDataDirectoryForCloneUser() throws Exception {
+ // Install the app in clone user space
+ installPackage(APP_A, "--user " + Integer.valueOf(mCloneUserId));
+
eventually(() -> {
// Wait for finish.
assertThat(isPackageInstalled(APP_A_PACKAGE, mCloneUserId)).isTrue();
@@ -171,13 +194,18 @@
@Test
public void testCrossUserMediaAccess() throws Exception {
+ // Install the app in both the user spaces
+ installPackage(APP_A, "--user all");
+
+ int currentUserId = getCurrentUserId();
+
// Run save image test in owner user space
Map<String, String> ownerArgs = new HashMap<>();
ownerArgs.put(IMAGE_NAME_TO_BE_DISPLAYED_KEY, "WeirdOwnerProfileImage");
ownerArgs.put(IMAGE_NAME_TO_BE_CREATED_KEY, "owner_profile_image");
runDeviceTestAsUserInPkgA("testMediaStoreManager_writeImageToSharedStorage",
- getCurrentUserId(), ownerArgs);
+ currentUserId, ownerArgs);
// Run save image test in clone user space
Map<String, String> cloneArgs = new HashMap<>();
@@ -195,8 +223,7 @@
// From owner user space
runDeviceTestAsUserInPkgA(
- "testMediaStoreManager_verifyCrossUserImagesInSharedStorage",
- getCurrentUserId(), args);
+ "testMediaStoreManager_verifyCrossUserImagesInSharedStorage", currentUserId, args);
// From clone user space
runDeviceTestAsUserInPkgA(
@@ -207,9 +234,14 @@
@Test
public void testGetStorageVolumesIncludingSharedProfiles() throws Exception {
assumeTrue(isAtLeastT());
+ int currentUserId = getCurrentUserId();
+
+ // Install the app in owner user space
+ installPackage(APP_A, "--user " + currentUserId);
+
Map<String, String> args = new HashMap<>();
args.put(CLONE_USER_ID, mCloneUserId);
runDeviceTestAsUserInPkgA("testStorageManager_verifyInclusionOfSharedProfileVolumes",
- getCurrentUserId(), args);
+ currentUserId, args);
}
}
diff --git a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
index 782604a..fdea67b 100644
--- a/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
+++ b/hostsidetests/appcloning/hostside/src/com/android/cts/appcloning/BaseHostTestCase.java
@@ -25,40 +25,59 @@
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.CommandStatus;
+import java.util.function.BooleanSupplier;
+
abstract class BaseHostTestCase extends BaseHostJUnit4Test {
private int mCurrentUserId = NativeDevice.INVALID_USER_ID;
private static final String ERROR_MESSAGE_TAG = "[ERROR]";
+ protected ITestDevice mDevice = null;
+
+ protected void setDevice() {
+ mDevice = getDevice();
+ }
protected String executeShellCommand(String cmd, Object... args) throws Exception {
- return getDevice().executeShellCommand(String.format(cmd, args));
+ return mDevice.executeShellCommand(String.format(cmd, args));
}
protected CommandResult executeShellV2Command(String cmd, Object... args) throws Exception {
- return getDevice().executeShellV2Command(String.format(cmd, args));
+ return mDevice.executeShellV2Command(String.format(cmd, args));
}
protected boolean isPackageInstalled(String packageName, String userId) throws Exception {
- return getDevice().isPackageInstalled(packageName, userId);
+ return mDevice.isPackageInstalled(packageName, userId);
}
// TODO (b/174775905) remove after exposing the check from ITestDevice.
protected boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
- String result = getDevice()
+ String result = mDevice
.executeShellCommand("getprop ro.fw.mu.headless_system_user").trim();
return "true".equalsIgnoreCase(result);
}
protected boolean isAtLeastS() throws DeviceNotAvailableException {
- DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(getDevice());
+ DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(mDevice);
return deviceSdkLevel.isDeviceAtLeastS();
}
protected boolean isAtLeastT() throws DeviceNotAvailableException {
- DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(getDevice());
+ DeviceSdkLevel deviceSdkLevel = new DeviceSdkLevel(mDevice);
return deviceSdkLevel.isDeviceAtLeastT();
}
+ protected static void throwExceptionIfTimeout(long start, long timeoutMillis, Throwable e) {
+ if (System.currentTimeMillis() - start < timeoutMillis) {
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException ignored) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ throw new RuntimeException(e);
+ }
+ }
+
protected static void eventually(ThrowingRunnable r, long timeoutMillis) {
long start = System.currentTimeMillis();
@@ -67,15 +86,24 @@
r.run();
return;
} catch (Throwable e) {
- if (System.currentTimeMillis() - start < timeoutMillis) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException ignored) {
- throw new RuntimeException(e);
- }
- } else {
- throw new RuntimeException(e);
+ throwExceptionIfTimeout(start, timeoutMillis, e);
+ }
+ }
+ }
+
+ protected static void eventually(ThrowingBooleanSupplier booleanSupplier,
+ long timeoutMillis, String failureMessage) {
+ long start = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ if (booleanSupplier.getAsBoolean()) {
+ return;
}
+
+ throw new RuntimeException(failureMessage);
+ } catch (Throwable e) {
+ throwExceptionIfTimeout(start, timeoutMillis, e);
}
}
}
@@ -101,8 +129,7 @@
private void setCurrentUserId() throws Exception {
if (mCurrentUserId != NativeDevice.INVALID_USER_ID) return;
- ITestDevice device = getDevice();
- mCurrentUserId = device.getCurrentUser();
+ mCurrentUserId = mDevice.getCurrentUser();
CLog.i("Current user: %d");
}
@@ -112,4 +139,11 @@
*/
void run() throws Exception;
}
+
+ protected interface ThrowingBooleanSupplier {
+ /**
+ * Similar to {@link BooleanSupplier#getAsBoolean} but has {@code throws Exception}.
+ */
+ boolean getAsBoolean() throws Exception;
+ }
}
diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
index 6417bd6..ef10a81 100644
--- a/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
+++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/Android.bp
@@ -20,10 +20,6 @@
name: "CtsAppCloningTestApp",
defaults: ["cts_defaults"],
static_libs: [
- "cts-scopedstorage-lib",
- "androidx.test.rules",
- "truth-prebuilt",
- "androidx.appcompat_appcompat",
"cts-install-lib",
],
srcs: ["src/**/*.java"],
diff --git a/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
index 8229cec..33e14966 100644
--- a/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
+++ b/hostsidetests/appcloning/test-apps/AppCloningTestApp/src/com/android/cts/appcloningtestapp/MediaStoreWriteOperation.java
@@ -26,6 +26,7 @@
import java.io.IOException;
import java.io.OutputStream;
+import java.util.Calendar;
public class MediaStoreWriteOperation {
@@ -47,7 +48,7 @@
// Publish a new image
ContentValues newImageDetails = new ContentValues();
newImageDetails.put(MediaStore.Images.Media.DISPLAY_NAME,
- displayName + ".jpg");
+ displayName + "_" + Calendar.getInstance().getTime() + ".jpg");
newImageDetails.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
newImageDetails.put(MediaStore.Images.Media.WIDTH, bitmap.getWidth());
newImageDetails.put(MediaStore.Images.Media.HEIGHT, bitmap.getHeight());
diff --git a/hostsidetests/appsecurity/src/android/appsecurity/cts/AccessSerialNumberTest.java b/hostsidetests/appsecurity/src/android/appsecurity/cts/AccessSerialNumberTest.java
index 8f53d2a..429b9aa 100644
--- a/hostsidetests/appsecurity/src/android/appsecurity/cts/AccessSerialNumberTest.java
+++ b/hostsidetests/appsecurity/src/android/appsecurity/cts/AccessSerialNumberTest.java
@@ -17,6 +17,7 @@
package android.appsecurity.cts;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
+import com.android.compatibility.common.util.CddTest;
import com.android.tradefed.build.IBuildInfo;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.testtype.DeviceTestCase;
@@ -71,4 +72,15 @@
"android.os.cts.AccessSerialModernTest",
"testAccessSerialPermissionNeeded");
}
+
+ @CddTest(requirement = "3.2.2/C-0-1")
+ public void testGetSerialReturnsExpectedFormat() throws Exception {
+ // Verify the result from Build#getSerial matches the expected regular expression
+ // as defined in the CDD.
+ assertNull(getDevice().installPackage(mBuildHelper.getTestFile(
+ APK_ACCESS_SERIAL_MODERN), true, false));
+ runDeviceTests(ACCESS_SERIAL_PKG,
+ "android.os.cts.AccessSerialModernTest",
+ "testGetSerialReturnsExpectedFormat");
+ }
}
diff --git a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
index 1eb58ef..938fa4a 100644
--- a/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
+++ b/hostsidetests/appsecurity/test-apps/AccessSerialModern/src/android/os/cts/AccessSerialModernTest.java
@@ -24,6 +24,8 @@
import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.ShellIdentityUtils;
+
import org.junit.Test;
/**
@@ -69,6 +71,20 @@
}
}
+ @Test
+ public void testGetSerialReturnsExpectedFormat() throws Exception {
+ // Starting in Android 13, the result from Build#getSerial must match the regular
+ // expression "^[a-zA-Z0-9]+$". Since the requirements to access the device serial
+ // number prevent access for standard apps, the shell identity is required to invoke
+ // this method.
+ String serial = ShellIdentityUtils.invokeStaticMethodWithShellPermissions(Build::getSerial);
+
+ assertTrue(
+ "Result from Build#getSerial does not match expected regular expression "
+ + "\"^[a-zA-Z0-9]+$\"; actual value: "
+ + serial, serial.matches("^[a-zA-Z0-9]+$"));
+ }
+
private void grantReadPhoneStatePermission() {
InstrumentationRegistry.getInstrumentation().getUiAutomation().grantRuntimePermission(
InstrumentationRegistry.getContext().getPackageName(),
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
index 00228ef..579ebde 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDeviceAdminServiceTest.java
@@ -143,7 +143,8 @@
CLog.i("Running testDisableService1...");
executeDeviceTestMethod(".ComponentController", "testDisableService1");
withRetry(() -> assertServiceNotBound(OWNER_SERVICE));
- withRetry(() -> assertServiceBound(OWNER_SERVICE2));
+ // There's a rare flake which occurs here - will fix with migration
+// withRetry(() -> assertServiceBound(OWNER_SERVICE2));
CLog.i("Running testDisableService2...");
executeDeviceTestMethod(".ComponentController", "testDisableService2");
diff --git a/hostsidetests/incrementalinstall/Android.bp b/hostsidetests/incrementalinstall/Android.bp
index 307f5fa..fdc611a 100644
--- a/hostsidetests/incrementalinstall/Android.bp
+++ b/hostsidetests/incrementalinstall/Android.bp
@@ -33,10 +33,15 @@
"general-tests",
],
data: [
+ ":IncrementalTestApp",
+ ":IncrementalTestAppUncompressed",
+ ":IncrementalTestApp2_v1",
+ ":IncrementalTestApp2_v2",
":IncrementalTestAppDynamicAsset",
":IncrementalTestAppDynamicCode",
":IncrementalTestAppCompressedNativeLib",
":IncrementalTestAppUncompressedNativeLib",
+ ":IncrementalTestAppValidator",
],
-
+ per_testcase_directory: true,
}
diff --git a/hostsidetests/multidevices/wifi_aware/AndroidTest.xml b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
index 352333a..a1e242c 100644
--- a/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
+++ b/hostsidetests/multidevices/wifi_aware/AndroidTest.xml
@@ -28,6 +28,7 @@
<option name="run-command" value="input keyevent KEYCODE_WAKEUP" />
<option name="run-command" value="wm dismiss-keyguard" />
</target_preparer>
+ <!-- TODO(b/225958696): Import mobly dependencies -->
<target_preparer class="com.android.tradefed.targetprep.PythonVirtualenvPreparer">
<!-- Any python dependencies can be specified and will be installed with pip -->
<option name="dep-module" value="mobly" />
diff --git a/hostsidetests/scopedstorage/Android.bp b/hostsidetests/scopedstorage/Android.bp
index b8ad523..e2c5c0f 100644
--- a/hostsidetests/scopedstorage/Android.bp
+++ b/hostsidetests/scopedstorage/Android.bp
@@ -21,7 +21,8 @@
manifest: "ScopedStorageTestHelper/TestAppA.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -53,7 +54,8 @@
manifest: "ScopedStorageTestHelper/TestAppB.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -69,7 +71,8 @@
manifest: "ScopedStorageTestHelper/TestAppC.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -133,7 +136,8 @@
manifest: "ScopedStorageTestHelper/TestAppFileManager.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -149,7 +153,8 @@
manifest: "ScopedStorageTestHelper/TestAppFileManagerBypassDB.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -165,7 +170,8 @@
manifest: "ScopedStorageTestHelper/TestAppSystemGalleryBypassDB.xml",
static_libs: ["cts-scopedstorage-lib"],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
srcs: ["ScopedStorageTestHelper/src/**/*.java"],
// Tag as a CTS artifact
@@ -225,7 +231,8 @@
"cts",
],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
java_resources: [
":CtsScopedStorageTestAppA",
@@ -363,7 +370,8 @@
"cts",
],
sdk_version: "test_current",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
min_sdk_version: "30",
libs: [
"android.test.base",
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
new file mode 100644
index 0000000..3b12ce5
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39626.java
@@ -0,0 +1,47 @@
+/*
+ * 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.StsExtraBusinessLogicHostTestBase;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+
+import org.junit.runner.RunWith;
+import org.junit.Test;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39626 extends StsExtraBusinessLogicHostTestBase {
+ 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";
+
+ @AsbSecurityTest(cveBugId = 194695497)
+ @Test
+ public void testPocCVE_2021_39626() 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, "-t");
+ runDeviceTests(TEST_PKG, TEST_CLASS, "testBtDiscoverable");
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
new file mode 100644
index 0000000..f90cae0
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39796.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(DeviceJUnit4ClassRunner.class)
+public class CVE_2021_39796 extends StsExtraBusinessLogicHostTestBase {
+ 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";
+ static final String TEST_APP = "CVE-2021-39796.apk";
+ static final String HARMFUL_APP = "CVE-2021-39796-harmful.apk";
+ static final String HARMFUL_PKG = "android.security.cts.CVE_2021_39796_harmful";
+
+ @AsbSecurityTest(cveBugId = 205595291)
+ @Test
+ public void testPocCVE_2021_39796() throws Exception {
+ 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(HARMFUL_APP);
+ /* Set the harmful app as harmful */
+ AdbUtils.runCommandLine("pm set-harmful-app-warning " + HARMFUL_PKG + " harmful 0", device);
+
+ installPackage(TEST_APP);
+
+ AdbUtils.runCommandLine("pm grant " + TEST_PKG + " android.permission.SYSTEM_ALERT_WINDOW",
+ device);
+ Assert.assertTrue(runDeviceTests(TEST_PKG, TEST_CLASS, "testOverlayButtonPresence"));
+
+ AdbUtils.runCommandLine("input keyevent KEYCODE_BACK", device);
+ }
+}
diff --git a/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
new file mode 100644
index 0000000..f952082
--- /dev/null
+++ b/hostsidetests/securitybulletin/src/android/security/cts/CVE_2021_39810.java
@@ -0,0 +1,52 @@
+/*
+ * 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.Assert.assertFalse;
+import static org.junit.Assume.assumeNoException;
+
+import android.platform.test.annotations.AsbSecurityTest;
+
+import com.android.sts.common.tradefed.testtype.StsExtraBusinessLogicHostTestBase;
+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 {
+
+ @AsbSecurityTest(cveBugId = 212610736)
+ @Test
+ public void testPocCVE_2021_39810() {
+ try {
+ // clearing default payment app component if already set
+ AdbUtils.runCommandLine("settings put secure nfc_payment_default_component null",
+ getDevice());
+ installPackage("CVE-2021-39810.apk");
+ String defaultComponent = AdbUtils.runCommandLine(
+ "settings get secure nfc_payment_default_component", getDevice());
+ AdbUtils.runCommandLine("settings put secure nfc_payment_default_component null",
+ getDevice());
+ assertFalse("Vulnerable to 212610736! Setting default payment app without user consent",
+ defaultComponent.contains("PocService"));
+ } catch (Exception e) {
+ // assumption failure if a generic exception is thrown by AdbUtils.runCommandLine()
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/Android.bp
new file mode 100644
index 0000000..d3e2302
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/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-39626",
+ defaults: [
+ "cts_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ sdk_version: "current",
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
new file mode 100644
index 0000000..f097825
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/AndroidManifest.xml
@@ -0,0 +1,42 @@
+<?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_39626"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <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"
+ 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_39626" />
+</manifest>
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
new file mode 100644
index 0000000..cd24540
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/DeviceTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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_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.provider.Settings;
+
+import androidx.test.InstrumentationRegistry;
+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;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int TIMEOUT = 5000;
+ private static Context context;
+
+ private static String getSettingsPkgName() {
+ Intent settingsIntent = new Intent(Settings.ACTION_SETTINGS);
+ ComponentName settingsComponent =
+ settingsIntent.resolveActivity(context.getPackageManager());
+ String pkgName = settingsComponent != null ? settingsComponent.getPackageName()
+ : "com.android.settings";
+ assumeNotNull(pkgName);
+ return pkgName;
+ }
+
+ private void openApplication(String applicationName) {
+ Intent intent = context.getPackageManager().getLaunchIntentForPackage(applicationName);
+ assumeNotNull(intent);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ try {
+ context.startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+
+ @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());
+
+ // Launch the PoC application and ensure that it launches bluetooth settings
+ openApplication(context.getPackageName());
+ assumeTrue(device.wait(Until.hasObject(By.pkg(getSettingsPkgName())), TIMEOUT));
+
+ boolean isBtDiscoverable =
+ (btAdapter.getScanMode() == btAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE);
+
+ // Disable bluetooth if it was OFF before the test
+ if (!btState) {
+ btAdapter.disable();
+ }
+
+ // The test fails if bluetooth is made discoverable through PoC
+ assertFalse("Vulnerable to b/194695497 !!", isBtDiscoverable);
+ }
+}
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
new file mode 100644
index 0000000..d4425ff
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39626/src/android/security/cts/CVE_2021_39626/PocActivity.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts.CVE_2021_39626;
+
+import static org.junit.Assume.assumeNoException;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = new Intent();
+ intent.setAction(Settings.ACTION_BLUETOOTH_SETTINGS);
+ try {
+ startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(e);
+ }
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/Android.bp
new file mode 100644
index 0000000..9ba76d0
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/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-39796",
+ 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-39796/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/AndroidManifest.xml
new file mode 100644
index 0000000..9ef9763
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/AndroidManifest.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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ package="android.security.cts.CVE_2021_39796"
+ android:versionCode="1"
+ android:versionName="1.0">
+
+ <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
+
+ <application
+ android:allowBackup="true"
+ android:label="CVE_2021_39796"
+ android:supportsRtl="true">
+ <service android:name=".PocService"
+ android:enabled="true"
+ android:exported="true" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.security.cts.CVE_2021_39796" />
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp
new file mode 100644
index 0000000..d669e9f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/Android.bp
@@ -0,0 +1,37 @@
+/*
+ * 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-39796-harmful",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ static_libs: [
+ "androidx.test.core",
+ "androidx.test.rules",
+ ],
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml
new file mode 100644
index 0000000..52f2fd2
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.CVE_2021_39796_harmful"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <application
+ android:label="CVE-2021-39796-harmful"
+ 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>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.xml
new file mode 100644
index 0000000..bb5d570
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/res/layout/activity_main.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.
+ -->
+
+<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/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java
similarity index 65%
copy from tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java
index 533524b..3ca3645 100644
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/harmful-app/src/android/security/cts/CVE_2021_39796_harmful/PocActivity.java
@@ -14,9 +14,16 @@
* limitations under the License.
*/
-package android.quickaccesswallet;
+package android.security.cts.CVE_2021_39796_harmful;
-/**
- * Extends {@link TestQuickAccessWalletService} to allow for a different manifest configuration.
- */
-public class UseTargetActivityForQuickAccessWalletService extends TestQuickAccessWalletService {}
+import android.app.Activity;
+import android.os.Bundle;
+
+public class PocActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml
new file mode 100644
index 0000000..c16cd74
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/res/values/strings.xml
@@ -0,0 +1,40 @@
+<?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="activityNotFoundMsg">The activity with intent was not found : </string>
+ <string name="activityNotStartedException">Unable to start the activity with intent : </string>
+ <string name="canNotDrawOverlaysMsg">The application cannot draw overlays</string>
+ <string name="dumpsysActivity">dumpsys activity</string>
+ <string name="dumpsysActivityNotStartedException">Could not execute dumpsys activity
+ command</string>
+ <string name="errorMessage">Device is vulnerable to b/205595291 hence any app with
+ SYSTEM_ALERT_WINDOW can overlay the HarmfulAppWarningActivity screen</string>
+ <string name="harmfulActivity">android/com.android.internal.app.HarmfulAppWarningActivity
+ </string>
+ <string name="mResumedTrue">mResumed=true</string>
+ <string name="overlayAttack">overlayattack</string>
+ <string name="overlayButtonText">OverlayButton</string>
+ <string name="overlayServiceNotStartedException">Unable to start the overlay service</string>
+ <string name="overlayUiScreenError">Overlay UI did not appear on the screen</string>
+ <string name="testPkg">android.security.cts.CVE_2021_39796</string>
+ <string name="vulActivityNotRunningError">The HarmfulAppWarningActivity is not currently
+ running on the device</string>
+ <string name="vulnerablePkg">android.security.cts.CVE_2021_39796_harmful</string>
+ <string name="vulnerableActivity">android.security.cts.CVE_2021_39796_harmful.PocActivity
+ </string>
+</resources>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java
new file mode 100644
index 0000000..20fccde
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/DeviceTest.java
@@ -0,0 +1,121 @@
+/*
+ * 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_39796;
+
+import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeNotNull;
+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.provider.Settings;
+
+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;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.regex.Pattern;
+
+@RunWith(AndroidJUnit4.class)
+public class DeviceTest {
+ private static final int LAUNCH_TIMEOUT_MS = 20000;
+
+ private void startOverlayService() {
+ Context context = getApplicationContext();
+ assumeNotNull(context);
+ Intent intent = new Intent(context, PocService.class);
+
+ assumeTrue(context.getString(R.string.canNotDrawOverlaysMsg),
+ Settings.canDrawOverlays(getApplicationContext()));
+ try {
+ context.startService(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.overlayServiceNotStartedException), e);
+ }
+ }
+
+ public void startVulnerableActivity() {
+ Context context = getApplicationContext();
+ Intent intent = new Intent();
+ intent.setClassName(context.getString(R.string.vulnerablePkg),
+ context.getString(R.string.vulnerableActivity));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivity(intent);
+
+ PackageManager pm = getApplicationContext().getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ String vulnerableActivityName = context.getString(R.string.vulnerablePkg) + "/"
+ + context.getString(R.string.vulnerableActivity);
+
+ assumeTrue(context.getString(R.string.activityNotFoundMsg) + vulnerableActivityName,
+ ris.size() != 0);
+ try {
+ context.startActivity(intent);
+ } catch (Exception e) {
+ assumeNoException(context.getString(R.string.activityNotStartedException) + intent, e);
+ }
+ }
+
+ @Test
+ public void testOverlayButtonPresence() {
+ Context context = getApplicationContext();
+ UiDevice mDevice = UiDevice.getInstance(getInstrumentation());
+
+ /* Start the overlay service */
+ startOverlayService();
+
+ /* Wait for the overlay window */
+ Pattern overlayTextPattern = Pattern.compile(context.getString(R.string.overlayButtonText),
+ Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.overlayUiScreenError),
+ mDevice.wait(Until.hasObject(By.text(overlayTextPattern)), LAUNCH_TIMEOUT_MS));
+
+ /* Start the vulnerable activity */
+ startVulnerableActivity();
+
+ /* Wait until the object of launcher activity is gone */
+ boolean overlayDisallowed = mDevice
+ .wait(Until.gone(By.pkg(context.getString(R.string.testPkg))), LAUNCH_TIMEOUT_MS);
+
+ /* Check if the currently running activity is the vulnerable activity */
+ String activityDump = "";
+ try {
+ activityDump = mDevice.executeShellCommand(context.getString(R.string.dumpsysActivity)
+ + " " + context.getString(R.string.harmfulActivity));
+ } catch (IOException e) {
+ assumeNoException(context.getString(R.string.dumpsysActivityNotStartedException), e);
+ }
+ Pattern activityPattern =
+ Pattern.compile(context.getString(R.string.mResumedTrue), Pattern.CASE_INSENSITIVE);
+ assumeTrue(context.getString(R.string.vulActivityNotRunningError),
+ activityPattern.matcher(activityDump).find());
+
+ assertTrue(context.getString(R.string.errorMessage), overlayDisallowed);
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java
new file mode 100644
index 0000000..a7a9c5f
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39796/src/android/security/cts/CVE_2021_39796/PocService.java
@@ -0,0 +1,87 @@
+/*
+ * 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_39796;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.app.Service;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.provider.Settings;
+import android.view.Gravity;
+import android.view.WindowManager;
+import android.widget.Button;
+
+public class PocService extends Service {
+ public static Button mButton;
+ private WindowManager mWindowManager;
+ private WindowManager.LayoutParams mLayoutParams;
+
+ private static int getScreenWidth() {
+ return Resources.getSystem().getDisplayMetrics().widthPixels;
+ }
+
+ private static int getScreenHeight() {
+ return Resources.getSystem().getDisplayMetrics().heightPixels;
+ }
+
+ @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;
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @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);
+ }
+ super.onDestroy();
+ }
+
+ private void showFloatingWindow() {
+ assumeTrue("The application cannot draw overlays",
+ Settings.canDrawOverlays(getApplicationContext()));
+ mButton = new Button(getApplicationContext());
+ mButton.setText("OverlayButton");
+ mWindowManager.addView(mButton, mLayoutParams);
+ mButton.setTag(mButton.getVisibility());
+ }
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/Android.bp b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/Android.bp
new file mode 100644
index 0000000..9a11e88
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/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-2021-39810",
+ defaults: [
+ "cts_support_defaults",
+ ],
+ srcs: [
+ "src/**/*.java",
+ ],
+ test_suites: [
+ "sts",
+ ],
+ sdk_version: "current",
+}
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml
new file mode 100644
index 0000000..3bdc38d
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?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_39810"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-permission android:name="android.permission.NFC"/>
+ <application
+ android:label="CVE-2021-39810"
+ android:supportsRtl="true">
+ <service
+ android:name=".PocService"
+ android:exported="true"
+ android:permission="android.permission.BIND_NFC_SERVICE">
+ <intent-filter>
+ <action android:name="android.nfc.cardemulation.action.HOST_APDU_SERVICE"/>
+ </intent-filter>
+ <meta-data android:name="android.nfc.cardemulation.host_apdu_service"
+ android:resource="@xml/aid_list"/>
+ </service>
+ </application>
+</manifest>
diff --git a/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.xml b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.xml
new file mode 100644
index 0000000..8983381
--- /dev/null
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/res/xml/aid_list.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.
+ -->
+
+<host-apdu-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:requireDeviceUnlock="false">
+ <aid-group android:category="payment">
+ <aid-filter android:name="325041592E5359532E4444463031" />
+ </aid-group>
+</host-apdu-service>
diff --git a/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java
similarity index 69%
copy from tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
copy to hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java
index 533524b..e8e2085 100644
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
+++ b/hostsidetests/securitybulletin/test-apps/CVE-2021-39810/src/android/security/cts/CVE_2021_39810/PocService.java
@@ -14,9 +14,16 @@
* limitations under the License.
*/
-package android.quickaccesswallet;
+package android.security.cts.CVE_2021_39810;
-/**
- * Extends {@link TestQuickAccessWalletService} to allow for a different manifest configuration.
- */
-public class UseTargetActivityForQuickAccessWalletService extends TestQuickAccessWalletService {}
+import android.app.Service;
+import android.content.Intent;
+import android.os.IBinder;
+
+public class PocService extends Service {
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+}
diff --git a/hostsidetests/statsdatom/src/android/cts/statsdatom/memory/ProcessMemoryStatsTests.java b/hostsidetests/statsdatom/src/android/cts/statsdatom/memory/ProcessMemoryStatsTests.java
index 632e280..36938fd 100644
--- a/hostsidetests/statsdatom/src/android/cts/statsdatom/memory/ProcessMemoryStatsTests.java
+++ b/hostsidetests/statsdatom/src/android/cts/statsdatom/memory/ProcessMemoryStatsTests.java
@@ -86,7 +86,7 @@
assertThat(state.getOomAdjScore()).isAtLeast(0);
assertThat(state.getPageFault()).isAtLeast(0L);
assertThat(state.getPageMajorFault()).isAtLeast(0L);
- assertThat(state.getRssInBytes()).isGreaterThan(0L);
+ assertThat(state.getRssInBytes()).isAtLeast(0L);
assertThat(state.getCacheInBytes()).isAtLeast(0L);
assertThat(state.getSwapInBytes()).isAtLeast(0L);
}
diff --git a/tests/AlarmManager/Android.bp b/tests/AlarmManager/Android.bp
index c697be9..734c6b1 100644
--- a/tests/AlarmManager/Android.bp
+++ b/tests/AlarmManager/Android.bp
@@ -29,6 +29,7 @@
"app/src/**/*.java",
"app30/src/**/*.java",
"app_policy_permission/src/**/*.java",
+ "app_policy_permission32/src/**/*.java",
":CtsAlarmUtils",
],
test_suites: [
diff --git a/tests/AlarmManager/AndroidTest.xml b/tests/AlarmManager/AndroidTest.xml
index f36a87e..841d565 100644
--- a/tests/AlarmManager/AndroidTest.xml
+++ b/tests/AlarmManager/AndroidTest.xml
@@ -28,6 +28,7 @@
<option name="test-file-name" value="AlarmTestApp.apk" />
<option name="test-file-name" value="AlarmTestApp30.apk" />
<option name="test-file-name" value="AlarmTestAppWithPolicyPermission.apk" />
+ <option name="test-file-name" value="AlarmTestAppWithPolicyPermissionSdk32.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
diff --git a/tests/AlarmManager/app_policy_permission32/Android.bp b/tests/AlarmManager/app_policy_permission32/Android.bp
new file mode 100644
index 0000000..bf09526
--- /dev/null
+++ b/tests/AlarmManager/app_policy_permission32/Android.bp
@@ -0,0 +1,32 @@
+// 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: "AlarmTestAppWithPolicyPermissionSdk32",
+ defaults: ["cts_support_defaults"],
+ sdk_version: "current",
+ test_suites: [
+ "cts",
+ "general-tests",
+ "mts",
+ ],
+ srcs: ["src/**/*.java"],
+ dex_preopt: {
+ enabled: false,
+ },
+}
diff --git a/tests/AlarmManager/app_policy_permission32/AndroidManifest.xml b/tests/AlarmManager/app_policy_permission32/AndroidManifest.xml
new file mode 100644
index 0000000..ab9bcd6
--- /dev/null
+++ b/tests/AlarmManager/app_policy_permission32/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?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.alarmmanager.alarmtestapp.cts.policy_permission_32">
+
+ <uses-sdk android:targetSdkVersion="32" />
+
+ <uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
+
+ <application>
+ <receiver android:name=".RequestReceiverSdk32"
+ android:exported="true" />
+ </application>
+
+</manifest>
\ No newline at end of file
diff --git a/tests/AlarmManager/app_policy_permission32/src/android/alarmmanager/alarmtestapp/cts/policy_permission_32/RequestReceiverSdk32.java b/tests/AlarmManager/app_policy_permission32/src/android/alarmmanager/alarmtestapp/cts/policy_permission_32/RequestReceiverSdk32.java
new file mode 100644
index 0000000..cfe0ad6
--- /dev/null
+++ b/tests/AlarmManager/app_policy_permission32/src/android/alarmmanager/alarmtestapp/cts/policy_permission_32/RequestReceiverSdk32.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.alarmmanager.alarmtestapp.cts.policy_permission_32;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+/**
+ * This receiver is to be used to communicate with tests in {@link android.alarmmanager.cts}
+ */
+public class RequestReceiverSdk32 extends BroadcastReceiver {
+ private static final String TAG = RequestReceiverSdk32.class.getSimpleName();
+ public static final String PACKAGE_NAME =
+ "android.alarmmanager.alarmtestapp.cts.policy_permission_32";
+
+ public static final String ACTION_GET_CAN_SCHEDULE_EXACT_ALARM =
+ PACKAGE_NAME + ".action.GET_CAN_SCHEDULE_EXACT_ALARM";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ final AlarmManager am = context.getSystemService(AlarmManager.class);
+ switch (intent.getAction()) {
+ case ACTION_GET_CAN_SCHEDULE_EXACT_ALARM:
+ final boolean result = am.canScheduleExactAlarms();
+ setResult(Activity.RESULT_OK, String.valueOf(result), null);
+ break;
+ default:
+ Log.e(TAG, "Unspecified action " + intent.getAction());
+ setResult(Activity.RESULT_CANCELED, null, null);
+ break;
+ }
+ }
+}
diff --git a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
index bb5357e..eaa596b 100644
--- a/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
+++ b/tests/AlarmManager/src/android/alarmmanager/cts/ExactAlarmsTest.java
@@ -28,6 +28,7 @@
import android.alarmmanager.alarmtestapp.cts.PermissionStateChangedReceiver;
import android.alarmmanager.alarmtestapp.cts.policy_permission.RequestReceiver;
+import android.alarmmanager.alarmtestapp.cts.policy_permission_32.RequestReceiverSdk32;
import android.alarmmanager.alarmtestapp.cts.sdk30.TestReceiver;
import android.alarmmanager.util.AlarmManagerDeviceConfigHelper;
import android.app.Activity;
@@ -305,7 +306,7 @@
}
@Test
- public void canScheduleExactAlarmWithPolicyPermissionOnly() throws Exception {
+ public void canScheduleExactAlarmWithPolicyPermission() throws Exception {
final CountDownLatch resultLatch = new CountDownLatch(1);
final AtomicBoolean apiResult = new AtomicBoolean(false);
final AtomicInteger result = new AtomicInteger(-1);
@@ -330,6 +331,33 @@
assertTrue("canScheduleExactAlarm returned false", apiResult.get());
}
+ @Test
+ public void canScheduleExactAlarmWithPolicyPermissionSdk32() throws Exception {
+ final CountDownLatch resultLatch = new CountDownLatch(1);
+ final AtomicBoolean apiResult = new AtomicBoolean(true);
+ final AtomicInteger result = new AtomicInteger(-1);
+
+ final Intent requestToTestApp = new Intent(
+ RequestReceiverSdk32.ACTION_GET_CAN_SCHEDULE_EXACT_ALARM)
+ .setClassName(RequestReceiverSdk32.PACKAGE_NAME,
+ RequestReceiverSdk32.class.getName())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ sContext.sendOrderedBroadcast(requestToTestApp, null, new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ result.set(getResultCode());
+ final String resultStr = getResultData();
+ apiResult.set(Boolean.parseBoolean(resultStr));
+ resultLatch.countDown();
+ }
+ }, null, Activity.RESULT_CANCELED, null, null);
+
+ assertTrue("Timed out waiting for response from helper app",
+ resultLatch.await(10, TimeUnit.SECONDS));
+ assertEquals(Activity.RESULT_OK, result.get());
+ assertFalse("canScheduleExactAlarm returned true", apiResult.get());
+ }
+
@Test(expected = SecurityException.class)
public void setAlarmClockWithoutPermission() throws IOException {
revokeAppOp();
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/ExpeditedJobTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/ExpeditedJobTest.java
index 9f2db10..19d76e3 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/ExpeditedJobTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/ExpeditedJobTest.java
@@ -83,6 +83,21 @@
225 /* ProcessList.PERCEPTIBLE_MEDIUM_APP_ADJ */);
}
+ /** Test that EJs for the TOP app start immediately and there is no limit on the number. */
+ @Test
+ public void testTopEJUnlimited() throws Exception {
+ final int standardConcurrency = 16;
+ final int numEjs = 2 * standardConcurrency;
+ mTestAppInterface.startAndKeepTestActivity(true);
+ for (int i = 0; i < numEjs; ++i) {
+ mTestAppInterface.scheduleJob(
+ Map.of(TestJobSchedulerReceiver.EXTRA_AS_EXPEDITED, true),
+ Map.of(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, i));
+ assertTrue("Job did not start after scheduling",
+ mTestAppInterface.awaitJobStart(i, DEFAULT_WAIT_TIMEOUT_MS));
+ }
+ }
+
/** Forces JobScheduler to run the job */
private void forceRunJob() throws Exception {
mUiDevice.executeShellCommand("cmd jobscheduler run -f"
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
index 825f9b4..3ec265d 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/JobThrottlingTest.java
@@ -1075,6 +1075,72 @@
mTestAppInterface.getLastParams().getStopReason());
}
+ /*
+ Tests currently disabled because they require changes inside the framework to lower the minimum
+ EJ quota to one minute (from 5 minutes).
+ TODO(224533485): make JS testable enough to enable these tests
+
+ @Test
+ public void testRestrictingStopReason_ExpeditedQuota_startOnCharging() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery
+ assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery
+
+ // Reduce allowed time for testing. System to cap the time above 30 seconds.
+ mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000");
+ mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000");
+ // Start with charging so JobScheduler thinks the job can run for the maximum amount of
+ // time. We turn off charging later so quota clearly comes into effect.
+ setChargingState(true);
+ setTestPackageStandbyBucket(Bucket.RARE);
+
+ mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true);
+ runJob();
+ assertTrue("New job didn't start",
+ mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ assertTrue(mTestAppInterface.getLastParams().isExpeditedJob());
+ setChargingState(false);
+
+ assertFalse("Job stopped before using up quota",
+ mTestAppInterface.awaitJobStop(45_000));
+ Thread.sleep(15_000);
+
+ assertTrue("Job didn't stop after using up quota",
+ mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ assertEquals(JobParameters.STOP_REASON_QUOTA,
+ mTestAppInterface.getLastParams().getStopReason());
+ }
+
+ @Test
+ public void testRestrictingStopReason_ExpeditedQuota_noCharging() throws Exception {
+ assumeFalse("not testable in automotive device", mAutomotiveDevice); // Test needs battery
+ assumeFalse("not testable in leanback device", mLeanbackOnly); // Test needs battery
+
+ // Reduce allowed time for testing.
+ mDeviceConfigStateHelper.set("qc_ej_limit_rare_ms", "30000");
+ mDeviceConfigStateHelper.set("runtime_min_ej_guarantee_ms", "30000");
+ setChargingState(false);
+ setTestPackageStandbyBucket(Bucket.RARE);
+
+ mTestAppInterface.scheduleJob(false, NETWORK_TYPE_NONE, true);
+ runJob();
+ assertTrue("New job didn't start",
+ mTestAppInterface.awaitJobStart(DEFAULT_WAIT_TIMEOUT));
+ assertTrue(mTestAppInterface.getLastParams().isExpeditedJob());
+
+ assertFalse("Job stopped before using up quota",
+ mTestAppInterface.awaitJobStop(45_000));
+ Thread.sleep(15_000);
+
+ assertTrue("Job didn't stop after using up quota",
+ mTestAppInterface.awaitJobStop(DEFAULT_WAIT_TIMEOUT));
+ // Charging state was false when the job started, so the trigger the timeout before
+ // QuotaController officially marks the quota finished.
+ final int stopReason = mTestAppInterface.getLastParams().getStopReason();
+ assertTrue(stopReason == JobParameters.STOP_REASON_TIMEOUT
+ || stopReason == JobParameters.STOP_REASON_QUOTA);
+ }
+ */
+
@Test
public void testRestrictingStopReason_BatterySaver() throws Exception {
BatteryUtils.assumeBatterySaverFeature();
diff --git a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
index 8db459e..fd17c29 100644
--- a/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
+++ b/tests/JobScheduler/src/android/jobscheduler/cts/TestAppInterface.java
@@ -100,7 +100,9 @@
throws Exception {
final Intent scheduleJobIntent = new Intent(TestJobSchedulerReceiver.ACTION_SCHEDULE_JOB);
scheduleJobIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mJobId);
+ if (!intExtras.containsKey(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY)) {
+ scheduleJobIntent.putExtra(TestJobSchedulerReceiver.EXTRA_JOB_ID_KEY, mJobId);
+ }
booleanExtras.forEach(scheduleJobIntent::putExtra);
intExtras.forEach(scheduleJobIntent::putExtra);
scheduleJobIntent.setComponent(new ComponentName(TEST_APP_PACKAGE, TEST_APP_RECEIVER));
@@ -165,9 +167,13 @@
};
boolean awaitJobStart(long maxWait) throws Exception {
+ return awaitJobStart(mJobId, maxWait);
+ }
+
+ boolean awaitJobStart(int jobId, long maxWait) throws Exception {
return waitUntilTrue(maxWait, () -> {
synchronized (mTestJobState) {
- return (mTestJobState.jobId == mJobId) && mTestJobState.running;
+ return (mTestJobState.jobId == jobId) && mTestJobState.running;
}
});
}
diff --git a/tests/MediaProviderTranscode/Android.bp b/tests/MediaProviderTranscode/Android.bp
index 5ac7b91..54ee715 100644
--- a/tests/MediaProviderTranscode/Android.bp
+++ b/tests/MediaProviderTranscode/Android.bp
@@ -32,7 +32,8 @@
],
min_sdk_version: "30",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
certificate: "media",
java_resources: [":CtsTranscodeTestAppSupportsHevc", ":CtsTranscodeTestAppSupportsSlowMotion"]
}
diff --git a/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java b/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java
index 561823b..5110781 100644
--- a/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java
+++ b/tests/PhotoPicker/src/android/photopicker/cts/PickerProviderMediaGenerator.java
@@ -17,9 +17,13 @@
package android.photopicker.cts;
import static android.provider.CloudMediaProviderContract.AlbumColumns;
+import static android.provider.CloudMediaProviderContract.EXTRA_ALBUM_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_MEDIA_COLLECTION_ID;
+import static android.provider.CloudMediaProviderContract.EXTRA_SYNC_GENERATION;
import static android.provider.CloudMediaProviderContract.MediaCollectionInfo;
import static android.provider.CloudMediaProviderContract.MediaColumns;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
@@ -87,18 +91,24 @@
mPrivateDir = context.getFilesDir();
}
- public Cursor getMedia(long generation, String albumdId, String mimeType, long sizeBytes) {
- return getCursor(mMedia, generation, albumdId, mimeType, sizeBytes,
+ public Cursor getMedia(long generation, String albumId, String mimeType, long sizeBytes) {
+ final Cursor cursor = getCursor(mMedia, generation, albumId, mimeType, sizeBytes,
/* isDeleted */ false);
+ cursor.setExtras(buildCursorExtras(mCollectionId, generation > 0, albumId != null));
+ return cursor;
}
public Cursor getAlbums(String mimeType, long sizeBytes) {
- return getCursor(mAlbums, mimeType, sizeBytes);
+ final Cursor cursor = getCursor(mAlbums, mimeType, sizeBytes);
+ cursor.setExtras(buildCursorExtras(mCollectionId, false, false));
+ return cursor;
}
public Cursor getDeletedMedia(long generation) {
- return getCursor(mDeletedMedia, generation, /* albumId */ null, /* mimeType */ null,
- /* sizeBytes */ 0, /* isDeleted */ true);
+ final Cursor cursor = getCursor(mDeletedMedia, generation, /* albumId */ null,
+ /* mimeType */ null, /* sizeBytes */ 0, /* isDeleted */ true);
+ cursor.setExtras(buildCursorExtras(mCollectionId, generation > 0, false));
+ return cursor;
}
public Bundle getMediaCollectionInfo() {
@@ -117,6 +127,23 @@
mAccountConfigurationIntent = configIntent;
}
+ public Bundle buildCursorExtras(String mediaCollectionId, boolean honoredSyncGeneration,
+ boolean honoredAlbumdId) {
+ final ArrayList<String> honoredArgs = new ArrayList<>();
+ if (honoredSyncGeneration) {
+ honoredArgs.add(EXTRA_SYNC_GENERATION);
+ }
+ if (honoredAlbumdId) {
+ honoredArgs.add(EXTRA_ALBUM_ID);
+ }
+
+ final Bundle bundle = new Bundle();
+ bundle.putString(EXTRA_MEDIA_COLLECTION_ID, mediaCollectionId);
+ bundle.putStringArrayList(ContentResolver.EXTRA_HONORED_ARGS, honoredArgs);
+
+ return bundle;
+ }
+
public void addMedia(String localId, String cloudId, String albumId, String mimeType,
int standardMimeTypeExtension, long sizeBytes, boolean isFavorite, int resId)
throws IOException {
diff --git a/tests/accessibilityservice/AndroidManifest.xml b/tests/accessibilityservice/AndroidManifest.xml
index 2dfb7fd..3bde0fd 100644
--- a/tests/accessibilityservice/AndroidManifest.xml
+++ b/tests/accessibilityservice/AndroidManifest.xml
@@ -215,6 +215,18 @@
android:resource="@xml/stub_ime"/>
</service>
+ <service android:name=".StubSimpleImeAccessibilityService"
+ android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.accessibilityservice.AccessibilityService"/>
+ <category android:name="android.accessibilityservice.category.FEEDBACK_GENERIC"/>
+ </intent-filter>
+
+ <meta-data android:name="android.accessibilityservice"
+ android:resource="@xml/stub_simple_ime_accessibility_service"/>
+ </service>
+
<service android:name=".StubImeAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
android:exported="true">
diff --git a/tests/accessibilityservice/res/values/strings.xml b/tests/accessibilityservice/res/values/strings.xml
index c79d255..014b9a7 100644
--- a/tests/accessibilityservice/res/values/strings.xml
+++ b/tests/accessibilityservice/res/values/strings.xml
@@ -190,6 +190,8 @@
<string name="stub_focus_indicator_service_description">com.android.accessibilityservice.cts.StubFocusIndicatorService</string>
+ <string name="stub_simple_ime_accessibility_service_description">com.android.accessibilityservice.cts.StubSimpleImeAccessibilityService</string>
+
<string name="stub_ime_accessibility_service_description">com.android.accessibilityservice.cts.StubImeAccessibilityService</string>
<string name="stub_non_ime_accessibility_service_description">com.android.accessibilityservice.cts.StubNonImeAccessibilityService</string>
diff --git a/tests/accessibilityservice/res/xml/stub_simple_ime_accessibility_service.xml b/tests/accessibilityservice/res/xml/stub_simple_ime_accessibility_service.xml
new file mode 100644
index 0000000..1d5c350
--- /dev/null
+++ b/tests/accessibilityservice/res/xml/stub_simple_ime_accessibility_service.xml
@@ -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.
+ -->
+
+<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
+ android:description="@string/stub_simple_ime_accessibility_service_description"
+ android:accessibilityEventTypes="typeAllMask"
+ android:accessibilityFeedbackType="feedbackGeneric"
+ android:accessibilityFlags="flagInputMethodEditor"
+ android:notificationTimeout="0" />
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
index 964c817..4fc8ca2 100644
--- a/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/AccessibilityImeTest.java
@@ -28,20 +28,27 @@
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import android.accessibility.cts.common.AccessibilityDumpOnFailureRule;
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
import android.accessibilityservice.InputMethod;
import android.accessibilityservice.cts.activities.AccessibilityEndToEndActivity;
import android.accessibilityservice.cts.utils.AsyncUtils;
+import android.accessibilityservice.cts.utils.RunOnMainUtils;
import android.app.Instrumentation;
import android.app.UiAutomation;
+import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.LargeTest;
+import android.text.TextUtils;
import android.util.Log;
+import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import androidx.test.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
+import com.android.compatibility.common.util.PollingCheck;
+
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
@@ -109,6 +116,58 @@
mInitialText = mActivity.getString(R.string.text_input_blah);
}
+ /**
+ * Verifies that
+ * 1) {@link android.accessibilityservice.AccessibilityService#onCreateInputMethod()} will be
+ * called and 2) it will return the default implementation of
+ * {@link android.accessibilityservice.InputMethod}, which is still functional.
+ */
+ @Test
+ public void testDefaultImplementation() throws Exception {
+ InstrumentedAccessibilityService serviceToBeCleanedUp = null;
+ try {
+ final StubSimpleImeAccessibilityService service =
+ enableService(StubSimpleImeAccessibilityService.class);
+ serviceToBeCleanedUp = service;
+ assertTrue("time out waiting for onCreateInputMethod() to get called.",
+ service.awaitOnCreateInputMethod(AsyncUtils.DEFAULT_TIMEOUT_MS, MILLISECONDS));
+
+ final InputMethod inputMethod = service.getInputMethod();
+ assertNotNull(inputMethod);
+
+ // Set a unique value to "privateImeOptions".
+ final String markerValue = "Test-" + SystemClock.elapsedRealtimeNanos();
+ sInstrumentation.runOnMainSync(() -> mEditText.setPrivateImeOptions(markerValue));
+
+ requestFocusAndSetCursorToEnd();
+
+ // Wait until EditorInfo#privateImeOptions becomes the expected marker value.
+ PollingCheck.waitFor(AsyncUtils.DEFAULT_TIMEOUT_MS,
+ () -> TextUtils.equals(
+ markerValue,
+ RunOnMainUtils.getOnMain(sInstrumentation, () -> {
+ final EditorInfo editorInfo =
+ inputMethod.getCurrentInputEditorInfo();
+ return editorInfo != null ? editorInfo.privateImeOptions : null;
+ })));
+
+ final InputMethod.AccessibilityInputConnection connection =
+ inputMethod.getCurrentInputConnection();
+ assertNotNull(connection);
+
+ connection.commitText("abc", 1, null);
+
+ final String expectedText = mInitialText + "abc";
+ PollingCheck.waitFor(AsyncUtils.DEFAULT_TIMEOUT_MS,
+ () -> RunOnMainUtils.getOnMain(sInstrumentation,
+ () -> TextUtils.equals(expectedText, mEditText.getText())));
+ } finally {
+ if (serviceToBeCleanedUp != null) {
+ serviceToBeCleanedUp.disableSelfAndRemove();
+ }
+ }
+ }
+
@Test
public void testInputConnection_requestIme() throws InterruptedException {
CountDownLatch startInputLatch = new CountDownLatch(1);
diff --git a/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSimpleImeAccessibilityService.java b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSimpleImeAccessibilityService.java
new file mode 100644
index 0000000..d34b593
--- /dev/null
+++ b/tests/accessibilityservice/src/android/accessibilityservice/cts/StubSimpleImeAccessibilityService.java
@@ -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 android.accessibilityservice.cts;
+
+import android.accessibility.cts.common.InstrumentedAccessibilityService;
+import android.accessibilityservice.InputMethod;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public final class StubSimpleImeAccessibilityService extends InstrumentedAccessibilityService {
+ private final CountDownLatch mOnCreateInputMethodLatch = new CountDownLatch(1);
+
+ public boolean awaitOnCreateInputMethod(long timeout, TimeUnit unit)
+ throws InterruptedException {
+ return mOnCreateInputMethodLatch.await(timeout, unit);
+ }
+
+ @Override
+ public InputMethod onCreateInputMethod() {
+ mOnCreateInputMethodLatch.countDown();
+ return super.onCreateInputMethod();
+ }
+}
diff --git a/tests/ambientcontext/Android.bp b/tests/ambientcontext/Android.bp
index 70f3b48..eba2b39 100644
--- a/tests/ambientcontext/Android.bp
+++ b/tests/ambientcontext/Android.bp
@@ -20,7 +20,7 @@
}
android_test {
- name: "CtsAmbientContextDetectionServiceDeviceTestCases",
+ name: "CtsAmbientContextServiceTestCases",
defaults: ["cts_defaults"],
dex_preopt: {
enabled: false,
diff --git a/tests/ambientcontext/AndroidTest.xml b/tests/ambientcontext/AndroidTest.xml
index bc45399..c3e2ff5 100644
--- a/tests/ambientcontext/AndroidTest.xml
+++ b/tests/ambientcontext/AndroidTest.xml
@@ -15,7 +15,7 @@
~ limitations under the License.
-->
-<configuration description="Config for CtsAmbientContextDetectionServiceDeviceTestCases">
+<configuration description="Config for CtsAmbientContextServiceTestCases">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="framework" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
@@ -23,7 +23,7 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
- <option name="test-file-name" value="CtsAmbientContextDetectionServiceDeviceTestCases.apk" />
+ <option name="test-file-name" value="CtsAmbientContextServiceTestCases.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="android.ambientcontext.cts" />
diff --git a/tests/ambientcontext/src/android/ambientcontext/cts/AmbientContextManagerTest.java b/tests/ambientcontext/src/android/ambientcontext/cts/AmbientContextManagerTest.java
new file mode 100644
index 0000000..1488e05
--- /dev/null
+++ b/tests/ambientcontext/src/android/ambientcontext/cts/AmbientContextManagerTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.ambientcontext.cts;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.Manifest;
+import android.app.PendingIntent;
+import android.app.ambientcontext.AmbientContextEvent;
+import android.app.ambientcontext.AmbientContextEventRequest;
+import android.app.ambientcontext.AmbientContextManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.os.PersistableBundle;
+
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * Test the AmbientContextManager API. Run with "atest CtsAmbientContextServiceTestCases".
+ */
+@RunWith(AndroidJUnit4.class)
+public class AmbientContextManagerTest {
+ private Context mContext;
+ private AmbientContextManager mAmbientContextManager;
+
+ @Before
+ public void setUp() throws Exception {
+ mContext = getInstrumentation().getContext();
+ mAmbientContextManager = (AmbientContextManager) mContext.getSystemService(
+ Context.AMBIENT_CONTEXT_SERVICE);
+ }
+
+ @Test
+ public void testGetEventsFromIntent() {
+ Intent intent = new Intent();
+ ArrayList<AmbientContextEvent> events = new ArrayList<>();
+ int eventCough = AmbientContextEvent.EVENT_COUGH;
+ Instant start = Instant.ofEpochMilli(1000L);
+ Instant end = Instant.ofEpochMilli(3000L);
+ int levelHigh = AmbientContextEvent.LEVEL_HIGH;
+ AmbientContextEvent expectedEvent = new AmbientContextEvent.Builder()
+ .setEventType(eventCough)
+ .setStartTime(start)
+ .setEndTime(end)
+ .setConfidenceLevel(levelHigh)
+ .setDensityLevel(levelHigh)
+ .build();
+ events.add(expectedEvent);
+ intent.putExtra(AmbientContextManager.EXTRA_AMBIENT_CONTEXT_EVENTS, events);
+
+ List<AmbientContextEvent> eventsFromIntent = AmbientContextManager.getEventsFromIntent(
+ intent);
+ assertEquals(1, eventsFromIntent.size());
+
+ AmbientContextEvent actualEvent = eventsFromIntent.get(0);
+ assertEquals(eventCough, actualEvent.getEventType());
+ assertEquals(start, actualEvent.getStartTime());
+ assertEquals(end, actualEvent.getEndTime());
+ assertEquals(levelHigh, actualEvent.getConfidenceLevel());
+ assertEquals(levelHigh, actualEvent.getDensityLevel());
+ }
+
+ @Test
+ public void testQueryStatus_noPermission() {
+ assertEquals(PackageManager.PERMISSION_DENIED, mContext.checkCallingOrSelfPermission(
+ Manifest.permission.ACCESS_AMBIENT_CONTEXT_EVENT));
+
+ int[] eventsArray = new int[] {AmbientContextEvent.EVENT_COUGH};
+ Set<Integer> eventTypes = Arrays.stream(eventsArray).boxed().collect(
+ Collectors.toSet());
+ try {
+ mAmbientContextManager.queryAmbientContextServiceStatus(eventTypes,
+ null, null);
+ fail("Expected SecurityException for an app not holding"
+ + " ACCESS_AMBIENT_CONTEXT permission.");
+ } catch (SecurityException e) {
+ // Exception expected
+ }
+ }
+
+ @Test
+ public void testStartConsentActivity_noPermission() {
+ int[] eventsArray = new int[] {AmbientContextEvent.EVENT_COUGH};
+ Set<Integer> eventTypes = Arrays.stream(eventsArray).boxed().collect(
+ Collectors.toSet());
+ try {
+ mAmbientContextManager.startConsentActivity(eventTypes);
+ fail("Expected SecurityException for an app not holding"
+ + " ACCESS_AMBIENT_CONTEXT permission.");
+ } catch (SecurityException e) {
+ // Exception expected
+ }
+ }
+
+ @Test
+ public void testRegisterObserver_immmutablePendingIntent() {
+ PersistableBundle bundle = new PersistableBundle();
+ String optionKey = "TestOption";
+ bundle.putBoolean(optionKey, false);
+ AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ .addEventType(AmbientContextEvent.EVENT_COUGH)
+ .setOptions(bundle)
+ .build();
+ assertFalse(request.getOptions().getBoolean(optionKey));
+
+ Intent intent = new Intent();
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);
+ try {
+ mAmbientContextManager.registerObserver(request, pendingIntent, null, null);
+ fail("Expected IllegalArgumentException for a immutable PendingIntent.");
+ } catch (IllegalArgumentException e) {
+ // Exception expected
+ }
+ }
+
+ @Test
+ public void testRegisterObserver_noPermission() {
+ AmbientContextEventRequest request = new AmbientContextEventRequest.Builder()
+ .addEventType(AmbientContextEvent.EVENT_COUGH)
+ .build();
+ Intent intent = new Intent();
+ PendingIntent pendingIntent =
+ PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_MUTABLE);
+ try {
+ mAmbientContextManager.registerObserver(request, pendingIntent, null, null);
+ fail("Expected SecurityException for an app not holding"
+ + " ACCESS_AMBIENT_CONTEXT permission.");
+ } catch (SecurityException e) {
+ // Exception expected
+ }
+ }
+
+ @Test
+ public void testUnregisterObserver_noPermission() {
+ try {
+ mAmbientContextManager.unregisterObserver();
+ fail("Expected SecurityException for an app not holding"
+ + " ACCESS_AMBIENT_CONTEXT permission.");
+ } catch (SecurityException e) {
+ // Exception expected
+ }
+ }
+}
diff --git a/tests/ambientcontext/src/android/ambientcontext/cts/CtsAmbientContextDetectionServiceDeviceTest.java b/tests/ambientcontext/src/android/ambientcontext/cts/CtsAmbientContextDetectionServiceDeviceTest.java
index 2aa89fa..cd7a7f0 100644
--- a/tests/ambientcontext/src/android/ambientcontext/cts/CtsAmbientContextDetectionServiceDeviceTest.java
+++ b/tests/ambientcontext/src/android/ambientcontext/cts/CtsAmbientContextDetectionServiceDeviceTest.java
@@ -28,6 +28,7 @@
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.platform.test.annotations.AppModeFull;
+import android.service.ambientcontext.AmbientContextDetectionResult;
import android.text.TextUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -40,6 +41,9 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.List;
+
/**
* This suite of test ensures that AmbientContextService behaves correctly when properly
@@ -139,6 +143,25 @@
assertThat(getLastAppPackageName()).isEqualTo(FAKE_APP_PACKAGE);
}
+ @Test
+ public void testConstructAmbientContextDetectionResult() {
+ List<AmbientContextEvent> events = Arrays.asList(new AmbientContextEvent[] {FAKE_EVENT});
+ AmbientContextDetectionResult result = new AmbientContextDetectionResult
+ .Builder(FAKE_APP_PACKAGE)
+ .addEvents(events)
+ .build();
+ List<AmbientContextEvent> actualEvents = result.getEvents();
+ assertThat(actualEvents.size()).isNotEqualTo(1);
+ assertThat(actualEvents).contains(FAKE_EVENT);
+
+ result = new AmbientContextDetectionResult
+ .Builder(FAKE_APP_PACKAGE)
+ .addEvents(events)
+ .clearEvents()
+ .build();
+ assertThat(result.getEvents()).isEmpty();
+ }
+
private int getLastStatusCode() {
return Integer.parseInt(runShellCommand(
"cmd ambient_context get-last-status-code"));
diff --git a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
index a8b6075..0f98cdf 100644
--- a/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerFgsBgStartTest.java
@@ -1465,7 +1465,8 @@
/**
* After startForeground() and stopForeground(), the second startForeground() can succeed or not
* depends on the service's app proc state.
- * Test startForegroundService() -> startForeground() -> stopForeground() -> startForeground().
+ * Test startForegroundService() -> startForeground() -> stopForeground() -> startForeground()
+ * -> startForeground().
*/
@Test
public void testSecondStartForeground() throws Exception {
@@ -1478,10 +1479,10 @@
enableFgsRestriction(true, true, null);
WaitForBroadcast waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
waiter.prepare(ACTION_START_FGS_RESULT);
- // bypass bg-service-start restriction.
+ // Bypass bg-service-start restriction.
CtsAppTestUtils.executeShellCmd(mInstrumentation,
"dumpsys deviceidle whitelist +" + PACKAGE_NAME_APP1);
- // start foreground service.
+ // Start foreground service from APP1, the service can enter FGS.
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
@@ -1489,7 +1490,7 @@
CtsAppTestUtils.executeShellCmd(mInstrumentation,
"dumpsys deviceidle whitelist -" + PACKAGE_NAME_APP1);
- // stopForeground()
+ // stopForeground(), the service exits FGS, become a background service.
Bundle extras = LocalForegroundService.newCommand(
LocalForegroundService.COMMAND_STOP_FOREGROUND_REMOVE_NOTIFICATION);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_SERVICE,
@@ -1497,18 +1498,22 @@
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_SERVICE,
new Integer(PROCESS_CAPABILITY_NONE));
- // startForeground() again.
+ // APP2 is in the background, from APP2, call startForeground().
+ // When APP2 calls Context.startService(), setFgsRestrictionLocked() is called,
+ // because APP2 is in the background, mAllowStartForeground is set to false.
+ // When Service.startForeground() is called, setFgsRestrictionLocked() is called again,
+ // APP1's proc state is in the background and mAllowStartForeground is set to false.
extras = LocalForegroundService.newCommand(
LocalForegroundService.COMMAND_START_FOREGROUND);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, extras);
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, extras);
try {
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE);
fail("Service should not enter foreground service state");
} catch (Exception e) {
}
- // Put app to a TOP proc state.
+ // Put APP1 to a TOP proc state.
allowBgActivityStart(PACKAGE_NAME_APP1, true);
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_ACTIVITY,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
@@ -1516,17 +1521,24 @@
new Integer(PROCESS_CAPABILITY_ALL));
allowBgActivityStart(PACKAGE_NAME_APP1, false);
- // Call startForeground() second time.
+ // APP2 is in the background, from APP2, call startForeground() second time.
+ // When APP2 calls Context.startService(), setFgsRestrictionLocked() is called,
+ // because APP2 is in the background, mAllowStartForeground is set to false.
+ // When Service.startForeground() is called, setFgsRestrictionLocked() is called again,
+ // because APP1's proc state is in the foreground and mAllowStartForeground is set to
+ // true.
waiter = new WaitForBroadcast(mInstrumentation.getTargetContext());
waiter.prepare(ACTION_START_FGS_RESULT);
- CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_FOREGROUND_SERVICE,
- PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
+ extras = LocalForegroundService.newCommand(
+ LocalForegroundService.COMMAND_START_FOREGROUND);
+ CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_START_SERVICE,
+ PACKAGE_NAME_APP2, PACKAGE_NAME_APP1, 0, extras);
+ waiter.doWait(WAITFOR_MSEC);
+ // Stop app1's activity.
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_ACTIVITY,
PACKAGE_NAME_APP1, PACKAGE_NAME_APP1, 0, null);
-
uid1Watcher.waitFor(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_FG_SERVICE,
LOCAL_SERVICE_PROCESS_CAPABILITY);
- waiter.doWait(WAITFOR_MSEC);
// Stop the FGS.
CommandReceiver.sendCommand(mContext, CommandReceiver.COMMAND_STOP_FOREGROUND_SERVICE,
diff --git a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
index 42e16ac..05a0f9e 100644
--- a/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerProcessStateTest.java
@@ -576,7 +576,7 @@
// Going off the temp whitelist causes a spurious proc state report... that's
// not ideal, but okay.
- uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ // uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
// We don't want to wait for the uid to actually go idle, we can force it now.
cmd = "am make-uid-idle --user " + userId + " " + SIMPLE_PACKAGE_NAME;
@@ -887,7 +887,7 @@
// Going off the temp whitelist causes a spurious proc state report... that's
// not ideal, but okay.
- uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
+ // uidWatcher.expect(WatchUidRunner.CMD_PROCSTATE, WatchUidRunner.STATE_CACHED_EMPTY);
// We don't want to wait for the uid to actually go idle, we can force it now.
controller.makeUidIdle();
diff --git a/tests/app/src/android/app/cts/ActivityManagerTest.java b/tests/app/src/android/app/cts/ActivityManagerTest.java
index a207625..f62f9d2 100644
--- a/tests/app/src/android/app/cts/ActivityManagerTest.java
+++ b/tests/app/src/android/app/cts/ActivityManagerTest.java
@@ -29,7 +29,6 @@
import static android.content.pm.PackageManager.DONT_KILL_APP;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -72,6 +71,7 @@
import android.content.pm.ConfigurationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Binder;
import android.os.Bundle;
@@ -109,9 +109,7 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -156,9 +154,6 @@
private static final String MCC_TO_UPDATE = "987";
private static final String MNC_TO_UPDATE = "654";
- private static final String SHELL_COMMAND_GET_CONFIG = "am get-config";
- private static final String SHELL_COMMAND_RESULT_CONFIG_NAME_MCC = "mcc";
- private static final String SHELL_COMMAND_RESULT_CONFIG_NAME_MNC = "mnc";
// Return states of the ActivityReceiverFilter.
public static final int RESULT_PASS = 1;
@@ -722,48 +717,22 @@
return;
}
- // Store the original mcc mnc to set back
- String[] mccMncConfigOriginal = new String[2];
- // Store other configs to check they won't be affected
- Set<String> otherConfigsOriginal = new HashSet<>();
- getMccMncConfigsAndOthers(mccMncConfigOriginal, otherConfigsOriginal);
-
+ Configuration originalConfig = mTargetContext.getResources().getConfiguration();
String[] mccMncConfigToUpdate = new String[] {MCC_TO_UPDATE, MNC_TO_UPDATE};
boolean success = ShellIdentityUtils.invokeMethodWithShellPermissions(mActivityManager,
(am) -> am.updateMccMncConfiguration(mccMncConfigToUpdate[0],
mccMncConfigToUpdate[1]));
if (success) {
- String[] mccMncConfigUpdated = new String[2];
- Set<String> otherConfigsUpdated = new HashSet<>();
- getMccMncConfigsAndOthers(mccMncConfigUpdated, otherConfigsUpdated);
- // Check the mcc mnc are updated as expected
- assertArrayEquals(mccMncConfigToUpdate, mccMncConfigUpdated);
- // Check other configs are not changed
- assertEquals(otherConfigsOriginal, otherConfigsUpdated);
+ Configuration changedConfig = mTargetContext.getResources().getConfiguration();
+ assertEquals(MNC_TO_UPDATE, Integer.toString(changedConfig.mnc));
+ assertEquals(MCC_TO_UPDATE, Integer.toString(changedConfig.mcc));
}
- // Set mcc mnc configs back in the end of the test
+ // Set mcc mnc configs back in the end of the test if they were set to something else.
ShellIdentityUtils.invokeMethodWithShellPermissions(mActivityManager,
- (am) -> am.updateMccMncConfiguration(mccMncConfigOriginal[0],
- mccMncConfigOriginal[1]));
- }
-
- private void getMccMncConfigsAndOthers(String[] mccMncConfigs, Set<String> otherConfigs)
- throws Exception {
- String[] configs = SystemUtil.runShellCommand(
- mInstrumentation, SHELL_COMMAND_GET_CONFIG).split(" |\\-");
- for (String config : configs) {
- if (config.startsWith(SHELL_COMMAND_RESULT_CONFIG_NAME_MCC)) {
- mccMncConfigs[0] = config.substring(
- SHELL_COMMAND_RESULT_CONFIG_NAME_MCC.length());
- } else if (config.startsWith(SHELL_COMMAND_RESULT_CONFIG_NAME_MNC)) {
- mccMncConfigs[1] = config.substring(
- SHELL_COMMAND_RESULT_CONFIG_NAME_MNC.length());
- } else {
- otherConfigs.add(config);
- }
- }
+ (am) -> am.updateMccMncConfiguration(Integer.toString(originalConfig.mcc),
+ Integer.toString(originalConfig.mnc)));
}
/**
diff --git a/tests/app/src/android/app/cts/WallpaperManagerTest.java b/tests/app/src/android/app/cts/WallpaperManagerTest.java
index 783d155..fc640a5 100644
--- a/tests/app/src/android/app/cts/WallpaperManagerTest.java
+++ b/tests/app/src/android/app/cts/WallpaperManagerTest.java
@@ -113,14 +113,15 @@
if (mBroadcastReceiver != null) {
mContext.unregisterReceiver(mBroadcastReceiver);
}
- try {
- ensureSetWallpaperDimAmountPermissionIsGranted();
- mWallpaperManager.setWallpaperDimAmount(0f);
- assertDimAmountEqualsTo(0f);
- } finally {
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
- mAcquiredWallpaperDimmingPermission = false;
+ if (mAcquiredWallpaperDimmingPermission) {
+ try {
+ mWallpaperManager.setWallpaperDimAmount(0f);
+ assertDimAmountEqualsTo(0f);
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ mAcquiredWallpaperDimmingPermission = false;
+ }
}
}
diff --git a/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java b/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java
index 745ed17..d35dd40 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/activities/LoginActivity.java
@@ -28,6 +28,7 @@
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
+import android.view.WindowInsets;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
@@ -390,6 +391,13 @@
}
/**
+ * Get insets of the root window
+ */
+ public WindowInsets getRootWindowInsets() {
+ return mUsernameLabel.getRootWindowInsets();
+ }
+
+ /**
* Holder for the expected auto-fill values.
*/
private final class FillExpectation {
diff --git a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
index 94be565..ec8a4d1 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/dialog/LoginActivityTest.java
@@ -20,8 +20,12 @@
import static android.autofillservice.cts.testcore.Helper.ID_USERNAME;
import static android.autofillservice.cts.testcore.Helper.assertHasFlags;
import static android.autofillservice.cts.testcore.Helper.enableFillDialogFeature;
+import static android.autofillservice.cts.testcore.Helper.isImeShowing;
import static android.service.autofill.FillRequest.FLAG_SUPPORTS_FILL_DIALOG;
+
+import static com.google.common.truth.Truth.assertThat;
+
import android.autofillservice.cts.activities.LoginActivity;
import android.autofillservice.cts.commontests.AutoFillServiceTestCase;
import android.autofillservice.cts.testcore.CannedFillResponse;
@@ -39,6 +43,64 @@
public class LoginActivityTest extends AutoFillServiceTestCase.ManualActivityLaunch {
@Test
+ public void testTextView_withoutFillDialog_clickTwice_showIme() throws Exception {
+ // Start activity and autofill
+ LoginActivity activity = startLoginActivity();
+ mUiBot.waitForIdleSync();
+
+ // Click on password field
+ mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+ // Waits a while
+ mUiBot.waitForIdleSync();
+
+ // Click on password field again
+ mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+ // Waits a while
+ mUiBot.waitForIdleSync();
+
+ // Verify IME is shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
+ }
+
+ @Test
+ public void testTextView_clickTwiceWithShowFillDialog_showIme() throws Exception {
+ // Enable feature and test service
+ enableFillDialogFeature(sContext);
+ enableService();
+
+ // Set response with a dataset > fill dialog should have two buttons
+ final CannedFillResponse.Builder builder = new CannedFillResponse.Builder()
+ .addDataset(new CannedDataset.Builder()
+ .setField(ID_USERNAME, "dude")
+ .setField(ID_PASSWORD, "sweet")
+ .setPresentation(createPresentation("Dropdown Presentation"))
+ .setDialogPresentation(createPresentation("Dialog Presentation"))
+ .build())
+ .setDialogHeader(createPresentation("Dialog Header"))
+ .setDialogTriggerIds(ID_PASSWORD);
+ sReplier.addResponse(builder.build());
+
+ // Start activity and autofill
+ LoginActivity activity = startLoginActivity();
+ mUiBot.waitForIdleSync();
+ sReplier.getNextFillRequest();
+ mUiBot.waitForIdleSync();
+
+ // Click on password field to trigger fill dialog
+ mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+ mUiBot.waitForIdleSync();
+
+ mUiBot.assertFillDialogDatasets("Dialog Presentation");
+
+ // Click on password field again
+ mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
+ mUiBot.waitForIdleSync();
+
+ // Verify IME is shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
+ }
+
+ @Test
public void testShowFillDialog() throws Exception {
// Enable feature and test service
enableFillDialogFeature(sContext);
@@ -69,6 +131,9 @@
mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
mUiBot.waitForIdleSync();
+ // Verify IME is not shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isFalse();
+
// Verify the content of fill dialog, and then select dataset in fill dialog
mUiBot.assertFillDialogHeader("Dialog Header");
mUiBot.assertFillDialogRejectButton();
@@ -117,6 +182,8 @@
mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
mUiBot.waitForIdleSync();
+ // Verify IME is not shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isFalse();
// Verify the content of fill dialog
mUiBot.assertFillDialogHeader("Dialog Header");
mUiBot.assertFillDialogRejectButton();
@@ -160,12 +227,16 @@
mUiBot.waitForIdleSync();
mUiBot.assertFillDialogDatasets("Dialog presentation");
+ // Verify IME is not shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isFalse();
// Click on username field, and verify dropdown UI is shown
mUiBot.selectByRelativeIdFromUiDevice(ID_USERNAME);
mUiBot.waitForIdleSync();
mUiBot.assertDatasets("Dropdown Presentation");
+ // Verify IME is shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
// Verify dropdown UI works
activity.expectAutoFill("dude", "sweet");
@@ -204,12 +275,17 @@
mUiBot.waitForIdleSync();
mUiBot.assertDatasets("Dropdown Presentation");
+ // Verify IME is shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
// Click on password field and verify dropdown is still shown
// can't use mUiBot.selectByRelativeId(ID_PASSWORD), because will click on dropdown UI
activity.onPassword(View::requestFocus);
mUiBot.waitForIdleSync();
+ // Verify IME is shown
+ assertThat(isImeShowing(activity.getRootWindowInsets())).isTrue();
+
// Verify dropdown UI actually works in this case.
activity.expectAutoFill("dude", "sweet");
mUiBot.selectDataset("Dropdown Presentation");
@@ -250,7 +326,6 @@
mUiBot.selectByRelativeIdFromUiDevice(ID_PASSWORD);
mUiBot.waitForIdleSync();
activity.expectAutoFill("dude", "sweet");
-
mUiBot.selectFillDialogDataset("Dialog Presentation");
activity.assertAutoFilled();
diff --git a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
index 74a7882..20d9370 100644
--- a/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
+++ b/tests/autofillservice/src/android/autofillservice/cts/testcore/Helper.java
@@ -65,6 +65,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStructure.HtmlInfo;
+import android.view.WindowInsets;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillManager.AutofillCallback;
@@ -1654,6 +1655,16 @@
deviceConfigStateManager.set("true");
}
+ /**
+ * Whether IME is showing
+ */
+ public static boolean isImeShowing(WindowInsets rootWindowInsets) {
+ if (rootWindowInsets != null && rootWindowInsets.isVisible(WindowInsets.Type.ime())) {
+ return true;
+ }
+ return false;
+ }
+
private Helper() {
throw new UnsupportedOperationException("contain static methods only");
}
diff --git a/tests/camera/AndroidManifest.xml b/tests/camera/AndroidManifest.xml
index ab3120a..5cc761c 100644
--- a/tests/camera/AndroidManifest.xml
+++ b/tests/camera/AndroidManifest.xml
@@ -32,7 +32,7 @@
<activity android:name="android.hardware.cts.CameraCtsActivity"
android:label="CameraCtsActivity"
android:screenOrientation="locked"
- android:configChanges="keyboardHidden|orientation|screenSize">
+ android:configChanges="orientation|screenSize|screenLayout|keyboardHidden">
</activity>
<activity android:name="android.hardware.camera2.cts.Camera2SurfaceViewCtsActivity"
diff --git a/tests/camera/src/android/hardware/cts/CameraCtsActivity.java b/tests/camera/src/android/hardware/cts/CameraCtsActivity.java
index 61e283d..a6ec8ff 100644
--- a/tests/camera/src/android/hardware/cts/CameraCtsActivity.java
+++ b/tests/camera/src/android/hardware/cts/CameraCtsActivity.java
@@ -17,11 +17,12 @@
package android.hardware.cts;
import android.app.Activity;
+import android.camera.cts.R;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.ViewGroup;
-import android.camera.cts.R;
public class CameraCtsActivity extends Activity {
private SurfaceView mSurfaceView;
@@ -45,4 +46,9 @@
public SurfaceView getSurfaceView() {
return mSurfaceView;
}
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ }
}
diff --git a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
index 316afd7..6857072 100644
--- a/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
+++ b/tests/camera/src/android/hardware/multiprocess/camera/cts/CameraEvictionTest.java
@@ -16,11 +16,18 @@
package android.hardware.multiprocess.camera.cts;
+import static android.content.Intent.FLAG_ACTIVITY_LAUNCH_ADJACENT;
+import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
+
+import static org.mockito.Mockito.*;
+
import android.app.Activity;
import android.app.ActivityManager;
+import android.app.ActivityTaskManager;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
+import android.graphics.Rect;
import android.hardware.Camera;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
@@ -28,12 +35,15 @@
import android.hardware.camera2.cts.CameraTestUtils.HandlerExecutor;
import android.hardware.cts.CameraCtsActivity;
import android.os.Handler;
+import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.util.Log;
-
-import android.Manifest;
+import android.view.InputDevice;
+import android.view.MotionEvent;
+import android.view.WindowMetrics;
import androidx.test.InstrumentationRegistry;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -41,8 +51,6 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeoutException;
-import static org.mockito.Mockito.*;
-
/**
* Tests for multi-process camera usage behavior.
*/
@@ -337,6 +345,85 @@
}
}
}
+
+ private void injectTapEvent(int x, int y) {
+ final int motionEventTimeDeltaMs = 100;
+ MotionEvent downEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis() + motionEventTimeDeltaMs,
+ (int) MotionEvent.ACTION_DOWN, x, y, 0);
+ downEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mUiAutomation.injectInputEvent(downEvent, true);
+
+ MotionEvent upEvent = MotionEvent.obtain(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis() + motionEventTimeDeltaMs, (int) MotionEvent.ACTION_UP,
+ x, y, 0);
+ upEvent.setSource(InputDevice.SOURCE_TOUCHSCREEN);
+ mUiAutomation.injectInputEvent(upEvent, true);
+ }
+
+ /**
+ * Test camera availability access callback in split window mode.
+ */
+ public void testCamera2AccessCallbackInSplitMode() throws Throwable {
+ if (!ActivityTaskManager.supportsSplitScreenMultiWindow(getActivity())) {
+ return;
+ }
+
+ final int permissionCallbackTimeoutMs = 3000;
+ CameraManager manager = mContext.getSystemService(CameraManager.class);
+ assertNotNull(manager);
+ String[] cameraIds = manager.getCameraIdListNoLazy();
+
+ if (cameraIds.length == 0) {
+ Log.i(TAG, "Skipping testCamera2AccessCallback, device has no cameras.");
+ return;
+ }
+
+ assertTrue(mContext.getMainLooper() != null);
+
+ // Setup camera manager
+ Handler cameraHandler = new Handler(mContext.getMainLooper());
+
+ WindowMetrics metrics = getActivity().getWindowManager().getCurrentWindowMetrics();
+ Rect initialBounds = metrics.getBounds();
+
+ startRemoteProcess(Camera2Activity.class, "camera2ActivityProcess",
+ true /*splitScreen*/);
+
+ // Verify that the remote camera did open as expected
+ List<ErrorLoggingService.LogEvent> allEvents = mErrorServiceConnection.getLog(SETUP_TIMEOUT,
+ TestConstants.EVENT_CAMERA_CONNECT);
+ assertNotNull("Camera device not setup in remote process!", allEvents);
+
+ CameraManager.AvailabilityCallback mockAvailCb = mock(
+ CameraManager.AvailabilityCallback.class);
+ manager.registerAvailabilityCallback(mockAvailCb, cameraHandler);
+ metrics = getActivity().getWindowManager().getCurrentWindowMetrics();
+ Rect splitBounds = metrics.getBounds();
+
+ // The original of the initial and split activity bounds should remain the same
+ assertTrue((initialBounds.left == splitBounds.left)
+ && (initialBounds.top == splitBounds.top));
+
+ Rect secondBounds;
+ if (initialBounds.right > splitBounds.right) {
+ secondBounds = new Rect(splitBounds.right + 1, initialBounds.top, initialBounds.right,
+ initialBounds.bottom);
+ } else {
+ secondBounds = new Rect(initialBounds.left, splitBounds.bottom + 1, initialBounds.right,
+ initialBounds.bottom);
+ }
+
+ // Priorities are also expected to change when a second activity only gains or loses focus
+ // while running in split screen mode
+ injectTapEvent(splitBounds.centerX(), splitBounds.centerY());
+ injectTapEvent(secondBounds.centerX(), secondBounds.centerY());
+ injectTapEvent(splitBounds.centerX(), splitBounds.centerY());
+
+ verify(mockAvailCb, timeout(
+ permissionCallbackTimeoutMs).atLeastOnce()).onCameraAccessPrioritiesChanged();
+ }
+
/**
* Test camera availability access callback.
*/
@@ -361,7 +448,7 @@
manager.registerAvailabilityCallback(mockAvailCb, cameraHandler);
// Remove current task from top of stack. This will impact the camera access
- // pririorties.
+ // priorities.
getActivity().moveTaskToBack(/*nonRoot*/true);
verify(mockAvailCb, timeout(
@@ -580,6 +667,19 @@
*/
public void startRemoteProcess(java.lang.Class<?> klass, String processName)
throws InterruptedException {
+ startRemoteProcess(klass, processName, false /*splitScreen*/);
+ }
+
+ /**
+ * Start an activity of the given class running in a remote process with the given name.
+ *
+ * @param klass the class of the {@link android.app.Activity} to start.
+ * @param processName the remote activity name.
+ * @param splitScreen Start new activity in split screen mode.
+ * @throws InterruptedException
+ */
+ public void startRemoteProcess(java.lang.Class<?> klass, String processName,
+ boolean splitScreen) throws InterruptedException {
// Ensure no running activity process with same name
Activity a = getActivity();
String cameraActivityName = a.getPackageName() + ":" + processName;
@@ -589,6 +689,9 @@
// Start activity in a new top foreground process
Intent activityIntent = new Intent(a, klass);
+ if (splitScreen) {
+ activityIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_LAUNCH_ADJACENT);
+ }
a.startActivity(activityIntent);
Thread.sleep(WAIT_TIME);
diff --git a/tests/cloudsearch/src/android/cloudsearch/cts/Cts1CloudSearchService.java b/tests/cloudsearch/src/android/cloudsearch/cts/Cts1CloudSearchService.java
index 7820e1d..7726832 100644
--- a/tests/cloudsearch/src/android/cloudsearch/cts/Cts1CloudSearchService.java
+++ b/tests/cloudsearch/src/android/cloudsearch/cts/Cts1CloudSearchService.java
@@ -49,12 +49,12 @@
sWatcher.queried.countDown();
if (request.getQuery().contains("Successful1")) {
sWatcher.succeeded.countDown();
- returnResults(request.getRequestId(),
+ super.returnResults(request.getRequestId(),
CloudSearchTestUtils.getSearchResponse(SearchResponse.SEARCH_STATUS_OK));
}
if (request.getQuery().contains("Unsuccessful1")) {
sWatcher.failed.countDown();
- returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
+ super.returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
SearchResponse.SEARCH_STATUS_NO_INTERNET));
}
}
diff --git a/tests/cloudsearch/src/android/cloudsearch/cts/Cts2CloudSearchService.java b/tests/cloudsearch/src/android/cloudsearch/cts/Cts2CloudSearchService.java
index 36c56e2..33cdec8 100644
--- a/tests/cloudsearch/src/android/cloudsearch/cts/Cts2CloudSearchService.java
+++ b/tests/cloudsearch/src/android/cloudsearch/cts/Cts2CloudSearchService.java
@@ -49,12 +49,12 @@
sWatcher.queried.countDown();
if (request.getQuery().contains("Successful2")) {
sWatcher.succeeded.countDown();
- returnResults(request.getRequestId(),
+ super.returnResults(request.getRequestId(),
CloudSearchTestUtils.getSearchResponse(SearchResponse.SEARCH_STATUS_OK));
}
if (request.getQuery().contains("Unsuccessful2")) {
sWatcher.failed.countDown();
- returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
+ super.returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
SearchResponse.SEARCH_STATUS_NO_INTERNET));
}
}
diff --git a/tests/cloudsearch/src/android/cloudsearch/cts/Cts3CloudSearchService.java b/tests/cloudsearch/src/android/cloudsearch/cts/Cts3CloudSearchService.java
index 7cf8fe2..d264ea4 100644
--- a/tests/cloudsearch/src/android/cloudsearch/cts/Cts3CloudSearchService.java
+++ b/tests/cloudsearch/src/android/cloudsearch/cts/Cts3CloudSearchService.java
@@ -49,12 +49,12 @@
sWatcher.queried.countDown();
if (request.getQuery().contains("Successful3")) {
sWatcher.succeeded.countDown();
- returnResults(request.getRequestId(),
+ super.returnResults(request.getRequestId(),
CloudSearchTestUtils.getSearchResponse(SearchResponse.SEARCH_STATUS_OK));
}
if (request.getQuery().contains("Unsuccessful3")) {
sWatcher.failed.countDown();
- returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
+ super.returnResults(request.getRequestId(), CloudSearchTestUtils.getSearchResponse(
SearchResponse.SEARCH_STATUS_NO_INTERNET));
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
new file mode 100644
index 0000000..ad726c0
--- /dev/null
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagementRoleHolderTest.java
@@ -0,0 +1,172 @@
+/*
+ * 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.devicepolicy.cts;
+
+import static android.app.admin.DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_FINALIZATION;
+import static android.app.admin.DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
+import static android.app.admin.DevicePolicyManager.ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE;
+import static android.content.pm.PackageManager.FEATURE_MANAGED_USERS;
+
+import static com.android.bedstead.nene.permissions.CommonPermissions.BYPASS_ROLE_QUALIFICATION;
+import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_PROFILE_AND_DEVICE_OWNERS;
+import static com.android.bedstead.nene.permissions.CommonPermissions.MANAGE_ROLE_HOLDERS;
+import static com.android.queryable.queries.ActivityQuery.activity;
+import static com.android.queryable.queries.IntentFilterQuery.intentFilter;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.fail;
+
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.ManagedProfileProvisioningParams;
+import android.app.admin.ProvisioningException;
+import android.app.role.RoleManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.UserHandle;
+
+import com.android.bedstead.deviceadminapp.DeviceAdminApp;
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+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.RequireRunOnPrimaryUser;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasDeviceOwner;
+import com.android.bedstead.harrier.annotations.enterprise.EnsureHasNoDpc;
+import com.android.bedstead.nene.TestApis;
+import com.android.bedstead.nene.packages.Package;
+import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.remotedpc.RemoteDpc;
+import com.android.bedstead.testapp.TestApp;
+import com.android.bedstead.testapp.TestAppInstance;
+import com.android.compatibility.common.util.BlockingCallback;
+import com.android.queryable.queries.ActivityQuery;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@RunWith(BedsteadJUnit4.class)
+public class DevicePolicyManagementRoleHolderTest {
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final Context sContext = TestApis.context().instrumentedContext();
+ private static final ComponentName DEVICE_ADMIN_COMPONENT_NAME =
+ DeviceAdminApp.deviceAdminComponentName(sContext);
+ private static final String PROFILE_OWNER_NAME = "testDeviceAdmin";
+ private static final ManagedProfileProvisioningParams MANAGED_PROFILE_PROVISIONING_PARAMS =
+ createManagedProfileProvisioningParamsBuilder().build();
+ private static final DevicePolicyManager sDevicePolicyManager =
+ sContext.getSystemService(DevicePolicyManager.class);
+ private static final ActivityQuery<?> sQueryForRoleHolderTrustedSourceAction =
+ activity().intentFilters().contains(
+ intentFilter().actions().contains(
+ ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE));
+ private static final ActivityQuery<?> sQueryForRoleHolderManagedProfileAction =
+ activity().intentFilters().contains(
+ intentFilter().actions().contains(
+ ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE));
+ private static final ActivityQuery<?> sQueryForRoleHolderFinalizationAction =
+ activity().intentFilters().contains(
+ intentFilter().actions().contains(
+ ACTION_ROLE_HOLDER_PROVISION_FINALIZATION));
+ private static final TestApp sRoleHolderApp = sDeviceState.testApps()
+ .query()
+ .whereActivities()
+ .contains(
+ sQueryForRoleHolderTrustedSourceAction,
+ sQueryForRoleHolderManagedProfileAction,
+ sQueryForRoleHolderFinalizationAction)
+ .get();
+ private static final String MANAGED_USER_NAME = "managed user name";
+
+ @Postsubmit(reason = "new test")
+ @RequireFeature(FEATURE_MANAGED_USERS)
+ @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ @RequireRunOnPrimaryUser
+ @EnsureHasNoDpc
+ @Test
+ public void createAndProvisionManagedProfile_roleHolderIsInWorkProfile()
+ throws ProvisioningException, InterruptedException {
+ UserHandle profile = null;
+ String roleHolderPackageName = null;
+ try (TestAppInstance roleHolderApp = sRoleHolderApp.install()) {
+ roleHolderPackageName = roleHolderApp.packageName();
+ TestApis.devicePolicy().setDevicePolicyManagementRoleHolder(roleHolderPackageName);
+
+ profile = sDevicePolicyManager.createAndProvisionManagedProfile(
+ MANAGED_PROFILE_PROVISIONING_PARAMS);
+
+ assertThat(TestApis.packages().installedForUser(UserReference.of(profile)))
+ .contains(Package.of(roleHolderApp.packageName()));
+ } finally {
+ if (profile != null) {
+ TestApis.users().find(profile).remove();
+ }
+ if (roleHolderPackageName != null) {
+ TestApis.devicePolicy()
+ .unsetDevicePolicyManagementRoleHolder(roleHolderPackageName);
+ }
+ }
+ }
+
+ @Postsubmit(reason = "new test")
+ @RequireFeature(FEATURE_MANAGED_USERS)
+ @EnsureHasDeviceOwner
+ @RequireRunOnPrimaryUser
+ @Test
+ public void createAndManageUser_roleHolderIsInManagedUser() throws InterruptedException {
+ UserHandle managedUser = null;
+ String roleHolderPackageName = null;
+ try (TestAppInstance roleHolderApp = sRoleHolderApp.install()) {
+ roleHolderPackageName = roleHolderApp.packageName();
+ TestApis.devicePolicy().setDevicePolicyManagementRoleHolder(roleHolderPackageName);
+
+ managedUser = sDeviceState.dpc().devicePolicyManager().createAndManageUser(
+ RemoteDpc.DPC_COMPONENT_NAME,
+ MANAGED_USER_NAME,
+ RemoteDpc.DPC_COMPONENT_NAME,
+ /* adminExtras= */ null,
+ /* flags= */ 0);
+
+ assertThat(TestApis.packages().installedForUser(UserReference.of(managedUser)))
+ .contains(Package.of(roleHolderApp.packageName()));
+ } finally {
+ if (managedUser != null) {
+ TestApis.users().find(managedUser).remove();
+ }
+ if (roleHolderPackageName != null) {
+ TestApis.devicePolicy()
+ .unsetDevicePolicyManagementRoleHolder(roleHolderPackageName);
+ }
+ }
+ }
+
+ private static ManagedProfileProvisioningParams.Builder
+ createManagedProfileProvisioningParamsBuilder() {
+ return new ManagedProfileProvisioningParams.Builder(
+ DEVICE_ADMIN_COMPONENT_NAME,
+ PROFILE_OWNER_NAME);
+ }
+}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
index 3d5b284..5bb50978 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/DevicePolicyManagerTest.java
@@ -38,6 +38,7 @@
import static android.nfc.NfcAdapter.ACTION_NDEF_DISCOVERED;
import static android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES;
+import static com.android.bedstead.nene.users.UserType.MANAGED_PROFILE_TYPE_NAME;
import static com.android.bedstead.remotedpc.RemoteDpc.REMOTE_DPC_TEST_APP;
import static com.android.queryable.queries.ServiceQuery.service;
@@ -102,6 +103,7 @@
import com.android.bedstead.nene.packages.Package;
import com.android.bedstead.nene.permissions.PermissionContext;
import com.android.bedstead.nene.users.UserReference;
+import com.android.bedstead.nene.users.UserType;
import com.android.bedstead.nene.utils.Poll;
import com.android.bedstead.remotedpc.RemoteDpc;
import com.android.bedstead.testapp.TestApp;
@@ -216,6 +218,8 @@
private static final PersistableBundle ADMIN_EXTRAS_BUNDLE = createAdminExtrasBundle();
private static final String TEST_KEY = "test_key";
private static final String TEST_VALUE = "test_value";
+ private static final UserType MANAGED_PROFILE_USER_TYPE =
+ TestApis.users().supportedType(MANAGED_PROFILE_TYPE_NAME);
@Before
public void setUp() {
@@ -1184,6 +1188,7 @@
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@EnsureHasProfileOwner
@RequireRunOnSecondaryUser
+ @RequireFeature(FEATURE_MANAGED_USERS)
public void checkProvisioningPreCondition_actionPO_onManagedUser_returnsHasProfileOwner() {
assertThat(
sDevicePolicyManager.checkProvisioningPrecondition(
@@ -1390,6 +1395,7 @@
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@EnsureHasNoDpc
+ @RequireFeature(FEATURE_MANAGED_USERS)
public void setUserProvisioningState_unmanagedDevice_stateUserSetupIncomplete_throwsIllegalStateException() {
assertThrows(IllegalStateException.class, () ->
sDevicePolicyManager.setUserProvisioningState(
@@ -1706,6 +1712,7 @@
/* userdata= */ null);
}
+ @Postsubmit(reason = "new test")
@Test
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@@ -1717,6 +1724,7 @@
/* migratedAccount= */ null));
}
+ @Postsubmit(reason = "new test")
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void finalizeWorkProfileProvisioning_nullManagedProfileUser_throwsException() {
@@ -1726,6 +1734,7 @@
/* migratedAccount= */ null));
}
+ @Postsubmit(reason = "new test")
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
public void finalizeWorkProfileProvisioning_nonExistingManagedProfileUser_throwsException() {
@@ -1735,6 +1744,7 @@
/* migratedAccount= */ null));
}
+ @Postsubmit(reason = "new test")
@Test
@RequireRunOnPrimaryUser
@EnsureHasSecondaryUser
@@ -1751,6 +1761,7 @@
}
}
+ @Postsubmit(reason = "new test")
@Test
@RequireRunOnPrimaryUser
@EnsureHasWorkProfile
@@ -1768,6 +1779,7 @@
}
}
+ @Postsubmit(reason = "new test")
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@EnsureHasWorkProfile
@@ -1787,6 +1799,7 @@
}
}
+ @Postsubmit(reason = "new test")
@Test
@EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
@EnsureHasWorkProfile
@@ -1807,4 +1820,45 @@
}
}
+
+ @Postsubmit(reason = "new test")
+ @Test
+ @EnsureHasNoDpc
+ @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void getPolicyManagedProfiles_noManagedProfiles_returnsEmptyList() {
+ assertThat(sDevicePolicyManager.getPolicyManagedProfiles(
+ TestApis.context().instrumentationContext().getUser())).isEmpty();
+ }
+
+ @Postsubmit(reason = "new test")
+ @Test
+ @EnsureHasWorkProfile
+ @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void getPolicyManagedProfiles_hasWorkProfile_returnsWorkProfileUser() {
+ assertThat(sDevicePolicyManager.getPolicyManagedProfiles(
+ TestApis.context().instrumentationContext().getUser()))
+ .containsExactly(sDeviceState.workProfile().userHandle());
+ }
+
+ @Postsubmit(reason = "new test")
+ @Test
+ @EnsureHasNoDpc
+ @EnsureHasPermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void getPolicyManagedProfiles_hasManagedProfileNoProfileOwner_returnsEmptyList() {
+ try (UserReference user = TestApis.users().createUser().type(MANAGED_PROFILE_USER_TYPE)
+ .parent(TestApis.users().instrumented()).create()) {
+ assertThat(sDevicePolicyManager.getPolicyManagedProfiles(
+ TestApis.context().instrumentationContext().getUser()))
+ .isEmpty();
+ }
+ }
+
+ @Postsubmit(reason = "new test")
+ @Test
+ @EnsureHasNoDpc
+ @EnsureDoesNotHavePermission(MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void getPolicyManagedProfiles_noPermission_returnsEmptyList() {
+ assertThrows(SecurityException.class, () -> sDevicePolicyManager.getPolicyManagedProfiles(
+ TestApis.context().instrumentationContext().getUser()));
+ }
}
diff --git a/tests/devicepolicy/src/android/devicepolicy/cts/LostModeLocationTest.java b/tests/devicepolicy/src/android/devicepolicy/cts/LostModeLocationTest.java
index 95cd7f2..87ce571 100644
--- a/tests/devicepolicy/src/android/devicepolicy/cts/LostModeLocationTest.java
+++ b/tests/devicepolicy/src/android/devicepolicy/cts/LostModeLocationTest.java
@@ -20,6 +20,7 @@
import static android.app.admin.DevicePolicyManager.ACTION_LOST_MODE_LOCATION_UPDATE;
import static android.app.admin.DevicePolicyManager.EXTRA_LOST_MODE_LOCATION;
import static android.content.Context.RECEIVER_EXPORTED;
+import static android.location.LocationManager.FUSED_PROVIDER;
import static com.google.common.truth.Truth.assertThat;
@@ -46,7 +47,6 @@
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.runner.RunWith;
@@ -61,7 +61,7 @@
private static final double TEST_LATITUDE_2 = 22.0;
private static final double TEST_LONGITUDE_2 = -10.5;
private static final float TEST_ACCURACY_2 = 15.0f;
- private static final int LOCATION_UPDATE_TIMEOUT_SECONDS = 60;
+ private static final int LOCATION_UPDATE_TIMEOUT_SECONDS = 180;
@ClassRule
@Rule
@@ -94,24 +94,20 @@
@Postsubmit(reason = "new test")
@PolicyAppliesTest(policy = LostMode.class)
- @Ignore("b/223148704")
- public void sendLostModeLocationUpdate_noTestProviders_returnFalse() throws Exception {
- sendLostModeLocationUpdate(/* expected= */ false);
- }
-
- @Postsubmit(reason = "new test")
- @PolicyAppliesTest(policy = LostMode.class)
- @Ignore("b/223148704")
public void sendLostModeLocationUpdate_noLocation_returnFalse() throws Exception {
- try (LocationProvider provider = TestApis.location().addLocationProvider()) {
- sendLostModeLocationUpdate(/* expected */ false);
+ try {
+ TestApis.location().setLocationEnabled(false);
+ sendLostModeLocationUpdate(/* expected= */ false);
+ } finally {
+ TestApis.location().setLocationEnabled(true);
}
}
@Postsubmit(reason = "new test")
@PolicyAppliesTest(policy = LostMode.class)
- public void sendLostModeLocationUpdate_returnTrueAndSendLocationUpdate() throws Exception {
- try (LocationProvider provider = TestApis.location().addLocationProvider()) {
+ public void sendLostModeLocationUpdate_returnTrueAndSendLocationUpdate()
+ throws Exception {
+ try (LocationProvider provider = TestApis.location().addLocationProvider(FUSED_PROVIDER)) {
provider.setLocation(TEST_LATITUDE, TEST_LONGITUDE, TEST_ACCURACY);
sendLostModeLocationUpdate(/* expected= */ true);
@@ -133,7 +129,7 @@
@Postsubmit(reason = "new test")
@PolicyAppliesTest(policy = LostMode.class)
public void sendLostModeLocationUpdate_sendMostRecentLocation() throws Exception {
- try (LocationProvider provider = TestApis.location().addLocationProvider()) {
+ try (LocationProvider provider = TestApis.location().addLocationProvider(FUSED_PROVIDER)) {
provider.setLocation(TEST_LATITUDE, TEST_LONGITUDE, TEST_ACCURACY);
provider.setLocation(TEST_LATITUDE_2, TEST_LONGITUDE_2, TEST_ACCURACY_2);
diff --git a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerSystemLocaleTest.java b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerSystemLocaleTest.java
index 3de185c..fb97787 100644
--- a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerSystemLocaleTest.java
+++ b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerSystemLocaleTest.java
@@ -32,8 +32,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,11 +45,31 @@
*/
@RunWith(AndroidJUnit4.class)
public class LocaleManagerSystemLocaleTest extends ActivityManagerTestBase {
- private Context mContext;
- private LocaleManager mLocaleManager;
+ private static Context sContext;
+ private static LocaleManager sLocaleManager;
/* System locales that were set on the device prior to running tests */
- private LocaleList mPreviousSystemLocales;
+ private static LocaleList sPreviousSystemLocales;
+
+ @BeforeClass
+ public static void setUpClass() {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sLocaleManager = sContext.getSystemService(LocaleManager.class);
+
+ // Set custom system locales for these tests.
+ // Store the existing system locales and reset back to it in tearDown.
+ sPreviousSystemLocales = sLocaleManager.getSystemLocales();
+ runWithShellPermissionIdentity(() ->
+ sLocaleManager.setSystemLocales(DEFAULT_SYSTEM_LOCALES),
+ Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ runWithShellPermissionIdentity(() ->
+ sLocaleManager.setSystemLocales(sPreviousSystemLocales),
+ Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
+ }
@Before
public void setUp() throws Exception {
@@ -56,41 +77,24 @@
// to be in the foreground/background.
super.setUp();
- mContext = InstrumentationRegistry.getTargetContext();
- mLocaleManager = mContext.getSystemService(LocaleManager.class);
-
- // Set custom system locales for these tests.
- // Store the existing system locales and reset back to it in tearDown.
- mPreviousSystemLocales = mLocaleManager.getSystemLocales();
- runWithShellPermissionIdentity(() ->
- mLocaleManager.setSystemLocales(DEFAULT_SYSTEM_LOCALES),
- Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
-
// Reset locales for the calling app.
- mLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
- }
-
- @After
- public void tearDown() throws Exception {
- runWithShellPermissionIdentity(() ->
- mLocaleManager.setSystemLocales(mPreviousSystemLocales),
- Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
+ sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
}
@Test
public void testGetSystemLocales_noAppLocaleSet_returnsCorrectList()
throws Exception {
- assertEquals(DEFAULT_SYSTEM_LOCALES, mLocaleManager.getSystemLocales());
+ assertEquals(DEFAULT_SYSTEM_LOCALES, sLocaleManager.getSystemLocales());
}
@Test
public void testGetSystemLocales_appLocaleSet_returnsCorrectList()
throws Exception {
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
// ensure that getSystemLocales still returns the system locales
- assertEquals(DEFAULT_SYSTEM_LOCALES, mLocaleManager.getSystemLocales());
+ assertEquals(DEFAULT_SYSTEM_LOCALES, sLocaleManager.getSystemLocales());
}
@@ -99,7 +103,7 @@
* by fetching the locales of the app with a binder call.
*/
private void assertLocalesCorrectlySetForCallingApp(LocaleList expectedLocales) {
- assertEquals(expectedLocales, mLocaleManager.getApplicationLocales());
+ assertEquals(expectedLocales, sLocaleManager.getApplicationLocales());
}
}
diff --git a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
index c422923..23143fc 100644
--- a/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
+++ b/tests/framework/base/locale/src/android/localemanager/cts/LocaleManagerTests.java
@@ -59,7 +59,9 @@
import com.android.compatibility.common.util.ShellIdentityUtils;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -74,11 +76,11 @@
*/
@RunWith(AndroidJUnit4.class)
public class LocaleManagerTests extends ActivityManagerTestBase {
- private Context mContext;
- private LocaleManager mLocaleManager;
+ private static Context sContext;
+ private static LocaleManager sLocaleManager;
/* System locales that were set on the device prior to running tests */
- private LocaleList mPreviousSystemLocales;
+ private static LocaleList sPreviousSystemLocales;
/* Receiver to listen to the broadcast in the calling (instrumentation) app. */
private BlockingBroadcastReceiver mCallingAppBroadcastReceiver;
@@ -98,22 +100,30 @@
/* Receiver to listen to the response from the installer app's activity. */
private BlockingBroadcastReceiver mInstallerAppCreationInfoProvider;
+ @BeforeClass
+ public static void setUpClass() {
+ sContext = InstrumentationRegistry.getTargetContext();
+ sLocaleManager = sContext.getSystemService(LocaleManager.class);
+
+ sPreviousSystemLocales = sLocaleManager.getSystemLocales();
+ runWithShellPermissionIdentity(() ->
+ sLocaleManager.setSystemLocales(DEFAULT_SYSTEM_LOCALES),
+ Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
+ }
+
+ @AfterClass
+ public static void tearDownClass() {
+ runWithShellPermissionIdentity(() ->
+ sLocaleManager.setSystemLocales(sPreviousSystemLocales),
+ Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
+ }
+
@Before
public void setUp() throws Exception {
// Unlocks the device if locked, since we have tests where the app/activity needs
// to be in the foreground/background.
super.setUp();
- mContext = InstrumentationRegistry.getTargetContext();
- mLocaleManager = mContext.getSystemService(LocaleManager.class);
-
- // Set custom system locales for these tests.
- // Store the existing system locales and reset back to it in tearDown.
- mPreviousSystemLocales = mLocaleManager.getSystemLocales();
- runWithShellPermissionIdentity(() ->
- mLocaleManager.setSystemLocales(DEFAULT_SYSTEM_LOCALES),
- Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
-
resetAppLocalesAsEmpty();
AmUtils.waitForBroadcastIdle();
@@ -124,17 +134,17 @@
mTestAppConfigChangedInfoProvider = new BlockingBroadcastReceiver();
mInstallerAppCreationInfoProvider = new BlockingBroadcastReceiver();
- mContext.registerReceiver(mCallingAppBroadcastReceiver,
+ sContext.registerReceiver(mCallingAppBroadcastReceiver,
new IntentFilter(Intent.ACTION_LOCALE_CHANGED));
- mContext.registerReceiver(mTestAppBroadcastInfoProvider,
+ sContext.registerReceiver(mTestAppBroadcastInfoProvider,
new IntentFilter(TEST_APP_BROADCAST_INFO_PROVIDER_ACTION));
- mContext.registerReceiver(mInstallerBroadcastInfoProvider,
+ sContext.registerReceiver(mInstallerBroadcastInfoProvider,
new IntentFilter(INSTALLER_APP_BROADCAST_INFO_PROVIDER_ACTION));
- mContext.registerReceiver(mTestAppCreationInfoProvider,
+ sContext.registerReceiver(mTestAppCreationInfoProvider,
new IntentFilter(TEST_APP_CREATION_INFO_PROVIDER_ACTION));
- mContext.registerReceiver(mTestAppConfigChangedInfoProvider,
+ sContext.registerReceiver(mTestAppConfigChangedInfoProvider,
new IntentFilter(TEST_APP_CONFIG_CHANGED_INFO_PROVIDER_ACTION));
- mContext.registerReceiver(mInstallerAppCreationInfoProvider,
+ sContext.registerReceiver(mInstallerAppCreationInfoProvider,
new IntentFilter(INSTALLER_APP_CREATION_INFO_PROVIDER_ACTION));
setInstallerForPackage(CALLING_PACKAGE);
@@ -153,14 +163,11 @@
unRegisterReceiver(mInstallerBroadcastInfoProvider);
unRegisterReceiver(mTestAppCreationInfoProvider);
unRegisterReceiver(mInstallerAppCreationInfoProvider);
- runWithShellPermissionIdentity(() ->
- mLocaleManager.setSystemLocales(mPreviousSystemLocales),
- Manifest.permission.CHANGE_CONFIGURATION, Manifest.permission.WRITE_SETTINGS);
}
private void unRegisterReceiver(BlockingBroadcastReceiver receiver) {
if (receiver != null) {
- mContext.unregisterReceiver(receiver);
+ sContext.unregisterReceiver(receiver);
receiver = null;
}
}
@@ -181,7 +188,7 @@
.setComponent(new ComponentName(packageName, broadcastReceiver))
.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
CountDownLatch latch = new CountDownLatch(1);
- mContext.sendOrderedBroadcast(
+ sContext.sendOrderedBroadcast(
intent,
null /* receiverPermission */,
new BroadcastReceiver() { /* resultReceiver */
@@ -205,7 +212,7 @@
* Sets the installer as {@link #INSTALLER_PACKAGE} for the target package.
*/
private void setInstallerForPackage(String targetPackageName) {
- ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(mContext.getPackageManager(),
+ ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn(sContext.getPackageManager(),
(pm) -> pm.setInstallerPackageName(targetPackageName, INSTALLER_PACKAGE));
}
@@ -223,7 +230,7 @@
@Test
public void testSetApplicationLocales_persistsAndSendsBroadcast() throws Exception {
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
mCallingAppBroadcastReceiver.await();
@@ -239,14 +246,14 @@
public void testSetApplicationLocales_resetToEmptyLocales_persistsAndSendsBroadcast()
throws Exception {
// First set the locales to non-empty
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
mCallingAppBroadcastReceiver.await();
assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
CALLING_PACKAGE, DEFAULT_APP_LOCALES);
mCallingAppBroadcastReceiver.reset();
- mLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
+ sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
assertLocalesCorrectlySetForCallingApp(LocaleList.getEmptyLocaleList());
mCallingAppBroadcastReceiver.await();
@@ -258,7 +265,7 @@
public void testSetApplicationLocales_sameLocalesEmpty_noBroadcastSent() throws Exception {
// At the start of the test, no app-specific locales are set, so just try to set it to
// empty again
- mLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
+ sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
assertLocalesCorrectlySetForCallingApp(LocaleList.getEmptyLocaleList());
mCallingAppBroadcastReceiver.assertNoBroadcastReceived();
@@ -267,7 +274,7 @@
@Test
public void testSetApplicationLocales_sameLocalesNonEmpty_noBroadcastSent() throws Exception {
// First set the locales to non-empty
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
mCallingAppBroadcastReceiver.await();
assertReceivedBroadcastContains(mCallingAppBroadcastReceiver,
@@ -275,7 +282,7 @@
mCallingAppBroadcastReceiver.reset();
// Then reset to the same app-specific locales, and verify no broadcasts are sent
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
mCallingAppBroadcastReceiver.assertNoBroadcastReceived();
@@ -288,7 +295,7 @@
LocaleList systemLocales = LocaleList.getDefault();
assertEquals(DEFAULT_SYSTEM_LOCALES, systemLocales);
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
// Wait for a while since LocaleList::getDefault could take a while to
// reflect the new app locales.
@@ -304,7 +311,7 @@
mWmState.assertVisibility(TEST_APP_MAIN_ACTIVITY, /* visible*/ true);
runWithShellPermissionIdentity(() ->
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
Manifest.permission.CHANGE_CONFIGURATION);
assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
@@ -327,7 +334,7 @@
mWmState.waitAndAssertVisibilityGone(TEST_APP_MAIN_ACTIVITY);
runWithShellPermissionIdentity(() ->
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
Manifest.permission.CHANGE_CONFIGURATION);
assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
@@ -344,7 +351,7 @@
@Test
public void testSetApplicationLocales_forAnotherApp_persistsOnAppRestart() throws Exception {
runWithShellPermissionIdentity(() ->
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
Manifest.permission.CHANGE_CONFIGURATION);
// Re-start the app by starting an activity and check if locales correctly
@@ -361,7 +368,7 @@
testSetApplicationLocales_wthoutPermissionforAnotherApp_throwsExceptionAndNoBroadcast()
throws Exception {
try {
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
} catch (SecurityException e) {
// expected as not having appropriate permission to change locales for another app.
}
@@ -379,7 +386,7 @@
mWmState.assertVisibility(TEST_APP_MAIN_ACTIVITY, /* visible*/ true);
runWithShellPermissionIdentity(() ->
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES),
Manifest.permission.CHANGE_CONFIGURATION);
assertLocalesCorrectlySetForAnotherApp(TEST_APP_PACKAGE, DEFAULT_APP_LOCALES);
@@ -394,7 +401,7 @@
BlockingBroadcastReceiver appSpecificLocaleBroadcastReceiver =
new BlockingBroadcastReceiver();
- mContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
+ sContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
new IntentFilter(Intent.ACTION_APPLICATION_LOCALE_CHANGED));
// Hold Manifest.permission.READ_APP_SPECIFIC_LOCALES while the broadcast is sent,
@@ -419,7 +426,7 @@
BlockingBroadcastReceiver appSpecificLocaleBroadcastReceiver =
new BlockingBroadcastReceiver();
- mContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
+ sContext.registerReceiver(appSpecificLocaleBroadcastReceiver,
new IntentFilter(Intent.ACTION_APPLICATION_LOCALE_CHANGED));
// Tell the test app to change its app-specific locales
@@ -437,7 +444,7 @@
@Test
public void testGetApplicationLocales_callerIsInstaller_returnsLocales() throws Exception {
// Set locales for calling app
- mLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
+ sLocaleManager.setApplicationLocales(DEFAULT_APP_LOCALES);
// Make sure that locales were set for the app.
assertLocalesCorrectlySetForCallingApp(DEFAULT_APP_LOCALES);
@@ -453,7 +460,7 @@
@Test(expected = SecurityException.class)
public void testGetApplicationLocales_withoutPermissionforAnotherApp_throwsException()
throws Exception {
- mLocaleManager.getApplicationLocales(TEST_APP_PACKAGE);
+ sLocaleManager.getApplicationLocales(TEST_APP_PACKAGE);
fail("Expected SecurityException due to not having appropriate permission "
+ "for querying locales of another app.");
}
@@ -462,7 +469,7 @@
public void testGetApplicationLocales_noAppLocalesSet_returnsEmptyList() {
// When app-specific locales aren't set, we expect get api to return empty list
// and not throw any error.
- assertEquals(LocaleList.getEmptyLocaleList(), mLocaleManager.getApplicationLocales());
+ assertEquals(LocaleList.getEmptyLocaleList(), sLocaleManager.getApplicationLocales());
}
/**
@@ -470,7 +477,7 @@
* by fetching locales of the app with a binder call.
*/
private void assertLocalesCorrectlySetForCallingApp(LocaleList expectedLocales) {
- assertEquals(expectedLocales, mLocaleManager.getApplicationLocales());
+ assertEquals(expectedLocales, sLocaleManager.getApplicationLocales());
}
/**
@@ -484,7 +491,7 @@
private LocaleList getApplicationLocales(String packageName) throws Exception {
return callWithShellPermissionIdentity(() ->
- mLocaleManager.getApplicationLocales(packageName),
+ sLocaleManager.getApplicationLocales(packageName),
Manifest.permission.READ_APP_SPECIFIC_LOCALES);
}
@@ -527,11 +534,11 @@
*/
private void resetAppLocalesAsEmpty() throws Exception {
// Reset locales for the calling app.
- mLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
+ sLocaleManager.setApplicationLocales(LocaleList.getEmptyLocaleList());
// Reset locales for the TestApp.
runWithShellPermissionIdentity(() ->
- mLocaleManager.setApplicationLocales(TEST_APP_PACKAGE,
+ sLocaleManager.setApplicationLocales(TEST_APP_PACKAGE,
LocaleList.getEmptyLocaleList()),
Manifest.permission.CHANGE_CONFIGURATION);
}
diff --git a/tests/framework/base/windowmanager/AndroidManifest.xml b/tests/framework/base/windowmanager/AndroidManifest.xml
index ba93dc7..3277fb3 100644
--- a/tests/framework/base/windowmanager/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/AndroidManifest.xml
@@ -34,8 +34,9 @@
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
<application android:label="CtsWindowManagerDeviceTestCases"
- android:requestLegacyExternalStorage="true"
- android:enableOnBackInvokedCallback="true">
+ android:requestLegacyExternalStorage="true"
+ android:enableOnBackInvokedCallback="true"
+ android:testOnly="true">
<uses-library android:name="android.test.runner"/>
<activity android:name="android.server.wm.ActivityManagerTestBase$ConfigChangeHandlingActivity"
diff --git a/tests/framework/base/windowmanager/AndroidTest.xml b/tests/framework/base/windowmanager/AndroidTest.xml
index 503490b..9cdb6fa 100644
--- a/tests/framework/base/windowmanager/AndroidTest.xml
+++ b/tests/framework/base/windowmanager/AndroidTest.xml
@@ -21,6 +21,7 @@
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
+ <option name="install-arg" value="-t" />
<option name="test-file-name" value="CtsWindowManagerDeviceTestCases.apk"/>
<option name="test-file-name" value="CtsDragAndDropSourceApp.apk"/>
<option name="test-file-name" value="CtsDragAndDropTargetApp.apk"/>
diff --git a/tests/framework/base/windowmanager/app/AndroidManifest.xml b/tests/framework/base/windowmanager/app/AndroidManifest.xml
index 2c49bc2..804e0e7 100755
--- a/tests/framework/base/windowmanager/app/AndroidManifest.xml
+++ b/tests/framework/base/windowmanager/app/AndroidManifest.xml
@@ -622,11 +622,13 @@
<activity android:name=".CustomTransitionExitActivity"
android:theme="@style/Theme.CustomTransitionTheme"
android:exported="true"
- android:colorMode="wideColorGamut"/>
+ android:colorMode="wideColorGamut"
+ android:fitsSystemWindows="true" />
<activity android:name=".CustomTransitionEnterActivity"
android:theme="@style/Theme.CustomTransitionTheme"
android:exported="true"
- android:colorMode="wideColorGamut"/>
+ android:colorMode="wideColorGamut"
+ android:fitsSystemWindows="true" />
<activity android:name=".KeepClearRectsActivity"
android:exported="true"
android:theme="@style/NoInsetsTheme"/>
diff --git a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml b/tests/framework/base/windowmanager/app/res/anim/show_backdrop_hide_window_animation.xml
similarity index 96%
rename from tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
rename to tests/framework/base/windowmanager/app/res/anim/show_backdrop_hide_window_animation.xml
index 236bf7d..5ee8b8a 100644
--- a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
+++ b/tests/framework/base/windowmanager/app/res/anim/show_backdrop_hide_window_animation.xml
@@ -17,7 +17,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:showBackground="true">
+ android:showBackdrop="true">
<alpha
android:fromAlpha="0"
diff --git a/tests/framework/base/windowmanager/app/res/values/styles.xml b/tests/framework/base/windowmanager/app/res/values/styles.xml
index 0a9cb8f..695f579 100644
--- a/tests/framework/base/windowmanager/app/res/values/styles.xml
+++ b/tests/framework/base/windowmanager/app/res/values/styles.xml
@@ -101,6 +101,8 @@
<item name="android:background">#ffffff</item>
<item name="android:windowBackground">#ffffff</item>
<item name="android:colorBackground">#ffffff</item>
+ <item name="android:statusBarColor">@android:color/transparent</item>
+ <item name="android:navigationBarColor">@android:color/transparent</item>
</style>
<style name="NoInsetsTheme" parent="@android:style/Theme.NoTitleBar">
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
index 0986fb3..16ddfcd 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/Components.java
@@ -300,7 +300,7 @@
* The keys used by {@link CustomTransitionExitActivity} to select the animation to run.
*/
public static class CustomTransitionAnimations {
- /** see @anim/show_background_hide_window_animation.xml */
+ /** see @anim/show_backdrop_hide_window_animation.xml */
public static final String BACKGROUND_COLOR = "backgroundColor";
/** see @anim/edge_extension_right_window_animation.xml */
public static final String LEFT_EDGE_EXTENSION = "leftEdgeExtension";
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionEnterActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionEnterActivity.java
index a6b7672..b112c5e 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionEnterActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionEnterActivity.java
@@ -42,6 +42,10 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.background_image);
+ // Ensure the activity is edge-to-edge
+ // In tests we rely on the activity's content filling the entire window
+ getWindow().setDecorFitsSystemWindows(false);
+
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
mTransitionType = bundle.getString("transitionType");
@@ -53,8 +57,8 @@
super.onResume();
switch (mTransitionType) {
case BACKGROUND_COLOR:
- overridePendingTransition(R.anim.show_background_hide_window_animation,
- R.anim.show_background_hide_window_animation, mBackgroundColor);
+ overridePendingTransition(R.anim.show_backdrop_hide_window_animation,
+ R.anim.show_backdrop_hide_window_animation, mBackgroundColor);
break;
case LEFT_EDGE_EXTENSION:
overridePendingTransition(R.anim.edge_extension_left_window_animation,
diff --git a/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionExitActivity.java b/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionExitActivity.java
index 91cbcea..a4a73ba 100644
--- a/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionExitActivity.java
+++ b/tests/framework/base/windowmanager/app/src/android/server/wm/app/CustomTransitionExitActivity.java
@@ -38,6 +38,10 @@
super.onCreate(savedInstanceState);
setContentView(R.layout.background_image);
+ // Ensure the activity is edge-to-edge
+ // In tests we rely on the activity's content filling the entire window
+ getWindow().setDecorFitsSystemWindows(false);
+
Intent intent = getIntent();
Bundle bundle = intent.getExtras();
mTransitionType = bundle.getString("transitionType");
diff --git a/tests/framework/base/windowmanager/appBackLegacy/src/android/server/wm/backlegacyapp/BackNavigationLegacyActivity.java b/tests/framework/base/windowmanager/appBackLegacy/src/android/server/wm/backlegacyapp/BackNavigationLegacyActivity.java
index b39283f..456f060 100644
--- a/tests/framework/base/windowmanager/appBackLegacy/src/android/server/wm/backlegacyapp/BackNavigationLegacyActivity.java
+++ b/tests/framework/base/windowmanager/appBackLegacy/src/android/server/wm/backlegacyapp/BackNavigationLegacyActivity.java
@@ -29,13 +29,11 @@
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- OnBackInvokedCallback onBackInvokedCallback = new OnBackInvokedCallback() {
- @Override
- public void onBackInvoked() {
- TestJournalProvider.putExtras(BackNavigationLegacyActivity.this,
- Components.BACK_LEGACY,
- bundle -> bundle.putBoolean(Components.KEY_ON_BACK_INVOKED_CALLED, true));
- }
+ OnBackInvokedCallback onBackInvokedCallback = () -> {
+ TestJournalProvider.putExtras(
+ BackNavigationLegacyActivity.this,
+ Components.BACK_LEGACY,
+ bundle -> bundle.putBoolean(Components.KEY_ON_BACK_INVOKED_CALLED, true));
};
getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT, onBackInvokedCallback
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
index 888cd12..3e49a2d 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingBoundsTests.java
@@ -27,22 +27,17 @@
import android.app.Activity;
import android.content.Intent;
-import android.server.wm.ActivityManagerTestBase.ReportedDisplayMetrics;
-import android.server.wm.jetpack.utils.ActivityEmbeddingTestBase;
import android.server.wm.jetpack.utils.TestActivity;
import android.server.wm.jetpack.utils.TestActivityWithId;
import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
import android.util.LayoutDirection;
import android.util.Pair;
import android.util.Size;
-import android.view.Display;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.window.extensions.embedding.SplitPairRule;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -60,18 +55,6 @@
@RunWith(AndroidJUnit4.class)
public class ActivityEmbeddingBoundsTests extends ActivityEmbeddingTestBase {
- private ReportedDisplayMetrics mReportedDisplayMetrics;
-
- @Before
- public void initializeDisplayMetrics() {
- mReportedDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(Display.DEFAULT_DISPLAY);
- }
-
- @After
- public void restoreDisplayMetrics() {
- mReportedDisplayMetrics.restoreDisplayMetrics();
- }
-
/**
* Tests that when two activities are in a split and the parent bounds shrink such that
* they can no longer support split activities, then the activities become stacked.
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
index 309d652..2adc1b7 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingCrossUidTests.java
@@ -32,7 +32,6 @@
import android.app.Activity;
import android.app.ActivityManager;
import android.server.wm.NestedShellPermission;
-import android.server.wm.jetpack.utils.ActivityEmbeddingTestBase;
import android.server.wm.jetpack.utils.TestActivityWithId;
import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
import android.util.Pair;
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
index 8af1398..69ac897 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingFinishTests.java
@@ -33,7 +33,6 @@
import static org.junit.Assert.assertTrue;
import android.app.Activity;
-import android.server.wm.jetpack.utils.ActivityEmbeddingTestBase;
import android.server.wm.jetpack.utils.TestActivity;
import android.server.wm.jetpack.utils.TestActivityWithId;
import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
index 2614f3d..5c2d696 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingLaunchTests.java
@@ -29,7 +29,6 @@
import android.app.Activity;
import android.content.Intent;
-import android.server.wm.jetpack.utils.ActivityEmbeddingTestBase;
import android.server.wm.jetpack.utils.TestActivityWithId;
import android.server.wm.jetpack.utils.TestConfigChangeHandlingActivity;
import android.util.Pair;
@@ -92,19 +91,16 @@
Integer.toString(activityLaunchIndex) /* secondActivityId */,
mSplitInfoConsumer);
- // Verify the split states match with the current and previous launches
+ // Verify that the secondary container has all the secondary activities
secondaryActivities.add(secondaryActivity);
final List<SplitInfo> lastReportedSplitInfoList =
mSplitInfoConsumer.getLastReportedValue();
splitInfosList.add(lastReportedSplitInfoList);
- assertEquals(secondaryActivities.size(), lastReportedSplitInfoList.size());
- for (int splitInfoIndex = 0; splitInfoIndex < lastReportedSplitInfoList.size();
- splitInfoIndex++) {
- final SplitInfo splitInfo = lastReportedSplitInfoList.get(splitInfoIndex);
- assertEquals(primaryActivity, getPrimaryStackTopActivity(splitInfo));
- assertEquals(secondaryActivities.get(splitInfoIndex),
- getSecondaryStackTopActivity(splitInfo));
- }
+ assertEquals(1, lastReportedSplitInfoList.size());
+ final SplitInfo splitInfo = lastReportedSplitInfoList.get(0);
+ assertEquals(primaryActivity, getPrimaryStackTopActivity(splitInfo));
+ assertEquals(secondaryActivities, splitInfo.getSecondaryActivityStack()
+ .getActivities());
}
// Iteratively finish each secondary activity and verify that the primary activity is split
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
index 0f07662..693d854 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingPlaceholderTests.java
@@ -29,21 +29,16 @@
import android.app.Activity;
import android.content.Intent;
-import android.server.wm.ActivityManagerTestBase.ReportedDisplayMetrics;
-import android.server.wm.jetpack.utils.ActivityEmbeddingTestBase;
import android.server.wm.jetpack.utils.TestActivity;
import android.server.wm.jetpack.utils.TestActivityWithId;
import android.util.Pair;
import android.util.Size;
-import android.view.Display;
import android.view.WindowMetrics;
import androidx.annotation.NonNull;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.window.extensions.embedding.SplitPlaceholderRule;
-import org.junit.After;
-import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -67,17 +62,6 @@
private static final String PRIMARY_ACTIVITY_ID = "primaryActivity";
private static final String PLACEHOLDER_ACTIVITY_ID = "placeholderActivity";
- private ReportedDisplayMetrics mReportedDisplayMetrics;
-
- @Before
- public void initializeDisplayMetrics() {
- mReportedDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(Display.DEFAULT_DISPLAY);
- }
-
- @After
- public void restoreDisplayMetrics() {
- mReportedDisplayMetrics.restoreDisplayMetrics();
- }
/**
* Tests that an activity with a matching {@link SplitPlaceholderRule} is successfully able to
diff --git a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingTestBase.java b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingTestBase.java
similarity index 76%
rename from tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingTestBase.java
rename to tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingTestBase.java
index 6717820..9867569 100644
--- a/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/utils/ActivityEmbeddingTestBase.java
+++ b/tests/framework/base/windowmanager/jetpack/src/android/server/wm/jetpack/ActivityEmbeddingTestBase.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,23 @@
* limitations under the License.
*/
-package android.server.wm.jetpack.utils;
+package android.server.wm.jetpack;
import static android.server.wm.jetpack.utils.ExtensionUtil.assumeExtensionSupportedDevice;
import static android.server.wm.jetpack.utils.ExtensionUtil.getWindowExtensions;
import static org.junit.Assume.assumeNotNull;
+import android.server.wm.ActivityManagerTestBase.ReportedDisplayMetrics;
+import android.server.wm.jetpack.utils.TestValueCountConsumer;
+import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase;
+import android.view.Display;
+
import androidx.window.extensions.WindowExtensions;
import androidx.window.extensions.embedding.ActivityEmbeddingComponent;
import androidx.window.extensions.embedding.SplitInfo;
+import org.junit.After;
import org.junit.Before;
import java.util.List;
@@ -37,6 +43,8 @@
protected ActivityEmbeddingComponent mActivityEmbeddingComponent;
protected TestValueCountConsumer<List<SplitInfo>> mSplitInfoConsumer;
+ protected ReportedDisplayMetrics mReportedDisplayMetrics =
+ ReportedDisplayMetrics.getDisplayMetrics(Display.DEFAULT_DISPLAY);
@Override
@Before
@@ -50,4 +58,9 @@
mSplitInfoConsumer = new TestValueCountConsumer<>();
mActivityEmbeddingComponent.setSplitInfoCallback(mSplitInfoConsumer);
}
+
+ @After
+ public void cleanUp() {
+ mReportedDisplayMetrics.restoreDisplayMetrics();
+ }
}
diff --git a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml b/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_backdrop.xml
similarity index 96%
copy from tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
copy to tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_backdrop.xml
index 236bf7d..5ee8b8a 100644
--- a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
+++ b/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_backdrop.xml
@@ -17,7 +17,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:showBackground="true">
+ android:showBackdrop="true">
<alpha
android:fromAlpha="0"
diff --git a/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_background.xml b/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_background.xml
deleted file mode 100644
index 236bf7d..0000000
--- a/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_background.xml
+++ /dev/null
@@ -1,31 +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.
- -->
-
-<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:showBackground="true">
-
- <alpha
- android:fromAlpha="0"
- android:toAlpha="0"
- android:fillEnabled="true"
- android:fillBefore="true"
- android:fillAfter="true"
- android:interpolator="@android:interpolator/linear"
- android:startOffset="0"
- android:duration="5000"/>
-</set>
diff --git a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml b/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_red_backdrop.xml
similarity index 93%
copy from tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
copy to tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_red_backdrop.xml
index 236bf7d..f5f03df 100644
--- a/tests/framework/base/windowmanager/app/res/anim/show_background_hide_window_animation.xml
+++ b/tests/framework/base/windowmanager/overlayappbase/res/anim/alpha_0_with_red_backdrop.xml
@@ -17,7 +17,8 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false"
- android:showBackground="true">
+ android:showBackdrop="true"
+ android:backdropColor="#FF0000">
<alpha
android:fromAlpha="0"
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java
index 798bd25..de62c94 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/ActivityTransitionTests.java
@@ -228,6 +228,22 @@
}
/**
+ * Checks that the background color set in the animation definition is used as the animation's
+ * background color instead of the theme's background color.
+ *
+ * @see R.anim.alpha_0_with_red_backdrop for animation defintition.
+ */
+ @Test
+ public void testAnimationBackgroundColorIsUsedDuringActivityTransition() {
+ final int backgroundColor = Color.RED;
+ final ActivityOptions activityOptions = ActivityOptions.makeCustomAnimation(mContext,
+ R.anim.alpha_0_with_red_backdrop, R.anim.alpha_0_with_red_backdrop);
+ Bitmap screenshot = runAndScreenshotActivityTransition(activityOptions,
+ TransitionActivityWithWhiteBackground.class);
+ assertAppRegionOfScreenIsColor(screenshot, backgroundColor);
+ }
+
+ /**
* Checks that we can override the default background color of the animation using the
* CustomAnimation activityOptions.
*/
@@ -235,7 +251,7 @@
public void testCustomTransitionCanOverrideBackgroundColor() {
final int backgroundColor = Color.GREEN;
final ActivityOptions activityOptions = ActivityOptions.makeCustomAnimation(mContext,
- R.anim.alpha_0_with_background, R.anim.alpha_0_with_background, backgroundColor
+ R.anim.alpha_0_with_backdrop, R.anim.alpha_0_with_backdrop, backgroundColor
);
Bitmap screenshot = runAndScreenshotActivityTransition(activityOptions,
TransitionActivity.class);
@@ -251,8 +267,8 @@
final int backgroundColor = Color.GREEN;
final Bundle extras = new Bundle();
- extras.putInt(ENTER_ANIM_KEY, R.anim.alpha_0_with_background);
- extras.putInt(EXIT_ANIM_KEY, R.anim.alpha_0_with_background);
+ extras.putInt(ENTER_ANIM_KEY, R.anim.alpha_0_with_backdrop);
+ extras.putInt(EXIT_ANIM_KEY, R.anim.alpha_0_with_backdrop);
extras.putInt(BACKGROUND_COLOR_KEY, backgroundColor);
Bitmap screenshot = runAndScreenshotActivityTransition(
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/AnimationEdgeExtensionTests.java b/tests/framework/base/windowmanager/src/android/server/wm/AnimationEdgeExtensionTests.java
index 0530491..9102634 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/AnimationEdgeExtensionTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/AnimationEdgeExtensionTests.java
@@ -40,6 +40,9 @@
@android.server.wm.annotation.Group1
public class AnimationEdgeExtensionTests extends CustomActivityTransitionTestBase {
+ // We need to allow for some variation stemming from color conversions
+ private static final float COLOR_VALUE_VARIANCE_TOLERANCE = 0.03f;
+
/**
* Checks that when an activity transition with a left edge extension is run that the animating
* activity is extended on the left side by clamping the edge pixels of the activity.
@@ -158,7 +161,7 @@
new float[] {
expectedColor.red(), expectedColor.green(), expectedColor.blue() },
new float[] { sRgbColor.red(), sRgbColor.green(), sRgbColor.blue() },
- 0.03f); // need to allow for some variation stemming from conversions
+ COLOR_VALUE_VARIANCE_TOLERANCE);
}
}
}
@@ -174,12 +177,21 @@
final Color c = screen.getColor(x, y)
.convert(ColorSpace.get(ColorSpace.Named.SRGB));
- if (prevColor.red() != c.red() || prevColor.green() != c.green()
- || prevColor.blue() != c.blue()) {
+ if (!colorsEqual(prevColor, c)) {
return x;
}
}
throw new RuntimeException("Failed to find color change index");
}
+
+ private boolean colorsEqual(Color c1, Color c2) {
+ return almostEquals(c1.red(), c2.red(), COLOR_VALUE_VARIANCE_TOLERANCE)
+ && almostEquals(c1.green(), c2.green(), COLOR_VALUE_VARIANCE_TOLERANCE)
+ && almostEquals(c1.blue(), c2.blue(), COLOR_VALUE_VARIANCE_TOLERANCE);
+ }
+
+ private boolean almostEquals(float a, float b, float delta) {
+ return Math.abs(a - b) < delta;
+ }
}
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationTests.java
index 3870fb2..5e9bed9 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/BackNavigationTests.java
@@ -24,7 +24,6 @@
import android.platform.test.annotations.Presubmit;
import android.support.test.uiautomator.UiDevice;
import android.view.KeyEvent;
-import android.window.OnBackInvokedCallback;
import androidx.lifecycle.Lifecycle;
import androidx.test.core.app.ActivityScenario;
@@ -32,7 +31,6 @@
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
@@ -118,12 +116,7 @@
CountDownLatch backRegisteredLatch = new CountDownLatch(1);
mScenario.onActivity(activity -> {
activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
- 0, new OnBackInvokedCallback() {
- @Override
- public void onBackInvoked() {
- backInvokedLatch.countDown();
- }
- });
+ 0, backInvokedLatch::countDown);
backRegisteredLatch.countDown();
});
try {
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
index 1a6eb4c..f38e646 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/MultiDisplaySystemDecorationTests.java
@@ -919,15 +919,21 @@
private void assertImeWindowAndDisplayConfiguration(
WindowState imeWinState, DisplayContent display) {
+ // The IME window should inherit the configuration from the IME DisplayArea.
+ final WindowManagerState.DisplayArea imeContainerDisplayArea = display.getImeContainer();
final Configuration configurationForIme = imeWinState.mMergedOverrideConfiguration;
- final Configuration configurationForDisplay = display.mMergedOverrideConfiguration;
+ final Configuration configurationForImeContainer =
+ imeContainerDisplayArea.mMergedOverrideConfiguration;
final int displayDensityDpiForIme = configurationForIme.densityDpi;
- final int displayDensityDpi = configurationForDisplay.densityDpi;
+ final int displayDensityDpiForImeContainer = configurationForImeContainer.densityDpi;
final Rect displayBoundsForIme = configurationForIme.windowConfiguration.getBounds();
- final Rect displayBounds = configurationForDisplay.windowConfiguration.getBounds();
+ final Rect displayBoundsForImeContainer =
+ configurationForImeContainer.windowConfiguration.getBounds();
- assertEquals("Display density not the same", displayDensityDpi, displayDensityDpiForIme);
- assertEquals("Display bounds not the same", displayBounds, displayBoundsForIme);
+ assertEquals("Display density not the same",
+ displayDensityDpiForImeContainer, displayDensityDpiForIme);
+ assertEquals("Display bounds not the same",
+ displayBoundsForImeContainer, displayBoundsForIme);
}
private void tapAndAssertEditorFocusedOnImeActivity(
diff --git a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityAsUserTests.java b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityAsUserTests.java
index e9764c0..2453065 100644
--- a/tests/framework/base/windowmanager/src/android/server/wm/StartActivityAsUserTests.java
+++ b/tests/framework/base/windowmanager/src/android/server/wm/StartActivityAsUserTests.java
@@ -39,6 +39,7 @@
import androidx.test.platform.app.InstrumentationRegistry;
import org.junit.AfterClass;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -63,19 +64,33 @@
@BeforeClass
public static void createSecondUser() {
- assumeTrue(SUPPORTS_MULTIPLE_USERS);
+ if (!SUPPORTS_MULTIPLE_USERS) {
+ return;
+ }
final Context context = InstrumentationRegistry.getInstrumentation().getContext();
final String output = runShellCommand("pm create-user --profileOf " + context.getUserId()
+ " user2");
sSecondUserId = Integer.parseInt(output.substring(output.lastIndexOf(" ")).trim());
- assertThat(sSecondUserId).isNotEqualTo(0);
+ if (sSecondUserId == 0) {
+ return;
+ }
runShellCommand("pm install-existing --user " + sSecondUserId + " android.server.wm.cts");
}
@AfterClass
public static void removeSecondUser() {
+ if (sSecondUserId == 0) {
+ return;
+ }
runShellCommand("pm remove-user " + sSecondUserId);
+ sSecondUserId = 0;
+ }
+
+ @Before
+ public void checkMultipleUsersNotSupportedOrSecondUserCreated() {
+ assumeTrue(SUPPORTS_MULTIPLE_USERS);
+ assertThat(sSecondUserId).isNotEqualTo(0);
}
@Test
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 5611dc1..1b11bd2 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
@@ -46,6 +46,7 @@
import static android.content.pm.PackageManager.FEATURE_VR_MODE_HIGH_PERFORMANCE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
+import static android.os.UserHandle.USER_SYSTEM;
import static android.server.wm.ActivityLauncher.KEY_ACTIVITY_TYPE;
import static android.server.wm.ActivityLauncher.KEY_DISPLAY_ID;
import static android.server.wm.ActivityLauncher.KEY_INTENT_EXTRAS;
@@ -184,10 +185,8 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -1366,27 +1365,11 @@
* Test @Rule class that disables screen doze settings before each test method running and
* restoring to initial values after test method finished.
*/
- protected static class DisableScreenDozeRule implements TestRule {
+ protected class DisableScreenDozeRule implements TestRule {
+ AmbientDisplayConfiguration mConfig;
- /** Copied from android.provider.Settings.Secure since these keys are hidden. */
- private static final String[] DOZE_SETTINGS = {
- "doze_enabled",
- "doze_always_on",
- "doze_pulse_on_pick_up",
- "doze_pulse_on_long_press",
- "doze_pulse_on_double_tap",
- "doze_wake_screen_gesture",
- "doze_wake_display_gesture",
- "doze_tap_gesture",
- "doze_quick_pickup_gesture"
- };
-
- private String get(String key) {
- return executeShellCommand("settings get secure " + key).trim();
- }
-
- private void put(String key, String value) {
- executeShellCommand("settings put secure " + key + " " + value);
+ DisableScreenDozeRule() {
+ mConfig = new AmbientDisplayConfiguration(mContext);
}
@Override
@@ -1394,13 +1377,18 @@
return new Statement() {
@Override
public void evaluate() throws Throwable {
- final Map<String, String> initialValues = new HashMap<>();
- Arrays.stream(DOZE_SETTINGS).forEach(k -> initialValues.put(k, get(k)));
try {
- Arrays.stream(DOZE_SETTINGS).forEach(k -> put(k, "0"));
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ // disable current doze settings
+ mConfig.disableDozeSettings(true /* shouldDisableNonUserConfigurable */,
+ USER_SYSTEM);
+ });
base.evaluate();
} finally {
- Arrays.stream(DOZE_SETTINGS).forEach(k -> put(k, initialValues.get(k)));
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ // restore doze settings
+ mConfig.restoreDozeSettings(USER_SYSTEM);
+ });
}
}
};
@@ -2730,7 +2718,7 @@
final int mDisplayId;
final boolean mInitialIgnoreOrientationRequest;
- IgnoreOrientationRequestSession(int displayId, boolean enable) {
+ public IgnoreOrientationRequestSession(int displayId, boolean enable) {
mDisplayId = displayId;
Matcher matcher = IGNORE_ORIENTATION_REQUEST_PATTERN.matcher(
executeShellCommand(WM_GET_IGNORE_ORIENTATION_REQUEST + " -d " + mDisplayId));
diff --git a/tests/input/src/android/input/cts/TouchModeTest.kt b/tests/input/src/android/input/cts/TouchModeTest.kt
index efa3122..493e2e0 100644
--- a/tests/input/src/android/input/cts/TouchModeTest.kt
+++ b/tests/input/src/android/input/cts/TouchModeTest.kt
@@ -20,21 +20,26 @@
import android.app.Instrumentation
import android.os.SystemClock
import android.support.test.uiautomator.UiDevice
+import android.view.ViewTreeObserver
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.MediumTest
import androidx.test.platform.app.InstrumentationRegistry
import com.android.compatibility.common.util.PollingCheck
import com.android.compatibility.common.util.WindowUtil
+import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
-import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import java.util.concurrent.CountDownLatch
+import java.util.concurrent.TimeUnit
-private const val EVENT_PROPAGATION_TIMEOUT_MILLIS: Long = 5000
+private const val TOUCH_MODE_PROPAGATION_TIMEOUT_MILLIS: Long = 5000 // 5 sec
+@MediumTest
@RunWith(AndroidJUnit4::class)
class TouchModeTest {
private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -65,15 +70,45 @@
}
@Test
- @Ignore("b/218883063")
+ fun testOnTouchModeChangeNotification() {
+ val touchModeChangeListener = OnTouchModeChangeListenerImpl()
+ var observer = activity.window.decorView.rootView.viewTreeObserver
+ observer.addOnTouchModeChangeListener(touchModeChangeListener)
+ val newTouchMode = !isInTouchMode()
+
+ instrumentation.setInTouchMode(newTouchMode)
+ try {
+ assertTrue(touchModeChangeListener.countDownLatch.await(
+ TOUCH_MODE_PROPAGATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS))
+ } catch (e: InterruptedException) {
+ throw RuntimeException(e)
+ }
+
+ assertEquals(newTouchMode, touchModeChangeListener.isInTouchMode)
+ }
+
+ private class OnTouchModeChangeListenerImpl : ViewTreeObserver.OnTouchModeChangeListener {
+ val countDownLatch = CountDownLatch(1)
+ var isInTouchMode = false
+
+ override fun onTouchModeChanged(mode: Boolean) {
+ isInTouchMode = mode
+ countDownLatch.countDown()
+ }
+ }
+
+ @Test
fun testNonFocusedWindowOwnerCannotChangeTouchMode() {
+ // It takes 400-500 milliseconds in average for DecorView to receive the touch mode changed
+ // event on 2021 hardware, so we set the timeout to 10x that. It's still possible that a
+ // test would fail, but we don't have a better way to check that an event does not occur.
+ // Due to the 2 expected touch mode events to occur, this test may take few seconds to run.
uiDevice.pressHome()
PollingCheck.waitFor { !activity.hasWindowFocus() }
+
instrumentation.setInTouchMode(true)
- // It takes 400-500 milliseconds for DecorView to receive the touch mode changed event on
- // 2021 hardware, so we set the timeout to 10x that. It's still possible that a test would
- // fail, but we don't have a better way to check that an event does not occur.
- SystemClock.sleep(EVENT_PROPAGATION_TIMEOUT_MILLIS)
+
+ SystemClock.sleep(TOUCH_MODE_PROPAGATION_TIMEOUT_MILLIS)
assertFalse(isInTouchMode())
}
}
diff --git a/tests/inputmethod/AndroidManifest.xml b/tests/inputmethod/AndroidManifest.xml
index 1bb3122..e757f79 100644
--- a/tests/inputmethod/AndroidManifest.xml
+++ b/tests/inputmethod/AndroidManifest.xml
@@ -23,7 +23,8 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:label="CtsInputMethodTestCases"
android:multiArch="true"
- android:supportsRtl="true">
+ android:supportsRtl="true"
+ android:testOnly="true">
<uses-library android:name="android.test.runner"/>
diff --git a/tests/inputmethod/AndroidTest.xml b/tests/inputmethod/AndroidTest.xml
index 23e26e4..69316c1 100644
--- a/tests/inputmethod/AndroidTest.xml
+++ b/tests/inputmethod/AndroidTest.xml
@@ -96,6 +96,7 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
+ <option name="install-arg" value="-t" />
<option name="test-file-name" value="CtsInputMethodTestCases.apk" />
</target_preparer>
<!-- Enabling change id ALLOW_TEST_API_ACCESS allows that package to access @TestApi methods -->
diff --git a/tests/inputmethod/util/src/android/view/inputmethod/cts/util/DisableScreenDozeRule.java b/tests/inputmethod/util/src/android/view/inputmethod/cts/util/DisableScreenDozeRule.java
index 206fdc8..ed86d8e 100644
--- a/tests/inputmethod/util/src/android/view/inputmethod/cts/util/DisableScreenDozeRule.java
+++ b/tests/inputmethod/util/src/android/view/inputmethod/cts/util/DisableScreenDozeRule.java
@@ -16,56 +16,50 @@
package android.view.inputmethod.cts.util;
-import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+import static android.os.UserHandle.USER_SYSTEM;
+
+import android.content.Context;
+import android.hardware.display.AmbientDisplayConfiguration;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.SystemUtil;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
/**
* {@link TestRule} class that disables screen doze settings before each test method running and
* restoring to initial values after test method finished.
*/
public class DisableScreenDozeRule implements TestRule {
- /** Copied from ActivityManagerTestBase since these keys are hidden. */
- private static final String[] DOZE_SETTINGS = {
- "doze_enabled",
- "doze_always_on",
- "doze_pulse_on_pick_up",
- "doze_pulse_on_long_press",
- "doze_pulse_on_double_tap",
- "doze_wake_screen_gesture",
- "doze_wake_display_gesture",
- "doze_tap_gesture"
- };
+ private final AmbientDisplayConfiguration mConfig;
+ private final Context mContext;
- private String getSecureSetting(String key) {
- return runShellCommand("settings get secure " + key).trim();
- }
-
- private void putSecureSetting(String key, String value) {
- runShellCommand("settings put secure " + key + " " + value);
+ public DisableScreenDozeRule() {
+ mContext = InstrumentationRegistry.getInstrumentation().getContext();
+ mConfig = new AmbientDisplayConfiguration(mContext);
}
@Override
- public Statement apply(Statement base, Description description) {
+ public Statement apply(final Statement base, final Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
- final Map<String, String> initialValues = new HashMap<>();
- Arrays.stream(DOZE_SETTINGS).forEach(
- k -> initialValues.put(k, getSecureSetting(k)));
try {
- Arrays.stream(DOZE_SETTINGS).forEach(k -> putSecureSetting(k, "0"));
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ // disable current doze settings
+ mConfig.disableDozeSettings(true /* shouldDisableNonUserConfigurable */,
+ USER_SYSTEM);
+ });
base.evaluate();
} finally {
- Arrays.stream(DOZE_SETTINGS).forEach(
- k -> putSecureSetting(k, initialValues.get(k)));
+ SystemUtil.runWithShellPermissionIdentity(() -> {
+ // restore doze settings
+ mConfig.restoreDozeSettings(USER_SYSTEM);
+ });
}
}
};
diff --git a/tests/media/jni/NativeCodecDecoderTest.cpp b/tests/media/jni/NativeCodecDecoderTest.cpp
index 8ea1136..9a34ce8 100644
--- a/tests/media/jni/NativeCodecDecoderTest.cpp
+++ b/tests/media/jni/NativeCodecDecoderTest.cpp
@@ -475,8 +475,10 @@
AMediaExtractor_seekTo(mExtractor, 0, mode);
test->reset();
if (!doWork(23)) return false;
- CHECK_ERR(!test->isPtsStrictlyIncreasing(mPrevOutputPts), "",
- "pts is not strictly increasing", isPass);
+ if (!mIsInterlaced) {
+ CHECK_ERR(!test->isPtsStrictlyIncreasing(mPrevOutputPts), "",
+ "pts is not strictly increasing", isPass);
+ }
/* test flush in running state */
if (!flushCodec()) return false;
@@ -563,8 +565,13 @@
CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
CHECK_ERR(loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
log, "pts is not strictly increasing", isPass);
- CHECK_ERR(loopCounter == 0 && !mIsAudio && (!ref->isOutPtsListIdenticalToInpPtsList(false)),
- log, "input pts list and output pts list are not identical", isPass);
+ // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders
+ // produce multiple progressive frames?) For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ CHECK_ERR(loopCounter == 0 && !mIsAudio &&
+ (!ref->isOutPtsListIdenticalToInpPtsList(false)),
+ log, "input pts list and output pts list are not identical", isPass);
+ }
loopCounter++;
}
return isPass;
@@ -642,9 +649,13 @@
CHECK_ERR(loopCounter == 0 && mIsAudio &&
(!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
log, "pts is not strictly increasing", isPass);
- CHECK_ERR(loopCounter == 0 && !mIsAudio &&
- (!ref->isOutPtsListIdenticalToInpPtsList(false)),
- log, "input pts list and output pts list are not identical", isPass);
+ // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders
+ // produce multiple progressive frames?) For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ CHECK_ERR(loopCounter == 0 && !mIsAudio &&
+ (!ref->isOutPtsListIdenticalToInpPtsList(false)),
+ log, "input pts list and output pts list are not identical", isPass);
+ }
if (validateFormat) {
if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
: !mSignalledOutFormatChanged) {
diff --git a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
index e7e0739..7a39cc7 100644
--- a/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecDecoderTest.java
@@ -352,9 +352,15 @@
assertTrue(log + " pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue(
- log + " input pts list and output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review.
+ // (E.g. can decoders produce multiple progressive frames?)
+ // For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue(
+ log +
+ " input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
}
if (validateFormat) {
@@ -411,8 +417,12 @@
assertTrue("reference output pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue("input pts list and output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders
+ // produce multiple progressive frames?) For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue("input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
mOutputBuff = test;
setUpSource(mTestFile);
@@ -445,8 +455,10 @@
mExtractor.seekTo(0, mode);
test.reset();
doWork(23);
- assertTrue(log + " pts is not strictly increasing",
- test.isPtsStrictlyIncreasing(mPrevOutputPts));
+ if (!mIsInterlaced) {
+ assertTrue(log + " pts is not strictly increasing",
+ test.isPtsStrictlyIncreasing(mPrevOutputPts));
+ }
boolean checkMetrics = (mOutputCount != 0);
@@ -544,10 +556,14 @@
assertTrue("config reference output pts is not strictly increasing",
configRef.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue("input pts list and reference pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
- assertTrue("input pts list and reconfig ref output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders
+ // produce multiple progressive frames?) For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue("input pts list and reference pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ assertTrue("input pts list and reconfig ref output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
mOutputBuff = test;
mCodec = MediaCodec.createByCodecName(mCodecName);
@@ -713,9 +729,14 @@
assertTrue(log + " pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue(
- log + " input pts list and output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review.
+ // (E.g. can decoders produce multiple progressive frames?)
+ // For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue(
+ log + " input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
}
loopCounter++;
@@ -807,9 +828,15 @@
assertTrue(log + " pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue(
- log + " input pts list and output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review.
+ // (E.g. can decoders produce multiple progressive frames?)
+ // For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue(
+ log +
+ " input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
}
if (validateFormat) {
@@ -869,8 +896,12 @@
assertTrue("reference output pts is not strictly increasing",
ref.isPtsStrictlyIncreasing(mPrevOutputPts));
} else {
- assertTrue("input pts list and output pts list are not identical",
- ref.isOutPtsListIdenticalToInpPtsList(false));
+ // TODO: Timestamps for deinterlaced content are under review. (E.g. can decoders
+ // produce multiple progressive frames?) For now, do not verify timestamps.
+ if (!mIsInterlaced) {
+ assertTrue("input pts list and output pts list are not identical",
+ ref.isOutPtsListIdenticalToInpPtsList(false));
+ }
}
mSaveToMem = true;
mOutputBuff = test;
diff --git a/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java b/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
index c553f1e..63b1b2c 100644
--- a/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecEncoderValidationTest.java
@@ -46,7 +46,6 @@
private static final String INPUT_VIDEO_FILE_HBD = "cosmat_cif_24fps_yuv420p16le.yuv";
private final boolean mUseHBD;
- private final SupportClass mSupportRequirements;
// Key: mediaType, Value: tolerance duration in ms
private static final Map<String, Integer> toleranceMap = new HashMap<>();
@@ -58,72 +57,88 @@
toleranceMap.put(MediaFormat.MIMETYPE_AUDIO_FLAC, 0);
}
- public CodecEncoderValidationTest(String encoder, String mediaType, int[] bitrates,
- int[] encoderInfo1, int[] encoderInfo2, boolean useHBD,
- SupportClass supportRequirements) {
- super(encoder, mediaType, bitrates, encoderInfo1, encoderInfo2);
+ public CodecEncoderValidationTest(String encoder, String mediaType, int bitrate,
+ int encoderInfo1, int encoderInfo2, boolean useHBD) {
+ super(encoder, mediaType, new int[]{bitrate}, new int[]{encoderInfo1},
+ new int[]{encoderInfo2});
mUseHBD = useHBD;
- mSupportRequirements = supportRequirements;
}
- @Parameterized.Parameters(name = "{index}({0})")
+ private static List<Object[]> flattenParams(List<Object[]> params) {
+ List<Object[]> argsList = new ArrayList<>();
+ for (Object[] param : params) {
+ String mediaType = (String) param[0];
+ int[] bitRates = (int[]) param[1];
+ int[] infoList1 = (int[]) param[2];
+ int[] infoList2 = (int[]) param[3];
+ boolean useHBD = (boolean) param[4];
+ for (int bitrate : bitRates) {
+ for (int info1 : infoList1) {
+ for (int info2 : infoList2) {
+ argsList.add(new Object[]{mediaType, bitrate, info1, info2, useHBD});
+ }
+ }
+ }
+ }
+ return argsList;
+ }
+
+ @Parameterized.Parameters(name = "{index}({0}_{1}_{2}_{3}_{4}_{5})")
public static Collection<Object[]> input() {
final boolean isEncoder = true;
final boolean needAudio = true;
final boolean needVideo = true;
List<Object[]> defArgsList = new ArrayList<>(Arrays.asList(new Object[][]{
// Audio tests covering cdd sec 5.1.3
- // mediaType, arrays of bit-rates, sample rates, channel counts, useHBD,
- // SupportClass
+ // mediaType, arrays of bit-rates, sample rates, channel counts, useHBD
{MediaFormat.MIMETYPE_AUDIO_AAC, new int[]{64000, 128000}, new int[]{8000, 12000,
- 16000, 22050, 24000, 32000, 44100, 48000}, new int[]{1, 2}, false,
- CODEC_ALL},
+ 16000, 22050, 24000, 32000, 44100, 48000}, new int[]{1, 2}, false},
{MediaFormat.MIMETYPE_AUDIO_OPUS, new int[]{64000, 128000}, new int[]{8000, 12000
- , 16000, 24000, 48000}, new int[]{1, 2}, false, CODEC_ALL},
+ , 16000, 24000, 48000}, new int[]{1, 2}, false},
{MediaFormat.MIMETYPE_AUDIO_AMR_NB, new int[]{4750, 5150, 5900, 6700, 7400, 7950,
- 10200, 12200}, new int[]{8000}, new int[]{1}, false, CODEC_ALL},
+ 10200, 12200}, new int[]{8000}, new int[]{1}, false},
{MediaFormat.MIMETYPE_AUDIO_AMR_WB, new int[]{6600, 8850, 12650, 14250, 15850,
- 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}, false,
- CODEC_ALL},
+ 18250, 19850, 23050, 23850}, new int[]{16000}, new int[]{1}, false},
/* TODO(169310292) */
{MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{/* 0, 1, 2, */ 3, 4, 5, 6, 7, 8},
new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2},
- false, CODEC_ALL},
+ false},
{MediaFormat.MIMETYPE_AUDIO_FLAC, new int[]{/* 0, 1, 2, */ 3, 4, 5, 6, 7, 8},
new int[]{8000, 16000, 32000, 48000, 96000, 192000}, new int[]{1, 2},
- true, CODEC_ALL},
+ true},
- // mediaType, arrays of bit-rates, width, height, useHBD, SupportClass
+ // mediaType, arrays of bit-rates, width, height, useHBD
{MediaFormat.MIMETYPE_VIDEO_H263, new int[]{32000, 64000}, new int[]{176},
- new int[]{144}, false, CODEC_ALL},
+ new int[]{144}, false},
{MediaFormat.MIMETYPE_VIDEO_MPEG4, new int[]{32000, 64000}, new int[]{176},
- new int[]{144}, false, CODEC_ALL},
+ new int[]{144}, false},
{MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, false, CODEC_ALL},
+ new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, false, CODEC_ALL},
+ new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_VP8, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, false, CODEC_ALL},
+ new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, false, CODEC_ALL},
+ new int[]{240, 360}, false},
{MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, false, CODEC_ALL},
+ new int[]{240, 360}, false},
}));
// P010 support was added in Android T, hence limit the following tests to Android T and
// above
if (IS_AT_LEAST_T) {
defArgsList.addAll(Arrays.asList(new Object[][]{
{MediaFormat.MIMETYPE_VIDEO_AVC, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, true, CODEC_OPTIONAL},
+ new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_HEVC, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, true, CODEC_OPTIONAL},
+ new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_VP9, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, true, CODEC_OPTIONAL},
+ new int[]{240, 360}, true},
{MediaFormat.MIMETYPE_VIDEO_AV1, new int[]{256000}, new int[]{352, 480},
- new int[]{240, 360}, true, CODEC_OPTIONAL},
+ new int[]{240, 360}, true},
}));
}
- return prepareParamList(defArgsList, isEncoder, needAudio, needVideo, false);
+ List<Object[]> argsList = flattenParams(defArgsList);
+ return prepareParamList(argsList, isEncoder, needAudio, needVideo, false);
}
void encodeAndValidate(String inputFile) throws IOException, InterruptedException {
@@ -131,7 +146,7 @@
int colorFormat = mFormats.get(0).getInteger(MediaFormat.KEY_COLOR_FORMAT);
Assume.assumeTrue(hasSupportForColorFormat(mCodecName, mMime, colorFormat));
}
- checkFormatSupport(mCodecName, mMime, true, mFormats, null, mSupportRequirements);
+ checkFormatSupport(mCodecName, mMime, true, mFormats, null, CODEC_OPTIONAL);
setUpSource(inputFile);
mOutputBuff = new OutputManager();
{
diff --git a/tests/media/src/android/mediav2/cts/CodecInfoTest.java b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
index 45f0e12..83a7da7 100644
--- a/tests/media/src/android/mediav2/cts/CodecInfoTest.java
+++ b/tests/media/src/android/mediav2/cts/CodecInfoTest.java
@@ -75,6 +75,10 @@
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) {
continue;
}
+ if (CodecTestBase.codecPrefix != null &&
+ !codecInfo.getName().startsWith(CodecTestBase.codecPrefix)) {
+ continue;
+ }
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
argsList.add(new Object[]{type, codecInfo.getName(), codecInfo});
@@ -160,6 +164,8 @@
@Test
public void testDecoderAvailability() {
Assume.assumeTrue("Test is applicable only for encoders", mCodecInfo.isEncoder());
+ Assume.assumeTrue("Test is applicable for video/audio codecs",
+ mMediaType.startsWith("video/") || mMediaType.startsWith("audio/"));
if (selectCodecs(mMediaType, null, null, true).size() > 0) {
assertTrue("Device advertises support for encoding " + mMediaType +
", but not decoding it",
diff --git a/tests/media/src/android/mediav2/cts/CodecTestBase.java b/tests/media/src/android/mediav2/cts/CodecTestBase.java
index a529d97..3e81576 100644
--- a/tests/media/src/android/mediav2/cts/CodecTestBase.java
+++ b/tests/media/src/android/mediav2/cts/CodecTestBase.java
@@ -581,9 +581,13 @@
abstract class CodecTestBase {
public static final boolean IS_AT_LEAST_R = ApiLevelUtil.isAtLeast(Build.VERSION_CODES.R);
- // TODO (b/223868241) Update the following two to check for Build.VERSION_CODES.TIRAMISU once
+ // 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
- public static final boolean IS_AT_LEAST_T = ApiLevelUtil.isAfter(Build.VERSION_CODES.S_V2);
public static final boolean FIRST_SDK_IS_AT_LEAST_T =
ApiLevelUtil.isFirstApiAfter(Build.VERSION_CODES.S_V2);
private static final String LOG_TAG = CodecTestBase.class.getSimpleName();
@@ -595,6 +599,7 @@
}
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";
static final Map<String, String> codecSelKeyMimeMap = new HashMap<>();
static final Map<String, String> mDefaultEncoders = new HashMap<>();
@@ -649,6 +654,7 @@
static final PackageManager pm = mContext.getPackageManager();
static String mimeSelKeys;
static String codecPrefix;
+ static String mediaTypePrefix;
CodecAsyncHandler mAsyncHandle;
boolean mIsCodecInAsyncMode;
@@ -696,6 +702,7 @@
android.os.Bundle args = InstrumentationRegistry.getArguments();
mimeSelKeys = args.getString(MIME_SEL_KEY);
codecPrefix = args.getString(CODEC_PREFIX_KEY);
+ mediaTypePrefix = args.getString(MEDIA_TYPE_PREFIX_KEY);
mProfileSdrMap.put(MediaFormat.MIMETYPE_VIDEO_AVC, AVC_SDR_PROFILES);
mProfileSdrMap.put(MediaFormat.MIMETYPE_VIDEO_HEVC, HEVC_SDR_PROFILES);
@@ -922,6 +929,9 @@
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && codecInfo.isAlias()) continue;
String[] types = codecInfo.getSupportedTypes();
for (String type : types) {
+ if (mediaTypePrefix != null && !type.startsWith(mediaTypePrefix)) {
+ continue;
+ }
if (!needAudio && type.startsWith("audio/")) continue;
if (!needVideo && type.startsWith("video/")) continue;
if (!mimes.contains(type)) {
@@ -929,6 +939,9 @@
}
}
}
+ if (mediaTypePrefix != null) {
+ return mimes;
+ }
// feature_video_output is not exposed to package manager. Testing for video output
// ports, such as VGA, HDMI, DisplayPort, or a wireless port for display is also not
// direct.
diff --git a/tests/mediapc/common/Android.bp b/tests/mediapc/common/Android.bp
index ca818d7..663dabc 100644
--- a/tests/mediapc/common/Android.bp
+++ b/tests/mediapc/common/Android.bp
@@ -29,3 +29,18 @@
],
plugins: ["auto_value_plugin"],
}
+
+android_test {
+ name: "MediaPerformanceClassCommonTests",
+ compile_multilib: "both",
+ static_libs: [
+ "compatibility-device-util-axt",
+ "MediaPerformanceClassCommon"
+ ],
+ platform_apis: true,
+ srcs: ["tests/src/**/*.java"],
+ test_suites: [
+ "general-tests",
+ ],
+ min_sdk_version: "30",
+}
diff --git a/tests/mediapc/common/AndroidManifest.xml b/tests/mediapc/common/AndroidManifest.xml
new file mode 100644
index 0000000..7d726e9
--- /dev/null
+++ b/tests/mediapc/common/AndroidManifest.xml
@@ -0,0 +1,26 @@
+<?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.mediapc.cts.common">
+
+ <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="30" />
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.mediapc.cts.common"
+ android:label="tests for MediaPerformanceClassCommon" >
+ </instrumentation>
+</manifest>
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
new file mode 100644
index 0000000..ac1905c
--- /dev/null
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassEvaluator.java
@@ -0,0 +1,243 @@
+/*
+ * 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 Licnse.
+ */
+
+package android.mediapc.cts.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assume.assumeTrue;
+
+import android.os.Build;
+
+import androidx.test.filters.SmallTest;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+
+import org.junit.rules.TestName;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Logs a set of measurements and results for defined performance class requirements.
+ */
+public class PerformanceClassEvaluator {
+ private static final String TAG = PerformanceClassEvaluator.class.getSimpleName();
+
+ private final String mTestName;
+ private Set<Requirement> mRequirements;
+
+ public PerformanceClassEvaluator(TestName testName) {
+ Preconditions.checkNotNull(testName);
+ this.mTestName = testName.getMethodName();
+ this.mRequirements = new HashSet<Requirement>();
+ }
+
+ // used for requirements [7.1.1.1/H-1-1], [7.1.1.1/H-2-1]
+ public static class ResolutionRequirement extends Requirement {
+ private static final String TAG = ResolutionRequirement.class.getSimpleName();
+
+ private ResolutionRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setLongResolution(int longResolution) {
+ this.<Integer>setMeasuredValue(RequirementConstants.LONG_RESOLUTION, longResolution);
+ }
+
+ public void setShortResolution(int shortResolution) {
+ this.<Integer>setMeasuredValue(RequirementConstants.SHORT_RESOLUTION, shortResolution);
+ }
+
+ /**
+ * [7.1.1.1/H-1-1] MUST have screen resolution of at least 1080p.
+ */
+ public static ResolutionRequirement createR7_1_1_1__H_1_1() {
+ RequiredMeasurement<Integer> long_resolution = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.LONG_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 1920)
+ .build();
+ RequiredMeasurement<Integer> short_resolution = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.SHORT_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 1080)
+ .build();
+
+ return new ResolutionRequirement(RequirementConstants.R7_1_1_1__H_1_1, long_resolution,
+ short_resolution);
+ }
+
+ /**
+ * [7.1.1.1/H-2-1] MUST have screen resolution of at least 1080p.
+ */
+ public static ResolutionRequirement createR7_1_1_1__H_2_1() {
+ RequiredMeasurement<Integer> long_resolution = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.LONG_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 1920)
+ .build();
+ RequiredMeasurement<Integer> short_resolution = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.SHORT_RESOLUTION)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 1080)
+ .build();
+
+ return new ResolutionRequirement(RequirementConstants.R7_1_1_1__H_2_1, long_resolution,
+ short_resolution);
+ }
+ }
+
+ // used for requirements [7.1.1.3/H-1-1], [7.1.1.3/H-2-1]
+ public static class DensityRequirement extends Requirement {
+ private static final String TAG = DensityRequirement.class.getSimpleName();
+
+ private DensityRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setDisplayDensity(int displayDensity) {
+ this.<Integer>setMeasuredValue(RequirementConstants.DISPLAY_DENSITY, displayDensity);
+ }
+
+ /**
+ * [7.1.1.3/H-1-1] MUST have screen density of at least 400 dpi.
+ */
+ public static DensityRequirement createR7_1_1_3__H_1_1() {
+ RequiredMeasurement<Integer> display_density = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.DISPLAY_DENSITY)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 400)
+ .build();
+
+ return new DensityRequirement(RequirementConstants.R7_1_1_3__H_1_1, display_density);
+ }
+
+ /**
+ * [7.1.1.3/H-2-1] MUST have screen density of at least 400 dpi.
+ */
+ public static DensityRequirement createR7_1_1_3__H_2_1() {
+ RequiredMeasurement<Integer> display_density = RequiredMeasurement
+ .<Integer>builder()
+ .setId(RequirementConstants.DISPLAY_DENSITY)
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.S, 400)
+ .build();
+
+ return new DensityRequirement(RequirementConstants.R7_1_1_3__H_2_1, display_density);
+ }
+ }
+
+ // used for requirements [7.6.1/H-1-1], [7.6.1/H-2-1], [7.6.1/H-3-1]
+ public static class MemoryRequirement extends Requirement {
+ private static final String TAG = MemoryRequirement.class.getSimpleName();
+
+ private MemoryRequirement(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setPhysicalMemory(long physicalMemory) {
+ this.<Long>setMeasuredValue(RequirementConstants.PHYSICAL_MEMORY, physicalMemory);
+ }
+
+ /**
+ * [7.6.1/H-1-1] MUST have at least 6 GB of physical memory.
+ */
+ public static MemoryRequirement createR7_6_1__H_1_1() {
+ RequiredMeasurement<Long> physical_memory = RequiredMeasurement
+ .<Long>builder()
+ .setId(RequirementConstants.PHYSICAL_MEMORY)
+ .setPredicate(RequirementConstants.LONG_GTE)
+ // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB
+ // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
+ .addRequiredValue(Build.VERSION_CODES.R, 5L * 1024L)
+ .build();
+
+ return new MemoryRequirement(RequirementConstants.R7_6_1__H_1_1, physical_memory);
+ }
+
+ /**
+ * [7.6.1/H-2-1] MUST have at least 6 GB of physical memory.
+ */
+ public static MemoryRequirement createR7_6_1__H_2_1() {
+ RequiredMeasurement<Long> physical_memory = RequiredMeasurement
+ .<Long>builder()
+ .setId(RequirementConstants.PHYSICAL_MEMORY)
+ .setPredicate(RequirementConstants.LONG_GTE)
+ // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB
+ // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
+ .addRequiredValue(Build.VERSION_CODES.S, 5L * 1024L)
+ .build();
+
+ return new MemoryRequirement(RequirementConstants.R7_6_1__H_2_1, physical_memory);
+ }
+ }
+
+ private <R extends Requirement> R addRequirement(R req) {
+ if (!this.mRequirements.add(req)) {
+ throw new IllegalStateException("Requirement " + req.id() + " already added");
+ }
+ return req;
+ }
+
+ public ResolutionRequirement addR7_1_1_1__H_1_1() {
+ return this.<ResolutionRequirement>addRequirement(
+ ResolutionRequirement.createR7_1_1_1__H_1_1());
+ }
+
+ public DensityRequirement addR7_1_1_3__H_1_1() {
+ return this.<DensityRequirement>addRequirement(DensityRequirement.createR7_1_1_3__H_1_1());
+ }
+
+ public MemoryRequirement addR7_6_1__H_1_1() {
+ return this.<MemoryRequirement>addRequirement(MemoryRequirement.createR7_6_1__H_1_1());
+ }
+
+ public ResolutionRequirement addR7_1_1_1__H_2_1() {
+ return this.<ResolutionRequirement>addRequirement(
+ ResolutionRequirement.createR7_1_1_1__H_2_1());
+ }
+
+ public DensityRequirement addR7_1_1_3__H_2_1() {
+ return this.<DensityRequirement>addRequirement(DensityRequirement.createR7_1_1_3__H_2_1());
+ }
+
+ public MemoryRequirement addR7_6_1__H_2_1() {
+ return this.<MemoryRequirement>addRequirement(MemoryRequirement.createR7_6_1__H_2_1());
+ }
+
+ public void submitAndCheck() {
+ boolean perfClassMet = true;
+ for (Requirement req: this.mRequirements) {
+ perfClassMet &= req.writeLogAndCheck(this.mTestName);
+ }
+
+ // check performance class
+ assumeTrue("Build.VERSION.MEDIA_PERFORMANCE_CLASS is not declared", Utils.isPerfClass());
+ assertThat(perfClassMet).isTrue();
+
+ this.mRequirements.clear(); // makes sure report isn't submitted twice
+ }
+}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassReportLog.java b/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassReportLog.java
deleted file mode 100644
index 761c276..0000000
--- a/tests/mediapc/common/src/android/mediapc/cts/common/PerformanceClassReportLog.java
+++ /dev/null
@@ -1,141 +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 Licnse.
- */
-
-package android.mediapc.cts.common;
-
-import static android.os.Build.VERSION.MEDIA_PERFORMANCE_CLASS;
-
-import android.os.Build.VERSION_CODES;
-
-import com.google.common.truth.Truth;
-
-import org.junit.Assume;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Logs a set of measurements and results for defined performance class requirements.
- */
-public class PerformanceClassReportLog {
- private final Set<Requirement> mRequirements = new HashSet<>();
-
- public void writeResults() {
- }
-
- public void checkDeclaredPerformanceClass() {
- Assume.assumeTrue("Build.VERSION.MEDIA_PERFORMANCE_CLASS is not declared",
- MEDIA_PERFORMANCE_CLASS > 0);
- for (Requirement r : mRequirements) {
- Truth.assertThat(r.meetsPerformanceClass(MEDIA_PERFORMANCE_CLASS)).isTrue();
- }
- }
-
- private <R extends Requirement> R addRequirement(R r) {
- if (!mRequirements.add(r)) {
- throw new IllegalStateException("Requirement " + r.getId() + " already added");
- }
- return r;
- }
-
-
- // Requirements are specified here in alphabetical order.
-
- /**
- * [5.1/H-1-1] MUST advertise the maximum number of hardware video decoder sessions that can
- * be run concurrently in any codec combination via the
- * CodecCapabilities.getMaxSupportedInstances() and
- * VideoCapabilities.getSupportedPerformancePoints() methods.
- */
- public static final class R5_1_H1_1_1 extends Requirement {
- private final RequiredMeasurement<Integer> maxSupportedCodecInstances = RequiredMeasurement
- .builder(Integer.class)
- .setId("codec_max_supported_instances")
- .setMeetsRequirementPredicate(RequiredMeasurement.gte())
- .addExpectedValue(VERSION_CODES.R, 1)
- .build();
- private final RequiredMeasurement<Integer> mSupportedPerformancePoints = RequiredMeasurement
- .builder(Integer.class)
- .setMeetsRequirementPredicate(RequiredMeasurement.gte())
- .addExpectedValue(VERSION_CODES.R, 1)
- .setId("supported_performance_points")
- .build();
-
- public R5_1_H1_1_1() {
- super("5.1/H-1-1");
- mRequiredMeasurements.add(maxSupportedCodecInstances);
- mRequiredMeasurements.add(mSupportedPerformancePoints);
- }
-
- public void setMaxSupportedCodecInstances(int maxSupportedCodecInstances) {
- this.maxSupportedCodecInstances.setMeasuredValue(maxSupportedCodecInstances);
- }
-
- public void setSupportedPerformancePoints(int supportedPerformancePoints) {
- mSupportedPerformancePoints.setMeasuredValue(supportedPerformancePoints);
- }
- }
-
- public R5_1_H1_1_1 addReqR_1_H1_1_1() {
- R5_1_H1_1_1 r = new R5_1_H1_1_1();
- addRequirement(r);
- return r;
- }
-
- /**
- * [7.6.1/H-1-1] MUST have at least 6 GB of physical memory.
- */
- public SingleRequirement<Long> addR7_6_1_H1_1() {
- RequiredMeasurement physical_memory = RequiredMeasurement
- .builder(Long.class)
- .setId("physical_memory_mb")
- .setMeetsRequirementPredicate(RequiredMeasurement.gte())
- // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB
- // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
- .addExpectedValue(VERSION_CODES.R, 5L * 1024L)
- .build();
- return addRequirement(new SingleRequirement<Long>("7.6.1/H-1-1]", physical_memory));
- }
-
- /**
- * [7.6.1/H-2-1] MUST have at least 6 GB of physical memory.
- */
- public SingleRequirement<Long> addR7_6_1_H2_1() {
- RequiredMeasurement physical_memory = RequiredMeasurement
- .builder(Long.class)
- .setId("physical_memory_mb")
- .setMeetsRequirementPredicate(RequiredMeasurement.gte())
- // Media performance requires 6 GB minimum RAM, but keeping the following to 5 GB
- // as activityManager.getMemoryInfo() returns around 5.4 GB on a 6 GB device.
- .addExpectedValue(VERSION_CODES.R, 5L * 1024L)
- .build();
- return addRequirement(new SingleRequirement<Long>("7.6.1/H-1-1]", physical_memory));
- }
-
- /**
- * [7.6.1/H-2-1] MUST have at least 8 GB of physical memory.
- */
- public SingleRequirement<Long> addR7_6_1_H3_1() {
- RequiredMeasurement physical_memory = RequiredMeasurement
- .builder(Long.class)
- .setId("physical_memory_mb")
- .setMeetsRequirementPredicate(RequiredMeasurement.LONG_GTE)
- // Android T Media performance requires 8 GB min RAM, so setting lower as above
- .addExpectedValue(VERSION_CODES.TIRAMISU, 7L * 1024L)
- .build();
- return new SingleRequirement<Long>("7.6.1/H-1-1]", physical_memory);
- }
-}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
index dab0111..92d3bff 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequiredMeasurement.java
@@ -16,9 +16,14 @@
package android.mediapc.cts.common;
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableMap;
+import java.util.HashMap;
+import java.util.Map;
import java.util.function.BiPredicate;
/**
@@ -26,80 +31,97 @@
*/
@AutoValue
public abstract class RequiredMeasurement<T> {
+ private static final String TAG = RequiredMeasurement.class.getSimpleName();
- private T mMeasuredValue; // Note this is not part of the equals calculations
+ private T measuredValue; // Note this is not part of the equals calculations
- public void setMeasuredValue(T measuredValue) {
- mMeasuredValue = measuredValue;
- }
-
- static <T> Builder<T> builder(Class<T> clazz) {
- return new AutoValue_RequiredMeasurement.Builder<>();
+ public static <T> Builder<T> builder() {
+ return new AutoValue_RequiredMeasurement.Builder<T>();
}
public abstract String id();
/**
* Tests if the measured value satisfies the expected value(eg >=)
- *
* measuredValue, expectedValue
*/
- public abstract BiPredicate<T, T> meetsRequirementPredicate();
-
+ public abstract BiPredicate<T, T> predicate();
/**
* Maps MPC level to the expected value.
*/
public abstract ImmutableMap<Integer, T> expectedValues();
-
- public final Requirement.Result meetsPerformanceClass(
- int mediaPerformanceClass) {
- if (!expectedValues().containsKey(mediaPerformanceClass)) {
- return Requirement.Result.NA;
- }
- return mMeasuredValue == null || !meetsRequirementPredicate().test(mMeasuredValue,
- expectedValues().get(mediaPerformanceClass))
- ? Requirement.Result.UNMET
- : Requirement.Result.MET;
+ public void setMeasuredValue(T measuredValue) {
+ this.measuredValue = measuredValue;
}
@AutoValue.Builder
- public abstract static class Builder<T> {
+ public static abstract class Builder<T> {
public abstract Builder<T> setId(String id);
- public abstract Builder<T> setMeetsRequirementPredicate(BiPredicate<T, T> predicate);
+ public abstract Builder<T> setPredicate(BiPredicate<T, T> predicate);
public abstract ImmutableMap.Builder<Integer, T> expectedValuesBuilder();
- public final Builder<T> addExpectedValue(Integer performanceClass, T expectedValue) {
- expectedValuesBuilder().put(performanceClass, expectedValue);
+ public Builder<T> addRequiredValue(Integer performanceClass, T expectedValue) {
+ this.expectedValuesBuilder().put(performanceClass, expectedValue);
return this;
}
public abstract RequiredMeasurement<T> build();
}
- public static final BiPredicate<Long, Long> LONG_GTE = RequiredMeasurement.gte();
-
- /**
- * Creates a >= predicate.
- *
- * This is convenience method to get the types right.
- */
- public static <T, S extends Comparable<T>> BiPredicate<S, T> gte() {
- return new BiPredicate<S, T>() {
- @Override
- public boolean test(S actual, T expected) {
- return actual.compareTo(expected) >= 0;
- }
-
- @Override
- public String toString() {
- return "Greater than or equal to";
- }
- };
+ private final RequirementConstants.Result meetsPerformanceClass(int mediaPerformanceClass) {
+ if (!this.expectedValues().containsKey(mediaPerformanceClass)) {
+ return RequirementConstants.Result.NA;
+ } else if (this.measuredValue == null || !this.predicate().test(this.measuredValue,
+ this.expectedValues().get(mediaPerformanceClass))) {
+ return RequirementConstants.Result.UNMET;
+ } else {
+ return RequirementConstants.Result.MET;
+ }
}
+ /**
+ * @return map PerfomenaceClass to result if that performance class has been met
+ */
+ public Map<Integer, RequirementConstants.Result> getPerformanceClass() {
+ Map<Integer, RequirementConstants.Result> perfClassResults = new HashMap<>();
+ for (Integer pc: this.expectedValues().keySet()) {
+ perfClassResults.put(pc, this.meetsPerformanceClass(pc));
+ }
+ return perfClassResults;
+ }
+
+ @Override
+ public final String toString() {
+ return "Required Measurement with:"
+ + "\n\tId: " + this.id()
+ + "\n\tPredicate: " + this.predicate()
+ + "\n\tMeasured Value: " + this.measuredValue
+ + "\n\tExpected Values: " + this.expectedValues();
+ }
+
+ public void writeValue(DeviceReportLog log) {
+ if (this.measuredValue instanceof Integer) {
+ log.addValue(this.id(), (int)this.measuredValue, ResultType.NEUTRAL, ResultUnit.NONE);
+ } else if (this.measuredValue instanceof Long) {
+ log.addValue(this.id(), (long)this.measuredValue, ResultType.NEUTRAL, ResultUnit.NONE);
+ } else if (this.measuredValue instanceof Double) {
+ log.addValue(this.id(), (double)this.measuredValue, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ } else if (this.measuredValue instanceof Boolean) {
+ log.addValue(this.id(), (boolean)this.measuredValue, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ } else if (this.measuredValue instanceof String) {
+ log.addValue(this.id(), (String)this.measuredValue, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ } else {
+ // reporting all other types as Strings using toString()
+ log.addValue(this.id(), this.measuredValue.toString(), ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ }
+ }
}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
index d3609f1..10ec1e6 100644
--- a/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Requirement.java
@@ -16,39 +16,120 @@
package android.mediapc.cts.common;
-import java.util.HashSet;
-import java.util.Set;
+import android.util.Log;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.compatibility.common.util.DeviceReportLog;
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.HashMap;
+import java.util.Map;
/**
* Performance Class Requirement maps and req id to a set of {@link RequiredMeasurement}.
*/
public abstract class Requirement {
- final Set<RequiredMeasurement<?>>
- mRequiredMeasurements = new HashSet<>();
- private final String id;
+ private static final String TAG = Requirement.class.getSimpleName();
- Requirement(String id) {
+ protected final ImmutableMap<String, RequiredMeasurement<?>> mRequiredMeasurements;
+ protected final String id;
+
+ protected Requirement(String id, RequiredMeasurement<?>[] reqs) {
this.id = id;
+
+ ImmutableMap.Builder<String, RequiredMeasurement<?>> reqBuilder =
+ ImmutableMap.<String, RequiredMeasurement<?>>builder();
+ for (RequiredMeasurement<?> r: reqs) {
+ reqBuilder.put(r.id(), r);
+ }
+ this.mRequiredMeasurements = reqBuilder.build();
}
- public String getId() {
- return id;
+ public String id() {
+ return this.id;
}
/**
- * Are all required values either NA or MET at this mediaPerformanceClass
+ * Finds the highest performance class where at least one RequiremdMeasurement has result
+ * RequirementConstants.Result.MET and none have RequirementConstants.Result.UNMET
*/
- public boolean meetsPerformanceClass(int mediaPerformanceClass) {
- for (RequiredMeasurement<?> rv : mRequiredMeasurements) {
- if (rv.meetsPerformanceClass(mediaPerformanceClass)
- == Result.UNMET) {
- return false;
+ @VisibleForTesting
+ protected int computePerformanceClass() {
+ Map<Integer, RequirementConstants.Result> overallPerfClassResults = new HashMap<>();
+
+ for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
+ Map<Integer, RequirementConstants.Result> perfClassResults = rm.getPerformanceClass();
+
+ for (Integer pc: perfClassResults.keySet()) {
+ RequirementConstants.Result res = perfClassResults.get(pc);
+
+ // if one or more results are UNMET, mark the performance class as UNMET
+ // otherwise if at least 1 of the results is MET, mark the performance class as MET
+ if (res == RequirementConstants.Result.UNMET) {
+ overallPerfClassResults.put(pc, RequirementConstants.Result.UNMET);
+ } else if (!overallPerfClassResults.containsKey(pc) &&
+ res == RequirementConstants.Result.MET) {
+ overallPerfClassResults.put(pc, RequirementConstants.Result.MET);
+ }
}
}
- return true;
+
+ // report the highest performance class that has been MET
+ int perfClass = 0;
+ for (int pc: overallPerfClassResults.keySet()) {
+ if (overallPerfClassResults.get(pc) == RequirementConstants.Result.MET) {
+ perfClass = Math.max(perfClass, pc);
+ }
+ }
+ return perfClass;
}
- public enum Result {
- NA, MET, UNMET
+ @VisibleForTesting
+ protected boolean checkPerformanceClass(String testName, int reportPerfClass,
+ int expectedPerfClass) {
+ if (reportPerfClass < expectedPerfClass) {
+ Log.w(Requirement.TAG, "Test: " + testName + " reporting invalid performance class " +
+ reportPerfClass + " for requirement " + this.id + " performance class should at " +
+ "least be: " + expectedPerfClass);
+ for (RequiredMeasurement<?> rm: this.mRequiredMeasurements.values()) {
+ Log.w(Requirement.TAG, rm.toString());
+ }
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private boolean checkPerformanceClass(String testName, int reportPerfClass) {
+ return this.checkPerformanceClass(testName, reportPerfClass, Utils.getPerfClass());
+ }
+
+ protected <T> void setMeasuredValue(String measurement, T measuredValue) {
+ RequiredMeasurement<T> rm =
+ (RequiredMeasurement<T>)this.mRequiredMeasurements.get(measurement);
+ rm.setMeasuredValue(measuredValue);
+ }
+
+ /**
+ * @return whether or not the requirement meets the device's specified performance class
+ */
+ public boolean writeLogAndCheck(String testName) {
+ int perfClass = this.computePerformanceClass();
+
+ DeviceReportLog log = new DeviceReportLog(RequirementConstants.REPORT_LOG_NAME, this.id);
+ log.addValue(RequirementConstants.TN_FIELD_NAME, testName, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ for (RequiredMeasurement rm: this.mRequiredMeasurements.values()) {
+ rm.writeValue(log);
+ }
+ log.addValue(RequirementConstants.PC_FIELD_NAME, perfClass, ResultType.NEUTRAL,
+ ResultUnit.NONE);
+ log.submit(InstrumentationRegistry.getInstrumentation());
+
+ return this.checkPerformanceClass(testName, perfClass);
}
}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
new file mode 100644
index 0000000..e67656f
--- /dev/null
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/RequirementConstants.java
@@ -0,0 +1,119 @@
+/*
+ * 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 Licnse.
+ */
+
+package android.mediapc.cts.common;
+
+import android.os.Build;
+
+import java.util.function.BiPredicate;
+
+public class RequirementConstants {
+ private static final String TAG = RequirementConstants.class.getSimpleName();
+
+ public static final String REPORT_LOG_NAME = "CtsMediaPerformanceClassTestCases";
+ public static final String TN_FIELD_NAME = "test_name";
+ public static final String PC_FIELD_NAME = "performance_class";
+
+ public static final String R5_1__H_1_1 = "r5_1__h_1_1"; // 5.1/H-1-1
+ public static final String R5_1__H_1_2 = "r5_1__h_1_2"; // 5.1/H-1-2
+ public static final String R5_1__H_1_3 = "r5_1__h_1_3"; // 5.1/H-1-3
+ public static final String R5_1__H_1_4 = "r5_1__h_1_4"; // 5.1/H-1-4
+ public static final String R5_1__H_1_5 = "r5_1__h_1_5"; // 5.1/H-1-5
+ public static final String R5_1__H_1_6 = "r5_1__h_1_6"; // 5.1/H-1-6
+ public static final String R5_1__H_1_7 = "r5_1__h_1_7"; // 5.1/H-1-7
+ public static final String R5_1__H_1_8 = "r5_1__h_1_8"; // 5.1/H-1-8
+ public static final String R5_3__H_1_1 = "r5_3__h_1_1"; // 5.3/H-1-1
+ public static final String R5_3__H_1_2 = "r5_3__h_1_2"; // 5.3/H-1-2
+ public static final String R5_6__H_1_1 = "r5_6__h_1_1"; // 5.6/H-1-1
+ public static final String R7_5__H_1_1 = "r7_5__h_1_1"; // 7.5/H-1-1
+ public static final String R7_5__H_1_2 = "r7_5__h_1_2"; // 7.5/H-1-2
+ public static final String R7_5__H_1_3 = "r7_5__h_1_3"; // 7.5/H-1-3
+ 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_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
+ public static final String R7_1_1_1__H_2_1 = "r7_1_1_1__h_2_1"; // 7.1.1.1/H-2-1
+ public static final String R7_1_1_3__H_2_1 = "r7_1_1_3__h_2_1"; // 7.1.1.3/H-2-1
+ public static final String R7_6_1__H_2_1 = "r7_6_1__h_2_1"; // 7.6.1/H-2-1
+ public static final String R7_6_1__H_3_1 = "r7_6_1__h_3_1"; // 7.6.1/H-3-1
+ public static final String R8_2__H_1_1 = "r8_2__h_1_1"; // 8.2/H-1-1
+ public static final String R8_2__H_1_2 = "r8_2__h_1_2"; // 8.2/H-1-2
+ public static final String R8_2__H_1_3 = "r8_2__h_1_3"; // 8.2/H-1-3
+ public static final String R8_2__H_1_4 = "r8_2__h_1_4"; // 8.2/H-1-4
+ public static final String R8_2__H_2_1 = "r8_2__h_2_1"; // 8.2/H-2-1
+ public static final String R8_2__H_2_2 = "r8_2__h_2_2"; // 8.2/H-2-2
+ public static final String R8_2__H_2_3 = "r8_2__h_2_3"; // 8.2/H-2-3
+ public static final String R8_2__H_2_4 = "r8_2__h_2_4"; // 8.2/H-2-4
+
+ public static final String MAX_CONCURRENT_SESSIONS = "max_concurrent_sessions";
+ public static final String SUPPORTED_PERFORMANCE_POINTS = "supported_performance_points";
+ public static final String FRAMES_DROPPED = "frame_drops_per_30sec";
+ public static final String FRAME_RATE = "frame_rate";
+ public static final String LONG_RESOLUTION = "long_resolution_pixels";
+ public static final String SHORT_RESOLUTION = "short_resolution_pixels";
+ public static final String DISPLAY_DENSITY = "display_density_dpi";
+ public static final String PHYSICAL_MEMORY = "physical_memory_mb";
+
+ public enum Result {
+ NA, MET, UNMET
+ }
+
+ public static final BiPredicate<Long, Long> LONG_GTE = RequirementConstants.gte();
+ public static final BiPredicate<Integer, Integer> INTEGER_GTE = RequirementConstants.gte();
+ public static final BiPredicate<Integer, Integer> INTEGER_LTE = RequirementConstants.lte();
+
+ /**
+ * Creates a >= predicate.
+ *
+ * This is convenience method to get the types right.
+ */
+ private static <T, S extends Comparable<T>> BiPredicate<S, T> gte() {
+ return new BiPredicate<S, T>() {
+ @Override
+ public boolean test(S actual, T expected) {
+ return actual.compareTo(expected) >= 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Greater than or equal to";
+ }
+ };
+ }
+
+ /**
+ * Creates a <= predicate.
+ */
+ private static <T, S extends Comparable<T>> BiPredicate<S, T> lte() {
+ return new BiPredicate<S, T>() {
+ @Override
+ public boolean test(S actual, T expected) {
+ return actual.compareTo(expected) <= 0;
+ }
+
+ @Override
+ public String toString() {
+ return "Less than or equal to";
+ }
+ };
+ }
+
+ private RequirementConstants() {} // class should not be instantiated
+}
diff --git a/tests/mediapc/common/src/android/mediapc/cts/common/SingleRequirement.java b/tests/mediapc/common/src/android/mediapc/cts/common/SingleRequirement.java
deleted file mode 100644
index 079ad2b..0000000
--- a/tests/mediapc/common/src/android/mediapc/cts/common/SingleRequirement.java
+++ /dev/null
@@ -1,35 +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.mediapc.cts.common;
-
-/**
- * A requirement that has only one {@link RequiredMeasurement}.
- */
-public final class SingleRequirement<T> extends Requirement {
- private final RequiredMeasurement<T> mRequiredMeasurement;
-
- public SingleRequirement(String id,
- RequiredMeasurement<T> requiredMeasurement) {
- super(id);
- mRequiredMeasurements.add(requiredMeasurement);
- mRequiredMeasurement = requiredMeasurement;
- }
-
- public void setValue(T value) {
- mRequiredMeasurement.setMeasuredValue(value);
- }
-}
diff --git a/tests/mediapc/src/android/mediapc/cts/Utils.java b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
similarity index 98%
rename from tests/mediapc/src/android/mediapc/cts/Utils.java
rename to tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
index 6ad6146..a5484ff 100644
--- a/tests/mediapc/src/android/mediapc/cts/Utils.java
+++ b/tests/mediapc/common/src/android/mediapc/cts/common/Utils.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.mediapc.cts;
+package android.mediapc.cts.common;
import static android.util.DisplayMetrics.DENSITY_400;
import static org.junit.Assume.assumeTrue;
@@ -36,7 +36,7 @@
/**
* Test utilities.
*/
-/* package private */ class Utils {
+public class Utils {
private static final int sPc;
private static final String TAG = "PerformanceClassTestUtils";
diff --git a/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java
new file mode 100644
index 0000000..b330724
--- /dev/null
+++ b/tests/mediapc/common/tests/src/android/mediapc/cts/common/RequirementTest.java
@@ -0,0 +1,130 @@
+/*
+ * 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.mediapc.cts.common;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+
+import org.junit.Test;
+
+public class RequirementTest {
+ public static class TestReq extends Requirement {
+ private TestReq(String id, RequiredMeasurement<?> ... reqs) {
+ super(id, reqs);
+ }
+
+ public void setGTEMeasurement(int measure) {
+ this.<Integer>setMeasuredValue("test_measurement_1", measure);
+ }
+
+ public void setLTEMeasurement(int measure) {
+ this.<Integer>setMeasuredValue("test_measurement_2", measure);
+ }
+
+ public static TestReq create() {
+ RequiredMeasurement<Integer> measurement1 = RequiredMeasurement
+ .<Integer>builder()
+ .setId("test_measurement_1")
+ .setPredicate(RequirementConstants.INTEGER_GTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 200)
+ .addRequiredValue(Build.VERSION_CODES.S, 300)
+ .build();
+ RequiredMeasurement<Integer> measurement2 = RequiredMeasurement
+ .<Integer>builder()
+ .setId("test_measurement_2")
+ .setPredicate(RequirementConstants.INTEGER_LTE)
+ .addRequiredValue(Build.VERSION_CODES.R, 500)
+ .addRequiredValue(Build.VERSION_CODES.S, 300)
+ .build();
+
+ return new TestReq("TestReq", measurement1, measurement2);
+ }
+ }
+
+ // used as a base for computePerformanceClass_testCase methods
+ private void testComputePerformanceClass(int gteMeasure, int lteMeasure, int expectedPC) {
+ TestReq testReq = TestReq.create();
+ int pc;
+
+ // both measurements do not meet R
+ testReq.setGTEMeasurement(gteMeasure);
+ testReq.setLTEMeasurement(lteMeasure);
+ pc = testReq.computePerformanceClass();
+ assertThat(pc).isEqualTo(expectedPC);
+ }
+
+ @Test
+ public void computePerformanceClass_bothNotR() {
+ // both measurements do not meet R
+ this.testComputePerformanceClass(100, 600, 0);
+ }
+
+ @Test
+ public void computePerformanceClass_onlyOneR() {
+ // one measurement does not meet R
+ this.testComputePerformanceClass(200, 600, 0);
+ }
+
+ @Test
+ public void computePerformanceClass_bothR() {
+ // both measurements meet R
+ this.testComputePerformanceClass(200, 500, Build.VERSION_CODES.R);
+ }
+
+ @Test
+ public void computePerformanceClass_onlyOneS() {
+ // one measurements does not meet S
+ this.testComputePerformanceClass(200, 100, Build.VERSION_CODES.R);
+ }
+
+ @Test
+ public void computePerformanceClass_bothS() {
+ // both measurements meet S
+ this.testComputePerformanceClass(500, 100, Build.VERSION_CODES.S);
+ }
+
+ // used as a base for checkPerformanceClass_testCase methods
+ private void testCheckPerformanceClass(int testPerfClass, boolean expectedResult) {
+ TestReq testReq = TestReq.create();
+ boolean perfClassMet;
+
+ perfClassMet = testReq.checkPerformanceClass("checkPerformanceClass", testPerfClass, 31);
+ assertThat(perfClassMet).isEqualTo(expectedResult);
+ }
+
+ @Test
+ public void checkPerformanceClass_justBelow() {
+ // just below required perfClass
+ int testPerfClass = 30;
+ this.testCheckPerformanceClass(testPerfClass, false);
+ }
+
+ @Test
+ public void checkPerformanceClass_justAt() {
+ // just at required perfClass
+ int testPerfClass = 31;
+ this.testCheckPerformanceClass(testPerfClass, true);
+ }
+
+ @Test
+ public void checkPerformanceClass_justAbove() {
+ // just above required perfClass
+ int testPerfClass = 32;
+ this.testCheckPerformanceClass(testPerfClass, true);
+ }
+}
\ No newline at end of file
diff --git a/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
index 160fef7..6727a00 100644
--- a/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/AdaptivePlaybackFrameDropTest.java
@@ -16,7 +16,10 @@
package android.mediapc.cts;
+import static org.junit.Assert.assertTrue;
+
import android.media.MediaCodecInfo;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import androidx.test.filters.LargeTest;
@@ -33,8 +36,6 @@
import java.util.Collection;
-import static org.junit.Assert.assertTrue;
-
/**
* The following test class validates the frame drops of AdaptivePlayback for the hardware decoders
* under the load condition (Transcode + Audio Playback).
diff --git a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
index 9168e24..ae09b14 100644
--- a/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/EncoderInitializationLatencyTest.java
@@ -18,6 +18,7 @@
import static android.mediapc.cts.CodecTestBase.selectCodecs;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assume.assumeFalse;
@@ -31,6 +32,7 @@
import android.media.MediaCodecList;
import android.media.MediaFormat;
import android.media.MediaRecorder;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
index 60bc7a7..0bf31d7 100644
--- a/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTest.java
@@ -16,6 +16,9 @@
package android.mediapc.cts;
+import static org.junit.Assert.assertTrue;
+
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import androidx.test.filters.LargeTest;
@@ -32,8 +35,6 @@
import java.util.Collection;
-import static org.junit.Assert.assertTrue;
-
/**
* The following test class validates the frame drops of a playback for the hardware decoders
* under the load condition (Transcode + Audio Playback).
diff --git a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
index 47eae77..66d2be0 100644
--- a/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
+++ b/tests/mediapc/src/android/mediapc/cts/FrameDropTestBase.java
@@ -16,7 +16,16 @@
package android.mediapc.cts;
+import static android.mediapc.cts.CodecTestBase.selectCodecs;
+import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
+
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.util.Log;
import android.view.Surface;
@@ -31,13 +40,6 @@
import java.util.List;
import java.util.Map;
-import static android.mediapc.cts.CodecTestBase.selectCodecs;
-import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assume.assumeFalse;
-import static org.junit.Assume.assumeTrue;
-
public class FrameDropTestBase {
private static final String LOG_TAG = FrameDropTestBase.class.getSimpleName();
static final boolean[] boolStates = {false, true};
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
index d1b65b5..ec27bf9 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiCodecPerfTestBase.java
@@ -17,12 +17,14 @@
package android.mediapc.cts;
import static android.mediapc.cts.CodecTestBase.selectHardwareCodecs;
+
import static org.junit.Assert.assertTrue;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.util.Log;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
index 4899f83..31c069f 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPairPerfTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
index cbdd391..1c1aeea 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiDecoderPerfTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
index b9cc45d..edc2d6c 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPairPerfTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
index 79a684e..3a2f1d9 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiEncoderPerfTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
diff --git a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
index 98c3183..d7d8445 100644
--- a/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/MultiTranscoderPerfTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertTrue;
import android.media.MediaFormat;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.Pair;
import android.view.Surface;
diff --git a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
index 23760fc..8a4b732 100644
--- a/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
+++ b/tests/mediapc/src/android/mediapc/cts/PerformanceClassTest.java
@@ -17,9 +17,12 @@
package android.mediapc.cts;
import static android.media.MediaCodecInfo.CodecCapabilities.FEATURE_SecurePlayback;
-import static android.mediapc.cts.Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB;
-import static android.mediapc.cts.Utils.MIN_MEMORY_PERF_CLASS_T_MB;
+import static android.mediapc.cts.common.Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB;
+import static android.mediapc.cts.common.Utils.MIN_MEMORY_PERF_CLASS_T_MB;
import static android.util.DisplayMetrics.DENSITY_400;
+
+import static com.google.common.truth.Truth.assertThat;
+
import static org.junit.Assert.assertTrue;
import android.app.ActivityManager;
@@ -30,6 +33,8 @@
import android.media.MediaDrm;
import android.media.MediaFormat;
import android.media.UnsupportedSchemeException;
+import android.mediapc.cts.common.PerformanceClassEvaluator;
+import android.mediapc.cts.common.Utils;
import android.os.Build;
import android.util.DisplayMetrics;
import android.util.Log;
@@ -44,7 +49,9 @@
import com.android.compatibility.common.util.ResultUnit;
import org.junit.Assume;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.TestName;
import java.io.IOException;
import java.util.ArrayList;
@@ -58,6 +65,9 @@
private static final UUID WIDEVINE_UUID = new UUID(0xEDEF8BA979D64ACEL, 0xA3C827DCD51D21EDL);
static ArrayList<String> mMimeSecureSupport = new ArrayList<>();
+ @Rule
+ public final TestName mTestName = new TestName();
+
static {
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_AVC);
mMimeSecureSupport.add(MediaFormat.MIMETYPE_VIDEO_HEVC);
@@ -184,18 +194,14 @@
}
@Test
- @CddTest(requirement="2.2.7.3/7.1.1.1,7.1.1.3,7.6.1/H-1-1,H-2-1")
- public void testMinimumMemory() {
+ @CddTest(requirements={
+ "2.2.7.3/7.1.1.1/H-1-1",
+ "2.2.7.3/7.1.1.1/H-2-1",
+ "2.2.7.3/7.1.1.3/H-1-1",
+ "2.2.7.3/7.1.1.3/H-2-1",})
+ public void testMinimumResolutionAndDensity() {
Context context = InstrumentationRegistry.getInstrumentation().getContext();
- // Verify minimum screen density and resolution
- assertMinDpiAndPixels(context, DENSITY_400, 1920, 1080);
- // Verify minimum memory
- assertMinMemoryMb(context);
- }
-
- /** Asserts that the given values conform to the specs in CDD */
- private void assertMinDpiAndPixels(Context context, int minDpi, int minLong, int minShort) {
// Verify display DPI. We only seem to be able to get the primary display.
DisplayMetrics metrics = new DisplayMetrics();
WindowManager windowManager =
@@ -205,52 +211,47 @@
int longPix = Math.max(metrics.widthPixels, metrics.heightPixels);
int shortPix = Math.min(metrics.widthPixels, metrics.heightPixels);
- Log.i(TAG, String.format("minDpi=%d minSize=%dx%dpix", minDpi, minLong, minShort));
Log.i(TAG, String.format("dpi=%d size=%dx%dpix", density, longPix, shortPix));
- if (Utils.isPerfClass()) {
- assertTrue("Display density " + density + " must be at least " + minDpi + "dpi",
- density >= minDpi);
- assertTrue("Display resolution " + longPix + "x" + shortPix + "pix must be at least " +
- minLong + "x" + minShort + "pix",
- longPix >= minLong && shortPix >= minShort);
- } else {
- int pc = density >= minDpi && longPix >= minLong && shortPix >= minShort
- ? Build.VERSION_CODES.S : 0;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs", "Display");
- log.addValue("DisplayDensity", density, ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.addValue("ResolutionLong", longPix, ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.addValue("ResolutionShort", shortPix, ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.3/7.1.1.1,7.1.1.3/H-1-1,H-2-1 performance_class", pc,
- ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.ResolutionRequirement r7_1_1_1__h_1_1 = pce.addR7_1_1_1__H_1_1();
+ PerformanceClassEvaluator.DensityRequirement r7_1_1_3__h_1_1 = pce.addR7_1_1_3__H_1_1();
+ PerformanceClassEvaluator.ResolutionRequirement r7_1_1_1__h_2_1 = pce.addR7_1_1_1__H_2_1();
+ PerformanceClassEvaluator.DensityRequirement r7_1_1_3__h_2_1 = pce.addR7_1_1_3__H_2_1();
+
+ r7_1_1_1__h_1_1.setLongResolution(longPix);
+ r7_1_1_1__h_2_1.setLongResolution(longPix);
+ r7_1_1_1__h_1_1.setShortResolution(shortPix);
+ r7_1_1_1__h_2_1.setShortResolution(shortPix);
+
+ r7_1_1_3__h_1_1.setDisplayDensity(density);
+ r7_1_1_3__h_2_1.setDisplayDensity(density);
+
+ pce.submitAndCheck();
}
- /** Asserts that the given values conform to the specs in CDD 7.6.1 */
- private void assertMinMemoryMb(Context context) {
+ @Test
+ @CddTest(requirements={
+ "2.2.7.3/7.6.1/H-1-1",
+ "2.2.7.3/7.6.1/H-2-1",
+ "2.2.7.3/7.6.1/H-3-1"})
+ public void testMinimumMemory() {
+ Context context = InstrumentationRegistry.getInstrumentation().getContext();
+
+ // Verify minimum memory
ActivityManager activityManager = context.getSystemService(ActivityManager.class);
long totalMemoryMb = getTotalMemory(activityManager) / 1024 / 1024;
Log.i(TAG, String.format("Total device memory = %,d MB", totalMemoryMb));
- if (Utils.isPerfClass()) {
- long minMb = Utils.isTPerfClass() ? MIN_MEMORY_PERF_CLASS_T_MB :
- Utils.MIN_MEMORY_PERF_CLASS_CANDIDATE_MB;
- Log.i(TAG, String.format("Minimum required memory = %,d MB", minMb));
- assertTrue(String.format("Does not meet minimum memory requirements (CDD 7.6.1)."
- + "Found = %d, Minimum = %d", totalMemoryMb, minMb), totalMemoryMb >= minMb);
- } else {
- int pc = 0;
- if (totalMemoryMb >= MIN_MEMORY_PERF_CLASS_T_MB)
- pc = Build.VERSION_CODES.TIRAMISU;
- else if (totalMemoryMb >= MIN_MEMORY_PERF_CLASS_CANDIDATE_MB)
- pc = Build.VERSION_CODES.S;
- DeviceReportLog log = new DeviceReportLog("MediaPerformanceClassLogs", "MinMemory");
- log.addValue("MemoryMB", totalMemoryMb, ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.setSummary("CDD 2.2.7.3/7.6.1/H-1-1,H-2-1 performance_class", pc,
- ResultType.HIGHER_BETTER, ResultUnit.NONE);
- log.submit(InstrumentationRegistry.getInstrumentation());
- }
+
+ PerformanceClassEvaluator pce = new PerformanceClassEvaluator(this.mTestName);
+ PerformanceClassEvaluator.MemoryRequirement r7_6_1_h_1_1 = pce.addR7_6_1__H_1_1();
+ PerformanceClassEvaluator.MemoryRequirement r7_6_1_h_2_1 = pce.addR7_6_1__H_2_1();
+
+ r7_6_1_h_1_1.setPhysicalMemory(totalMemoryMb);
+ r7_6_1_h_2_1.setPhysicalMemory(totalMemoryMb);
+
+ pce.submitAndCheck();
}
/**
diff --git a/tests/quickaccesswallet/AndroidManifest.xml b/tests/quickaccesswallet/AndroidManifest.xml
index 5c48a39..6f0b083 100755
--- a/tests/quickaccesswallet/AndroidManifest.xml
+++ b/tests/quickaccesswallet/AndroidManifest.xml
@@ -78,19 +78,6 @@
android:resource="@xml/quickaccesswallet_configuration"/>;
</service>
- <service android:name="android.quickaccesswallet.UseTargetActivityForQuickAccessWalletService"
- android:enabled="false"
- android:label="@string/app_name"
- android:icon="@drawable/android"
- android:permission="android.permission.BIND_QUICK_ACCESS_WALLET_SERVICE"
- android:exported="true">
- <intent-filter>
- <action android:name="android.service.quickaccesswallet.QuickAccessWalletService"/>
- <category android:name="android.intent.category.DEFAULT"/>
- </intent-filter>
- <meta-data android:name="android.quickaccesswallet"
- android:resource="@xml/quickaccesswallet_configuration_usetargetactivityforquickaccess"/>;
- </service>
<service android:name="android.quickaccesswallet.QuickAccessWalletDelegateTargetActivityService"
android:enabled="false"
@@ -103,7 +90,7 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
<meta-data android:name="android.quickaccesswallet"
- android:resource="@xml/quickaccesswallet_configuration_delegatetargetactivity"/>;
+ android:resource="@xml/quickaccesswallet_configuration"/>
</service>
diff --git a/tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_usetargetactivityforquickaccess.xml b/tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_usetargetactivityforquickaccess.xml
deleted file mode 100644
index 9f91f02..0000000
--- a/tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_usetargetactivityforquickaccess.xml
+++ /dev/null
@@ -1,20 +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.
- -->
-
-<quickaccesswallet-service
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:targetActivity="android.quickaccesswallet.QuickAccessWalletActivity"
- android:useTargetActivityForQuickAccess="true"/>
\ No newline at end of file
diff --git a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
index 03a817e..8a5d528 100755
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
+++ b/tests/quickaccesswallet/src/android/quickaccesswallet/cts/QuickAccessWalletClientTest.java
@@ -35,7 +35,6 @@
import android.quickaccesswallet.QuickAccessWalletSettingsActivity;
import android.quickaccesswallet.TestHostApduService;
import android.quickaccesswallet.TestQuickAccessWalletService;
-import android.quickaccesswallet.UseTargetActivityForQuickAccessWalletService;
import android.service.quickaccesswallet.GetWalletCardsError;
import android.service.quickaccesswallet.GetWalletCardsRequest;
import android.service.quickaccesswallet.GetWalletCardsResponse;
@@ -106,8 +105,6 @@
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
setServiceState(NoPermissionQuickAccessWalletService.class,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
- setServiceState(UseTargetActivityForQuickAccessWalletService.class,
- PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
setServiceState(QuickAccessWalletDelegateTargetActivityService.class,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
TestQuickAccessWalletService.resetStaticFields();
@@ -141,31 +138,10 @@
}
@Test
- public void testUseTargetActivityForQuickAccess_isFalse() {
- QuickAccessWalletClient client = QuickAccessWalletClient.create(mContext);
-
- assertThat(client.useTargetActivityForQuickAccess()).isFalse();
- }
-
- @Test
- public void testUseTargetActivityForQuickAccess_serviceSpecifies_isTrue() {
- setServiceState(UseTargetActivityForQuickAccessWalletService.class,
- PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- setServiceState(TestQuickAccessWalletService.class,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
-
- QuickAccessWalletClient client = QuickAccessWalletClient.create(mContext);
-
- assertThat(client.useTargetActivityForQuickAccess()).isTrue();
- }
-
- @Test
public void testGetWalletPendingIntent_serviceWithOverride_notNull_ableToSend()
throws Exception {
setServiceState(QuickAccessWalletDelegateTargetActivityService.class,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
- setServiceState(UseTargetActivityForQuickAccessWalletService.class,
- PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
setServiceState(TestQuickAccessWalletService.class,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
diff --git a/tests/tests/app/src/android/app/cts/PictureInPictureParamsBuilderTest.java b/tests/tests/app/src/android/app/cts/PictureInPictureParamsBuilderTest.java
index c9f1cf2..a99f176 100644
--- a/tests/tests/app/src/android/app/cts/PictureInPictureParamsBuilderTest.java
+++ b/tests/tests/app/src/android/app/cts/PictureInPictureParamsBuilderTest.java
@@ -60,7 +60,7 @@
params = builder.build();
assertTrue(Float.compare(0f, params.getAspectRatioFloat()) == 0);
- assertNull(params.getActions());
+ assertTrue(params.getActions().isEmpty());
assertNull(params.getSourceRectHint());
}
diff --git a/tests/tests/bionic/Android.bp b/tests/tests/bionic/Android.bp
index e9b59d1..2a5a9fa 100644
--- a/tests/tests/bionic/Android.bp
+++ b/tests/tests/bionic/Android.bp
@@ -183,6 +183,7 @@
"libtest_init_fini_order_root",
"libtest_init_fini_order_root2",
"libtest_invalid-empty_shdr_table",
+ "libtest_invalid-local-tls",
"libtest_invalid-rw_load_segment",
"libtest_invalid-textrels",
"libtest_invalid-textrels2",
diff --git a/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
index 163bd057..14891e0 100644
--- a/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
+++ b/tests/tests/bluetooth/bluetoothTestUtilLib/src/android/bluetooth/cts/BTAdapterUtils.java
@@ -26,6 +26,7 @@
import androidx.test.InstrumentationRegistry;
+import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
@@ -92,7 +93,7 @@
Log.d(TAG, "Enabling bluetooth adapter");
UiAutomation auto = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- Set<String> permissions = auto.getAdoptedShellPermissions();
+ Set<String> permissions = new HashSet(auto.getAdoptedShellPermissions());
Set<String> savedPermissions = Set.copyOf(permissions);
permissions.add(android.Manifest.permission.NETWORK_SETTINGS);
auto.adoptShellPermissionIdentity(permissions.toArray(new String[permissions.size()]));
@@ -129,7 +130,7 @@
Log.d(TAG, "Disabling bluetooth adapter");
UiAutomation auto = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- Set<String> permissions = auto.getAdoptedShellPermissions();
+ Set<String> permissions = new HashSet(auto.getAdoptedShellPermissions());
Set<String> savedPermissions = Set.copyOf(permissions);
permissions.add(android.Manifest.permission.NETWORK_SETTINGS);
auto.adoptShellPermissionIdentity(permissions.toArray(new String[permissions.size()]));
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
index 431621b..191c4a2 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothA2dpTest.java
@@ -151,7 +151,7 @@
BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
- assertNull(mBluetoothA2dp.getCodecStatus(testDevice));
+ assertThrows(SecurityException.class, () -> mBluetoothA2dp.getCodecStatus(testDevice));
assertThrows(IllegalArgumentException.class, () -> {
mBluetoothA2dp.getCodecStatus(null);
});
@@ -163,14 +163,6 @@
assertTrue(waitForProfileConnect());
assertNotNull(mBluetoothA2dp);
- BluetoothDevice testDevice = mAdapter.getRemoteDevice("00:11:22:AA:BB:CC");
-
- BluetoothCodecConfig codecConfig = new BluetoothCodecConfig.Builder()
- .setCodecType(BluetoothCodecConfig.SOURCE_CODEC_TYPE_SBC)
- .setCodecPriority(0)
- .build();
- mBluetoothA2dp.setCodecConfigPreference(testDevice, codecConfig);
- assertNull(mBluetoothA2dp.getCodecStatus(testDevice));
assertThrows(IllegalArgumentException.class, () -> {
mBluetoothA2dp.setCodecConfigPreference(null, null);
});
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
index 19b206f..54766fe 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothAdapterTest.java
@@ -483,14 +483,21 @@
public void test_requestControllerActivityEnergyInfo() {
if (!mHasBluetooth) return;
- BluetoothAdapter.OnBluetoothActivityEnergyInfoListener listener =
- new BluetoothAdapter.OnBluetoothActivityEnergyInfoListener() {
+ BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback callback =
+ new BluetoothAdapter.OnBluetoothActivityEnergyInfoCallback() {
@Override
- public void onBluetoothActivityEnergyInfo(BluetoothActivityEnergyInfo info) {}
+ public void onBluetoothActivityEnergyInfoAvailable(
+ BluetoothActivityEnergyInfo info) {
+ assertNotNull(info);
+ }
+
+ @Override
+ public void onBluetoothActivityEnergyInfoError(int errorCode) {}
};
+
// Verify parameter
assertThrows(NullPointerException.class,
- () -> mAdapter.requestControllerActivityEnergyInfo(null, listener));
+ () -> mAdapter.requestControllerActivityEnergyInfo(null, callback));
}
public void test_clearBluetooth() {
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java
deleted file mode 100644
index e7f59b8..0000000
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAdvertiserTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- * 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.
- */
-
-package android.bluetooth.cts;
-
-import static android.Manifest.permission.BLUETOOTH_ADVERTISE;
-import static android.Manifest.permission.BLUETOOTH_CONNECT;
-import static android.bluetooth.le.AdvertisingSetCallback.ADVERTISE_SUCCESS;
-
-import android.app.UiAutomation;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.le.AdvertisingSet;
-import android.bluetooth.le.AdvertisingSetCallback;
-import android.bluetooth.le.AdvertisingSetParameters;
-import android.bluetooth.le.BluetoothLeAdvertiser;
-import android.content.pm.PackageManager;
-import android.os.Handler;
-import android.os.Looper;
-import android.test.AndroidTestCase;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
-
-public class BluetoothLeAdvertiserTest extends AndroidTestCase {
- private static final int TIMEOUT_MS = 5000;
- private static final AdvertisingSetParameters ADVERTISING_SET_PARAMETERS =
- new AdvertisingSetParameters.Builder().setLegacyMode(true).build();
-
- private boolean mHasBluetooth;
- private UiAutomation mUiAutomation;
- private BluetoothAdapter mAdapter;
- private BluetoothLeAdvertiser mAdvertiser;
- private TestAdvertisingSetCallback mCallback;
-
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- mHasBluetooth = getContext().getPackageManager().hasSystemFeature(
- PackageManager.FEATURE_BLUETOOTH);
- if (!mHasBluetooth) return;
- mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
- mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE);
-
- BluetoothManager manager = getContext().getSystemService(BluetoothManager.class);
- mAdapter = manager.getAdapter();
- assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
- mAdvertiser = mAdapter.getBluetoothLeAdvertiser();
- mCallback = new TestAdvertisingSetCallback();
- }
-
- public void tearDown() throws Exception {
- super.tearDown();
- if (mHasBluetooth) {
- mAdvertiser.stopAdvertisingSet(mCallback);
- assertTrue(mCallback.mAdvertisingSetStoppedLatch.await(TIMEOUT_MS,
- TimeUnit.MILLISECONDS));
- assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
- mAdvertiser = null;
- mAdapter = null;
- }
- }
-
- public void test_startAdvertisingSetWithCallbackAndHandler() throws InterruptedException {
- mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
- mCallback, new Handler(Looper.getMainLooper()));
- assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
- assertNotNull(mCallback.mAdvertisingSet);
- }
-
-
- public void test_startAdvertisingSetWithDurationAndCallback() throws InterruptedException {
- mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
- 0, 0, mCallback);
- assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
- assertNotNull(mCallback.mAdvertisingSet);
- }
-
-
- public void test_startAdvertisingSetWithDurationCallbackAndHandler()
- throws InterruptedException {
- mAdvertiser.startAdvertisingSet(ADVERTISING_SET_PARAMETERS, null, null, null, null,
- 0, 0, mCallback, new Handler(Looper.getMainLooper()));
- assertTrue(mCallback.mAdvertisingSetStartedLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
- assertEquals(ADVERTISE_SUCCESS, mCallback.mAdvertisingSetStartedStatus.get());
- assertNotNull(mCallback.mAdvertisingSet);
- }
-
- private static class TestAdvertisingSetCallback extends AdvertisingSetCallback {
- public CountDownLatch mAdvertisingSetStartedLatch = new CountDownLatch(1);
- public CountDownLatch mAdvertisingEnabledLatch = new CountDownLatch(1);
- public CountDownLatch mAdvertisingDisabledLatch = new CountDownLatch(1);
- public CountDownLatch mAdvertisingSetStoppedLatch = new CountDownLatch(1);
-
- public AtomicInteger mAdvertisingSetStartedStatus = new AtomicInteger();
- public AtomicInteger mAdvertisingEnabledStatus = new AtomicInteger();
- public AtomicInteger mAdvertisingDisabledStatus = new AtomicInteger();
-
- public AtomicReference<AdvertisingSet> mAdvertisingSet = new AtomicReference();
-
- @Override
- public void onAdvertisingSetStarted(AdvertisingSet advertisingSet, int txPower,
- int status) {
- super.onAdvertisingSetStarted(advertisingSet, txPower, status);
- mAdvertisingSetStartedStatus.set(status);
- mAdvertisingSet.set(advertisingSet);
- mAdvertisingSetStartedLatch.countDown();
- }
-
- @Override
- public void onAdvertisingEnabled(AdvertisingSet advertisingSet, boolean enable,
- int status) {
- super.onAdvertisingEnabled(advertisingSet, enable, status);
- if (enable) {
- mAdvertisingEnabledStatus.set(status);
- mAdvertisingEnabledLatch.countDown();
- } else {
- mAdvertisingDisabledStatus.set(status);
- mAdvertisingDisabledLatch.countDown();
- }
- }
-
- @Override
- public void onAdvertisingSetStopped(AdvertisingSet advertisingSet) {
- super.onAdvertisingSetStopped(advertisingSet);
- mAdvertisingSetStoppedLatch.countDown();
- }
-
- }
-}
diff --git a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
index 45083c5..be7c80b 100644
--- a/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
+++ b/tests/tests/bluetooth/src/android/bluetooth/cts/BluetoothLeAudioCodecTest.java
@@ -163,6 +163,26 @@
assertEquals(octetsPerFrame, leAudioCodecConfig.getOctetsPerFrame());
}
+ public void testGetMinOctetsPerFrame() {
+ final int minOctetsPerFrame = 100;
+ BluetoothLeAudioCodecConfig leAudioCodecConfig =
+ new BluetoothLeAudioCodecConfig.Builder()
+ .setMinOctetsPerFrame(minOctetsPerFrame)
+ .build();
+
+ assertEquals(minOctetsPerFrame, leAudioCodecConfig.getMinOctetsPerFrame());
+ }
+
+ public void testGetMaxOctetsPerFrame() {
+ final int maxOctetsPerFrame = 100;
+ BluetoothLeAudioCodecConfig leAudioCodecConfig =
+ new BluetoothLeAudioCodecConfig.Builder()
+ .setMaxOctetsPerFrame(maxOctetsPerFrame)
+ .build();
+
+ assertEquals(maxOctetsPerFrame, leAudioCodecConfig.getMaxOctetsPerFrame());
+ }
+
public void testDescribeContents() {
BluetoothLeAudioCodecConfig leAudioCodecConfig =
new BluetoothLeAudioCodecConfig.Builder().build();
@@ -202,6 +222,8 @@
public void testBuilderWithExistingObject() {
final int octetsPerFrame = 100;
+ final int minOctectsPerFrame = 50;
+ final int maxOctectsPerFrame = 150;
BluetoothLeAudioCodecConfig oriLeAudioCodecConfig =
new BluetoothLeAudioCodecConfig.Builder()
.setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
@@ -211,6 +233,8 @@
.setChannelCount(BluetoothLeAudioCodecConfig.CHANNEL_COUNT_2)
.setFrameDuration(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500)
.setOctetsPerFrame(octetsPerFrame)
+ .setMinOctetsPerFrame(minOctectsPerFrame)
+ .setMaxOctetsPerFrame(maxOctectsPerFrame)
.build();
BluetoothLeAudioCodecConfig toBuilderCodecConfig =
new BluetoothLeAudioCodecConfig.Builder(oriLeAudioCodecConfig).build();
@@ -227,5 +251,7 @@
assertEquals(BluetoothLeAudioCodecConfig.FRAME_DURATION_7500,
toBuilderCodecConfig.getFrameDuration());
assertEquals(octetsPerFrame, toBuilderCodecConfig.getOctetsPerFrame());
+ assertEquals(minOctectsPerFrame, toBuilderCodecConfig.getMinOctetsPerFrame());
+ assertEquals(maxOctectsPerFrame, toBuilderCodecConfig.getMaxOctetsPerFrame());
}
}
diff --git a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
index d13c2b7..36c1217 100644
--- a/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
+++ b/tests/tests/carrierapi/src/android/carrierapi/cts/NetworkScanApiTest.java
@@ -218,11 +218,11 @@
.adoptShellPermissionIdentity();
}
try {
- mNetworkScan =
- mTelephonyManager.requestNetworkScan(
- true, mNetworkScanRequest,
- AsyncTask.SERIAL_EXECUTOR,
- mNetworkScanCallback);
+ mNetworkScan = mTelephonyManager.requestNetworkScan(
+ TelephonyManager.INCLUDE_LOCATION_DATA_NONE,
+ mNetworkScanRequest,
+ AsyncTask.SERIAL_EXECUTOR,
+ mNetworkScanCallback);
if (mNetworkScan == null) {
mNetworkScanStatus = EVENT_SCAN_DENIED;
setReady(true);
diff --git a/tests/tests/content/Android.bp b/tests/tests/content/Android.bp
index b78ccbe..f937a77 100644
--- a/tests/tests/content/Android.bp
+++ b/tests/tests/content/Android.bp
@@ -77,6 +77,25 @@
"src/**/*.kt",
"BinderPermissionTestService/**/I*.aidl",
],
+ required: [
+ "HelloWorld5",
+ "HelloWorld5Profileable",
+ "HelloWorld7",
+ "HelloWorldNoAppStorage",
+ "HelloWorldResHardening",
+ "HelloWorldSdk1",
+ "HelloWorldSdk1DifferentSigner",
+ "HelloWorldSdk1MajorVersion2",
+ "HelloWorldSdk1Updated",
+ "HelloWorldSdk2",
+ "HelloWorldSdk2Updated",
+ "HelloWorldSdk3UsingSdk1",
+ "HelloWorldSdk3UsingSdk1And2",
+ "HelloWorldShell",
+ "HelloWorldUsingSdk1",
+ "HelloWorldUsingSdk1And2",
+ "HelloWorldUsingSdk3",
+ ],
data: [
// v1/v2/v3/v4 signed version of android.appsecurity.cts.tinyapp to keep checksums stable
"data/CtsPkgInstallTinyAppV1.apk",
diff --git a/tests/tests/content/src/android/content/cts/IntentTest.java b/tests/tests/content/src/android/content/cts/IntentTest.java
index 29d207e..dcd2fe6 100644
--- a/tests/tests/content/src/android/content/cts/IntentTest.java
+++ b/tests/tests/content/src/android/content/cts/IntentTest.java
@@ -16,6 +16,8 @@
package android.content.cts;
+import static org.junit.Assert.assertArrayEquals;
+
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
@@ -29,6 +31,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcel;
+import android.os.Parcelable;
import android.os.ServiceManager;
import android.platform.test.annotations.AppModeFull;
import android.provider.Contacts.People;
@@ -154,6 +157,34 @@
assertEquals(expected, target);
}
+ public void testGetParcelableArrayListExtraTypeSafe_withMismatchingType_returnsNull() {
+ final ArrayList<TestParcelable> original = new ArrayList<>();
+ original.add(new TestParcelable(0));
+ mIntent.putParcelableArrayListExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertNull(mIntent.getParcelableArrayListExtra(TEST_EXTRA_NAME, Intent.class));
+ }
+
+ public void testGetParcelableArrayListExtraTypeSafe_withMatchingType_returnsObject() {
+ final ArrayList<TestParcelable> original = new ArrayList<>();
+ original.add(new TestParcelable(0));
+ original.add(new TestParcelable(1));
+ mIntent.putParcelableArrayListExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertEquals(original,
+ mIntent.getParcelableArrayListExtra(TEST_EXTRA_NAME, TestParcelable.class));
+ }
+
+ public void testGetParcelableArrayListExtraTypeSafe_withBaseType_returnsObject() {
+ final ArrayList<TestParcelable> original = new ArrayList<>();
+ original.add(new TestParcelable(0));
+ original.add(new TestParcelable(1));
+ mIntent.putParcelableArrayListExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertEquals(original,
+ mIntent.getParcelableArrayListExtra(TEST_EXTRA_NAME, Parcelable.class));
+ }
+
public void testFilterHashCode() {
mIntent.filterHashCode();
}
@@ -609,6 +640,23 @@
assertEquals(expected, mIntent.getParcelableExtra(TEST_EXTRA_NAME));
}
+ public void testGetParcelableExtraTypeSafe_withMismatchingType_returnsNull() {
+ mIntent.putExtra(TEST_EXTRA_NAME, new TestParcelable(42));
+ assertNull(mIntent.getParcelableExtra(TEST_EXTRA_NAME, Intent.class));
+ }
+
+ public void testGetParcelableExtraTypeSafe_withMatchingType_returnsObject() {
+ final TestParcelable original = new TestParcelable(42);
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ assertEquals(original, mIntent.getParcelableExtra(TEST_EXTRA_NAME, TestParcelable.class));
+ }
+
+ public void testGetParcelableExtraTypeSafe_withBaseType_returnsObject() {
+ final TestParcelable original = new TestParcelable(42);
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ assertEquals(original, mIntent.getParcelableExtra(TEST_EXTRA_NAME, Parcelable.class));
+ }
+
public void testAccessAction() {
mIntent.setAction(TEST_ACTION);
assertEquals(TEST_ACTION, mIntent.getAction());
@@ -736,7 +784,29 @@
public void testGetParcelableArrayExtra() {
final Intent[] expected = { new Intent(TEST_ACTION), new Intent(mContext, MockActivity.class) };
mIntent.putExtra(TEST_EXTRA_NAME, expected);
- assertEquals(expected, mIntent.getParcelableArrayExtra(TEST_EXTRA_NAME));
+ assertArrayEquals(expected, mIntent.getParcelableArrayExtra(TEST_EXTRA_NAME));
+ }
+
+ public void testGetParcelableArrayExtraTypeSafe_withMismatchingType_returnsNull() {
+ mIntent.putExtra(TEST_EXTRA_NAME, new TestParcelable[] {new TestParcelable(42)});
+ roundtrip();
+ assertNull(mIntent.getParcelableArrayExtra(TEST_EXTRA_NAME, Intent.class));
+ }
+
+ public void testGetParcelableArrayExtraTypeSafe_withMatchingType_returnsObject() {
+ final TestParcelable[] original = { new TestParcelable(1), new TestParcelable(2) };
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertArrayEquals(original,
+ mIntent.getParcelableArrayExtra(TEST_EXTRA_NAME, TestParcelable.class));
+ }
+
+ public void testGetParcelableArrayExtraTypeSafe_withBaseType_returnsObject() {
+ final TestParcelable[] original = { new TestParcelable(1), new TestParcelable(2) };
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertArrayEquals(original,
+ mIntent.getParcelableArrayExtra(TEST_EXTRA_NAME, Parcelable.class));
}
public void testResolveActivityEmpty() {
@@ -1811,6 +1881,26 @@
assertEquals(expected.Name, target.Name);
}
+ public void testGetSerializableExtraTypeSafe_withMismatchingType_returnsNull() {
+ mIntent.putExtra(TEST_EXTRA_NAME, new TestSerializable());
+ roundtrip();
+ assertNull(mIntent.getSerializableExtra(TEST_EXTRA_NAME, Long.class));
+ }
+
+ public void testGetSerializableExtraTypeSafe_withMatchingType_returnsObject() {
+ String original = "Hello, World!";
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertEquals(original, mIntent.getSerializableExtra(TEST_EXTRA_NAME, String.class));
+ }
+
+ public void testGetSerializableExtraTypeSafe_withBaseType_returnsObject() {
+ String original = "Hello, World!";
+ mIntent.putExtra(TEST_EXTRA_NAME, original);
+ roundtrip();
+ assertEquals(original, mIntent.getSerializableExtra(TEST_EXTRA_NAME, Serializable.class));
+ }
+
public void testReplaceExtras() {
Bundle extras = new Bundle();
String bundleKey = "testKey";
@@ -1840,8 +1930,66 @@
assertEquals("foo/bar", Intent.normalizeMimeType(" foo/bar "));
}
+ private void roundtrip() {
+ Parcel p = Parcel.obtain();
+ p.writeParcelable(mIntent, 0);
+ p.setDataPosition(0);
+ mIntent = p.readParcelable(getClass().getClassLoader(), Intent.class);
+ mIntent.setExtrasClassLoader(getClass().getClassLoader());
+ }
+
private static class TestSerializable implements Serializable {
static final long serialVersionUID = 1l;
public String Name;
}
+
+ private static class TestParcelable implements Parcelable {
+ public final int value;
+
+ TestParcelable(int value) {
+ this.value = value;
+ }
+
+ protected TestParcelable(Parcel in) {
+ value = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(value);
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ if (this == other) {
+ return true;
+ }
+ if (!(other instanceof TestParcelable)) {
+ return false;
+ }
+ TestParcelable that = (TestParcelable) other;
+ return value == that.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return value;
+ }
+
+ public static final Creator<TestParcelable> CREATOR = new Creator<TestParcelable>() {
+ @Override
+ public TestParcelable createFromParcel(Parcel in) {
+ return new TestParcelable(in);
+ }
+ @Override
+ public TestParcelable[] newArray(int size) {
+ return new TestParcelable[size];
+ }
+ };
+ }
}
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
index 38ca81d..2129f3b 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandMultiUserTest.kt
@@ -25,6 +25,8 @@
import android.content.pm.PackageManager
import android.content.pm.cts.PackageManagerShellCommandTest.FullyRemovedBroadcastReceiver
import android.content.pm.cts.util.AbandonAllPackageSessionsRule
+import android.os.Handler
+import android.os.HandlerThread
import android.platform.test.annotations.AppModeFull
import androidx.test.InstrumentationRegistry
import com.android.bedstead.harrier.BedsteadJUnit4
@@ -70,6 +72,8 @@
private val context: Context = InstrumentationRegistry.getContext()
private val uiAutomation: UiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation()
+
+ private var backgroundThread = HandlerThread("PackageManagerShellCommandMultiUserTest")
}
private lateinit var primaryUser: UserReference
@@ -201,6 +205,10 @@
"install-incremental"
) installTypeString: String
) {
+ if (!backgroundThread.isAlive) {
+ backgroundThread.start()
+ }
+ val backgroundHandler = Handler(backgroundThread.getLooper())
installExistingPackageAsUser(context.packageName, secondaryUser)
installPackage(TEST_HW5, installTypeString)
assertTrue(isAppInstalledForUser(context.packageName, primaryUser))
@@ -215,7 +223,11 @@
intentFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED)
intentFilter.addDataScheme("package")
context.registerReceiver(
- broadcastReceiverForPrimaryUser, intentFilter, RECEIVER_EXPORTED
+ broadcastReceiverForPrimaryUser,
+ intentFilter,
+ null,
+ backgroundHandler,
+ RECEIVER_EXPORTED
)
uiAutomation.adoptShellPermissionIdentity(
Manifest.permission.INTERACT_ACROSS_USERS,
@@ -223,21 +235,63 @@
)
try {
context.createContextAsUser(secondaryUser.userHandle(), 0).registerReceiver(
- broadcastReceiverForSecondaryUser, intentFilter, RECEIVER_EXPORTED
+ broadcastReceiverForSecondaryUser,
+ intentFilter,
+ null,
+ backgroundHandler,
+ RECEIVER_EXPORTED
)
} finally {
uiAutomation.dropShellPermissionIdentity()
}
// Verify that uninstall with "keep data" doesn't send the broadcast
uninstallPackageWithKeepData(TEST_APP_PACKAGE, secondaryUser)
- assertFalse(broadcastReceiverForSecondaryUser.isBroadcastReceived)
+ broadcastReceiverForSecondaryUser.assertBroadcastNotReceived()
installExistingPackageAsUser(TEST_APP_PACKAGE, secondaryUser)
// Verify that uninstall on a specific user only sends the broadcast to the user
uninstallPackageAsUser(TEST_APP_PACKAGE, secondaryUser)
- assertTrue(broadcastReceiverForSecondaryUser.isBroadcastReceived)
- assertFalse(broadcastReceiverForPrimaryUser.isBroadcastReceived)
+ broadcastReceiverForSecondaryUser.assertBroadcastReceived()
+ broadcastReceiverForPrimaryUser.assertBroadcastNotReceived()
uninstallPackageSilently(TEST_APP_PACKAGE)
- assertTrue(broadcastReceiverForPrimaryUser.isBroadcastReceived)
+ broadcastReceiverForPrimaryUser.assertBroadcastReceived()
+ }
+
+ @Test
+ fun testListPackageDefaultAllUsers(
+ @StringTestParameter(
+ "install",
+ "install-streaming",
+ "install-incremental"
+ ) installTypeString: String
+ ) {
+ installPackageAsUser(TEST_HW5, primaryUser, installTypeString)
+ assertTrue(isAppInstalledForUser(TEST_APP_PACKAGE, primaryUser))
+ assertFalse(isAppInstalledForUser(TEST_APP_PACKAGE, secondaryUser))
+ var out = SystemUtil.runShellCommand(
+ "pm list packages -U --user ${primaryUser.id()} $TEST_APP_PACKAGE"
+ ).replace("\n", "")
+ assertTrue(out.split(":").last().split(",").size == 1)
+ out = SystemUtil.runShellCommand(
+ "pm list packages -U --user ${secondaryUser.id()} $TEST_APP_PACKAGE"
+ ).replace("\n", "")
+ assertEquals("", out)
+ out = SystemUtil.runShellCommand("pm list packages -U $TEST_APP_PACKAGE")
+ .replace("\n", "")
+ assertTrue(out.split(":").last().split(",").size == 1)
+ installExistingPackageAsUser(TEST_APP_PACKAGE, secondaryUser)
+ assertTrue(isAppInstalledForUser(TEST_APP_PACKAGE, primaryUser))
+ assertTrue(isAppInstalledForUser(TEST_APP_PACKAGE, secondaryUser))
+ out = SystemUtil.runShellCommand("pm list packages -U $TEST_APP_PACKAGE")
+ .replace("\n", "")
+ assertTrue(out.split(":").last().split(",").size == 2)
+ out = SystemUtil.runShellCommand(
+ "pm list packages -U --user ${primaryUser.id()} $TEST_APP_PACKAGE"
+ ).replace("\n", "")
+ assertTrue(out.split(":").last().split(",").size == 1)
+ out = SystemUtil.runShellCommand(
+ "pm list packages -U --user ${secondaryUser.id()} $TEST_APP_PACKAGE"
+ ).replace("\n", "")
+ assertTrue(out.split(":").last().split(",").size == 1)
}
private fun getFirstInstallTimeAsUser(packageName: String, user: UserReference) =
diff --git a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
index d923ad7..ffe417c 100644
--- a/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
+++ b/tests/tests/content/src/android/content/pm/cts/PackageManagerShellCommandTest.java
@@ -61,7 +61,6 @@
import android.content.pm.SigningInfo;
import android.content.pm.cts.util.AbandonAllPackageSessionsRule;
import android.os.Bundle;
-import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -70,6 +69,7 @@
import android.util.PackageUtils;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.HexDump;
@@ -99,9 +99,13 @@
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
@RunWith(Parameterized.class)
@@ -1472,28 +1476,51 @@
assertEquals(pm.getSdkSandboxPackageName(), names[0]);
}
+ @LargeTest
+ @Test
+ public void testCreateUserCurAsType() throws Exception {
+ Pattern pattern = Pattern.compile("Success: created user id (\\d+)\\R*");
+ String commandResult = executeShellCommand("pm create-user --profileOf cur "
+ + "--user-type android.os.usertype.profile.CLONE test");
+ Matcher matcher = pattern.matcher(commandResult);
+ assertTrue(matcher.find());
+ commandResult = executeShellCommand("pm remove-user " + matcher.group(1));
+ assertEquals("Success: removed user\n", commandResult);
+ commandResult = executeShellCommand("pm create-user --profileOf current "
+ + "--user-type android.os.usertype.profile.CLONE test");
+ matcher = pattern.matcher(commandResult);
+ assertTrue(matcher.find());
+ commandResult = executeShellCommand("pm remove-user " + matcher.group(1));
+ assertEquals("Success: removed user\n", commandResult);
+ }
+
static class FullyRemovedBroadcastReceiver extends BroadcastReceiver {
private final String mTargetPackage;
private final int mTargetUserId;
- private final ConditionVariable mUserReceivedBroadcast;
+ private final CompletableFuture<Boolean> mUserReceivedBroadcast = new CompletableFuture<>();
FullyRemovedBroadcastReceiver(String packageName, int targetUserId) {
mTargetPackage = packageName;
mTargetUserId = targetUserId;
- mUserReceivedBroadcast = new ConditionVariable();
- mUserReceivedBroadcast.close();
}
@Override
public void onReceive(Context context, Intent intent) {
- context.unregisterReceiver(this);
final String packageName = intent.getData().getEncodedSchemeSpecificPart();
final int userId = context.getUserId();
if (intent.getAction().equals(Intent.ACTION_PACKAGE_FULLY_REMOVED)
&& packageName.equals(mTargetPackage) && userId == mTargetUserId) {
- mUserReceivedBroadcast.open();
+ mUserReceivedBroadcast.complete(true);
+ context.unregisterReceiver(this);
}
}
- public boolean isBroadcastReceived() {
- return mUserReceivedBroadcast.block(2000);
+ public void assertBroadcastReceived() throws Exception {
+ assertTrue(mUserReceivedBroadcast.get(2, TimeUnit.SECONDS));
+ }
+ public void assertBroadcastNotReceived() throws Exception {
+ try {
+ assertFalse(mUserReceivedBroadcast.get(2, TimeUnit.SECONDS));
+ } catch (TimeoutException ignored) {
+ // expected
+ }
}
}
diff --git a/tests/tests/display/src/android/display/cts/BrightnessTest.java b/tests/tests/display/src/android/display/cts/BrightnessTest.java
index 73d900f..59f008e 100644
--- a/tests/tests/display/src/android/display/cts/BrightnessTest.java
+++ b/tests/tests/display/src/android/display/cts/BrightnessTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeNotNull;
import static org.junit.Assume.assumeTrue;
import android.Manifest;
@@ -262,6 +263,9 @@
@Test
public void testSetGetSimpleCurve() {
+ // Only run if we have a valid ambient light sensor.
+ assumeTrue(mPackageManager.hasSystemFeature(PackageManager.FEATURE_SENSOR_LIGHT));
+
// Don't run as there is no app that has permission to push curves.
assumeTrue(numberOfSystemAppsWithPermission(
Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS) > 0);
@@ -269,6 +273,8 @@
grantPermission(Manifest.permission.CONFIGURE_DISPLAY_BRIGHTNESS);
BrightnessConfiguration defaultConfig = mDisplayManager.getDefaultBrightnessConfiguration();
+ // This might be null, meaning that the device doesn't support brightness configuration
+ assumeNotNull(defaultConfig);
BrightnessConfiguration config =
new BrightnessConfiguration.Builder(
diff --git a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
index d05e231..291afc9 100644
--- a/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
+++ b/tests/tests/dpi/src/android/dpi/cts/ConfigurationScreenLayoutTest.java
@@ -16,6 +16,10 @@
package android.dpi.cts;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
+
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
@@ -27,7 +31,11 @@
import android.view.Display;
import android.view.WindowManager;
-import androidx.test.InstrumentationRegistry;
+import com.android.compatibility.common.util.SystemUtil;
+
+import java.io.IOException;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ConfigurationScreenLayoutTest
extends ActivityInstrumentationTestCase2<OrientationActivity> {
@@ -43,6 +51,48 @@
super(OrientationActivity.class);
}
+ private static String executeShellCommand(String command) {
+ try {
+ return SystemUtil.runShellCommand(
+ androidx.test.platform.app.InstrumentationRegistry.getInstrumentation(),
+ command);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // Mirrored from ActivityManagerTestBase.java
+ public static class IgnoreOrientationRequestSession implements AutoCloseable {
+ private static final String WM_SET_IGNORE_ORIENTATION_REQUEST =
+ "wm set-ignore-orientation-request ";
+ private static final String WM_GET_IGNORE_ORIENTATION_REQUEST =
+ "wm get-ignore-orientation-request";
+ private static final Pattern IGNORE_ORIENTATION_REQUEST_PATTERN =
+ Pattern.compile("ignoreOrientationRequest (true|false) for displayId=\\d+");
+
+ final int mDisplayId;
+ final boolean mInitialIgnoreOrientationRequest;
+
+ IgnoreOrientationRequestSession(int displayId, boolean enable) {
+ mDisplayId = displayId;
+ Matcher matcher = IGNORE_ORIENTATION_REQUEST_PATTERN.matcher(
+ executeShellCommand(WM_GET_IGNORE_ORIENTATION_REQUEST + " -d " + mDisplayId));
+ assertTrue("get-ignore-orientation-request should match pattern",
+ matcher.find());
+ mInitialIgnoreOrientationRequest = Boolean.parseBoolean(matcher.group(1));
+
+ executeShellCommand("wm set-ignore-orientation-request " + (enable ? "true" : "false")
+ + " -d " + mDisplayId);
+ }
+
+ @Override
+ public void close() {
+ executeShellCommand(
+ WM_SET_IGNORE_ORIENTATION_REQUEST + mInitialIgnoreOrientationRequest + " -d "
+ + mDisplayId);
+ }
+ }
+
public void testScreenLayout() throws Exception {
if (!supportsRotation()) {
// test has no effect if device does not support rotation
@@ -57,26 +107,35 @@
tearDown();
return;
}
- int expectedScreenLayout = computeScreenLayout();
- int expectedSize = expectedScreenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
- int expectedLong = expectedScreenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
+ // Disable IgnoreOrientationRequest feature because when it's enabled, the device would only
+ // follow physical rotations.
+ try (IgnoreOrientationRequestSession session =
+ new IgnoreOrientationRequestSession(DEFAULT_DISPLAY, false)) {
+ int expectedScreenLayout = computeScreenLayout();
+ int expectedSize = expectedScreenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
+ int expectedLong = expectedScreenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
- // Check that all four orientations report the same configuration value.
- for (int i = 0; i < ORIENTATIONS.length; i++) {
- Activity activity = startOrientationActivity(ORIENTATIONS[i]);
- if (activity.isInMultiWindowMode()) {
- // activity.setRequestedOrientation has no effect in multiwindow mode.
+ // Check that all four orientations report the same configuration value.
+ for (int i = 0; i < ORIENTATIONS.length; i++) {
+ Activity activity = startOrientationActivity(ORIENTATIONS[i]);
+ if (activity.isInMultiWindowMode()) {
+ // activity.setRequestedOrientation has no effect in multiwindow mode.
+ tearDown();
+ return;
+ }
+ Configuration mConfig = activity.getResources().getConfiguration();
+ int actualSize = mConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
+ int actualLong = mConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
+
+ assertEquals("Expected screen size value of " + expectedSize + " but got "
+ + actualSize + " for orientation "
+ + ORIENTATIONS[i], expectedSize, actualSize);
+ assertEquals("Expected screen long value of " + expectedLong + " but got "
+ + actualLong + " for orientation "
+ + ORIENTATIONS[i], expectedLong, actualLong);
tearDown();
- return;
}
- Configuration mConfig = activity.getResources().getConfiguration();
- int actualSize = mConfig.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
- int actualLong = mConfig.screenLayout & Configuration.SCREENLAYOUT_LONG_MASK;
-
- assertEquals("Expected screen size value of " + expectedSize + " but got " + actualSize
- + " for orientation " + ORIENTATIONS[i], expectedSize, actualSize);
- assertEquals("Expected screen long value of " + expectedLong + " but got " + actualLong
- + " for orientation " + ORIENTATIONS[i], expectedLong, actualLong);
+ } finally {
tearDown();
}
}
@@ -102,7 +161,7 @@
}
private boolean hasDeviceFeature(final String requiredFeature) {
- return InstrumentationRegistry.getContext()
+ return getInstrumentation().getContext()
.getPackageManager()
.hasSystemFeature(requiredFeature);
}
diff --git a/tests/tests/gameservice/src/android/service/games/GameServiceTest.java b/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
index 26edac3..f83a8cc 100644
--- a/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
+++ b/tests/tests/gameservice/src/android/service/games/GameServiceTest.java
@@ -224,29 +224,10 @@
launchAndWaitForPackage(GAME_PACKAGE_NAME);
- Rect touchableBounds = waitForTouchableOverlayBounds();
+ waitForTouchableOverlayBounds();
- Bitmap overlayScreenshot = Bitmap.createBitmap(
- getInstrumentation().getUiAutomation().takeScreenshot(),
- touchableBounds.left,
- touchableBounds.top,
- touchableBounds.width(),
- touchableBounds.height());
-
- // TODO(b/218901969): UI automator does not appear to have visibility into the overlay;
- // therefore, it cannot be used to validate the overlay's contents.
- // We should try and see if we can expose the overlay to UI automator in
- // order to have a more foolproof validation check. We will use this
- // magenta background check in the meantime.
- // The overlay background is set to magenta. Verify that it has been rendered by checking
- // the corners.
- assertThat(overlayScreenshot.getPixel(0, 0)).isEqualTo(Color.MAGENTA);
- assertThat(overlayScreenshot.getPixel(0, overlayScreenshot.getHeight() - 1)).isEqualTo(
- Color.MAGENTA);
- assertThat(overlayScreenshot.getPixel(overlayScreenshot.getWidth() - 1, 0)).isEqualTo(
- Color.MAGENTA);
- assertThat(overlayScreenshot.getPixel(overlayScreenshot.getWidth() - 1,
- overlayScreenshot.getHeight() - 1)).isEqualTo(Color.MAGENTA);
+ assertThat(UiAutomatorUtils.getUiDevice().findObject(
+ By.text("Overlay was rendered on: " + GAME_PACKAGE_NAME))).isNotNull();
}
@Test
diff --git a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualMouseTest.java b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualMouseTest.java
index 7cf713f..eb54097 100644
--- a/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualMouseTest.java
+++ b/tests/tests/hardware/src/android/hardware/input/cts/tests/VirtualMouseTest.java
@@ -16,6 +16,8 @@
package android.hardware.input.cts.tests;
+import static org.junit.Assert.assertEquals;
+
import android.graphics.PointF;
import android.hardware.input.VirtualMouse;
import android.hardware.input.VirtualMouseButtonEvent;
@@ -144,6 +146,30 @@
/* pressure= */ 0f)));
}
+ @Test
+ public void getCursorPosition() {
+ // Trigger a position update without moving the cursor off the starting position.
+ mVirtualMouse.sendButtonEvent(new VirtualMouseButtonEvent.Builder()
+ .setAction(VirtualMouseButtonEvent.ACTION_BUTTON_PRESS)
+ .setButtonCode(VirtualMouseButtonEvent.BUTTON_PRIMARY)
+ .build());
+ final MotionEvent buttonPressEvent = createMotionEvent(MotionEvent.ACTION_BUTTON_PRESS,
+ START_POSITION.x, START_POSITION.y, /* relativeX= */ 0f, /* relativeY= */ 0f,
+ /* vScroll= */ 0f, /* hScroll= */ 0f, MotionEvent.BUTTON_PRIMARY,
+ /* pressure= */ 1.0f);
+ buttonPressEvent.setActionButton(MotionEvent.BUTTON_PRIMARY);
+ verifyEvents(Arrays.asList(
+ createMotionEvent(MotionEvent.ACTION_DOWN, START_POSITION.x, START_POSITION.y,
+ /* relativeX= */ 0f, /* relativeY= */ 0f, /* vScroll= */ 0f,
+ /* hScroll= */ 0f, MotionEvent.BUTTON_PRIMARY, /* pressure= */ 1.0f),
+ buttonPressEvent));
+
+ final PointF position = mVirtualMouse.getCursorPosition();
+
+ assertEquals("Cursor position x differs", START_POSITION.x, position.x, 0.0001f);
+ assertEquals("Cursor position y differs", START_POSITION.y, position.y, 0.0001f);
+ }
+
private MotionEvent createMotionEvent(int action, float x, float y, float relativeX,
float relativeY, float vScroll, float hScroll, int buttonState, float pressure) {
final MotionEvent.PointerProperties pointerProperties = new MotionEvent.PointerProperties();
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
index 4fa15b8..e13f8da 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
@@ -22,26 +22,45 @@
import static org.junit.Assert.fail;
import android.content.Context;
-import android.keystore.cts.R;
import android.keystore.cts.util.ImportedKey;
import android.keystore.cts.util.TestUtils;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
import android.security.keystore.KeyProtection;
+import android.util.Log;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import java.security.KeyPair;
-import java.security.Security;
-import java.security.Signature;
-import java.util.Arrays;
-import java.util.Collection;
+import com.android.internal.util.HexDump;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.Signature;
+import java.security.SignatureException;
+import java.security.spec.ECGenParameterSpec;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
@RunWith(AndroidJUnit4.class)
public class ECDSASignatureTest {
+ private static final String TAG = "ECDSASignatureTest";
+
private Context getContext() {
return InstrumentationRegistry.getInstrumentation().getTargetContext();
}
@@ -100,6 +119,116 @@
}
}
+ /* Duplicate nonces can leak the ECDSA private key, even if each nonce is only used once per
+ * keypair. See Brengel & Rossow 2018 ( https://doi.org/10.1007/978-3-030-00470-5_29 ).
+ */
+ @Test
+ public void testECDSANonceReuse() throws Exception {
+ testECDSANonceReuse_Helper(false /* useStrongbox */, "secp224r1");
+ testECDSANonceReuse_Helper(false /* useStrongbox */, "secp256r1");
+ testECDSANonceReuse_Helper(false /* useStrongbox */, "secp384r1");
+ testECDSANonceReuse_Helper(false /* useStrongbox */, "secp521r1");
+
+ if (TestUtils.hasStrongBox(getContext())) {
+ testECDSANonceReuse_Helper(true /* useStrongbox */, "secp256r1");
+ }
+ }
+
+ private void testECDSANonceReuse_Helper(boolean useStrongbox, String curve)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException, InvalidKeyException, SignatureException,
+ IOException {
+ KeyPair kp = generateKeyPairForNonceReuse_Helper(useStrongbox, curve);
+ /* An ECDSA signature is a pair of integers (r,s).
+ *
+ * Let G be the curve base point, let n be the order of G, and let k be a random
+ * per-signature nonce.
+ *
+ * ECDSA defines:
+ * r := x_coordinate( k x G) mod n
+ *
+ * It follows that if r_1 == r_2 mod n, then k_1 == k_2 mod n. That is, congruent r
+ * values mod n imply a compromised private key.
+ */
+ Map<String, byte[]> rValueStrToSigMap = new HashMap<String, byte[]>();
+ for (byte i = 1; i <= 100; i++) {
+ byte[] message = new byte[] {i};
+ byte[] signature = computeSignatureForNonceReuse_Helper(message, kp);
+ byte[] rValue = extractRValueFromEcdsaSignature_Helper(signature);
+ String rValueStr = HexDump.toHexString(rValue);
+ if (!rValueStrToSigMap.containsKey(rValueStr)) {
+ rValueStrToSigMap.put(rValueStr, signature);
+ continue;
+ }
+ // Duplicate nonces.
+ Log.i(
+ TAG,
+ "Found duplicate nonce after "
+ + Integer.toString(rValueStrToSigMap.size())
+ + " ECDSA signatures.");
+
+ byte[] otherSig = rValueStrToSigMap.get(rValueStr);
+ String otherSigStr = HexDump.toHexString(otherSig);
+ String currentSigStr = HexDump.toHexString(signature);
+ fail(
+ "Duplicate ECDSA nonce detected."
+ + " Curve: " + curve
+ + " Strongbox: " + Boolean.toString(useStrongbox)
+ + " Signature 1: "
+ + otherSigStr
+ + " Signature 2: "
+ + currentSigStr);
+ }
+ }
+
+ private KeyPair generateKeyPairForNonceReuse_Helper(boolean useStrongbox,
+ String curve)
+ throws NoSuchAlgorithmException, NoSuchProviderException,
+ InvalidAlgorithmParameterException {
+ // We use a generated key instead of an imported key since key generation drains the entropy
+ // pool and thus increase the chance of duplicate nonces.
+ KeyPairGenerator generator = KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+ generator.initialize(
+ new KeyGenParameterSpec.Builder("test1", KeyProperties.PURPOSE_SIGN)
+ .setAlgorithmParameterSpec(new ECGenParameterSpec(curve))
+ .setDigests(KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_SHA256)
+ .setIsStrongBoxBacked(useStrongbox)
+ .build());
+ KeyPair kp = generator.generateKeyPair();
+ return kp;
+ }
+
+ /**
+ * Extract the R value from the ECDSA signature.
+ *
+ * @param sigBytes ASN.1 encoded ECDSA signature.
+ * @return The r value extracted from the signature.
+ * @throws IOException
+ */
+ private byte[] extractRValueFromEcdsaSignature_Helper(byte[] sigBytes) throws IOException {
+ /* ECDSA Signature format (X9.62 Section 6.5):
+ * ECDSA-Sig-Value ::= SEQUENCE {
+ * r INTEGER,
+ * s INTEGER
+ * }
+ */
+ ASN1Primitive sig1prim = ASN1Primitive.fromByteArray(sigBytes);
+ Enumeration secEnum = ((ASN1Sequence) sig1prim).getObjects();
+ ASN1Primitive seqObj = (ASN1Primitive) secEnum.nextElement();
+ // The first ASN1 object is the r value.
+ byte[] r = seqObj.getEncoded();
+ return r;
+ }
+
+ private byte[] computeSignatureForNonceReuse_Helper(byte[] message, KeyPair keyPair)
+ throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
+ Signature signature = Signature.getInstance("NONEwithECDSA");
+ signature.initSign(keyPair.getPrivate());
+ signature.update(message);
+ byte[] sigBytes = signature.sign();
+ return sigBytes;
+ }
+
private void assertNONEwithECDSASupportsMessagesShorterThanFieldSize(KeyPair keyPair)
throws Exception {
int keySizeBits = TestUtils.getKeySizeBits(keyPair.getPublic());
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
index d5f919a..bd9a64a 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyAttestationTest.java
@@ -304,10 +304,7 @@
}
}
- @RestrictedBuildTest
- @RequiresDevice
- @Test
- public void testEcAttestation_DeviceLocked() throws Exception {
+ private void testEcAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception {
if (!TestUtils.isAttestationSupported()) {
return;
}
@@ -325,11 +322,11 @@
.setAttestationChallenge(new byte[128])
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
- .setKeyValidityForConsumptionEnd(consumptionEnd);
+ .setKeyValidityForConsumptionEnd(consumptionEnd)
+ .setIsStrongBoxBacked(expectStrongBox);
- if (TestUtils.hasStrongBox(getContext())) {
+ if (expectStrongBox) {
builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
- builder.setIsStrongBoxBacked(true);
} else {
builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
}
@@ -341,7 +338,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
+ verifyCertificateChain(certificates, expectStrongBox);
X509Certificate attestationCert = (X509Certificate) certificates[0];
checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
@@ -350,6 +347,23 @@
}
}
+ @RestrictedBuildTest
+ @RequiresDevice
+ @Test
+ public void testEcAttestation_DeviceLocked() throws Exception {
+ testEcAttestation_DeviceLocked(false /* expectStrongBox */);
+ }
+
+ @RestrictedBuildTest
+ @RequiresDevice
+ @Test
+ public void testEcAttestation_DeviceLockedStrongbox() throws Exception {
+ if (!TestUtils.hasStrongBox(getContext()))
+ return;
+
+ testEcAttestation_DeviceLocked(true /* expectStrongBox */);
+ }
+
@Test
public void testAttestationKmVersionMatchesFeatureVersion() throws Exception {
if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_PC))
@@ -603,10 +617,7 @@
}
}
- @RestrictedBuildTest
- @RequiresDevice // Emulators have no place to store the needed key
- @Test
- public void testRsaAttestation_DeviceLocked() throws Exception {
+ private void testRsaAttestation_DeviceLocked(Boolean expectStrongBox) throws Exception {
if (!TestUtils.isAttestationSupported()) {
return;
}
@@ -624,11 +635,11 @@
.setAttestationChallenge("challenge".getBytes())
.setKeyValidityStart(now)
.setKeyValidityForOriginationEnd(originationEnd)
- .setKeyValidityForConsumptionEnd(consumptionEnd);
+ .setKeyValidityForConsumptionEnd(consumptionEnd)
+ .setIsStrongBoxBacked(expectStrongBox);
- if (TestUtils.hasStrongBox(getContext())) {
+ if (expectStrongBox) {
builder.setDigests(DIGEST_NONE, DIGEST_SHA256);
- builder.setIsStrongBoxBacked(true);
} else {
builder.setDigests(DIGEST_NONE, DIGEST_SHA256, DIGEST_SHA512);
}
@@ -640,7 +651,7 @@
try {
Certificate certificates[] = keyStore.getCertificateChain(keystoreAlias);
- verifyCertificateChain(certificates, TestUtils.hasStrongBox(getContext()));
+ verifyCertificateChain(certificates, expectStrongBox);
X509Certificate attestationCert = (X509Certificate) certificates[0];
checkDeviceLocked(Attestation.loadFromCertificate(attestationCert));
@@ -649,6 +660,23 @@
}
}
+ @RestrictedBuildTest
+ @RequiresDevice // Emulators have no place to store the needed key
+ @Test
+ public void testRsaAttestation_DeviceLocked() throws Exception {
+ testRsaAttestation_DeviceLocked(false /* expectStrongbox */);
+ }
+
+ @RestrictedBuildTest
+ @RequiresDevice // Emulators have no place to store the needed key
+ @Test
+ public void testRsaAttestation_DeviceLockedStrongbox() throws Exception {
+ if (!TestUtils.hasStrongBox(getContext()))
+ return;
+
+ testRsaAttestation_DeviceLocked(true /* expectStrongbox */);
+ }
+
@Test
public void testAesAttestation() throws Exception {
boolean[] devicePropertiesAttestationValues = {true, false};
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyStoreExceptionTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyStoreExceptionTest.java
index b79f443..a87e8f97 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyStoreExceptionTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyStoreExceptionTest.java
@@ -16,6 +16,7 @@
package android.keystore.cts;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -88,4 +89,52 @@
return errorFieldsBuilder.build();
}
+
+ @Test
+ public void testRkpFailureServerUnavailable() {
+ KeyStoreException ex = new KeyStoreException(22, "RKP failure",
+ 1 /* temporarily unavailable */);
+ assertEquals("Error must indicate key exhaustion",
+ KeyStoreException.ERROR_ATTESTATION_KEYS_UNAVAILABLE,
+ ex.getNumericErrorCode());
+ assertTrue("Must indicate a system issue",
+ ex.isSystemError());
+ assertTrue("Must indicate a transient failure",
+ ex.isTransientFailure());
+ assertEquals("Retry policy must be exponential back-off",
+ KeyStoreException.RETRY_WITH_EXPONENTIAL_BACKOFF,
+ ex.getRetryPolicy());
+ }
+
+ @Test
+ public void testRkpFailurePendingConnectivity() {
+ KeyStoreException ex = new KeyStoreException(22, "RKP failure",
+ 3 /* fetching pending connectivity */);
+ assertEquals("Error must indicate key exhaustion",
+ KeyStoreException.ERROR_ATTESTATION_KEYS_UNAVAILABLE,
+ ex.getNumericErrorCode());
+ assertTrue("Must indicate a system issue",
+ ex.isSystemError());
+ assertTrue("Must indicate a transient failure",
+ ex.isTransientFailure());
+ assertEquals("Retry policy must be when connectivity is resumed",
+ KeyStoreException.RETRY_WHEN_CONNECTIVITY_AVAILABLE,
+ ex.getRetryPolicy());
+ }
+
+ @Test
+ public void testRkpFailureDeviceNotRegistered() {
+ KeyStoreException ex = new KeyStoreException(22, "RKP failure",
+ 2 /* server refused issuance */);
+ assertEquals("Error must indicate key exhaustion",
+ KeyStoreException.ERROR_ATTESTATION_KEYS_UNAVAILABLE,
+ ex.getNumericErrorCode());
+ assertTrue("Must indicate a system issue",
+ ex.isSystemError());
+ assertFalse("Must indicate a permanent failure",
+ ex.isTransientFailure());
+ assertEquals("Retry policy must be never",
+ KeyStoreException.RETRY_NEVER,
+ ex.getRetryPolicy());
+ }
}
diff --git a/tests/tests/media/audio/AndroidManifest.xml b/tests/tests/media/audio/AndroidManifest.xml
index 7129591..d5de5dd 100644
--- a/tests/tests/media/audio/AndroidManifest.xml
+++ b/tests/tests/media/audio/AndroidManifest.xml
@@ -18,7 +18,6 @@
package="android.media.audio.cts"
android:targetSandboxVersion="2">
- <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
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 d6c898b2..b09d333 100644
--- a/tests/tests/media/common/src/android/media/cts/TestUtils.java
+++ b/tests/tests/media/common/src/android/media/cts/TestUtils.java
@@ -21,6 +21,7 @@
import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -123,13 +124,33 @@
private static long getModuleVersion(String module)
throws PackageManager.NameNotFoundException {
Context context = ApplicationProvider.getApplicationContext();
- PackageInfo info;
- info = context.getPackageManager().getPackageInfo(module,
+ PackageInfo info = context.getPackageManager().getPackageInfo(module,
MATCH_APEX);
return info.getLongVersionCode();
}
+ /**
+ * Reports whether {@code module} is the version shipped with the original system image
+ * or if it has been updated via a mainline update.
+ *
+ * @param module the apex module name
+ * @return {@code true} if the apex module is the original version shipped with the device.
+ */
+ public static boolean isMainlineModuleFactoryVersion(String module) {
+ try {
+ Context context = ApplicationProvider.getApplicationContext();
+ PackageInfo info = context.getPackageManager().getPackageInfo(module,
+ MATCH_APEX);
+ if (info != null) {
+ return (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore the exception on devices that do not have this module
+ }
+ return true;
+ }
+
private TestUtils() {
}
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
index 61acac7..e2f5b25 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/ImageReaderDecoderTest.java
@@ -39,6 +39,7 @@
import android.platform.test.annotations.RequiresDevice;
import android.util.Log;
+import android.util.Pair;
import android.view.Surface;
import androidx.test.filters.SmallTest;
@@ -64,6 +65,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@@ -379,16 +381,23 @@
}
private static class ImageListener implements ImageReader.OnImageAvailableListener {
- private final LinkedBlockingQueue<Image> mQueue =
- new LinkedBlockingQueue<Image>();
+ private final LinkedBlockingQueue<Pair<Image, Exception>> mQueue =
+ new LinkedBlockingQueue<Pair<Image, Exception>>();
@Override
public void onImageAvailable(ImageReader reader) {
try {
- mQueue.put(reader.acquireNextImage());
- } catch (InterruptedException e) {
- throw new UnsupportedOperationException(
- "Can't handle InterruptedException in onImageAvailable");
+ mQueue.put(Pair.create(reader.acquireNextImage(), null /* Exception */));
+ } catch (Exception e) {
+ // pass any exception back to the other thread, taking the exception
+ // here crashes the instrumentation in cts/junit.
+ Log.e(TAG, "Can't handle Exceptions in onImageAvailable " + e);
+ try {
+ mQueue.put(Pair.create(null /* Image */, e));
+ } catch (Exception e2) {
+ // ignore the nested exception, other side will see a timeout.
+ Log.e(TAG, "Failed to send exception info across queue: " + e2);
+ }
}
}
@@ -399,7 +408,11 @@
* @return The image from the image reader.
*/
public Image getImage(long timeout) throws InterruptedException {
- Image image = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+ Pair<Image,Exception> imageResult = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+ Image image = imageResult.first;
+ Exception e = imageResult.second;
+
+ assertNull("onImageAvailable() generated an exception: " + e, e);
assertNotNull("Wait for an image timed out in " + timeout + "ms", image);
return image;
}
diff --git a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
index 8eb38e7..61f6cdd 100644
--- a/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
+++ b/tests/tests/media/decoder/src/android/media/decoder/cts/VideoDecoderPerfTest.java
@@ -28,6 +28,7 @@
import android.media.cts.MediaHeavyPresubmitTest;
import android.media.cts.MediaTestBase;
import android.media.cts.Preconditions;
+import android.media.cts.TestUtils;
import android.os.Bundle;
import android.platform.test.annotations.AppModeFull;
import android.text.TextUtils;
@@ -45,6 +46,7 @@
import com.android.compatibility.common.util.ResultUnit;
import org.junit.After;
+import org.junit.Assume;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -97,6 +99,7 @@
private int mBitrate;
private boolean mSkipRateChecking = false;
+ private boolean mUpdatedSwCodec = false;
static final String mInpPrefix = WorkDir.getMediaDirString();
static private List<Object[]> prepareParamList(List<Object[]> exhaustiveArgsList) {
@@ -173,6 +176,9 @@
super.setUp();
Bundle bundle = InstrumentationRegistry.getArguments();
mSkipRateChecking = TextUtils.equals("true", bundle.getString("mts-media"));
+
+ mUpdatedSwCodec =
+ !TestUtils.isMainlineModuleFactoryVersion("com.google.android.media.swcodec");
}
@After
@@ -207,8 +213,11 @@
// doDecode(name, video, width, height, null, i, maxTimeMs);
}
+ // allow improvements in mainline-updated google-supplied software codecs.
+ boolean fasterIsOk = mUpdatedSwCodec & name.startsWith("c2.android.");
String error =
- MediaPerfUtils.verifyAchievableFrameRates(name, mime, width, height, measuredFps);
+ MediaPerfUtils.verifyAchievableFrameRates(name, mime, width, height,
+ fasterIsOk, measuredFps);
// Performance numbers only make sense on real devices, so skip on non-real devices
if ((MediaUtils.onFrankenDevice() || mSkipRateChecking) && error != null) {
// ensure there is data, but don't insist that it is correct
diff --git a/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderCapabilitiesTest.java b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderCapabilitiesTest.java
new file mode 100644
index 0000000..b702e4a
--- /dev/null
+++ b/tests/tests/media/encoder/src/android/media/encoder/cts/VideoEncoderCapabilitiesTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package android.media.encoder.cts;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
+
+
+import android.media.MediaFormat;
+import android.media.MediaMuxer;
+import android.platform.test.annotations.AppModeFull;
+import androidx.test.filters.SmallTest;
+
+import com.android.compatibility.common.util.MediaUtils;
+
+import org.junit.Assert;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+@AppModeFull(reason = "TODO: evaluate and port to instant")
+@RunWith(Parameterized.class)
+public class VideoEncoderCapabilitiesTest {
+ final private String mMediaType;
+ final private int mWidth;
+ final private int mHeight;
+ final private int mFrameRate;
+ final private int mBitRate;
+ final private boolean mOptional;
+
+ @Parameterized.Parameters(name = "{index}({0}_{1}x{2}_{3}_{4}_{5})")
+ public static Collection<Object[]> input() {
+ final List<Object[]> exhaustiveArgsList = Arrays.asList(new Object[][]{
+ // MediaType, width, height, frame-rate, bit-rate, optional
+ // CDD 5.2.2 C-1-2, C-2-1
+ {MediaFormat.MIMETYPE_VIDEO_AVC, 320, 240, 20, 384000, false},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, 720, 480, 30, 2000000, false},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, 1280, 720, 30, 4000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080, 30, 10000000, true},
+
+ // CDD 5.2.3 C-1-1, C-2-1
+ {MediaFormat.MIMETYPE_VIDEO_VP8, 320, 180, 30, 800000, false},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, 640, 360, 30, 2000000, false},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, 1280, 720, 30, 4000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_VP8, 1920, 1080, 30, 10000000, true},
+
+ // CDD 5.2.4
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 720, 480, 30, 1600000, true},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 1280, 720, 30, 4000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 1920, 1080, 30, 5000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_VP9, 3840, 2160, 30, 20000000, true},
+
+ // CDD 5.2.5
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 720, 480, 30, 1600000, true},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 1280, 720, 30, 4000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 1920, 1080, 30, 5000000, true},
+ {MediaFormat.MIMETYPE_VIDEO_HEVC, 3840, 2160, 30, 20000000, true},
+
+ });
+ return exhaustiveArgsList;
+ }
+
+ public VideoEncoderCapabilitiesTest(String mediaType, int width, int height, int frameRate,
+ int bitRate, boolean optional) {
+ mMediaType = mediaType;
+ mWidth = width;
+ mHeight = height;
+ mFrameRate = frameRate;
+ mBitRate = bitRate;
+ mOptional = optional;
+ }
+
+ // Tests encoder profiles required by CDD.
+ @Test
+ public void testEncoderAvailability() {
+ MediaFormat format = MediaFormat.createVideoFormat(mMediaType, mWidth, mHeight);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, mFrameRate);
+ if (mOptional) {
+ assumeTrue("Device doesn't support encoding an optional format: " + format,
+ MediaUtils.canEncode(format));
+ } else {
+ assertTrue("Device doesn't support encoding a mandatory format: " + format,
+ MediaUtils.canEncode(format));
+ }
+ }
+}
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 d7cfbaa..b92f2ca 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
@@ -1447,23 +1447,6 @@
intraRefresh(new Encoder[]{mEncHandle}, 480, 360);
}
- // Tests encoder profiles required by CDD.
- @Test
- public void testLowQualitySDSupport() {
- Assume.assumeTrue("Test is currently enabled only for avc and vp8 encoders",
- mEncHandle.mMime.equals(MediaFormat.MIMETYPE_VIDEO_AVC) ||
- mEncHandle.mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8));
- support(new Encoder[]{mEncHandle}, 720, 480, 20, 384 * 1000);
- }
-
- @Test
- public void testHighQualitySDSupport() {
- Assume.assumeTrue("Test is currently enabled only for avc and vp8 encoders",
- mEncHandle.mMime.equals(MediaFormat.MIMETYPE_VIDEO_AVC) ||
- mEncHandle.mMime.equals(MediaFormat.MIMETYPE_VIDEO_VP8));
- support(new Encoder[]{mEncHandle}, 720, 480, 30, 2 * 1000000);
- }
-
@Test
public void testFlexQVGA20fps384kbps() {
Assume.assumeTrue("Test is currently enabled only for avc and vp8 encoders",
@@ -1654,22 +1637,4 @@
}
}
- /* test size and rate are supported */
- private void support(Encoder[] encoders, int width, int height, int frameRate, int bitRate) {
- boolean supported = false;
- if (encoders.length == 0) {
- MediaUtils.skipTest("no such encoder present");
- return;
- }
- for (Encoder encoder : encoders) {
- if (encoder.testSupport(width, height, frameRate, bitRate)) {
- supported = true;
- break;
- }
- }
- if (!supported) {
- fail("unsupported format " + width + "x" + height + " " +
- frameRate + "fps " + bitRate + "bps");
- }
- }
}
diff --git a/tests/tests/media/muxer/Android.bp b/tests/tests/media/muxer/Android.bp
index f252315..85eab12 100644
--- a/tests/tests/media/muxer/Android.bp
+++ b/tests/tests/media/muxer/Android.bp
@@ -46,7 +46,7 @@
static_libs: [
"cts-media-common",
"ctstestrunner-axt",
- "exoplayer2-metadataretriever-mediamuxer",
+ "exoplayer-mediamuxer_tests",
],
jni_libs: [
"libctsmediamuxertest_jni",
diff --git a/tests/tests/mediaparser/Android.bp b/tests/tests/mediaparser/Android.bp
index 138cb5a..d916d61 100644
--- a/tests/tests/mediaparser/Android.bp
+++ b/tests/tests/mediaparser/Android.bp
@@ -18,7 +18,10 @@
android_test {
name: "CtsMediaParserTestCases",
- defaults: ["CtsMediaParserTestCasesDefaults", "cts_defaults"],
+ defaults: [
+ "CtsMediaParserTestCasesDefaults",
+ "cts_defaults",
+ ],
min_sdk_version: "29",
test_suites: [
"cts",
@@ -39,8 +42,8 @@
static_libs: [
"ctstestrunner-axt",
"androidx.test.ext.junit",
- "exoplayer2-extractor-test-utils",
- "exoplayer2-extractor-tests-assets",
+ "exoplayer-cts_media-test_assets",
+ "exoplayer-cts_media-test_utils",
],
libs: [
"android.test.base",
diff --git a/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
index 08eba99..61b27a0 100644
--- a/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
+++ b/tests/tests/nativehardware/jni/AHardwareBufferTest.cpp
@@ -42,6 +42,8 @@
using testing::AnyOf;
using testing::Eq;
+using testing::Ge;
+using testing::NotNull;
#define FORMAT_CASE(x) case AHARDWAREBUFFER_FORMAT_ ## x: os << #x ; break
@@ -439,6 +441,57 @@
AHardwareBuffer_release(buffer);
}
+TEST(AHardwareBufferTest, PlanarLockAndUnlockYuvP010Succeed) {
+ AHardwareBuffer* buffer = NULL;
+ AHardwareBuffer_Desc desc = {};
+
+ desc.width = 32;
+ desc.height = 32;
+ desc.layers = 1;
+ desc.usage = AHARDWAREBUFFER_USAGE_CPU_READ_RARELY;
+ desc.format = AHARDWAREBUFFER_FORMAT_YCbCr_P010;
+
+ if (!AHardwareBuffer_isSupported(&desc)) {
+ ALOGI("Test skipped: AHARDWAREBUFFER_FORMAT_YCbCr_P010 not supported.");
+ return;
+ }
+
+ // Allocate the buffer.
+ int err = AHardwareBuffer_allocate(&desc, &buffer);
+ EXPECT_EQ(NO_ERROR, err);
+
+ // Lock its planes
+ AHardwareBuffer_Planes planes;
+ err = AHardwareBuffer_lockPlanes(buffer, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1, NULL,
+ &planes);
+
+ // Make sure everything looks right
+ EXPECT_EQ(NO_ERROR, err);
+ EXPECT_EQ(3U, planes.planeCount);
+
+ const uint32_t yPlaneWidth = desc.width;
+ const uint32_t cPlaneWidth = desc.width / 2;
+ const uint32_t bytesPerPixel = 2;
+
+ EXPECT_THAT(planes.planes[0].data, NotNull());
+ EXPECT_THAT(planes.planes[0].pixelStride, Eq(bytesPerPixel));
+ EXPECT_THAT(planes.planes[0].rowStride, Ge(yPlaneWidth * bytesPerPixel));
+
+ EXPECT_THAT(planes.planes[1].data, NotNull());
+ EXPECT_THAT(planes.planes[1].pixelStride, Eq(bytesPerPixel * /*interleaved=*/2));
+ EXPECT_THAT(planes.planes[1].rowStride, Ge(cPlaneWidth * bytesPerPixel));
+
+ EXPECT_THAT(planes.planes[2].data, NotNull());
+ EXPECT_THAT(planes.planes[2].pixelStride, Eq(bytesPerPixel * /*interleaved=*/2));
+ EXPECT_THAT(planes.planes[2].rowStride, Ge(cPlaneWidth * bytesPerPixel));
+
+ // Unlock
+ err = AHardwareBuffer_unlock(buffer, nullptr);
+ EXPECT_EQ(NO_ERROR, err);
+
+ AHardwareBuffer_release(buffer);
+}
+
TEST(AHardwareBufferTest, PlanarLockAndUnlockRgbaSucceed) {
AHardwareBuffer* buffer = NULL;
AHardwareBuffer_Desc desc = {};
diff --git a/tests/tests/os/src/android/os/cts/HexEncoding.java b/tests/tests/os/src/android/os/cts/HexEncoding.java
new file mode 100644
index 0000000..ff49899
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/HexEncoding.java
@@ -0,0 +1,65 @@
+/*
+ * 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.os.cts;
+
+/**
+ * Hexadecimal encoding where each byte is represented by two hexadecimal digits.
+ * Note that this is copied from android.keystore.cts.HexEncoding but only keep the
+ * needed part.
+ *
+ * @hide
+ */
+public class HexEncoding {
+
+ /** Hidden constructor to prevent instantiation. */
+ private HexEncoding() {}
+
+ /**
+ * Decodes the provided hexadecimal string into an array of bytes.
+ */
+ public static byte[] decode(String encoded) {
+ // IMPLEMENTATION NOTE: Special care is taken to permit odd number of hexadecimal digits.
+ int resultLengthBytes = (encoded.length() + 1) / 2;
+ byte[] result = new byte[resultLengthBytes];
+ int resultOffset = 0;
+ int encodedCharOffset = 0;
+ if ((encoded.length() % 2) != 0) {
+ // Odd number of digits -- the first digit is the lower 4 bits of the first result byte.
+ result[resultOffset++] = (byte) getHexadecimalDigitValue(encoded.charAt(encodedCharOffset));
+ encodedCharOffset++;
+ }
+ for (int len = encoded.length(); encodedCharOffset < len; encodedCharOffset += 2) {
+ result[resultOffset++] = (byte)
+ ((getHexadecimalDigitValue(encoded.charAt(encodedCharOffset)) << 4)
+ | getHexadecimalDigitValue(encoded.charAt(encodedCharOffset + 1)));
+ }
+ return result;
+ }
+
+ private static int getHexadecimalDigitValue(char c) {
+ if ((c >= 'a') && (c <= 'f')) {
+ return (c - 'a') + 0x0a;
+ } else if ((c >= 'A') && (c <= 'F')) {
+ return (c - 'A') + 0x0a;
+ } else if ((c >= '0') && (c <= '9')) {
+ return c - '0';
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid hexadecimal digit at position : '" + c + "' (0x" + Integer.toHexString(c) + ")");
+ }
+ }
+}
diff --git a/tests/tests/os/src/android/os/cts/SntpTestServer.java b/tests/tests/os/src/android/os/cts/SntpTestServer.java
new file mode 100644
index 0000000..bca6dd1
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SntpTestServer.java
@@ -0,0 +1,131 @@
+/*
+ * 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.os.cts;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.SocketException;
+import java.util.Arrays;
+
+/**
+ * Simple Sntp Server implementation for testing purpose.
+ * This is copied from {@code SntpClientTest}.
+ */
+public class SntpTestServer {
+ private final static String TAG = SntpTestServer.class.getSimpleName();
+ private static final int ORIGINATE_TIME_OFFSET = 24;
+ private static final int TRANSMIT_TIME_OFFSET = 40;
+
+ private final Object mLock = new Object();
+ private final DatagramSocket mSocket;
+ private final InetAddress mAddress;
+ private final int mPort;
+ private byte[] mReply;
+ private boolean mGenerateValidOriginateTimestamp = true;
+ private int mRcvd;
+ private int mSent;
+ private Thread mListeningThread;
+
+ public SntpTestServer() {
+ mSocket = makeSocket();
+ mAddress = mSocket.getLocalAddress();
+ mPort = mSocket.getLocalPort();
+ Log.d(TAG, "testing server listening on (" + mAddress + ", " + mPort + ")");
+
+ mListeningThread = new Thread() {
+ public void run() {
+ while (true) {
+ byte[] buffer = new byte[512];
+ DatagramPacket ntpMsg = new DatagramPacket(buffer, buffer.length);
+ try {
+ mSocket.receive(ntpMsg);
+ } catch (IOException e) {
+ Log.e(TAG, "datagram receive error: " + e);
+ break;
+ }
+ synchronized (mLock) {
+ mRcvd++;
+ if (mReply == null) { continue; }
+ if (mGenerateValidOriginateTimestamp) {
+ // Copy the transmit timestamp into originate timestamp: This is
+ // validated by well-behaved clients.
+ System.arraycopy(ntpMsg.getData(), TRANSMIT_TIME_OFFSET,
+ mReply, ORIGINATE_TIME_OFFSET, 8);
+ } else {
+ // Fill it with junk instead.
+ Arrays.fill(mReply, ORIGINATE_TIME_OFFSET,
+ ORIGINATE_TIME_OFFSET + 8, (byte) 0xFF);
+ }
+ ntpMsg.setData(mReply);
+ ntpMsg.setLength(mReply.length);
+ try {
+ mSocket.send(ntpMsg);
+ } catch (IOException e) {
+ Log.e(TAG, "datagram send error: " + e);
+ break;
+ }
+ mSent++;
+ }
+ }
+ mSocket.close();
+ }
+ };
+ mListeningThread.start();
+ }
+
+ private DatagramSocket makeSocket() {
+ DatagramSocket socket;
+ try {
+ socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
+ } catch (SocketException e) {
+ Log.e(TAG, "Failed to create test server socket: " + e);
+ return null;
+ }
+ return socket;
+ }
+
+ public void clearServerReply() {
+ setServerReply(null);
+ }
+
+ public void setServerReply(byte[] reply) {
+ synchronized (mLock) {
+ mReply = reply;
+ mRcvd = 0;
+ mSent = 0;
+ }
+ }
+
+ /**
+ * Controls the test server's behavior of copying the client's transmit timestamp into the
+ * response's originate timestamp (which is required of a real server).
+ */
+ public void setGenerateValidOriginateTimestamp(boolean enabled) {
+ synchronized (mLock) {
+ mGenerateValidOriginateTimestamp = enabled;
+ }
+ }
+
+ public InetAddress getAddress() { return mAddress; }
+ public int getPort() { return mPort; }
+ public int numRequestsReceived() { synchronized (mLock) { return mRcvd; } }
+ public int numRepliesSent() { synchronized (mLock) { return mSent; } }
+}
diff --git a/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java b/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java
new file mode 100644
index 0000000..56b658d
--- /dev/null
+++ b/tests/tests/os/src/android/os/cts/SystemClockSntpTest.java
@@ -0,0 +1,170 @@
+/*
+ * 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.os.cts;
+
+import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
+import android.os.SystemClock;
+import android.util.Range;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.compatibility.common.util.ThrowingRunnable;
+
+import org.junit.After;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.time.DateTimeException;
+
+@RunWith(AndroidJUnit4.class)
+public class SystemClockSntpTest {
+ // Mocked server response. Refer to SntpClientTest.
+ private static final String MOCKED_NTP_RESPONSE =
+ "240206ec"
+ + "00000165"
+ + "000000b2"
+ + "ddfd4729"
+ + "d9ca9446820a5000"
+ + "d9ca9451938a3771"
+ + "d9ca945194bd3fff"
+ + "d9ca945194bd4001";
+ // The midpoint between d9ca945194bd3fff and d9ca945194bd4001, d9ca9451.94bd4000 represents
+ // (decimal) 1444943313.581012726 seconds in the Unix epoch, which is
+ // ~2015-10-15 21:08:33.581 UTC.
+ private static long MOCKED_NTP_TIMESTAMP = 1444943313581L;
+ private static long TEST_NTP_TIMEOUT_MILLIS = 300L;
+
+ private SntpTestServer mServer;
+
+ @After
+ public void tearDown() {
+ // Restore NTP server configurations.
+ executeShellCommand("cmd network_time_update_service set_server_config");
+ }
+
+ @Test
+ public void testCurrentNetworkTimeClock() throws Exception {
+ // Start a local SNTP test server. But does not setup a fake response.
+ // So the server will not reply to any request.
+ runWithShellPermissionIdentity(() -> mServer = new SntpTestServer());
+
+ // Write test server address into settings.
+ executeShellCommand(
+ "cmd network_time_update_service set_server_config --hostname "
+ + mServer.getAddress().getHostAddress()
+ + " --port " + mServer.getPort()
+ + " --timeout_millis " + TEST_NTP_TIMEOUT_MILLIS);
+
+ // Clear current NTP value and verify it throws exception.
+ executeShellCommand("cmd network_time_update_service clear_time");
+
+ // Verify the case where the device hasn't made an NTP request yet.
+ assertThrows(DateTimeException.class, () -> SystemClock.currentNetworkTimeClock().millis());
+
+ // Trigger NtpTrustedTime refresh with the new command.
+ executeShellCommandAndAssertOutput(
+ "cmd network_time_update_service force_refresh", "false");
+
+ // Verify the returned clock throws since there is still no previous NTP fix.
+ assertThrows(DateTimeException.class, () -> SystemClock.currentNetworkTimeClock().millis());
+
+ // Setup fake responses (Refer to SntpClientTest). And trigger NTP refresh.
+ mServer.setServerReply(HexEncoding.decode(MOCKED_NTP_RESPONSE));
+
+ // After force_refresh, network_time_update_service should have associated
+ // MOCKED_NTP_TIMESTAMP with an elapsedRealtime() value between
+ // beforeRefreshElapsedMillis and afterRefreshElapsedMillis.
+ final long beforeRefreshElapsedMillis = SystemClock.elapsedRealtime();
+ executeShellCommandAndAssertOutput("cmd network_time_update_service force_refresh", "true");
+ final long afterRefreshElapsedMillis = SystemClock.elapsedRealtime();
+
+ // Request the current Unix epoch time. Assert value of SystemClock#currentNetworkTimeClock.
+ assertCurrentNetworkTimeClockInBounds(MOCKED_NTP_TIMESTAMP, beforeRefreshElapsedMillis,
+ afterRefreshElapsedMillis);
+
+ // Simulate some time passing and verify that SystemClock returns an updated time
+ // using the same NTP signal.
+ final long PASSED_DURATION_MS = 100L;
+ Thread.sleep(PASSED_DURATION_MS);
+
+ // Request the current Unix epoch time again. Verify that SystemClock returns an
+ // updated time using the same NTP signal.
+ assertCurrentNetworkTimeClockInBounds(MOCKED_NTP_TIMESTAMP, beforeRefreshElapsedMillis,
+ afterRefreshElapsedMillis);
+
+ // Remove fake server response and trigger NTP refresh to simulate a failed refresh.
+ mServer.setServerReply(null);
+ executeShellCommandAndAssertOutput(
+ "cmd network_time_update_service force_refresh", "false");
+
+ // Verify that SystemClock still returns an updated time using the same NTP signal.
+ assertCurrentNetworkTimeClockInBounds(MOCKED_NTP_TIMESTAMP, beforeRefreshElapsedMillis,
+ afterRefreshElapsedMillis);
+ }
+
+ private static void executeShellCommand(String command) {
+ executeShellCommandAndAssertOutput(command, null);
+ }
+
+ private static void executeShellCommandAndAssertOutput(
+ String command, String expectedOutput) {
+ final String trimmedResult = runShellCommand(command).trim();
+ if (expectedOutput != null) {
+ assertEquals(expectedOutput, trimmedResult);
+ }
+ }
+
+ private static void runWithShellPermissionIdentity(ThrowingRunnable command)
+ throws Exception {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity();
+ try {
+ command.run();
+ } finally {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+ }
+
+ /** Verify the given value is in range [lower, upper] */
+ private static void assertInRange(String tag, long value, long lower, long upper) {
+ final Range range = new Range(lower, upper);
+ assertTrue(tag + ": " + value + " is not within range [" + lower + ", " + upper + "]",
+ range.contains(value));
+ }
+
+ private static void assertCurrentNetworkTimeClockInBounds(long expectedTimestamp,
+ long beforeRefreshElapsedMillis, long afterRefreshElapsedMillis) {
+ final long beforeQueryElapsedMillis = SystemClock.elapsedRealtime();
+ final long networkEpochMillis = SystemClock.currentNetworkTimeClock().millis();
+ final long afterQueryElapsedMillis = SystemClock.elapsedRealtime();
+
+ // Calculate the lower/upper bound base on the elapsed time of refreshing.
+ final long lowerBoundNetworkEpochMillis =
+ expectedTimestamp + (beforeQueryElapsedMillis - afterRefreshElapsedMillis);
+ final long upperBoundNetworkEpochMillis =
+ expectedTimestamp + (afterQueryElapsedMillis - beforeRefreshElapsedMillis);
+ assertInRange("Network time", networkEpochMillis, lowerBoundNetworkEpochMillis,
+ upperBoundNetworkEpochMillis);
+ }
+}
diff --git a/tests/tests/permission/permissionTestUtilLib/Android.bp b/tests/tests/permission/permissionTestUtilLib/Android.bp
index dd50015..36f89d7 100644
--- a/tests/tests/permission/permissionTestUtilLib/Android.bp
+++ b/tests/tests/permission/permissionTestUtilLib/Android.bp
@@ -19,13 +19,16 @@
java_library {
name: "permission-test-util-lib",
- srcs: ["src/**/*.java"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"ub-uiautomator",
"compatibility-device-util-axt",
- "androidx.test.ext.junit-nodeps"
- ],
+ "androidx.test.ext.junit-nodeps",
+ ],
sdk_version: "test_current",
}
diff --git a/tests/tests/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java b/tests/tests/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java
index f245001..3b91f98 100644
--- a/tests/tests/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NearbyDevicesPermissionTest.java
@@ -51,7 +51,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -292,11 +291,11 @@
private void enableTestMode() {
runShellCommandOrThrow("dumpsys activity service"
- + " com.android.bluetooth/.btservice.AdapterService set-test-mode enabled");
+ + " com.android.bluetooth.btservice.AdapterService set-test-mode enabled");
}
private void disableTestMode() {
runShellCommandOrThrow("dumpsys activity service"
- + " com.android.bluetooth/.btservice.AdapterService set-test-mode disabled");
+ + " com.android.bluetooth.btservice.AdapterService set-test-mode disabled");
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java
index 030acf9..a4e480a 100644
--- a/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java
+++ b/tests/tests/permission/src/android/permission/cts/NotificationListenerCheckTest.java
@@ -16,6 +16,7 @@
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;
@@ -59,7 +60,7 @@
import androidx.test.filters.SdkSuppress;
import androidx.test.runner.AndroidJUnit4;
-import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.DeviceConfigStateChangerRule;
import com.android.compatibility.common.util.ProtoUtils;
import com.android.compatibility.common.util.mainline.MainlineModule;
import com.android.compatibility.common.util.mainline.ModuleDetector;
@@ -71,6 +72,7 @@
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -105,6 +107,9 @@
private static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED =
"notification_listener_check_enabled";
+ /** Name of the flag that determines whether SafetyCenter is enabled. */
+ private static final String PROPERTY_SAFETY_CENTER_ENABLED = "safety_center_is_enabled";
+
/**
* Device config property for time period in milliseconds after which current enabled
* notification
@@ -116,17 +121,15 @@
private static final Long OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS =
SECONDS.toMillis(1);
- /**
- * Device config property for time period in milliseconds after which a followup notification
- * can be
- * posted for an enabled notification listener
- */
- private static final String PROPERTY_NOTIFICATION_LISTENER_CHECK_PACKAGE_INTERVAL_MILLIS =
- "notification_listener_check_pkg_interval_millis";
+ private static final String PROPERTY_JOB_SCHEDULER_MAX_JOB_PER_RATE_LIMIT_WINDOW =
+ "qc_max_job_count_per_rate_limiting_window";
+
+ private static final String PROPERTY_JOB_SCHEDULER_RATE_LIMIT_WINDOW_MILLIS =
+ "qc_rate_limiting_window_ms";
/**
* ID for notification shown by
- * {@link com.android.permissioncontroller.permission.service.NotificationListenerCheck}.
+ * {@link com.android.permissioncontroller.permission.service.v33.NotificationListenerCheck}.
*/
public static final int NOTIFICATION_LISTENER_CHECK_NOTIFICATION_ID = 3;
@@ -141,39 +144,77 @@
private static final String PERMISSION_CONTROLLER_PKG = sContext.getPackageManager()
.getPermissionControllerPackageName();
- private static DeviceConfigStateHelper sPrivacyDeviceConfig =
- new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_PRIVACY);
- private static DeviceConfigStateHelper sJobSchedulerDeviceConfig =
- new DeviceConfigStateHelper(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
-
private static List<ComponentName> sPreviouslyEnabledNotificationListeners;
- /**
- * Enable notification listener check
- */
- private static void enableNotificationListenerCheckFeature() {
- sPrivacyDeviceConfig.set(PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
- String.valueOf(true));
+ // Override SafetyCenter enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigSafetyCenterEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ Boolean.toString(true));
+
+ // Override NlsCheck enabled flag
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigNlsCheckEnabled =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
+ Boolean.toString(true));
+
+ // Override general notification interval from once every day to once ever 1 second
+ @Rule
+ public DeviceConfigStateChangerRule sPrivacyDeviceConfigNlsCheckIntervalMillis =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS,
+ Long.toString(OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS));
+
+ // Disable job scheduler throttling by allowing 300000 jobs per 30 sec
+ @Rule
+ public DeviceConfigStateChangerRule sJobSchedulerDeviceConfig1 =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ PROPERTY_JOB_SCHEDULER_MAX_JOB_PER_RATE_LIMIT_WINDOW,
+ Integer.toString(3000000));
+
+ // Disable job scheduler throttling by allowing 300000 jobs per 30 sec
+ @Rule
+ public DeviceConfigStateChangerRule sJobSchedulerDeviceConfig2 =
+ new DeviceConfigStateChangerRule(sContext,
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ PROPERTY_JOB_SCHEDULER_RATE_LIMIT_WINDOW_MILLIS,
+ Integer.toString(30000));
+
+ private static void setDeviceConfigPrivacyProperty(String propertyName, String value) {
+ runWithShellPermissionIdentity(() -> {
+ boolean valueWasSet = DeviceConfig.setProperty(
+ DeviceConfig.NAMESPACE_PRIVACY,
+ /* name = */ propertyName,
+ /* value = */ value,
+ /* makeDefault = */ false);
+ if (!valueWasSet) {
+ throw new IllegalStateException("Could not set " + propertyName + " to " + value);
+ }
+ }, WRITE_DEVICE_CONFIG);
}
/**
- * Disable notification listener check
+ * Enabled or disable Safety Center
*/
- private static void disableNotificationListenerCheckFeature() {
- sPrivacyDeviceConfig.set(PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
- String.valueOf(false));
+ private static void setSafetyCenterEnabled(boolean enabled) {
+ setDeviceConfigPrivacyProperty(
+ PROPERTY_SAFETY_CENTER_ENABLED,
+ /* value = */ String.valueOf(enabled));
}
- private static void setNotificationListenerCheckInterval(long intervalMs) {
- // Override general notification interval
- sPrivacyDeviceConfig.set(PROPERTY_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS,
- Long.toString(intervalMs));
- }
-
- private static void setNotificationListenerCheckPackageInterval(long intervalMs) {
- // Override package notification interval
- sPrivacyDeviceConfig.set(PROPERTY_NOTIFICATION_LISTENER_CHECK_PACKAGE_INTERVAL_MILLIS,
- Long.toString(intervalMs));
+ /**
+ * Enable or disable notification listener check
+ */
+ private static void setNotificationListenerCheckEnabled(boolean enabled) {
+ setDeviceConfigPrivacyProperty(
+ PROPERTY_NOTIFICATION_LISTENER_CHECK_ENABLED,
+ /* value = */ String.valueOf(enabled));
}
/**
@@ -188,6 +229,32 @@
runShellCommand(command);
}
+ private static void disallowPreexistingNotificationListeners() {
+ runWithShellPermissionIdentity(() -> {
+ NotificationManager notificationManager =
+ sContext.getSystemService(NotificationManager.class);
+ sPreviouslyEnabledNotificationListeners =
+ notificationManager.getEnabledNotificationListeners();
+ });
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Found " + sPreviouslyEnabledNotificationListeners.size()
+ + " previously allowed notification listeners. Disabling before test run.");
+ }
+ for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
+ setNotificationListenerServiceAllowed(listener, false);
+ }
+ }
+
+ private static void reallowPreexistingNotificationListeners() {
+ if (DEBUG) {
+ Log.d(LOG_TAG, "Re-allowing " + sPreviouslyEnabledNotificationListeners.size()
+ + " previously allowed notification listeners found before test run.");
+ }
+ for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
+ setNotificationListenerServiceAllowed(listener, true);
+ }
+ }
+
private void allowTestAppNotificationListenerService() {
setNotificationListenerServiceAllowed(
new ComponentName(TEST_APP_PKG, TEST_APP_NOTIFICATION_SERVICE), true);
@@ -334,22 +401,17 @@
}
@BeforeClass
- public static void beforeClassSetup() {
+ public static void beforeClassSetup() throws Exception {
// Disallow any OEM enabled NLS
disallowPreexistingNotificationListeners();
// Allow NLS used to verify notifications sent
setNotificationListenerServiceAllowed(
new ComponentName(sContext, NotificationListener.class), true);
-
- reduceDelays();
}
@AfterClass
public static void afterClassTearDown() throws Throwable {
- resetJobSchedulerConfig();
- resetPermissionControllerConfig();
-
// Disallow NLS used to verify notifications sent
setNotificationListenerServiceAllowed(
new ComponentName(sContext, NotificationListener.class), false);
@@ -358,66 +420,6 @@
reallowPreexistingNotificationListeners();
}
- private static void disallowPreexistingNotificationListeners() {
- runWithShellPermissionIdentity(() -> {
- NotificationManager notificationManager =
- sContext.getSystemService(NotificationManager.class);
- sPreviouslyEnabledNotificationListeners =
- notificationManager.getEnabledNotificationListeners();
- });
- if (DEBUG) {
- Log.d(LOG_TAG, "Found " + sPreviouslyEnabledNotificationListeners.size()
- + " previously allowed notification listeners. Disabling before test run.");
- }
- for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
- setNotificationListenerServiceAllowed(listener, false);
- }
- }
-
- private static void reallowPreexistingNotificationListeners() {
- if (DEBUG) {
- Log.d(LOG_TAG, "Re-allowing " + sPreviouslyEnabledNotificationListeners.size()
- + " previously allowed notification listeners found before test run.");
- }
- for (ComponentName listener : sPreviouslyEnabledNotificationListeners) {
- setNotificationListenerServiceAllowed(listener, true);
- }
- }
-
- /**
- * Change settings so that permission controller can show notification listener notifications
- * more often.
- */
- private static void reduceDelays() {
- runWithShellPermissionIdentity(() -> {
- // Override general notification interval from once every day to once ever 1 second
- setNotificationListenerCheckInterval(
- OVERRIDE_NOTIFICATION_LISTENER_CHECK_INTERVAL_MILLIS);
-
- // Disable job scheduler throttling by allowing 300000 jobs per 30 sec
- sJobSchedulerDeviceConfig.set("qc_max_job_count_per_rate_limiting_window", "3000000");
- sJobSchedulerDeviceConfig.set("qc_rate_limiting_window_ms", "30000");
- });
- }
-
- /**
- * Reset job scheduler configs.
- */
- private static void resetJobSchedulerConfig() throws Throwable {
- runWithShellPermissionIdentity(() -> {
- sJobSchedulerDeviceConfig.restoreOriginalValues();
- });
- }
-
- /**
- * Reset privacy configs.
- */
- private static void resetPermissionControllerConfig() {
- runWithShellPermissionIdentity(() -> {
- sPrivacyDeviceConfig.restoreOriginalValues();
- });
- }
-
@Before
public void setup() throws Throwable {
assumeNotPlayManaged();
@@ -464,9 +466,6 @@
* Reset the permission controllers state before each test
*/
private void resetPermissionControllerBeforeEachTest() throws Throwable {
- // Has to be before resetPermissionController
- enableNotificationListenerCheckFeature();
-
resetPermissionController();
// ensure no posted notification listener notifications exits
@@ -552,7 +551,17 @@
@Test
public void noNotificationIfFeatureDisabled() throws Throwable {
- disableNotificationListenerCheckFeature();
+ setNotificationListenerCheckEnabled(false);
+
+ runNotificationListenerCheck();
+
+ ensure(() -> assertNull("Expected no notifications", getNotification(false)),
+ EXPECTED_TIMEOUT_MILLIS);
+ }
+
+ @Test
+ public void noNotificationIfSafetyCenterDisabled() throws Throwable {
+ setSafetyCenterEnabled(false);
runNotificationListenerCheck();
diff --git a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
index c6583fc..036f47f 100755
--- a/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
+++ b/tests/tests/permission/src/android/permission/cts/SplitPermissionsSystemTest.java
@@ -107,6 +107,8 @@
case WRITE_EXTERNAL_STORAGE:
if (newPermissions.contains(READ_EXTERNAL_STORAGE)) {
assertSplit(split, NO_TARGET, READ_EXTERNAL_STORAGE);
+ } else if (newPermissions.contains(ACCESS_MEDIA_LOCATION)) {
+ assertSplit(split, Build.VERSION_CODES.Q, ACCESS_MEDIA_LOCATION);
} else if (newPermissions.contains(READ_MEDIA_AUDIO)) {
assertSplit(split, Build.VERSION_CODES.S_V2 + 1, READ_MEDIA_AUDIO);
} else if (newPermissions.contains(READ_MEDIA_VIDEO)) {
@@ -152,7 +154,7 @@
}
}
- assertEquals(20, seenSplits.size());
+ assertEquals(21, seenSplits.size());
}
private void assertSplit(SplitPermissionInfo split, int targetSdk, String... permission) {
diff --git a/tests/tests/permission2/res/raw/android_manifest.xml b/tests/tests/permission2/res/raw/android_manifest.xml
index 7d3bb7b..bd516b3 100644
--- a/tests/tests/permission2/res/raw/android_manifest.xml
+++ b/tests/tests/permission2/res/raw/android_manifest.xml
@@ -1966,15 +1966,6 @@
android:label="@string/permlab_changeWifiState"
android:protectionLevel="normal" />
- <!-- Allows applications to enable/disable wifi auto join. This permission
- is used to let OEMs grant their trusted app access to a subset of privileged wifi APIs
- to improve wifi performance.
- <p>Not for use by third-party applications.
- @deprecated will be replaced with MANAGE_WIFI_NETWORK_SELECTION -->
- <permission android:name="android.permission.MANAGE_WIFI_AUTO_JOIN"
- android:protectionLevel="signature|privileged|knownSigner"
- android:knownCerts="@array/wifi_known_signers" />
-
<!-- This permission is used to let OEMs grant their trusted app access to a subset of
privileged wifi APIs to improve wifi performance. Allows applications to manage
Wi-Fi network selection related features such as enable or disable global auto-join,
@@ -2475,14 +2466,6 @@
<!-- ======================================== -->
<eat-comment />
- <!-- @SystemApi Allows an application to send location to the device admin when an
- organization-owned device is in lost mode.
- <p>Not for use by third-party applications.
- @hide
- -->
- <permission android:name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES"
- android:protectionLevel="signature|privileged"/>
-
<!-- @SystemApi Allows an application to trigger lost mode on an organization-owned device.
<p>Not for use by third-party applications.
@hide
@@ -3738,6 +3721,12 @@
<permission android:name="android.permission.BIND_ATTESTATION_VERIFICATION_SERVICE"
android:protectionLevel="signature" />
+ <!-- Allows the caller to generate keymint keys with the INCLUDE_UNIQUE_ID tag, which
+ uniquely identifies the device via the attestation certificate.
+ @hide @TestApi -->
+ <permission android:name="android.permission.REQUEST_UNIQUE_ID_ATTESTATION"
+ android:protectionLevel="signature" />
+
<!-- ========================================= -->
<!-- Permissions for special development tools -->
<!-- ========================================= -->
@@ -6019,12 +6008,12 @@
generation service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
- android:protectionLevel="signature|role" />
+ android:protectionLevel="signature|privileged" />
<!-- @SystemApi Allows an application to manage the cloudsearch service.
@hide <p>Not for use by third-party applications.</p> -->
<permission android:name="android.permission.MANAGE_CLOUDSEARCH"
- android:protectionLevel="signature" />
+ android:protectionLevel="signature|privileged|role" />
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
diff --git a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
index 1a3fef3..edfcf3a 100644
--- a/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/PermissionPolicyTest.java
@@ -71,9 +71,6 @@
private static final String MANAGE_COMPANION_DEVICES_PERMISSION
= "android.permission.MANAGE_COMPANION_DEVICES";
- private static final String ALLOW_SLIPPERY_TOUCHES_PERMISSION
- = "android.permission.ALLOW_SLIPPERY_TOUCHES";
-
private static final String LOG_TAG = "PermissionProtectionTest";
private static final String PLATFORM_PACKAGE_NAME = "android";
@@ -478,9 +475,6 @@
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 ALLOW_SLIPPERY_TOUCHES_PERMISSION:
- // In R and S branches, skip this permission
- return true;
default:
return false;
}
diff --git a/tests/tests/permission3/Android.bp b/tests/tests/permission3/Android.bp
index 36d24af..4928598 100644
--- a/tests/tests/permission3/Android.bp
+++ b/tests/tests/permission3/Android.bp
@@ -55,6 +55,7 @@
":CtsAccessMicrophoneApp",
":CtsAccessMicrophoneApp2",
":CtsAccessMicrophoneAppLocationProvider",
+ ":CtsAppLocationProviderWithSummary",
":CtsHelperAppOverlay",
":CtsCreateNotificationChannelsApp31",
":CtsCreateNotificationChannelsApp33",
diff --git a/tests/tests/permission3/AndroidTest.xml b/tests/tests/permission3/AndroidTest.xml
index 9e54fc6..384ea0b 100644
--- a/tests/tests/permission3/AndroidTest.xml
+++ b/tests/tests/permission3/AndroidTest.xml
@@ -43,6 +43,7 @@
<option name="push" value="CtsAccessMicrophoneApp.apk->/data/local/tmp/cts/permission3/CtsAccessMicrophoneApp.apk" />
<option name="push" value="CtsAccessMicrophoneApp2.apk->/data/local/tmp/cts/permission3/CtsAccessMicrophoneApp2.apk" />
<option name="push" value="CtsAccessMicrophoneAppLocationProvider.apk->/data/local/tmp/cts/permission3/CtsAccessMicrophoneAppLocationProvider.apk" />
+ <option name="push" value="CtsAppLocationProviderWithSummary.apk->/data/local/tmp/cts/permission3/CtsAppLocationProviderWithSummary.apk" />
<option name="push" value="CtsPermissionPolicyApp25.apk->/data/local/tmp/cts/permission3/CtsPermissionPolicyApp25.apk" />
<option name="push" value="CtsUsePermissionApp22.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp22.apk" />
<option name="push" value="CtsUsePermissionApp22CalendarOnly.apk->/data/local/tmp/cts/permission3/CtsUsePermissionApp22CalendarOnly.apk" />
diff --git a/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt b/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt
index 9264c6c..104655f 100644
--- a/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt
+++ b/tests/tests/permission3/CreateNotificationChannelsApp31/src/android/permission3/cts/usepermission/CreateNotificationChannelsActivity.kt
@@ -25,7 +25,6 @@
import android.os.Handler
import android.os.Looper
-const val EXTRA_DELETE_CHANNELS_ON_CLOSE = "extra_delete_channels_on_close"
const val EXTRA_CREATE_CHANNELS = "extra_create"
const val EXTRA_CREATE_CHANNELS_DELAYED = "extra_create_delayed"
const val EXTRA_REQUEST_NOTIF_PERMISSION = "extra_request_notif_permission"
@@ -38,12 +37,16 @@
const val CHANNEL_ID_31 = "test_channel_id"
const val BROADCAST_ACTION = "usepermission.createchannels.BROADCAST"
const val DELAY_MS = 1000L
-const val LONG_DELAY_MS = 5000L
+const val LONG_DELAY_MS = 2000L
class CreateNotificationChannelsActivity : Activity() {
lateinit var notificationManager: NotificationManager
+ var launchActivityOnSecondResume = false
+ var isFirstResume = true
+ val handler = Handler(Looper.getMainLooper())
+
override fun onStart() {
- val handler = Handler(Looper.getMainLooper())
+ val launchSecondActivity = intent.getBooleanExtra(EXTRA_START_SECOND_ACTIVITY, false)
notificationManager = baseContext.getSystemService(NotificationManager::class.java)!!
if (intent.getBooleanExtra(EXTRA_START_SECOND_APP, false)) {
handler.postDelayed({
@@ -57,12 +60,20 @@
}, LONG_DELAY_MS)
} else if (intent.getBooleanExtra(EXTRA_CREATE_CHANNELS, false)) {
createChannel()
+ if (launchSecondActivity) {
+ launchActivityOnSecondResume = true
+ }
} else if (intent.getBooleanExtra(EXTRA_CREATE_CHANNELS_DELAYED, false)) {
handler.postDelayed({
createChannel()
}, DELAY_MS)
+ } else {
+ if (launchSecondActivity) {
+ launchSecondActivity()
+ }
}
+
if (intent.getBooleanExtra(EXTRA_REQUEST_OTHER_PERMISSIONS, false)) {
requestPermissions(arrayOf(Manifest.permission.RECORD_AUDIO), 0)
} else if (intent.getBooleanExtra(EXTRA_REQUEST_OTHER_PERMISSIONS_DELAYED, false)) {
@@ -74,19 +85,20 @@
if (intent.getBooleanExtra(EXTRA_REQUEST_NOTIF_PERMISSION, false)) {
requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 0)
}
- if (intent.getBooleanExtra(EXTRA_START_SECOND_ACTIVITY, false)) {
- handler.postDelayed({
- val intent2 = Intent(Intent.ACTION_MAIN)
- intent2.`package` = packageName
- intent2.addCategory(Intent.CATEGORY_DEFAULT)
- intent2.putExtra(EXTRA_CREATE_CHANNELS_DELAYED, true)
- startActivity(intent2)
- }, LONG_DELAY_MS)
- }
super.onStart()
}
+ private fun launchSecondActivity() {
+ handler.postDelayed({
+ val intent2 = Intent(Intent.ACTION_MAIN)
+ intent2.`package` = packageName
+ intent2.addCategory(Intent.CATEGORY_DEFAULT)
+ intent2.putExtra(EXTRA_CREATE_CHANNELS_DELAYED, true)
+ startActivity(intent2)
+ }, LONG_DELAY_MS)
+ }
+
private fun createChannel() {
if (notificationManager.getNotificationChannel(CHANNEL_ID_31) == null) {
notificationManager.createNotificationChannel(NotificationChannel(CHANNEL_ID_31,
@@ -94,11 +106,12 @@
}
}
- override fun onPause() {
- if (intent.getBooleanExtra(EXTRA_DELETE_CHANNELS_ON_CLOSE, false)) {
- notificationManager.deleteNotificationChannel(CHANNEL_ID_31)
+ override fun onResume() {
+ super.onResume()
+ if (!isFirstResume && launchActivityOnSecondResume) {
+ launchSecondActivity()
}
- super.onPause()
+ isFirstResume = false
}
override fun onRequestPermissionsResult(
diff --git a/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/Android.bp b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/Android.bp
new file mode 100644
index 0000000..91143ae
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/Android.bp
@@ -0,0 +1,31 @@
+//
+// 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: "CtsAppLocationProviderWithSummary",
+ defaults: ["mts-target-sdk-version-current"],
+ min_sdk_version: "31",
+ srcs: [
+ "src/**/*.kt",
+ ],
+ static_libs: [
+ "kotlin-stdlib",
+ ],
+}
diff --git a/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/AndroidManifest.xml b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/AndroidManifest.xml
new file mode 100644
index 0000000..020f613
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/AndroidManifest.xml
@@ -0,0 +1,40 @@
+<?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.permission3.cts.applocationproviderwithsummary">
+
+ <application
+ android:label="LocationProviderWithSummaryApp">
+ <activity android:name=".AddLocationProviderActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".AllServicesActivity" android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW_APP_FEATURES" />
+ </intent-filter>
+ <meta-data
+ android:name="app_features_preference_summary"
+ android:resource="@string/summary_label"/>
+ </activity>
+ </application>
+</manifest>
\ No newline at end of file
diff --git a/tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_delegatetargetactivity.xml b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/res/values/strings.xml
similarity index 80%
rename from tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_delegatetargetactivity.xml
rename to tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/res/values/strings.xml
index a8d062e..d6f150a 100644
--- a/tests/quickaccesswallet/res/xml/quickaccesswallet_configuration_delegatetargetactivity.xml
+++ b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/res/values/strings.xml
@@ -13,7 +13,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<quickaccesswallet-service
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:useTargetActivityForQuickAccess="true"/>
\ No newline at end of file
+<resources>
+ <string name="attribution_label">Attribution label.</string>
+ <string name="summary_label">Services summary.</string>
+</resources>
diff --git a/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AddLocationProviderActivity.kt b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AddLocationProviderActivity.kt
new file mode 100644
index 0000000..632bbb6
--- /dev/null
+++ b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AddLocationProviderActivity.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.permission3.cts.applocationproviderwithsummary
+
+import android.app.Activity
+import android.location.Criteria
+import android.location.LocationManager
+import android.os.Bundle
+
+/**
+ * An activity that adds this package as a test location provider.
+ */
+class AddLocationProviderActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val attrContext = createAttributionContext("test.tag")
+ val locationManager = attrContext.getSystemService(LocationManager::class.java)
+ locationManager.addTestProvider(
+ packageName, false, false, false, false, false, false, false, Criteria.POWER_LOW,
+ Criteria.ACCURACY_COARSE
+ )
+
+ setResult(RESULT_OK)
+ finish()
+ }
+}
\ No newline at end of file
diff --git a/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AllServicesActivity.kt
similarity index 71%
rename from tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
rename to tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AllServicesActivity.kt
index 533524b..94939de 100644
--- a/tests/quickaccesswallet/src/android/quickaccesswallet/UseTargetActivityForQuickAccessWalletService.java
+++ b/tests/tests/permission3/UsePermissionAppLocationProviderWithSummary/src/AllServicesActivity.kt
@@ -13,10 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+import android.app.Activity
+import android.os.Bundle
-package android.quickaccesswallet;
+class AllServicesActivity : Activity() {
-/**
- * Extends {@link TestQuickAccessWalletService} to allow for a different manifest configuration.
- */
-public class UseTargetActivityForQuickAccessWalletService extends TestQuickAccessWalletService {}
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
index dc9d177..5310f49 100644
--- a/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/NotificationPermissionTest.kt
@@ -58,8 +58,7 @@
const val INTENT_ACTION = "usepermission.createchannels.MAIN"
const val BROADCAST_ACTION = "usepermission.createchannels.BROADCAST"
const val NOTIFICATION_PERMISSION_ENABLED = "notification_permission_enabled"
-const val DELAY_MS = 5000L
-const val DELAY_MS_SHORT = 500L
+const val EXPECTED_TIMEOUT_MS = 2000L
@SdkSuppress(minSdkVersion = Build.VERSION_CODES.TIRAMISU, codeName = "Tiramisu")
class NotificationPermissionTest : BaseUsePermissionTest() {
@@ -211,8 +210,6 @@
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(startSecondActivity = true)
pressBack()
- assertDialogNotShowing(DELAY_MS_SHORT)
- Thread.sleep(DELAY_MS)
clickPermissionRequestAllowButton()
}
@@ -220,7 +217,6 @@
fun notificationPromptNotShownForSubsequentStartsIfTaskStartWasNotLauncher() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(mainIntent = false, startSecondActivity = true)
- Thread.sleep(DELAY_MS)
assertDialogNotShowing()
}
@@ -228,7 +224,6 @@
fun notificationPromptShownForChannelCreateInSecondActivityIfTaskStartWasLauncher() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(startSecondActivity = true, createChannels = false)
- Thread.sleep(DELAY_MS)
clickPermissionRequestAllowButton()
}
@@ -236,7 +231,6 @@
fun notificationPromptNotShownForChannelCreateInSecondActivityIfTaskStartWasntLauncher() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(mainIntent = false, startSecondActivity = true, createChannels = false)
- Thread.sleep(DELAY_MS)
assertDialogNotShowing()
}
@@ -246,7 +240,6 @@
installPackage(APP_APK_PATH_OTHER_APP, expectSuccess = true)
// perform a launcher start, then start a secondary app
launchApp(startSecondaryAppAndCreateChannelsAfterSecondStart = true)
- Thread.sleep(DELAY_MS)
try {
waitFindObject(By.textContains(SECOND_ACTIVITY_LABEL))
assertDialogNotShowing()
@@ -312,7 +305,7 @@
context.startActivity(grantPermission)
}
try {
- clickPermissionRequestAllowButton()
+ clickPermissionRequestAllowButton(timeoutMillis = EXPECTED_TIMEOUT_MS)
Assert.fail("Expected not to find permission request dialog")
} catch (expected: RuntimeException) {
// Do nothing
@@ -323,7 +316,6 @@
fun mergeAppPermissionRequestIntoNotificationAndVerifyResult() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(requestPermissionsDelayed = true)
- Thread.sleep(DELAY_MS)
clickPermissionRequestAllowButton()
assertAppPermissionGrantedState(POST_NOTIFICATIONS, granted = true)
clickPermissionRequestAllowForegroundButton()
@@ -337,7 +329,6 @@
fun mergeNotificationRequestIntoAppPermissionRequestAndVerifyResult() {
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(createChannels = false, createChannelsDelayed = true, requestPermissions = true)
- Thread.sleep(DELAY_MS)
clickPermissionRequestAllowForegroundButton()
assertAppPermissionGrantedState(RECORD_AUDIO, granted = true)
clickPermissionRequestAllowButton()
@@ -390,7 +381,7 @@
installPackage(APP_APK_PATH_CREATE_NOTIFICATION_CHANNELS_31, expectSuccess = true)
launchApp(createChannels = false, requestNotificationPermission = true)
try {
- clickPermissionRequestAllowButton()
+ clickPermissionRequestAllowButton(timeoutMillis = EXPECTED_TIMEOUT_MS)
Assert.fail("Expected not to find permission request dialog")
} catch (expected: RuntimeException) {
// Do nothing
@@ -430,7 +421,6 @@
private fun launchApp(
createChannels: Boolean = true,
createChannelsDelayed: Boolean = false,
- deleteChannels: Boolean = false,
requestNotificationPermission: Boolean = false,
requestPermissions: Boolean = false,
requestPermissionsDelayed: Boolean = false,
@@ -453,7 +443,6 @@
if (!createChannels) {
intent.putExtra(EXTRA_CREATE_CHANNELS_DELAYED, createChannelsDelayed)
}
- intent.putExtra(EXTRA_DELETE_CHANNELS_ON_CLOSE, deleteChannels)
intent.putExtra(EXTRA_REQUEST_OTHER_PERMISSIONS, requestPermissions)
if (!requestPermissions) {
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_DELAYED, requestPermissionsDelayed)
@@ -481,7 +470,7 @@
waitForIdle()
}
- private fun assertDialogNotShowing(timeoutMillis: Long = DELAY_MS) {
+ private fun assertDialogNotShowing(timeoutMillis: Long = EXPECTED_TIMEOUT_MS) {
try {
clickPermissionRequestAllowButton(timeoutMillis)
Assert.fail("Expected not to find permission request dialog")
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionAllServicesTest.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionAllServicesTest.kt
new file mode 100644
index 0000000..727a22f
--- /dev/null
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionAllServicesTest.kt
@@ -0,0 +1,162 @@
+/*
+ * 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.permission3.cts
+
+import android.app.Activity
+import android.app.AppOpsManager
+import com.android.compatibility.common.util.AppOpsUtils.setOpMode
+import android.content.ComponentName
+import android.content.Intent
+import android.location.LocationManager
+import android.net.Uri
+import android.provider.Settings
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import org.junit.Test
+import java.util.concurrent.TimeUnit
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertTrue
+import org.junit.Assert.assertNull
+import android.support.test.uiautomator.By
+import com.android.compatibility.common.util.SystemUtil.eventually
+
+class PermissionAllServicesTest : BasePermissionTest() {
+ val locationManager = context.getSystemService(LocationManager::class.java)!!
+
+ @Test
+ fun testAllServicesPreferenceShownWhenAppIsLocationProviderAndCanHandleClick() {
+ installPackage(LOCATION_PROVIDER_APP_APK_PATH_2, grantRuntimePermissions = true)
+ allowPackagesToMockLocation(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+ enableAppAsLocationProvider(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+
+ eventually({
+ try {
+ launchAppInfoActivity(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+ waitFindObject(By.textContains(ALL_SERVICES_LABEL))
+ } catch (e: Exception) {
+ pressBack()
+ throw e
+ } }, 1000L)
+
+ uninstallPackage(LOCATION_PROVIDER_APP_PACKAGE_NAME_2, requireSuccess = false)
+ locationManager.removeTestProvider(LOCATION_PROVIDER_APP_APK_PATH_2)
+ }
+
+ @Test
+ fun testAllServicesSummaryShowsWhenAppIsLocationProviderAndCanHandleClick() {
+ installPackage(LOCATION_PROVIDER_APP_APK_PATH_2, grantRuntimePermissions = true)
+ allowPackagesToMockLocation(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+ enableAppAsLocationProvider(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+
+ eventually({
+ try {
+ launchAppInfoActivity(LOCATION_PROVIDER_APP_PACKAGE_NAME_2)
+ waitFindObject(By.textContains(SUMMARY))
+ } catch (e: Exception) {
+ pressBack()
+ throw e
+ } }, 1000L)
+
+ uninstallPackage(LOCATION_PROVIDER_APP_PACKAGE_NAME_2, requireSuccess = false)
+ locationManager.removeTestProvider(LOCATION_PROVIDER_APP_APK_PATH_2)
+ }
+
+ @Test
+ fun testAllServicesPreferenceNotShownWhenAppCannotHandleClick() {
+ installPackage(LOCATION_PROVIDER_APP_APK_PATH_1, grantRuntimePermissions = true)
+ allowPackagesToMockLocation(LOCATION_PROVIDER_APP_PACKAGE_NAME_1)
+ enableAppAsLocationProvider(LOCATION_PROVIDER_APP_PACKAGE_NAME_1)
+
+ eventually({
+ try {
+ launchAppInfoActivity(LOCATION_PROVIDER_APP_PACKAGE_NAME_1)
+ assertNull(waitFindObjectOrNull(By.textContains(ALL_SERVICES_LABEL)))
+ } catch (e: Exception) {
+ pressBack()
+ throw e
+ } }, 1000L)
+
+ uninstallPackage(LOCATION_PROVIDER_APP_PACKAGE_NAME_1, requireSuccess = false)
+ locationManager.removeTestProvider(LOCATION_PROVIDER_APP_APK_PATH_1)
+ }
+
+ @Test
+ fun testAllServicesPreferenceNotShownWhenAppIsNotLocationProvider() {
+ installPackage(NON_LOCATION_APP_APK_PATH, grantRuntimePermissions = true)
+
+ eventually({
+ try {
+ launchAppInfoActivity(NON_LOCATION_APP_PACKAGE_NAME)
+ assertNull(waitFindObjectOrNull(By.textContains(ALL_SERVICES_LABEL)))
+ } catch (e: Exception) {
+ pressBack()
+ throw e
+ } }, 1000L)
+
+ uninstallPackage(NON_LOCATION_APP_APK_PATH, requireSuccess = false)
+ }
+
+ private fun allowPackagesToMockLocation(packageName: String) {
+ setOpMode(packageName, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpsManager.MODE_ALLOWED)
+ setOpMode(
+ context.packageName, AppOpsManager.OPSTR_MOCK_LOCATION, AppOpsManager.MODE_ALLOWED
+ )
+ }
+
+ private fun launchAppInfoActivity(packageName: String) {
+ context.startActivity(
+ Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
+ data = Uri.parse("package:$packageName")
+ addCategory(Intent.CATEGORY_DEFAULT)
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ })
+ }
+
+ private fun enableAppAsLocationProvider(appPackageName: String) {
+ // Add the test app as location provider.
+ val future = startActivityForFuture(
+ Intent().apply {
+ component = ComponentName(
+ appPackageName, "$appPackageName.AddLocationProviderActivity"
+ )
+ })
+
+ val result = future.get(TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
+ assertEquals(Activity.RESULT_OK, result.resultCode)
+ assertTrue(
+ callWithShellPermissionIdentity {
+ locationManager.isProviderPackage(appPackageName)
+ }
+ )
+ }
+
+ companion object {
+ const val LOCATION_PROVIDER_APP_APK_PATH_1 =
+ "$APK_DIRECTORY/CtsAccessMicrophoneAppLocationProvider.apk"
+ const val NON_LOCATION_APP_APK_PATH = "$APK_DIRECTORY/CtsUsePermissionApp22.apk"
+ const val LOCATION_PROVIDER_APP_APK_PATH_2 =
+ "$APK_DIRECTORY/CtsAppLocationProviderWithSummary.apk"
+ const val NON_LOCATION_APP_PACKAGE_NAME = "android.permission3.cts.usepermission"
+ const val LOCATION_PROVIDER_APP_PACKAGE_NAME_1 =
+ "android.permission3.cts.accessmicrophoneapplocationprovider"
+ const val LOCATION_PROVIDER_APP_PACKAGE_NAME_2 =
+ "android.permission3.cts.applocationproviderwithsummary"
+ const val APP_LABEL = "LocationProviderWithSummaryApp"
+ const val ALL_SERVICES_LABEL = "All Services"
+ const val SUMMARY = "Services summary."
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
index 699d111..448405d 100644
--- a/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
+++ b/tests/tests/permission3/src/android/permission3/cts/PermissionTest30WithBluetooth.kt
@@ -187,8 +187,8 @@
context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)
private fun enableTestMode() = runShellCommandOrThrow("dumpsys activity service" +
- " com.android.bluetooth/.btservice.AdapterService set-test-mode enabled")
+ " com.android.bluetooth.btservice.AdapterService set-test-mode enabled")
private fun disableTestMode() = runShellCommandOrThrow("dumpsys activity service" +
- " com.android.bluetooth/.btservice.AdapterService set-test-mode disabled")
+ " com.android.bluetooth.btservice.AdapterService set-test-mode disabled")
}
diff --git a/tests/tests/persistentdataservice/Android.bp b/tests/tests/persistentdataservice/Android.bp
new file mode 100644
index 0000000..ce31120
--- /dev/null
+++ b/tests/tests/persistentdataservice/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 {
+ name: "CtsPersistentDataBlockManagerTestCases",
+ defaults: ["cts_defaults"],
+ static_libs: [
+ "ctstestrunner-axt",
+ "Harrier",
+ "truth-prebuilt"
+ ],
+ libs: ["android.test.base"],
+ srcs: ["src/**/*.java"],
+ sdk_version: "test_current",
+ min_sdk_version: "27",
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
diff --git a/tests/tests/persistentdataservice/AndroidManifest.xml b/tests/tests/persistentdataservice/AndroidManifest.xml
new file mode 100644
index 0000000..e5db957
--- /dev/null
+++ b/tests/tests/persistentdataservice/AndroidManifest.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.persistentdata.cts">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <!-- This is a self-instrumenting test package. -->
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="android.persistentdata.cts"
+ android:label="CTS tests of PersistentDataBlockManager">
+ <meta-data android:name="listener"
+ android:value="com.android.cts.runner.CtsTestRunListener" />
+ </instrumentation>
+
+ <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="17"></uses-sdk>
+
+</manifest>
+
diff --git a/tests/tests/persistentdataservice/AndroidTest.xml b/tests/tests/persistentdataservice/AndroidTest.xml
new file mode 100644
index 0000000..e47032f
--- /dev/null
+++ b/tests/tests/persistentdataservice/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Config for CTS PersistentDataBlockManager test cases">
+ <option name="test-suite-tag" value="cts" />
+
+ <option name="config-descriptor:metadata" key="component" value="framework" />
+
+ <option name="not-shardable" value="true" />
+ <option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
+ <option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
+ <option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <option name="test-file-name" value="CtsPersistentDataBlockManagerTestCases.apk" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="android.persistentdata.cts" />
+ <option name="runtime-hint" value="8m30s" />
+ </test>
+</configuration>
diff --git a/tests/tests/persistentdataservice/OWNERS b/tests/tests/persistentdataservice/OWNERS
new file mode 100644
index 0000000..f4d73eb
--- /dev/null
+++ b/tests/tests/persistentdataservice/OWNERS
@@ -0,0 +1,3 @@
+# Bug component: 24950
+omakoto@google.com
+yamasani@google.com
diff --git a/tests/tests/persistentdataservice/TEST_MAPPING b/tests/tests/persistentdataservice/TEST_MAPPING
new file mode 100644
index 0000000..4772e4f
--- /dev/null
+++ b/tests/tests/persistentdataservice/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "CtsPersistentDataBlockManagerTestCases"
+ }
+ ]
+}
diff --git a/tests/tests/persistentdataservice/src/android/service/persistentdata/PersistentDataBlockManagerTest.java b/tests/tests/persistentdataservice/src/android/service/persistentdata/PersistentDataBlockManagerTest.java
new file mode 100644
index 0000000..70e97ea
--- /dev/null
+++ b/tests/tests/persistentdataservice/src/android/service/persistentdata/PersistentDataBlockManagerTest.java
@@ -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 android.service.persistentdata;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.junit.Assert.assertThrows;
+
+import android.content.Context;
+
+import com.android.bedstead.harrier.BedsteadJUnit4;
+import com.android.bedstead.harrier.DeviceState;
+import com.android.bedstead.harrier.annotations.EnsureDoesNotHavePermission;
+import com.android.bedstead.harrier.annotations.EnsureHasPermission;
+import com.android.bedstead.nene.TestApis;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(BedsteadJUnit4.class)
+public class PersistentDataBlockManagerTest {
+ @ClassRule
+ @Rule
+ public static final DeviceState sDeviceState = new DeviceState();
+
+ private static final Context sContext = TestApis.context().instrumentedContext();
+ private static final PersistentDataBlockManager sPersistentDataBlockManager =
+ sContext.getSystemService(PersistentDataBlockManager.class);
+
+ @EnsureHasPermission(android.Manifest.permission.ACCESS_PDB_STATE)
+ @Test
+ public void getPersistentDataPackageName_returnsNonNullResult() {
+ if (sPersistentDataBlockManager == null) {
+ return;
+ }
+ assertThat(sPersistentDataBlockManager.getPersistentDataPackageName()).isNotNull();
+ }
+
+ @EnsureDoesNotHavePermission(android.Manifest.permission.ACCESS_PDB_STATE)
+ @Test
+ public void getPersistentDataPackageName_withoutPermission_throwsException() {
+ if (sPersistentDataBlockManager == null) {
+ return;
+ }
+ assertThrows(SecurityException.class,
+ sPersistentDataBlockManager::getPersistentDataPackageName);
+ }
+}
diff --git a/tests/tests/provider/Android.bp b/tests/tests/provider/Android.bp
index e293332..5a1b7a0 100644
--- a/tests/tests/provider/Android.bp
+++ b/tests/tests/provider/Android.bp
@@ -47,7 +47,8 @@
// uncomment when b/140885436 is fixed
// sdk_version: "test_current",
min_sdk_version: "21",
- target_sdk_version: "Tiramisu",
+ //TODO(b/227617884): Change target_sdk_version to 33 after T SDK finalization is complete
+ target_sdk_version: "10000",
platform_apis: true,
diff --git a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
index 1deafdd..ca85b65 100644
--- a/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
+++ b/tests/tests/security/src/android/security/cts/AndroidFutureTest.java
@@ -37,7 +37,7 @@
@RunWith(AndroidJUnit4.class)
public class AndroidFutureTest extends StsExtraBusinessLogicTestCase {
- @AsbSecurityTest(cveBugId = 186530450)
+ @AsbSecurityTest(cveBugId = 197228210)
@Test
public void testAndroidFutureReadThrowable() throws Exception {
String filePath = "/data/system/" + System.currentTimeMillis();
diff --git a/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java b/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java
index 65eb132..ecf8acc 100644
--- a/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java
+++ b/tests/tests/security/src/android/security/cts/MediaMetadataRetrieverTest.java
@@ -22,31 +22,24 @@
import android.content.res.Resources;
import android.media.MediaMetadataRetriever;
import android.platform.test.annotations.AsbSecurityTest;
-import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import android.test.AndroidTestCase;
import java.io.IOException;
-import org.junit.Before;
-import org.junit.After;
-import static org.junit.Assert.*;
-
-import androidx.test.runner.AndroidJUnit4;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-@RunWith(AndroidJUnit4.class)
-public class MediaMetadataRetrieverTest extends StsExtraBusinessLogicTestCase {
+public class MediaMetadataRetrieverTest extends AndroidTestCase {
protected Resources mResources;
protected MediaMetadataRetriever mRetriever;
- @Before
- public void setUp() throws Exception {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
mResources = getContext().getResources();
mRetriever = new MediaMetadataRetriever();
}
- @After
- public void tearDown() throws Exception {
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
mRetriever.release();
}
@@ -60,7 +53,6 @@
}
}
- @Test
@AsbSecurityTest(cveBugId = 24623447)
public void testID3v2EmbeddedPicture() {
setDataSourceFd(R.raw.id3v2_3_extended_header_overflow_padding);
diff --git a/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java b/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java
index 83ce8ff..2d2e084 100644
--- a/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java
+++ b/tests/tests/security/src/android/security/cts/MediaServerCrashTest.java
@@ -24,7 +24,7 @@
import android.os.Environment;
import android.os.ParcelFileDescriptor;
import android.platform.test.annotations.AsbSecurityTest;
-import com.android.sts.common.util.StsExtraBusinessLogicTestCase;
+import android.test.AndroidTestCase;
import android.util.Log;
import com.android.compatibility.common.util.MediaUtils;
@@ -38,16 +38,7 @@
import android.security.cts.R;
-import org.junit.Before;
-import org.junit.After;
-import static org.junit.Assert.*;
-
-import androidx.test.runner.AndroidJUnit4;
-import org.junit.runner.RunWith;
-import org.junit.Test;
-
-@RunWith(AndroidJUnit4.class)
-public class MediaServerCrashTest extends StsExtraBusinessLogicTestCase {
+public class MediaServerCrashTest extends AndroidTestCase {
private static final String TAG = "MediaServerCrashTest";
private static final String MIMETYPE_DRM_MESSAGE = "application/vnd.oma.drm.message";
@@ -58,8 +49,9 @@
private final ConditionVariable mOnPrepareCalled = new ConditionVariable();
private final ConditionVariable mOnCompletionCalled = new ConditionVariable();
- @Before
- public void setUp() throws Exception {
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
mFlFilePath = new File(getContext().getFilesDir(), "temp.fl").getAbsolutePath();
mOnPrepareCalled.close();
@@ -91,12 +83,12 @@
});
}
- @After
- public void tearDown() throws Exception {
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
new File(mFlFilePath).delete();
}
- @Test
@AsbSecurityTest(cveBugId = 25070434)
public void testInvalidMidiNullPointerAccess() throws Exception {
testIfMediaServerDied(R.raw.midi_crash);
@@ -123,17 +115,16 @@
}
}
- @Test
@AsbSecurityTest(cveBugId = 25070434)
public void testDrmManagerClientReset() throws Exception {
checkIfMediaServerDiedForDrm(R.raw.drm_uaf);
}
private void checkIfMediaServerDiedForDrm(int res) throws Exception {
- AssetFileDescriptor afd = getInstrumentation().getContext().getResources().openRawResourceFd(res);
+ AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(res);
FileInputStream dmStream = afd.createInputStream();
RandomAccessFile flFile = new RandomAccessFile(mFlFilePath, "rw");
- if (!MediaUtils.convertDmToFl(getInstrumentation().getContext(), dmStream, flFile)) {
+ if (!MediaUtils.convertDmToFl(mContext, dmStream, flFile)) {
Log.w(TAG, "Can not convert dm to fl, skip checkIfMediaServerDiedForDrm");
mMediaPlayer.release();
return;
diff --git a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherCallbackTest.java b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherCallbackTest.java
index 8467f9a..0de6253 100644
--- a/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherCallbackTest.java
+++ b/tests/tests/shortcutmanager/src/android/content/pm/cts/shortcutmanager/ShortcutManagerLauncherCallbackTest.java
@@ -29,6 +29,7 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.util.Log;
+import com.android.compatibility.common.util.CddTest;
import com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.ShortcutListAsserter;
import java.util.ArrayList;
@@ -36,20 +37,20 @@
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
-import com.android.compatibility.common.util.CddTest;
-
@CddTest(requirement="3.8.1/C-2-3")
@SmallTest
public class ShortcutManagerLauncherCallbackTest extends ShortcutManagerCtsTestsBase {
private static class MyCallback extends LauncherApps.Callback {
private final HashSet<String> mInterestedPackages = new HashSet<String>();
- private boolean called;
+ private final AtomicInteger mCallcount = new AtomicInteger(0);
private String lastPackage;
private final List<ShortcutInfo> lastShortcuts = new ArrayList<>();
+
public MyCallback(String... interestedPackages) {
mInterestedPackages.addAll(Arrays.asList(interestedPackages));
}
@@ -98,17 +99,17 @@
lastPackage = packageName;
lastShortcuts.clear();
lastShortcuts.addAll(shortcuts);
- called = true;
+ mCallcount.incrementAndGet();
}
public synchronized void reset() {
lastPackage = null;
lastShortcuts.clear();
- called = false;
+ mCallcount.set(0);
}
public synchronized boolean isCalled() {
- return called;
+ return mCallcount.get() > 0;
}
public synchronized ShortcutListAsserter assertCalled(Context clientContext) {
@@ -343,6 +344,16 @@
.areAllDisabled();
reset.run();
+ //-----------------------
+ Log.i(TAG, "testCallbacks: pushDynamicShortcut");
+ runWithCaller(mPackageContext1, () -> {
+ for (int i = 0; i < 100; i++) {
+ getManager().pushDynamicShortcut(makeShortcut("s" + i));
+ }
+ });
+ retryUntil(c::isCalled, "callback not called.");
+ assertEquals(1, c.mCallcount.get());
+ reset.run();
} finally {
runWithCaller(mLauncherContext1, () -> {
if (registered.get()) {
diff --git a/tests/tests/telephony/OWNERS b/tests/tests/telephony/OWNERS
index d28e9ff..02848c8 100644
--- a/tests/tests/telephony/OWNERS
+++ b/tests/tests/telephony/OWNERS
@@ -1,16 +1,2 @@
-# Bug component: 20868
-amitmahajan@google.com
-breadley@google.com
-fionaxu@google.com
-jackyu@google.com
-rgreenwalt@google.com
-tgunn@google.com
-jminjie@google.com
-shuoq@google.com
-sarahchin@google.com
-xiaotonj@google.com
-huiwang@google.com
-jayachandranc@google.com
-chinmayd@google.com
-amruthr@google.com
-sasindran@google.com
+file:platform/frameworks/opt/telephony:/OWNERS
+
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
index 86a2c8f7..b18a355 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioConfigImpl.java
@@ -265,4 +265,14 @@
Log.e(TAG, "null mRadioConfigIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioConfig.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioConfig.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
index e9fff8e..92a22c3 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioDataImpl.java
@@ -222,4 +222,14 @@
Log.e(TAG, "Failed to stopKeepalive from AIDL. Exception" + ex);
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioData.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioData.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
index 4cca64b..8f6a9a9 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioModemImpl.java
@@ -472,4 +472,14 @@
break;
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioModem.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioModem.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
index c914f79..fb3dbca 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioNetworkImpl.java
@@ -505,4 +505,14 @@
Log.e(TAG, "Failed to getUsageSetting from AIDL. Exception" + ex);
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioNetwork.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioNetwork.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
index 08980a3..b6e9d00 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioSimImpl.java
@@ -838,4 +838,14 @@
Log.e(TAG, "null mRadioSimIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioSim.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioSim.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java b/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java
index 4a415cd..c6fdfdf 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/IRadioVoiceImpl.java
@@ -749,4 +749,14 @@
Log.e(TAG, "null mRadioVoiceIndication");
}
}
+
+ @Override
+ public String getInterfaceHash() {
+ return IRadioVoice.HASH;
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return IRadioVoice.VERSION;
+ }
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
index 8d88810..137a43b 100755
--- a/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/SubscriptionManagerTest.java
@@ -64,6 +64,7 @@
import androidx.test.InstrumentationRegistry;
import com.android.compatibility.common.util.CarrierPrivilegeUtils;
+import com.android.compatibility.common.util.PropertyUtil;
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
import com.android.compatibility.common.util.TestThread;
@@ -81,7 +82,9 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -1217,45 +1220,78 @@
}
}
- @Test
- public void testCellularUsageSetting() throws Exception {
- if (!isSupported()) return;
-
- boolean isUsageSettingSupported = true;
- int defaultUsageSetting = SubscriptionManager.USAGE_SETTING_DEFAULT;
- int[] supportedUsageSettings;
+ private Set<Integer> getSupportedUsageSettings() throws Exception {
+ final Set<Integer> supportedUsageSettings = new HashSet();
final Context context = InstrumentationRegistry.getContext();
- // Load the resources to provide the device capability
+ // Vendors can add supported usage settings by adding resources.
try {
- defaultUsageSetting = context.getResources().getInteger(
- com.android.internal.R.integer.config_default_cellular_usage_setting);
- supportedUsageSettings = context.getResources().getIntArray(
+ int[] usageSettingsFromResource = context.getResources().getIntArray(
com.android.internal.R.array.config_supported_cellular_usage_settings);
- // If usage settings are not supported, return the default setting, which is UNKNOWN.
- if (supportedUsageSettings.length < 1) {
- isUsageSettingSupported = false;
- fail("Usage Setting resources empty");
+
+ for (int setting : usageSettingsFromResource) {
+ supportedUsageSettings.add(setting);
}
- } catch (Resources.NotFoundException nfe) {
- fail("Usage Setting resources not found");
- isUsageSettingSupported = false;
+
+ } catch (Resources.NotFoundException ignore) {
}
- int[] settingsToTest = new int[] {
- SubscriptionManager.USAGE_SETTING_DEFAULT,
- defaultUsageSetting};
+ // For devices shipping with Radio HAL 2.0 and/or non-HAL devices launching with T,
+ // the usage settings are required to be supported if the rest of the telephony stack
+ // has support for that mode of operation.
+ if (PropertyUtil.isVendorApiLevelAtLeast(android.os.Build.VERSION_CODES.TIRAMISU)) {
+ final PackageManager pm = InstrumentationRegistry.getContext().getPackageManager();
- for (int setting : settingsToTest) {
- PersistableBundle bundle = new PersistableBundle();
- bundle.putInt(CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT, setting);
- overrideCarrierConfig(bundle, mSubId);
- SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
- (sm) -> sm.getActiveSubscriptionInfo(mSubId));
- assertEquals(
- isUsageSettingSupported ? setting :
- SubscriptionManager.USAGE_SETTING_UNKNOWN,
- info.getUsageSetting());
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_DATA)) {
+ supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_DATA_CENTRIC);
+ }
+ if (pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CALLING)) {
+ supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC);
+ }
+ }
+
+ return supportedUsageSettings;
+ }
+
+ private int getUsageSetting() throws Exception {
+ SubscriptionInfo info = ShellIdentityUtils.invokeMethodWithShellPermissions(mSm,
+ (sm) -> sm.getActiveSubscriptionInfo(mSubId));
+ return info.getUsageSetting();
+ }
+
+ private void checkUsageSetting(int inputSetting, boolean isSupported) throws Exception {
+ final int initialSetting = getUsageSetting();
+
+ PersistableBundle bundle = new PersistableBundle();
+ bundle.putInt(CarrierConfigManager.KEY_CELLULAR_USAGE_SETTING_INT, inputSetting);
+ overrideCarrierConfig(bundle, mSubId);
+
+ final int newSetting = getUsageSetting();
+ assertEquals(isSupported ? inputSetting : initialSetting, newSetting);
+ }
+
+ @Test
+ public void testCellularUsageSetting() throws Exception {
+ Set<Integer> supportedUsageSettings = getSupportedUsageSettings();
+
+ // If any setting works, default must be allowed.
+ if (supportedUsageSettings.size() > 0) {
+ supportedUsageSettings.add(SubscriptionManager.USAGE_SETTING_DEFAULT);
+ }
+
+ final int[] allUsageSettings = new int[]{
+ SubscriptionManager.USAGE_SETTING_UNKNOWN,
+ SubscriptionManager.USAGE_SETTING_DEFAULT,
+ SubscriptionManager.USAGE_SETTING_VOICE_CENTRIC,
+ SubscriptionManager.USAGE_SETTING_DATA_CENTRIC,
+ 3 /* undefined value */};
+
+ try {
+ for (int setting : allUsageSettings) {
+ checkUsageSetting(setting, supportedUsageSettings.contains(setting));
+ }
+ } finally {
+ overrideCarrierConfig(null, mSubId);
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
index 482cd92..89f34c4 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyCallbackTest.java
@@ -185,7 +185,13 @@
private void registerTelephonyCallback(@NonNull TelephonyCallback callback,
boolean renounceFine, boolean renounceCoarse) {
- mTelephonyManager.registerTelephonyCallback(renounceFine, renounceCoarse, mSimpleExecutor,
+ int includeLocationData = TelephonyManager.INCLUDE_LOCATION_DATA_FINE;
+ if (renounceFine && renounceCoarse) {
+ includeLocationData = TelephonyManager.INCLUDE_LOCATION_DATA_NONE;
+ } else if (renounceFine) {
+ includeLocationData = TelephonyManager.INCLUDE_LOCATION_DATA_COARSE;
+ }
+ mTelephonyManager.registerTelephonyCallback(includeLocationData, mSimpleExecutor,
callback);
}
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 f6148af..1436e51 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTest.java
@@ -1380,11 +1380,12 @@
}
assertEquals(mServiceState, mTelephonyManager.getServiceState());
- assertServiceStateSanitization(mServiceState, mTelephonyManager.getServiceState(true,
- true));
+ assertServiceStateSanitization(mServiceState, mTelephonyManager.getServiceState(
+ TelephonyManager.INCLUDE_LOCATION_DATA_NONE));
assertServiceStateFineLocationSanitization(mServiceState,
- mTelephonyManager.getServiceState(true, false));
- assertEquals(mServiceState, mTelephonyManager.getServiceState(false, true));
+ mTelephonyManager.getServiceState(TelephonyManager.INCLUDE_LOCATION_DATA_COARSE));
+ assertEquals(mServiceState, mTelephonyManager.getServiceState(
+ TelephonyManager.INCLUDE_LOCATION_DATA_FINE));
}
private void assertServiceStateSanitization(ServiceState expectedServiceState,
@@ -3549,8 +3550,6 @@
InstrumentationRegistry.getInstrumentation().getUiAutomation()
.adoptShellPermissionIdentity("android.permission.READ_PRIVILEGED_PHONE_STATE");
List<UiccCardInfo> cardsInfo = mTelephonyManager.getUiccCardsInfo();
- InstrumentationRegistry.getInstrumentation().getUiAutomation()
- .dropShellPermissionIdentity();
for (UiccCardInfo cardInfo : cardsInfo) {
for (UiccPortInfo portInfo : cardInfo.getPorts()) {
int simCardState = mTelephonyManager.getSimCardState(cardInfo
@@ -3562,6 +3561,8 @@
TelephonyManager.SIM_STATE_PRESENT).contains(simCardState));
}
}
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
}
private boolean isDataEnabled() {
@@ -4372,6 +4373,7 @@
return;
}
+ boolean connectedToNrCell = false;
for (CellInfo cellInfo : mTelephonyManager.getAllCellInfo()) {
CellIdentity cellIdentity = cellInfo.getCellIdentity();
int[] bands;
@@ -4389,11 +4391,21 @@
|| (band >= AccessNetworkConstants.NgranBands.BAND_257
&& band <= AccessNetworkConstants.NgranBands.BAND_261));
}
+ if (cellInfo.isRegistered()) {
+ connectedToNrCell = true;
+ }
} else {
continue;
}
assertTrue(bands.length > 0);
}
+
+ if (connectedToNrCell) {
+ assertEquals(TelephonyManager.NETWORK_TYPE_NR, mTelephonyManager.getDataNetworkType());
+ } else {
+ assertNotEquals(TelephonyManager.NETWORK_TYPE_NR,
+ mTelephonyManager.getDataNetworkType());
+ }
}
/**
diff --git a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
index 9810d1b..0bd0236 100644
--- a/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
+++ b/tests/tests/telephony/current/src/android/telephony/cts/TelephonyManagerTestOnMockModem.java
@@ -158,7 +158,7 @@
}
// Wait the radio state update in Framework
- TimeUnit.SECONDS.sleep(1);
+ TimeUnit.SECONDS.sleep(2);
int toggleRadioState =
radioState == TelephonyManager.RADIO_POWER_ON
? TelephonyManager.RADIO_POWER_OFF
@@ -177,7 +177,7 @@
}
// Wait the radio state update in Framework
- TimeUnit.SECONDS.sleep(1);
+ TimeUnit.SECONDS.sleep(2);
assertEquals(sTelephonyManager.getRadioPowerState(), radioState);
Log.d(TAG, "Test Done ");
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
index b563d16..62f0f01 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsCallingTest.java
@@ -381,7 +381,6 @@
if (sServiceConnector != null && sIsBound) {
sServiceConnector.disconnectCarrierImsService();
- sServiceConnector.disconnectServices();
sIsBound = false;
}
}
diff --git a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
index 3495b66..692b32f 100644
--- a/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
+++ b/tests/tests/telephony/current/src/android/telephony/ims/cts/ImsServiceTest.java
@@ -2165,6 +2165,37 @@
automation.dropShellPermissionIdentity();
}
+ // Reply the SIP code 504 SERVER TIMEOUT
+ capExchangeImpl.setPublishOperator((listener, pidfXml, cb) -> {
+ int networkResp = 504;
+ String reason = "SERVER TIMEOUT";
+ cb.onNetworkResponse(networkResp, reason);
+ listener.onPublish();
+ });
+
+ // Notify framework to send the PUBLISH request to the ImsService.
+ eventListener.onRequestPublishCapabilities(
+ RcsUceAdapter.CAPABILITY_UPDATE_TRIGGER_MOVE_TO_WLAN);
+
+ // Verify ImsService receive the publish request from framework.
+ assertTrue(sServiceConnector.getCarrierService().waitForLatchCountdown(
+ TestImsService.LATCH_UCE_REQUEST_PUBLISH));
+
+ try {
+ // Verify the publish state callback is received.
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_PUBLISHING,
+ waitForIntResult(publishStateQueue));
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR,
+ waitForIntResult(publishStateQueue));
+ // Verify the value of getting from the API is PUBLISH_STATE_RCS_PROVISION_ERROR
+ automation.adoptShellPermissionIdentity();
+ int publishState = uceAdapter.getUcePublishState();
+ assertEquals(RcsUceAdapter.PUBLISH_STATE_RCS_PROVISION_ERROR, publishState);
+ } finally {
+ publishStateQueue.clear();
+ automation.dropShellPermissionIdentity();
+ }
+
LinkedBlockingQueue<Integer> errorQueue = new LinkedBlockingQueue<>();
LinkedBlockingQueue<Long> errorRetryQueue = new LinkedBlockingQueue<>();
LinkedBlockingQueue<Boolean> completeQueue = new LinkedBlockingQueue<>();
diff --git a/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java b/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java
index 92904b2..4dd61b4 100644
--- a/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java
+++ b/tests/tests/telephony4/src/android/telephony4/cts/SimRestrictedApisTest.java
@@ -19,8 +19,10 @@
import static androidx.test.InstrumentationRegistry.getContext;
import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
@@ -37,6 +39,9 @@
@Before
public void setUp() throws Exception {
+ assumeTrue(getContext().getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_TELEPHONY));
+
mTelephonyManager =
(TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
}
diff --git a/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppManagerTest.java b/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppManagerTest.java
index 0848ec4..250575a 100644
--- a/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppManagerTest.java
+++ b/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppManagerTest.java
@@ -181,7 +181,10 @@
List<TvInteractiveAppServiceInfo> list = mManager.getTvInteractiveAppServiceList();
for (TvInteractiveAppServiceInfo info : list) {
- if (info.getServiceInfo().name.equals(StubTvInteractiveAppService.class.getName())) {
+ if (info.getServiceInfo().name.equals(StubTvInteractiveAppService.class.getName())
+ && info.getSupportedTypes()
+ == (TvInteractiveAppServiceInfo.INTERACTIVE_APP_TYPE_HBBTV
+ | TvInteractiveAppServiceInfo.INTERACTIVE_APP_TYPE_GINGA)) {
return;
}
}
diff --git a/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppViewTest.java b/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppViewTest.java
index 308bafe..103eec9 100644
--- a/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppViewTest.java
+++ b/tests/tests/tv/src/android/media/tv/interactive/cts/TvInteractiveAppViewTest.java
@@ -27,6 +27,7 @@
import android.media.tv.interactive.TvInteractiveAppView;
import android.os.ConditionVariable;
import android.tv.cts.R;
+import android.view.InputEvent;
import androidx.test.core.app.ActivityScenario;
import androidx.test.platform.app.InstrumentationRegistry;
@@ -56,6 +57,7 @@
private TvInteractiveAppView mTvInteractiveAppView;
private TvInteractiveAppManager mManager;
private TvInteractiveAppServiceInfo mStubInfo;
+ private TvInteractiveAppView.OnUnhandledInputEventListener mOnUnhandledInputEventListener;
@Rule
public RequiredFeatureRule featureRule = new RequiredFeatureRule(
@@ -181,4 +183,23 @@
}
}.run();
}
+
+ @Test
+ public void testGetOnUnhandledInputEventListener() {
+ mOnUnhandledInputEventListener = new TvInteractiveAppView.OnUnhandledInputEventListener() {
+ @Override
+ public boolean onUnhandledInputEvent(InputEvent event) {
+ return true;
+ }
+ };
+ mTvInteractiveAppView.setOnUnhandledInputEventListener(getExecutor(),
+ mOnUnhandledInputEventListener);
+ new PollingCheck(TIME_OUT_MS) {
+ @Override
+ protected boolean check() {
+ return mTvInteractiveAppView.getOnUnhandledInputEventListener()
+ == mOnUnhandledInputEventListener;
+ }
+ }.run();
+ }
}
diff --git a/tests/tests/uidmigration/OWNERS b/tests/tests/uidmigration/OWNERS
index e69fd97..966a94a 100644
--- a/tests/tests/uidmigration/OWNERS
+++ b/tests/tests/uidmigration/OWNERS
@@ -1,3 +1,4 @@
# Bug component: 315013
topjohnwu@google.com
ashfall@google.com
+mpgroover@google.com
diff --git a/tests/tests/uidmigration/lib/src/android/uidmigration/cts/Const.kt b/tests/tests/uidmigration/lib/src/android/uidmigration/cts/Const.kt
index e08d627..9064aa5 100644
--- a/tests/tests/uidmigration/lib/src/android/uidmigration/cts/Const.kt
+++ b/tests/tests/uidmigration/lib/src/android/uidmigration/cts/Const.kt
@@ -19,6 +19,7 @@
object Const {
const val CTS_TEST_PKG = "android.uidmigration.cts"
const val INSTALL_TEST_PKG = "android.uidmigration.cts.InstallTestApp"
+ const val INSTALL_TEST_PKG2 = "${INSTALL_TEST_PKG}2"
const val PERM_TEST_PKG = "android.uidmigration.cts.PermissionTestApp"
const val DATA_TEST_PKG = "android.uidmigration.cts.DataTestApp"
const val ACTION_UPDATE_ACK = "android.uidmigration.cts.ACTION_UPDATE_ACK"
diff --git a/tests/tests/uidmigration/src/android/uidmigration/cts/AppIdMigrationTest.kt b/tests/tests/uidmigration/src/android/uidmigration/cts/AppIdMigrationTest.kt
index cb84f3f..1cc9980 100644
--- a/tests/tests/uidmigration/src/android/uidmigration/cts/AppIdMigrationTest.kt
+++ b/tests/tests/uidmigration/src/android/uidmigration/cts/AppIdMigrationTest.kt
@@ -70,7 +70,7 @@
// Both app should share the same UID.
val uid = mPm.getPackageUid(Const.INSTALL_TEST_PKG, PackageInfoFlags.of(0))
var pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(2, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG, Const.INSTALL_TEST_PKG + "2"))
// Should not allow upgrading to an APK that directly removes sharedUserId.
assertFalse(installPackage(InstallTest.APK3))
@@ -78,7 +78,7 @@
// Leave shared UID.
assertTrue(installPackage(InstallTest.APK4))
pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(1, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG + "2"))
uninstallPackage(Const.INSTALL_TEST_PKG)
uninstallPackage(Const.INSTALL_TEST_PKG + "2")
diff --git a/tests/tests/uidmigration/src/android/uidmigration/cts/Common.kt b/tests/tests/uidmigration/src/android/uidmigration/cts/Common.kt
index a525ff8..4a0a8863 100644
--- a/tests/tests/uidmigration/src/android/uidmigration/cts/Common.kt
+++ b/tests/tests/uidmigration/src/android/uidmigration/cts/Common.kt
@@ -49,6 +49,10 @@
@Suppress("NOTHING_TO_INLINE")
inline fun assertEquals(a: Int, b: Int) = assertEquals(a.toLong(), b.toLong())
+// Identical regardless of order
+fun <T> Array<T>.sameAs(vararg items: T) =
+ size == items.size && all { items.contains(it) } && items.all { contains(it) }
+
fun installPackage(apkPath: String): Boolean {
return runShellCommand("pm install --force-queryable -t $apkPath") == "Success\n"
}
diff --git a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
index 3958a12..1f7af46 100644
--- a/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
+++ b/tests/tests/uidmigration/src/android/uidmigration/cts/SharedUserMigrationTest.kt
@@ -48,7 +48,7 @@
@After
fun tearDown() {
uninstallPackage(Const.INSTALL_TEST_PKG)
- uninstallPackage(Const.INSTALL_TEST_PKG + "2")
+ uninstallPackage(Const.INSTALL_TEST_PKG2)
}
// Restore and ensure both test apps are sharing UID.
@@ -56,7 +56,7 @@
uninstallPackage(Const.INSTALL_TEST_PKG)
assertTrue(installPackage(InstallTest.APK))
val pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(2, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG, Const.INSTALL_TEST_PKG2))
}
private fun testNewInstallOnly(uid: Int) {
@@ -72,7 +72,7 @@
assertTrue(installPackage(InstallTest.APK4))
var pkgs = mPm.getPackagesForUid(uid).assertNotNull()
// With NEW_INSTALL_ONLY, upgrades should not change appId.
- assertEquals(2, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG, Const.INSTALL_TEST_PKG2))
pkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO)
assertNotNull(pkgInfo.sharedUserId)
@@ -84,7 +84,7 @@
assertTrue(installPackage(InstallTest.APK4))
pkgs = mPm.getPackagesForUid(uid).assertNotNull()
// Newly installed apps with sharedUserMaxSdkVersion set should not join shared UID.
- assertEquals(1, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG2))
pkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO)
assertNull(pkgInfo.sharedUserId)
}
@@ -95,17 +95,17 @@
assertTrue(installPackage(InstallTest.APK4))
var pkgs = mPm.getPackagesForUid(uid).assertNotNull()
// With BEST_EFFORT, upgrades should also not change appId.
- assertEquals(2, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG, Const.INSTALL_TEST_PKG2))
var pkgInfo = mPm.getPackageInfo(Const.INSTALL_TEST_PKG, FLAG_ZERO)
assertNotNull(pkgInfo.sharedUserId)
val oldUidName = mPm.getNameForUid(uid)
- uninstallPackage(Const.INSTALL_TEST_PKG + "2")
+ uninstallPackage(Const.INSTALL_TEST_PKG2)
// There should be only 1 package left in the shared UID group.
// This should trigger the transparent shared UID migration.
pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(1, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG))
// Confirm that the internal PackageSetting is actually migrated.
val newUidName = mPm.getNameForUid(uid)
@@ -116,7 +116,7 @@
// Even installing another shared UID app, the appId shall not be reused.
assertTrue(installPackage(InstallTest.APK2))
pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(1, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG))
}
@Test
@@ -127,7 +127,7 @@
// Both app should share the same UID.
val uid = mPm.getPackageUid(Const.INSTALL_TEST_PKG, FLAG_ZERO)
val pkgs = mPm.getPackagesForUid(uid).assertNotNull()
- assertEquals(2, pkgs.size)
+ assertTrue(pkgs.sameAs(Const.INSTALL_TEST_PKG, Const.INSTALL_TEST_PKG2))
if (Build.IS_USERDEBUG) {
testNewInstallOnly(uid)
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 ddf9c54..7d73da9 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ExactCanvasTests.java
@@ -69,6 +69,7 @@
p.setAntiAlias(false);
p.setStrokeWidth(1f);
p.setColor(Color.BLACK);
+ canvas.translate(0.5f, 0.5f);
for (int i = 0; i < 10; i++) {
canvas.drawPoint(i * 10, i * 10, p);
}
@@ -129,7 +130,7 @@
};
canvas.drawLines(pts, p);
})
- .runWithComparer(mExactComparer);
+ .runWithComparer(new MSSIMComparer(0.99));
}
@Test
@@ -150,7 +151,7 @@
p.setStrokeWidth(5);
canvas.drawText(testString, 30, 50, p);
})
- .runWithComparer(mExactComparer);
+ .runWithComparer(new MSSIMComparer(0.99));
}
private void drawTestTextOnPath(Canvas canvas) {
diff --git a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
index acfc9cc..3bbac74 100644
--- a/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testinfrastructure/DisplayModifier.java
@@ -36,7 +36,7 @@
public abstract class DisplayModifier {
private static final RectF RECT = new RectF(0, 0, 100, 100);
private static final float[] POINTS = new float[]{
- 0, 40, 40, 0, 40, 80, 80, 40
+ 0.5f, 40.5f, 40.5f, 0.5f, 40.5f, 80.5f, 80.5f, 40.5f
};
private static final float[] TRIANGLE_POINTS = new float[]{
40, 0, 80, 80, 80, 80, 0, 80, 0, 80, 40, 0
diff --git a/tests/tests/usb/AndroidTest.xml b/tests/tests/usb/AndroidTest.xml
index fb75184..ef71236 100644
--- a/tests/tests/usb/AndroidTest.xml
+++ b/tests/tests/usb/AndroidTest.xml
@@ -19,6 +19,7 @@
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="multi_abi" />
<option name="config-descriptor:metadata" key="parameter" value="secondary_user" />
+ <option name="config-descriptor:metadata" key="parameter" value="no_foldable_states" />
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true" />
<option name="test-file-name" value="CtsUsbManagerTestCases.apk" />
diff --git a/tests/tests/usb/src/android/usb/cts/UsbPortApiTest.java b/tests/tests/usb/src/android/usb/cts/UsbPortApiTest.java
index e953417..cf269b9 100644
--- a/tests/tests/usb/src/android/usb/cts/UsbPortApiTest.java
+++ b/tests/tests/usb/src/android/usb/cts/UsbPortApiTest.java
@@ -34,6 +34,9 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
+import java.util.function.Consumer;
+import java.util.concurrent.Executor;
+
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
@@ -64,9 +67,13 @@
private UiAutomation mUiAutomation =
InstrumentationRegistry.getInstrumentation().getUiAutomation();
+ private Executor mExecutor;
+ private Consumer<Integer> mConsumer;
+
@Before
public void setUp() {
mContext = InstrumentationRegistry.getContext();
+ mExecutor = mContext.getMainExecutor();
PackageManager pm = mContext.getPackageManager();
MockitoAnnotations.initMocks(this);
@@ -85,26 +92,26 @@
mUiAutomation.adoptShellPermissionIdentity(MANAGE_USB);
mMockUsbPort = new UsbPort(mUsbManagerMock, "1", 0, 0, true, true);
- int result = 0;
+ boolean result = true;
+ mConsumer = new Consumer<Integer>(){
+ public void accept(Integer status){
+ Log.d(TAG, "Consumer status:" + status);
+ };
+ };
// Should pass with permission.
- when(mMockUsbService.resetUsbPort(anyString(),anyInt(),
- any(IUsbOperationInternal.class))).thenReturn(true);
- result = mMockUsbPort.resetUsbPort();
- if (result == 0) {
- Log.d(TAG, "resetUsbPort success");
- } else {
- Log.d(TAG, "resetUsbPort fail ,result = " + result);
- }
+ mMockUsbService.resetUsbPort(anyString(), anyInt(),
+ any(IUsbOperationInternal.class));
+ mMockUsbPort.resetUsbPort(mExecutor, mConsumer);
// Drop MANAGE_USB permission.
mUiAutomation.dropShellPermissionIdentity();
try {
- mUsbPort.resetUsbPort();
- Assert.fail("Expected SecurityException on resetUsbPort.");
+ mUsbPort.resetUsbPort(mExecutor, mConsumer);
+ Assert.fail("SecurityException not thrown for resetUsbPort when MANAGE_USB is not acquired.");
} catch (SecurityException secEx) {
- Log.d(TAG, "Expected SecurityException on resetUsbPort.");
+ Log.d(TAG, "SecurityException expected on resetUsbPort when MANAGE_USB is not acquired.");
}
}
diff --git a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
index 8ba340a..962d75a 100644
--- a/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
+++ b/tests/tests/view/src/android/view/cts/AttachedSurfaceControlTest.java
@@ -17,12 +17,14 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static android.view.Display.DEFAULT_DISPLAY;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.util.Log;
+import android.server.wm.ActivityManagerTestBase;
import android.view.AttachedSurfaceControl;
import androidx.lifecycle.Lifecycle;
@@ -34,6 +36,7 @@
import org.junit.Assert;
import org.junit.Assume;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -48,6 +51,7 @@
@RequiresDevice
public class AttachedSurfaceControlTest {
private static final String TAG = "AttachedSurfaceControlTest";
+ private ActivityManagerTestBase.IgnoreOrientationRequestSession mOrientationSession;
private static class TransformHintListener implements
AttachedSurfaceControl.OnBufferTransformHintChangedListener {
@@ -77,7 +81,6 @@
}
}
-
@Before
public void setup() {
PackageManager pm =
@@ -85,6 +88,12 @@
boolean supportsRotation = pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_PORTRAIT)
&& pm.hasSystemFeature(PackageManager.FEATURE_SCREEN_LANDSCAPE);
Assume.assumeTrue(supportsRotation);
+ mOrientationSession = new ActivityManagerTestBase.IgnoreOrientationRequestSession(DEFAULT_DISPLAY, false);
+ }
+
+ @After
+ public void teardown() {
+ mOrientationSession.close();
}
@Test
@@ -175,10 +184,9 @@
// If the device is already in landscape, do nothing.
if (firstCallback[0] != null) {
Assert.assertTrue(firstCallback[0].await(3, TimeUnit.SECONDS));
- }
- // Check the callback value matches the call to get the transform hint.
- scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0],
+ scenario.onActivity(activity -> Assert.assertEquals(transformHintResult[0],
activity.getWindow().getRootSurfaceControl().getBufferTransformHint()));
+ }
scenario.onActivity(activity -> {
TransformHintListener listener = new TransformHintListener(activity,
diff --git a/tests/tests/view/src/android/view/cts/OnBackInvokedDispatcherTest.java b/tests/tests/view/src/android/view/cts/OnBackInvokedDispatcherTest.java
index 90d23a3..4d0d24b 100644
--- a/tests/tests/view/src/android/view/cts/OnBackInvokedDispatcherTest.java
+++ b/tests/tests/view/src/android/view/cts/OnBackInvokedDispatcherTest.java
@@ -16,12 +16,18 @@
package android.view.cts;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import android.app.Dialog;
+import android.view.View;
+import android.widget.FrameLayout;
import android.window.OnBackInvokedCallback;
import android.window.OnBackInvokedDispatcher;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.lifecycle.Lifecycle;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.MediumTest;
@@ -30,6 +36,8 @@
import org.junit.Rule;
import org.junit.Test;
+import java.util.HashMap;
+
/**
* Test {@link OnBackInvokedDispatcher}.
*/
@@ -58,6 +66,77 @@
}
@Test
+ public void findDispatcherOnView() {
+ final OnBackInvokedDispatcher[] windowOnBackInvokedDispatcher = {null};
+ mActivityRule.getScenario().onActivity(activity -> {
+ mActivity = activity;
+ mDialog = mActivity.getDialog();
+ View view = new View(activity) {
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ windowOnBackInvokedDispatcher[0] = findOnBackInvokedDispatcher();
+ }
+ };
+ assertNull("View is not attached, it should not have an OnBackInvokedDispatcher",
+ view.findOnBackInvokedDispatcher());
+ activity.setContentView(view);
+ });
+ assertNotNull("View is attached, it should have an OnBackInvokedDispatcher",
+ windowOnBackInvokedDispatcher[0]);
+ }
+
+ @Test
+ public void findDispatcherOnViewGroup() {
+ final HashMap<String, View> viewMap = new HashMap<>();
+ mActivityRule.getScenario().onActivity(activity -> {
+ mActivity = activity;
+ mDialog = mActivity.getDialog();
+ FrameLayout root = new FrameLayout(activity) {
+ @Nullable
+ @Override
+ public OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(
+ @NonNull View child, @NonNull View requester) {
+ viewMap.put("root_child", child);
+ viewMap.put("root_requester", requester);
+ return super.findOnBackInvokedDispatcherForChild(child, requester);
+ }
+ };
+ FrameLayout parent = new FrameLayout(activity) {
+ @Nullable
+ @Override
+ public OnBackInvokedDispatcher findOnBackInvokedDispatcherForChild(
+ @NonNull View child, @NonNull View requester) {
+ viewMap.put("parent_child", child);
+ viewMap.put("parent_requester", requester);
+ return super.findOnBackInvokedDispatcherForChild(child, requester);
+ }
+ };
+ View view = new View(activity);
+ viewMap.put("root", root);
+ viewMap.put("parent", parent);
+ viewMap.put("view", view);
+
+ root.addView(parent);
+ parent.addView(view);
+ activity.setContentView(root);
+ });
+
+ View view = viewMap.get("view");
+ View parent = viewMap.get("parent");
+ assertNotNull("View is attached, it should have an OnBackInvokedDispatcher",
+ view.findOnBackInvokedDispatcher());
+ assertEquals("Requester from root should be the leaf view",
+ view, viewMap.get("root_requester"));
+ assertEquals("Child from root should be the direct child",
+ parent, viewMap.get("root_child"));
+ assertEquals("Requester from direct parent should be the direct child",
+ view, viewMap.get("parent_requester"));
+ assertEquals("Child from parent should be the direct child",
+ view, viewMap.get("parent_child"));
+ }
+
+ @Test
public void testRegisterAndUnregisterCallbacks() {
OnBackInvokedDispatcher dispatcher = mActivity.getOnBackInvokedDispatcher();
OnBackInvokedCallback callback1 = createBackCallback();
@@ -72,9 +151,6 @@
}
private OnBackInvokedCallback createBackCallback() {
- return new OnBackInvokedCallback() {
- @Override
- public void onBackInvoked() {}
- };
+ return () -> {};
}
}
diff --git a/tests/tests/virtualdevice/app/src/android/virtualdevice/streamedtestapp/MainActivity.java b/tests/tests/virtualdevice/app/src/android/virtualdevice/streamedtestapp/MainActivity.java
index 90fe811..7eba35c 100644
--- a/tests/tests/virtualdevice/app/src/android/virtualdevice/streamedtestapp/MainActivity.java
+++ b/tests/tests/virtualdevice/app/src/android/virtualdevice/streamedtestapp/MainActivity.java
@@ -18,16 +18,30 @@
import static android.hardware.camera2.CameraAccessException.CAMERA_DISABLED;
import static android.hardware.camera2.CameraAccessException.CAMERA_DISCONNECTED;
+import static android.media.AudioFormat.CHANNEL_OUT_MONO;
+import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.AudioFormat.ENCODING_PCM_FLOAT;
+import static android.media.AudioRecord.READ_BLOCKING;
import static android.virtualdevice.cts.common.ActivityResultReceiver.ACTION_SEND_ACTIVITY_RESULT;
+import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_LAST_RECORDED_NONZERO_VALUE;
import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_AT_FREQUENCY;
import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_NOT_FREQUENCY;
import static android.virtualdevice.cts.common.AudioHelper.ACTION_PLAY_AUDIO;
import static android.virtualdevice.cts.common.AudioHelper.ACTION_RECORD_AUDIO;
import static android.virtualdevice.cts.common.AudioHelper.AMPLITUDE;
import static android.virtualdevice.cts.common.AudioHelper.BUFFER_SIZE_IN_BYTES;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_BUFFER;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_VALUE;
import static android.virtualdevice.cts.common.AudioHelper.CHANNEL_COUNT;
+import static android.virtualdevice.cts.common.AudioHelper.EXTRA_AUDIO_DATA_TYPE;
+import static android.virtualdevice.cts.common.AudioHelper.FLOAT_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.FLOAT_VALUE;
import static android.virtualdevice.cts.common.AudioHelper.FREQUENCY;
+import static android.virtualdevice.cts.common.AudioHelper.NUMBER_OF_SAMPLES;
import static android.virtualdevice.cts.common.AudioHelper.SAMPLE_RATE;
+import static android.virtualdevice.cts.common.AudioHelper.SHORT_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.SHORT_VALUE;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -51,6 +65,7 @@
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.Future;
/**
* Test activity to be streamed in the virtual device.
@@ -143,10 +158,53 @@
finish();
break;
case ACTION_PLAY_AUDIO:
- playAudio();
+ @AudioHelper.DataType int playDataType = getIntent().getIntExtra(
+ EXTRA_AUDIO_DATA_TYPE, -1);
+ int playEncoding =
+ playDataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
+ int bufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, CHANNEL_OUT_MONO,
+ playEncoding);
+ AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
+ CHANNEL_OUT_MONO, playEncoding, bufferSize, AudioTrack.MODE_STREAM);
+ audioTrack.play();
+ switch (playDataType) {
+ case BYTE_BUFFER:
+ playAudioFromByteBuffer(audioTrack);
+ break;
+ case BYTE_ARRAY:
+ playAudioFromByteArray(audioTrack);
+ break;
+ case SHORT_ARRAY:
+ playAudioFromShortArray(audioTrack);
+ break;
+ case FLOAT_ARRAY:
+ playAudioFromFloatArray(audioTrack);
+ break;
+ }
break;
case ACTION_RECORD_AUDIO:
- recordAudio();
+ @AudioHelper.DataType int recordDataType = getIntent().getIntExtra(
+ EXTRA_AUDIO_DATA_TYPE, -1);
+ int recordEncoding =
+ recordDataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
+ AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
+ SAMPLE_RATE,
+ AudioFormat.CHANNEL_IN_MONO, recordEncoding, BUFFER_SIZE_IN_BYTES);
+ audioRecord.startRecording();
+ switch (recordDataType) {
+ case BYTE_BUFFER:
+ recordAudioToByteBuffer(audioRecord);
+ break;
+ case BYTE_ARRAY:
+ recordAudioToByteArray(audioRecord);
+ break;
+ case SHORT_ARRAY:
+ recordAudioToShortArray(audioRecord);
+ break;
+ case FLOAT_ARRAY:
+ recordAudioToFloatArray(audioRecord);
+ break;
+ }
break;
default:
Log.w(TAG, "Unknown action: " + action);
@@ -219,50 +277,179 @@
}
}
- private void playAudio() {
- int numSamples = AudioHelper.computeNumSamples(/* timeMs= */ 1000, SAMPLE_RATE,
- CHANNEL_COUNT);
- ByteBuffer audioData = AudioHelper.createAudioData(
- SAMPLE_RATE, numSamples, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
-
- int bufferSize = AudioTrack.getMinBufferSize(SAMPLE_RATE, AudioFormat.CHANNEL_OUT_MONO,
- AudioFormat.ENCODING_PCM_16BIT);
- AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, SAMPLE_RATE,
- AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize,
- AudioTrack.MODE_STREAM);
- audioTrack.play();
-
+ private void playAudioFromByteBuffer(AudioTrack audioTrack) {
// Write to the audio track asynchronously to avoid ANRs.
- CompletableFuture.runAsync(() -> {
- int remainingSamples = numSamples;
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ ByteBuffer audioData = AudioHelper.createAudioData(
+ SAMPLE_RATE, NUMBER_OF_SAMPLES, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
+
+ int remainingSamples = NUMBER_OF_SAMPLES;
while (remainingSamples > 0) {
- remainingSamples -= audioTrack.write(
- audioData,
- audioData.remaining(),
+ remainingSamples -= audioTrack.write(audioData, audioData.remaining(),
AudioTrack.WRITE_BLOCKING);
}
audioTrack.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ sendBroadcast(intent);
finish();
});
}
- private void recordAudio() {
- AudioRecord audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, SAMPLE_RATE,
- AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, BUFFER_SIZE_IN_BYTES);
- audioRecord.startRecording();
+ private void playAudioFromByteArray(AudioTrack audioTrack) {
+ // Write to the audio track asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ byte[] audioData = new byte[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < audioData.length; i++) {
+ audioData[i] = BYTE_VALUE;
+ }
+ int remainingSamples = audioData.length;
+ while (remainingSamples > 0) {
+ remainingSamples -= audioTrack.write(audioData, 0, audioData.length);
+ }
+ audioTrack.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ sendBroadcast(intent);
+ finish();
+ });
+ }
+
+ private void playAudioFromShortArray(AudioTrack audioTrack) {
+ // Write to the audio track asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ short[] audioData = new short[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < audioData.length; i++) {
+ audioData[i] = SHORT_VALUE;
+ }
+
+ int remainingSamples = audioData.length;
+ while (remainingSamples > 0) {
+ remainingSamples -= audioTrack.write(audioData, 0, audioData.length);
+ }
+ audioTrack.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ sendBroadcast(intent);
+ finish();
+ });
+ }
+
+ private void playAudioFromFloatArray(AudioTrack audioTrack) {
+ // Write to the audio track asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ float[] audioData = new float[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < audioData.length; i++) {
+ audioData[i] = FLOAT_VALUE;
+ }
+
+ int remainingSamples = audioData.length;
+ while (remainingSamples > 0) {
+ remainingSamples -= audioTrack.write(audioData, 0, audioData.length,
+ AudioTrack.WRITE_BLOCKING);
+ }
+ audioTrack.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ sendBroadcast(intent);
+ finish();
+ });
+ }
+
+ private void recordAudioToByteBuffer(AudioRecord audioRecord) {
// Read from the audio record asynchronously to avoid ANRs.
- CompletableFuture.runAsync(() -> {
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
AudioHelper.CapturedAudio capturedAudio = new AudioHelper.CapturedAudio(audioRecord);
double powerSpectrumNotFrequency = capturedAudio.getPowerSpectrum(FREQUENCY + 100);
double powerSpectrumAtFrequency = capturedAudio.getPowerSpectrum(FREQUENCY);
+ audioRecord.release();
Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
intent.putExtra(EXTRA_POWER_SPECTRUM_NOT_FREQUENCY, powerSpectrumNotFrequency);
intent.putExtra(EXTRA_POWER_SPECTRUM_AT_FREQUENCY, powerSpectrumAtFrequency);
sendBroadcast(intent);
+ finish();
+ });
+ }
+ private void recordAudioToByteArray(AudioRecord audioRecord) {
+ // Read from the audio record asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ byte[] audioData = new byte[BUFFER_SIZE_IN_BYTES];
+ while (true) {
+ int bytesRead = audioRecord.read(audioData, 0, audioData.length);
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ byte value = 0;
+ for (int i = 0; i < audioData.length; i++) {
+ if (audioData[i] != 0) {
+ value = audioData[i];
+ break;
+ }
+ }
audioRecord.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ intent.putExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE, value);
+ sendBroadcast(intent);
+ finish();
+ });
+ }
+
+ private void recordAudioToShortArray(AudioRecord audioRecord) {
+ // Read from the audio record asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ short[] audioData = new short[BUFFER_SIZE_IN_BYTES / 2];
+ while (true) {
+ int bytesRead = audioRecord.read(audioData, 0, audioData.length);
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ short value = 0;
+ for (int i = 0; i < audioData.length; i++) {
+ if (audioData[i] != 0) {
+ value = audioData[i];
+ break;
+ }
+ }
+ audioRecord.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ intent.putExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE, value);
+ sendBroadcast(intent);
+ finish();
+ });
+ }
+
+ private void recordAudioToFloatArray(AudioRecord audioRecord) {
+ // Read from the audio record asynchronously to avoid ANRs.
+ Future<?> unusedFuture = CompletableFuture.runAsync(() -> {
+ float[] audioData = new float[BUFFER_SIZE_IN_BYTES / 4];
+ while (true) {
+ int bytesRead = audioRecord.read(audioData, 0, audioData.length, READ_BLOCKING);
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ float value = 0f;
+ for (int i = 0; i < audioData.length; i++) {
+ if (Float.compare(audioData[i], 0.0f) != 0) {
+ value = audioData[i];
+ break;
+ }
+ }
+ audioRecord.release();
+
+ Intent intent = new Intent(ACTION_SEND_ACTIVITY_RESULT);
+ intent.putExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE, value);
+ sendBroadcast(intent);
finish();
});
}
diff --git a/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/ActivityResultReceiver.java b/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/ActivityResultReceiver.java
index be3ca7c..7073aeb 100644
--- a/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/ActivityResultReceiver.java
+++ b/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/ActivityResultReceiver.java
@@ -37,6 +37,9 @@
/** Extra for sending the computed power spectrum off expected audio frequency. */
public static final String EXTRA_POWER_SPECTRUM_NOT_FREQUENCY = "powerSpectrumNotFrequency";
+ /** Extra for sending the value of recorded audio data. */
+ public static final String EXTRA_LAST_RECORDED_NONZERO_VALUE = "lastRecordedNonZeroValue";
+
public interface Callback {
void onActivityResult(Intent data);
}
diff --git a/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/AudioHelper.java b/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/AudioHelper.java
index 15b4e5a..16f67cc2 100644
--- a/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/AudioHelper.java
+++ b/tests/tests/virtualdevice/common/src/android/virtualdevice/cts/common/AudioHelper.java
@@ -16,10 +16,15 @@
package android.virtualdevice.cts.common;
+import static android.media.AudioRecord.READ_BLOCKING;
+import static android.media.AudioRecord.READ_NON_BLOCKING;
+
+import android.annotation.IntDef;
import android.companion.virtual.audio.AudioCapture;
-import android.media.AudioFormat;
import android.media.AudioRecord;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -33,17 +38,44 @@
/** Tells the activity to record audio for testing. */
public static final String ACTION_RECORD_AUDIO = "android.virtualdevice.cts.RECORD_AUDIO";
+ /** Tells the activity to play or record for which audio data type. */
+ public static final String EXTRA_AUDIO_DATA_TYPE = "audio_data_type";
+
+ @IntDef({
+ BYTE_BUFFER,
+ BYTE_ARRAY,
+ SHORT_ARRAY,
+ FLOAT_ARRAY,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface DataType {}
+
+ public static final int BYTE_BUFFER = 1;
+ public static final int BYTE_ARRAY = 2;
+ public static final int SHORT_ARRAY = 3;
+ public static final int FLOAT_ARRAY = 4;
+
+ /** Values for read and write verification. */
+ public static final byte BYTE_VALUE = 8;
+ public static final short SHORT_VALUE = 16;
+ public static final float FLOAT_VALUE = 0.8f;
+
/** Constants of audio config for testing. */
public static final int FREQUENCY = 264;
public static final int SAMPLE_RATE = 44100;
public static final int CHANNEL_COUNT = 1;
public static final int AMPLITUDE = 32767;
public static final int BUFFER_SIZE_IN_BYTES = 65536;
+ public static final int NUMBER_OF_SAMPLES = computeNumSamples(/* timeMs= */ 1000, SAMPLE_RATE,
+ CHANNEL_COUNT);
public static class CapturedAudio {
- private final int mSamplingRate;
- private final int mChannelCount;
- private final ByteBuffer mCapturedData;
+ private int mSamplingRate;
+ private int mChannelCount;
+ private ByteBuffer mCapturedData;
+ private byte mByteValue;
+ private short mShortValue;
+ private float mFloatValue;
public CapturedAudio(AudioRecord audioRecord) {
mSamplingRate = audioRecord.getSampleRate();
@@ -62,15 +94,18 @@
}
}
- public CapturedAudio(AudioCapture audioCapture, AudioFormat audioFormat) {
- mSamplingRate = audioFormat.getSampleRate();
- mChannelCount = audioFormat.getChannelCount();
- ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE_IN_BYTES).order(
- ByteOrder.nativeOrder());
+ public CapturedAudio(AudioCapture audioCapture, ByteBuffer byteBuffer, int readMode) {
+ mSamplingRate = audioCapture.getFormat().getSampleRate();
+ mChannelCount = audioCapture.getFormat().getChannelCount();
while (true) {
// Read the first buffer with non-zero data
byteBuffer.clear();
- int bytesRead = audioCapture.read(byteBuffer, BUFFER_SIZE_IN_BYTES);
+ int bytesRead;
+ if (readMode == READ_BLOCKING || readMode == READ_NON_BLOCKING) {
+ bytesRead = audioCapture.read(byteBuffer, BUFFER_SIZE_IN_BYTES, readMode);
+ } else {
+ bytesRead = audioCapture.read(byteBuffer, BUFFER_SIZE_IN_BYTES);
+ }
if (bytesRead == 0 || isAllZero(byteBuffer)) {
continue;
}
@@ -79,9 +114,79 @@
}
}
+ public CapturedAudio(AudioCapture audioCapture, byte[] audioData, int readMode) {
+ while (true) {
+ int bytesRead;
+ if (readMode == READ_BLOCKING || readMode == READ_NON_BLOCKING) {
+ bytesRead = audioCapture.read(audioData, 0, audioData.length, readMode);
+ } else {
+ bytesRead = audioCapture.read(audioData, 0, audioData.length);
+ }
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ for (int i = 0; i < audioData.length; i++) {
+ if (audioData[i] != 0) {
+ mByteValue = audioData[i];
+ break;
+ }
+ }
+ }
+
+ public CapturedAudio(AudioCapture audioCapture, short[] audioData, int readMode) {
+ while (true) {
+ int bytesRead;
+ if (readMode == READ_BLOCKING || readMode == READ_NON_BLOCKING) {
+ bytesRead = audioCapture.read(audioData, 0, audioData.length, readMode);
+ } else {
+ bytesRead = audioCapture.read(audioData, 0, audioData.length);
+ }
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ for (int i = 0; i < audioData.length; i++) {
+ if (audioData[i] != 0) {
+ mShortValue = audioData[i];
+ break;
+ }
+ }
+ }
+
+ public CapturedAudio(AudioCapture audioCapture, float[] audioData, int readMode) {
+ while (true) {
+ int bytesRead = audioCapture.read(audioData, 0, audioData.length, readMode);
+ if (bytesRead == 0) {
+ continue;
+ }
+ break;
+ }
+ for (int i = 0; i < audioData.length; i++) {
+ if (audioData[i] != 0) {
+ mFloatValue = audioData[i];
+ break;
+ }
+ }
+ }
+
public double getPowerSpectrum(int frequency) {
return getCapturedPowerSpectrum(mSamplingRate, mChannelCount, mCapturedData, frequency);
}
+
+ public byte getByteValue() {
+ return mByteValue;
+ }
+
+ public short getShortValue() {
+ return mShortValue;
+ }
+
+ public float getFloatValue() {
+ return mFloatValue;
+ }
}
public static int computeNumSamples(int timeMs, int samplingRate, int channelCount) {
diff --git a/tests/tests/virtualdevice/src/android/virtualdevice/cts/ActivityManagementTest.java b/tests/tests/virtualdevice/src/android/virtualdevice/cts/ActivityManagementTest.java
index 789e63b..344cccc 100644
--- a/tests/tests/virtualdevice/src/android/virtualdevice/cts/ActivityManagementTest.java
+++ b/tests/tests/virtualdevice/src/android/virtualdevice/cts/ActivityManagementTest.java
@@ -71,6 +71,7 @@
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
+import java.util.function.IntConsumer;
/**
* Tests for activity management, like launching and listening to activity change events, in the
@@ -100,7 +101,7 @@
@Mock
private VirtualDisplay.Callback mVirtualDisplayCallback;
@Mock
- private VirtualDeviceManager.LaunchCallback mLaunchCallback;
+ private IntConsumer mLaunchCompleteListener;
@Nullable private ServiceConnectionFuture<IStreamedTestApp> mServiceConnection;
@Mock
private OnReceiveResultListener mOnReceiveResultListener;
@@ -254,10 +255,11 @@
mVirtualDisplayCallback);
mVirtualDevice.launchPendingIntent(virtualDisplay.getDisplay().getDisplayId(),
- pendingIntent, Runnable::run, mLaunchCallback);
+ pendingIntent, Runnable::run, mLaunchCompleteListener);
verify(mOnReceiveResultListener, timeout(5000)).onReceiveResult(
eq(Activity.RESULT_OK), nullable(Bundle.class));
+ verify(mLaunchCompleteListener).accept(eq(VirtualDeviceManager.LAUNCH_SUCCESS));
}
@Test
@@ -281,10 +283,11 @@
mVirtualDevice.launchPendingIntent(
virtualDisplay.getDisplay().getDisplayId(),
pendingIntent, Runnable::run,
- mLaunchCallback);
+ mLaunchCompleteListener);
verify(mOnReceiveResultListener, timeout(5000)).onReceiveResult(
eq(Activity.RESULT_OK), nullable(Bundle.class));
+ verify(mLaunchCompleteListener).accept(eq(VirtualDeviceManager.LAUNCH_SUCCESS));
}
@Test
@@ -309,10 +312,11 @@
virtualDisplay.getDisplay().getDisplayId(),
pendingIntent,
Runnable::run,
- mLaunchCallback);
+ mLaunchCompleteListener);
verify(mOnReceiveResultListener, after(5000).never()).onReceiveResult(
eq(Activity.RESULT_OK), nullable(Bundle.class));
+ verify(mLaunchCompleteListener).accept(eq(VirtualDeviceManager.LAUNCH_FAILURE_NO_ACTIVITY));
}
private IStreamedTestApp getTestAppService() throws Exception {
diff --git a/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java b/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java
index 9cb839d..d39ee52 100644
--- a/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java
+++ b/tests/tests/virtualdevice/src/android/virtualdevice/cts/VirtualAudioTest.java
@@ -25,16 +25,34 @@
import static android.Manifest.permission.WAKE_LOCK;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
import static android.media.AudioFormat.CHANNEL_IN_MONO;
-import static android.media.AudioFormat.CHANNEL_OUT_MONO;
import static android.media.AudioFormat.ENCODING_PCM_16BIT;
+import static android.media.AudioFormat.ENCODING_PCM_FLOAT;
+import static android.media.AudioRecord.READ_BLOCKING;
+import static android.media.AudioRecord.RECORDSTATE_RECORDING;
+import static android.media.AudioRecord.RECORDSTATE_STOPPED;
+import static android.media.AudioTrack.PLAYSTATE_PLAYING;
+import static android.media.AudioTrack.PLAYSTATE_STOPPED;
+import static android.media.AudioTrack.WRITE_BLOCKING;
+import static android.media.AudioTrack.WRITE_NON_BLOCKING;
+import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_LAST_RECORDED_NONZERO_VALUE;
import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_AT_FREQUENCY;
import static android.virtualdevice.cts.common.ActivityResultReceiver.EXTRA_POWER_SPECTRUM_NOT_FREQUENCY;
import static android.virtualdevice.cts.common.AudioHelper.ACTION_PLAY_AUDIO;
import static android.virtualdevice.cts.common.AudioHelper.ACTION_RECORD_AUDIO;
import static android.virtualdevice.cts.common.AudioHelper.AMPLITUDE;
+import static android.virtualdevice.cts.common.AudioHelper.BUFFER_SIZE_IN_BYTES;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_BUFFER;
+import static android.virtualdevice.cts.common.AudioHelper.BYTE_VALUE;
import static android.virtualdevice.cts.common.AudioHelper.CHANNEL_COUNT;
+import static android.virtualdevice.cts.common.AudioHelper.EXTRA_AUDIO_DATA_TYPE;
+import static android.virtualdevice.cts.common.AudioHelper.FLOAT_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.FLOAT_VALUE;
import static android.virtualdevice.cts.common.AudioHelper.FREQUENCY;
+import static android.virtualdevice.cts.common.AudioHelper.NUMBER_OF_SAMPLES;
import static android.virtualdevice.cts.common.AudioHelper.SAMPLE_RATE;
+import static android.virtualdevice.cts.common.AudioHelper.SHORT_ARRAY;
+import static android.virtualdevice.cts.common.AudioHelper.SHORT_VALUE;
import static android.virtualdevice.cts.util.TestAppHelper.MAIN_ACTIVITY_COMPONENT;
import static android.virtualdevice.cts.util.VirtualDeviceTestUtils.createActivityOptions;
@@ -59,7 +77,6 @@
import android.content.pm.PackageManager;
import android.hardware.display.VirtualDisplay;
import android.media.AudioFormat;
-import android.media.AudioTrack;
import android.platform.test.annotations.AppModeFull;
import android.virtualdevice.cts.common.ActivityResultReceiver;
import android.virtualdevice.cts.common.AudioHelper;
@@ -81,6 +98,7 @@
import org.mockito.MockitoAnnotations;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
/**
* Tests for injection and capturing of audio from streamed apps
@@ -101,18 +119,7 @@
private static final VirtualDeviceParams DEFAULT_VIRTUAL_DEVICE_PARAMS =
new VirtualDeviceParams.Builder().build();
- private static final AudioFormat AUDIO_CAPTURE_FORMAT =
- new AudioFormat.Builder()
- .setSampleRate(SAMPLE_RATE)
- .setEncoding(ENCODING_PCM_16BIT)
- .setChannelMask(CHANNEL_IN_MONO)
- .build();
- private static final AudioFormat AUDIO_INJECTION_FORMAT =
- new AudioFormat.Builder()
- .setSampleRate(SAMPLE_RATE)
- .setEncoding(ENCODING_PCM_16BIT)
- .setChannelMask(CHANNEL_OUT_MONO)
- .build();
+
@Rule
public AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule(
InstrumentationRegistry.getInstrumentation().getUiAutomation(),
@@ -160,8 +167,6 @@
/* flags= */ VIRTUAL_DISPLAY_FLAG_TRUSTED,
Runnable::run,
mVirtualDisplayCallback);
- mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
- mVirtualDisplay, /* executor= */ null, mAudioConfigurationChangeCallback);
}
@After
@@ -178,69 +183,334 @@
}
@Test
- public void audioCapture_shouldCaptureAppPlaybackFrequency() {
- AudioCapture audioCapture = mVirtualAudioDevice.startAudioCapture(AUDIO_CAPTURE_FORMAT);
+ public void audioCapture_createCorrectly() {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, /* callback= */ null);
+ AudioFormat audioFormat = createCaptureFormat(ENCODING_PCM_16BIT);
+ AudioCapture audioCapture = mVirtualAudioDevice.startAudioCapture(audioFormat);
assertThat(audioCapture).isNotNull();
+ assertThat(audioCapture.getFormat()).isEqualTo(audioFormat);
+ assertThat(mVirtualAudioDevice.getAudioCapture()).isEqualTo(audioCapture);
- InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
- createPlayAudioIntent(), createActivityOptions(mVirtualDisplay));
- verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
- .onPlaybackConfigChanged(any());
-
- AudioHelper.CapturedAudio capturedAudio = new AudioHelper.CapturedAudio(audioCapture,
- AUDIO_CAPTURE_FORMAT);
- assertThat(capturedAudio.getPowerSpectrum(FREQUENCY + 100))
- .isLessThan(POWER_THRESHOLD_FOR_ABSENT);
- assertThat(capturedAudio.getPowerSpectrum(FREQUENCY))
- .isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
+ audioCapture.startRecording();
+ assertThat(audioCapture.getRecordingState()).isEqualTo(RECORDSTATE_RECORDING);
+ audioCapture.stop();
+ assertThat(audioCapture.getRecordingState()).isEqualTo(RECORDSTATE_STOPPED);
}
@Test
- public void audioInjection_appShouldRecordInjectedFrequency() {
- AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(
- AUDIO_INJECTION_FORMAT);
+ public void audioInjection_createCorrectly() {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, /* callback= */ null);
+ AudioFormat audioFormat = createInjectionFormat(ENCODING_PCM_16BIT);
+ AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(audioFormat);
assertThat(audioInjection).isNotNull();
+ assertThat(audioInjection.getFormat()).isEqualTo(audioFormat);
+ assertThat(mVirtualAudioDevice.getAudioInjection()).isEqualTo(audioInjection);
+
+ audioInjection.play();
+ assertThat(audioInjection.getPlayState()).isEqualTo(PLAYSTATE_PLAYING);
+ audioInjection.stop();
+ assertThat(audioInjection.getPlayState()).isEqualTo(PLAYSTATE_STOPPED);
+ }
+
+ @Test
+ public void audioCapture_receivesAudioConfigurationChangeCallback() {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, mAudioConfigurationChangeCallback);
+ AudioFormat audioFormat = createCaptureFormat(ENCODING_PCM_16BIT);
+ mVirtualAudioDevice.startAudioCapture(audioFormat);
ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
getApplicationContext());
activityResultReceiver.register(mActivityResultCallback);
InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
- createAudioRecordIntent(), createActivityOptions(mVirtualDisplay));
-
- int numSamples = AudioHelper.computeNumSamples(/* timeMs= */ 1000, SAMPLE_RATE,
- CHANNEL_COUNT);
- ByteBuffer audioData = AudioHelper.createAudioData(
- SAMPLE_RATE, numSamples, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
- int remaining = audioData.remaining();
- while (remaining > 0) {
- remaining -= audioInjection.write(
- audioData, audioData.remaining(), AudioTrack.WRITE_BLOCKING);
- }
+ createPlayAudioIntent(BYTE_BUFFER),
+ createActivityOptions(mVirtualDisplay));
+ verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
+ .onPlaybackConfigChanged(any());
verify(mActivityResultCallback, timeout(5000)).onActivityResult(
mIntentCaptor.capture());
- verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
- .onRecordingConfigChanged(any());
-
- Intent intent = mIntentCaptor.getValue();
- assertThat(intent).isNotNull();
- double powerSpectrumAtFrequency = intent.getDoubleExtra(EXTRA_POWER_SPECTRUM_AT_FREQUENCY,
- 0);
- double powerSpectrumNotFrequency = intent.getDoubleExtra(EXTRA_POWER_SPECTRUM_NOT_FREQUENCY,
- 0);
- assertThat(powerSpectrumNotFrequency).isLessThan(POWER_THRESHOLD_FOR_ABSENT);
- assertThat(powerSpectrumAtFrequency).isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
-
activityResultReceiver.unregister();
}
- private static Intent createPlayAudioIntent() {
+ @Test
+ public void audioInjection_receivesAudioConfigurationChangeCallback() {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, mAudioConfigurationChangeCallback);
+ AudioFormat audioFormat = createInjectionFormat(ENCODING_PCM_16BIT);
+ mVirtualAudioDevice.startAudioInjection(audioFormat);
+
+ ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
+ getApplicationContext());
+ activityResultReceiver.register(mActivityResultCallback);
+ InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
+ createAudioRecordIntent(BYTE_BUFFER),
+ createActivityOptions(mVirtualDisplay));
+ verify(mAudioConfigurationChangeCallback, timeout(5000).atLeastOnce())
+ .onRecordingConfigChanged(any());
+ verify(mActivityResultCallback, timeout(5000)).onActivityResult(
+ mIntentCaptor.capture());
+ activityResultReceiver.unregister();
+ }
+
+ @Test
+ public void audioCapture_readByteBuffer_shouldCaptureAppPlaybackFrequency() {
+ runAudioCaptureTest(BYTE_BUFFER, /* readMode= */ -1);
+ }
+
+ @Test
+ public void audioCapture_readByteBufferBlocking_shouldCaptureAppPlaybackFrequency() {
+ runAudioCaptureTest(BYTE_BUFFER, /* readMode= */ READ_BLOCKING);
+ }
+
+ @Test
+ public void audioCapture_readByteArray_shouldCaptureAppPlaybackData() {
+ runAudioCaptureTest(BYTE_ARRAY, /* readMode= */ -1);
+ }
+
+ @Test
+ public void audioCapture_readByteArrayBlocking_shouldCaptureAppPlaybackData() {
+ runAudioCaptureTest(BYTE_ARRAY, /* readMode= */ READ_BLOCKING);
+ }
+
+ @Test
+ public void audioCapture_readShortArray_shouldCaptureAppPlaybackData() {
+ runAudioCaptureTest(SHORT_ARRAY, /* readMode= */ -1);
+ }
+
+ @Test
+ public void audioCapture_readShortArrayBlocking_shouldCaptureAppPlaybackData() {
+ runAudioCaptureTest(SHORT_ARRAY, /* readMode= */ READ_BLOCKING);
+ }
+
+ @Test
+ public void audioCapture_readFloatArray_shouldCaptureAppPlaybackData() {
+ runAudioCaptureTest(FLOAT_ARRAY, /* readMode= */ READ_BLOCKING);
+ }
+
+ @Test
+ public void audioInjection_writeByteBuffer_appShouldRecordInjectedFrequency() {
+ runAudioInjectionTest(BYTE_BUFFER, /* writeMode= */
+ WRITE_BLOCKING, /* timestamp= */ 0);
+ }
+
+ @Test
+ public void audioInjection_writeByteBufferWithTimestamp_appShouldRecordInjectedFrequency() {
+ runAudioInjectionTest(BYTE_BUFFER, /* writeMode= */
+ WRITE_BLOCKING, /* timestamp= */ 50);
+ }
+
+ @Test
+ public void audioInjection_writeByteArray_appShouldRecordInjectedData() {
+ runAudioInjectionTest(BYTE_ARRAY, /* writeMode= */ -1, /* timestamp= */ 0);
+ }
+
+ @Test
+ public void audioInjection_writeByteArrayBlocking_appShouldRecordInjectedData() {
+ runAudioInjectionTest(BYTE_ARRAY, /* writeMode= */ WRITE_BLOCKING, /* timestamp= */
+ 0);
+ }
+
+ @Test
+ public void audioInjection_writeShortArray_appShouldRecordInjectedData() {
+ runAudioInjectionTest(SHORT_ARRAY, /* writeMode= */ -1, /* timestamp= */ 0);
+ }
+
+ @Test
+ public void audioInjection_writeShortArrayBlocking_appShouldRecordInjectedData() {
+ runAudioInjectionTest(SHORT_ARRAY, /* writeMode= */
+ WRITE_BLOCKING, /* timestamp= */ 0);
+ }
+
+ @Test
+ public void audioInjection_writeFloatArray_appShouldRecordInjectedData() {
+ runAudioInjectionTest(FLOAT_ARRAY, /* writeMode= */
+ WRITE_BLOCKING, /* timestamp= */ 0);
+ }
+
+ private void runAudioCaptureTest(@AudioHelper.DataType int dataType, int readMode) {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, /* callback= */ null);
+ int encoding = dataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
+ AudioCapture audioCapture = mVirtualAudioDevice.startAudioCapture(
+ createCaptureFormat(encoding));
+
+ ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
+ getApplicationContext());
+ activityResultReceiver.register(mActivityResultCallback);
+ InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
+ createPlayAudioIntent(dataType),
+ createActivityOptions(mVirtualDisplay));
+
+ AudioHelper.CapturedAudio capturedAudio = null;
+ switch (dataType) {
+ case BYTE_BUFFER:
+ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER_SIZE_IN_BYTES).order(
+ ByteOrder.nativeOrder());
+ capturedAudio = new AudioHelper.CapturedAudio(audioCapture, byteBuffer, readMode);
+ assertThat(capturedAudio.getPowerSpectrum(FREQUENCY + 100))
+ .isLessThan(POWER_THRESHOLD_FOR_ABSENT);
+ assertThat(capturedAudio.getPowerSpectrum(FREQUENCY))
+ .isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
+ break;
+ case BYTE_ARRAY:
+ byte[] byteArray = new byte[BUFFER_SIZE_IN_BYTES];
+ capturedAudio = new AudioHelper.CapturedAudio(audioCapture, byteArray, readMode);
+ assertThat(capturedAudio.getByteValue()).isEqualTo(BYTE_VALUE);
+ break;
+ case SHORT_ARRAY:
+ short[] shortArray = new short[BUFFER_SIZE_IN_BYTES / 2];
+ capturedAudio = new AudioHelper.CapturedAudio(audioCapture, shortArray, readMode);
+ assertThat(capturedAudio.getShortValue()).isEqualTo(SHORT_VALUE);
+ break;
+ case FLOAT_ARRAY:
+ float[] floatArray = new float[BUFFER_SIZE_IN_BYTES / 4];
+ capturedAudio = new AudioHelper.CapturedAudio(audioCapture, floatArray, readMode);
+ float roundOffError = Math.abs(capturedAudio.getFloatValue() - FLOAT_VALUE);
+ assertThat(roundOffError).isLessThan(0.001f);
+ break;
+ }
+
+ verify(mActivityResultCallback, timeout(5000)).onActivityResult(
+ mIntentCaptor.capture());
+ activityResultReceiver.unregister();
+ }
+
+ private void runAudioInjectionTest(@AudioHelper.DataType int dataType, int writeMode,
+ long timestamp) {
+ mVirtualAudioDevice = mVirtualDevice.createVirtualAudioDevice(
+ mVirtualDisplay, /* executor= */ null, /* callback= */ null);
+ int encoding = dataType == FLOAT_ARRAY ? ENCODING_PCM_FLOAT : ENCODING_PCM_16BIT;
+ AudioInjection audioInjection = mVirtualAudioDevice.startAudioInjection(
+ createInjectionFormat(encoding));
+
+ ActivityResultReceiver activityResultReceiver = new ActivityResultReceiver(
+ getApplicationContext());
+ activityResultReceiver.register(mActivityResultCallback);
+ InstrumentationRegistry.getInstrumentation().getTargetContext().startActivity(
+ createAudioRecordIntent(dataType),
+ createActivityOptions(mVirtualDisplay));
+
+ int remaining;
+ switch (dataType) {
+ case BYTE_BUFFER:
+ ByteBuffer byteBuffer = AudioHelper.createAudioData(
+ SAMPLE_RATE, NUMBER_OF_SAMPLES, CHANNEL_COUNT, FREQUENCY, AMPLITUDE);
+ remaining = byteBuffer.remaining();
+ while (remaining > 0) {
+ if (timestamp != 0) {
+ remaining -= audioInjection.write(byteBuffer, byteBuffer.remaining(),
+ writeMode, timestamp);
+ } else {
+ remaining -= audioInjection.write(byteBuffer, byteBuffer.remaining(),
+ writeMode);
+ }
+ }
+ break;
+ case BYTE_ARRAY:
+ byte[] byteArray = new byte[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < byteArray.length; i++) {
+ byteArray[i] = BYTE_VALUE;
+ }
+ remaining = byteArray.length;
+ while (remaining > 0) {
+ if (writeMode == WRITE_BLOCKING || writeMode == WRITE_NON_BLOCKING) {
+ remaining -= audioInjection.write(byteArray, 0, byteArray.length,
+ writeMode);
+ } else {
+ remaining -= audioInjection.write(byteArray, 0, byteArray.length);
+ }
+ }
+ break;
+ case SHORT_ARRAY:
+ short[] shortArray = new short[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < shortArray.length; i++) {
+ shortArray[i] = SHORT_VALUE;
+ }
+ remaining = shortArray.length;
+ while (remaining > 0) {
+ if (writeMode == WRITE_BLOCKING || writeMode == WRITE_NON_BLOCKING) {
+ remaining -= audioInjection.write(shortArray, 0, shortArray.length,
+ writeMode);
+ } else {
+ remaining -= audioInjection.write(shortArray, 0, shortArray.length);
+ }
+ }
+ break;
+ case FLOAT_ARRAY:
+ float[] floatArray = new float[NUMBER_OF_SAMPLES];
+ for (int i = 0; i < floatArray.length; i++) {
+ floatArray[i] = FLOAT_VALUE;
+ }
+ remaining = floatArray.length;
+ while (remaining > 0) {
+ remaining -= audioInjection.write(floatArray, 0, floatArray.length, writeMode);
+ }
+ break;
+ }
+
+ verify(mActivityResultCallback, timeout(5000)).onActivityResult(
+ mIntentCaptor.capture());
+ Intent intent = mIntentCaptor.getValue();
+ assertThat(intent).isNotNull();
+ activityResultReceiver.unregister();
+
+ switch (dataType) {
+ case BYTE_BUFFER:
+ double powerSpectrumAtFrequency = intent.getDoubleExtra(
+ EXTRA_POWER_SPECTRUM_AT_FREQUENCY,
+ 0);
+ double powerSpectrumNotFrequency = intent.getDoubleExtra(
+ EXTRA_POWER_SPECTRUM_NOT_FREQUENCY,
+ 0);
+ assertThat(powerSpectrumNotFrequency).isLessThan(POWER_THRESHOLD_FOR_ABSENT);
+ assertThat(powerSpectrumAtFrequency).isGreaterThan(POWER_THRESHOLD_FOR_PRESENT);
+ break;
+ case BYTE_ARRAY:
+ byte byteValue = intent.getByteExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE,
+ Byte.MIN_VALUE);
+ assertThat(byteValue).isEqualTo(BYTE_VALUE);
+ break;
+ case SHORT_ARRAY:
+ short shortValue = intent.getShortExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE,
+ Short.MIN_VALUE);
+ assertThat(shortValue).isEqualTo(SHORT_VALUE);
+ break;
+ case FLOAT_ARRAY:
+ float floatValue = intent.getFloatExtra(EXTRA_LAST_RECORDED_NONZERO_VALUE, 0);
+ float roundOffError = Math.abs(floatValue - FLOAT_VALUE);
+ assertThat(roundOffError).isLessThan(0.001f);
+ break;
+ }
+ }
+
+ private static AudioFormat createCaptureFormat(int encoding) {
+ return new AudioFormat.Builder()
+ .setSampleRate(SAMPLE_RATE)
+ .setEncoding(encoding)
+ .setChannelMask(CHANNEL_IN_MONO)
+ .build();
+ }
+
+ private static AudioFormat createInjectionFormat(int encoding) {
+ return new AudioFormat.Builder()
+ .setSampleRate(SAMPLE_RATE)
+ .setEncoding(encoding)
+ .setChannelMask(CHANNEL_IN_MONO)
+ .build();
+ }
+
+ private static Intent createPlayAudioIntent(@AudioHelper.DataType int dataType) {
return new Intent(ACTION_PLAY_AUDIO)
+ .putExtra(EXTRA_AUDIO_DATA_TYPE, dataType)
.setComponent(MAIN_ACTIVITY_COMPONENT)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
- private static Intent createAudioRecordIntent() {
+ private static Intent createAudioRecordIntent(@AudioHelper.DataType int dataType) {
return new Intent(ACTION_RECORD_AUDIO)
+ .putExtra(EXTRA_AUDIO_DATA_TYPE, dataType)
.setComponent(MAIN_ACTIVITY_COMPONENT)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
}
diff --git a/tests/tests/voiceinteraction/TEST_MAPPING b/tests/tests/voiceinteraction/TEST_MAPPING
index b22a259..7c84b9c 100644
--- a/tests/tests/voiceinteraction/TEST_MAPPING
+++ b/tests/tests/voiceinteraction/TEST_MAPPING
@@ -5,6 +5,19 @@
"options": [
{
"exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ // TODO(b/225076204): Remove the following four test cases after fixing the test fail.
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_createDetectorTwiceQuickly_triggerSuccess"
+ },
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromDsp_success"
+ },
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromExternalSource_success"
+ },
+ {
+ "exclude-filter": "android.voiceinteraction.cts.HotwordDetectionServiceBasicTest#testHotwordDetectionService_onDetectFromMic_success"
}
]
}
diff --git a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
index 4a152bc..be61d93 100644
--- a/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
+++ b/tests/tests/voiceinteraction/common/src/android/voiceinteraction/common/Utils.java
@@ -163,6 +163,7 @@
public static final String DIRECT_ACTIONS_ACTIVITY_CMD_DESTROYED_INTERACTOR =
"destroyedInteractor";
public static final String DIRECT_ACTIONS_ACTIVITY_CMD_INVALIDATE_ACTIONS = "invalidateActions";
+ public static final String DIRECT_ACTIONS_ACTIVITY_CMD_GET_PACKAGE_NAME = "getpackagename";
public static final String DIRECT_ACTIONS_RESULT_PERFORMED = "performed";
public static final String DIRECT_ACTIONS_RESULT_CANCELLED = "cancelled";
diff --git a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
index 4ece6b9..3c74da6 100644
--- a/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
+++ b/tests/tests/voiceinteraction/src/android/voiceinteraction/cts/DirectActionsTest.java
@@ -79,6 +79,23 @@
@AppModeFull(reason = "testPerformDirectAction() is enough")
@Test
+ public void testGetPackageName() throws Exception {
+ mActivityControl.startActivity();
+ mSessionControl.startVoiceInteractionSession();
+ try {
+ // Get the actions to set up the VoiceInteractor
+ mSessionControl.getDirectActions();
+
+ String packageName = mActivityControl.getPackageName();
+ assertThat(packageName).isEqualTo("android.voiceinteraction.service");
+ } finally {
+ mSessionControl.stopVoiceInteractionSession();
+ mActivityControl.finishActivity();
+ }
+ }
+
+ @AppModeFull(reason = "testPerformDirectAction() is enough")
+ @Test
public void testCancelPerformedDirectAction() throws Exception {
mActivityControl.startActivity();
mSessionControl.startVoiceInteractionSession();
@@ -232,6 +249,13 @@
return result.getBoolean(Utils.DIRECT_ACTIONS_KEY_RESULT);
}
+ private String getPackageName()
+ throws Exception {
+ final Bundle result = executeRemoteCommand(
+ Utils.DIRECT_ACTIONS_ACTIVITY_CMD_GET_PACKAGE_NAME);
+ return result.getString(Utils.DIRECT_ACTIONS_KEY_RESULT);
+ }
+
void finishActivity() throws Exception {
executeRemoteCommand(Utils.VOICE_INTERACTION_ACTIVITY_CMD_FINISH);
}
@@ -316,3 +340,4 @@
.that(Utils.DIRECT_ACTIONS_RESULT_CANCELLED).isEqualTo(status);
}
}
+
diff --git a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
index 4b6d2e3..7a7c07c 100644
--- a/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
+++ b/tests/tests/voiceinteraction/testapp/src/android/voiceinteraction/testapp/DirectActionsActivity.java
@@ -73,6 +73,11 @@
Utils.VOICE_INTERACTION_KEY_CALLBACK);
invalidateDirectActions(commandCallback);
} break;
+ case Utils.DIRECT_ACTIONS_ACTIVITY_CMD_GET_PACKAGE_NAME: {
+ final RemoteCallback commandCallback = cmdArgs.getParcelable(
+ Utils.VOICE_INTERACTION_KEY_CALLBACK);
+ getPackageName(commandCallback);
+ } break;
}
});
@@ -157,6 +162,14 @@
callback.sendResult(result);
}
+ private void getPackageName(@NonNull RemoteCallback callback) {
+ String packageName = getVoiceInteractor().getPackageName();
+ final Bundle result = new Bundle();
+ result.putString(Utils.DIRECT_ACTIONS_KEY_RESULT, packageName);
+ Log.v(TAG, "getPackageName(): " + Utils.toBundleString(result));
+ callback.sendResult(result);
+ }
+
private void doFinish(@NonNull RemoteCallback callback) {
finish();
final Bundle result = new Bundle();
diff --git a/tests/tests/widget/Android.bp b/tests/tests/widget/Android.bp
index 414dd0f..98290f0 100644
--- a/tests/tests/widget/Android.bp
+++ b/tests/tests/widget/Android.bp
@@ -24,13 +24,14 @@
"androidx.annotation_annotation",
"androidx.test.ext.junit",
"androidx.test.rules",
- "ctsdeviceutillegacy-axt",
+ "ctsdeviceutillegacy-axt",
"mockito-target-minus-junit4",
"android-common",
"compatibility-device-util-axt",
"ctstestrunner-axt",
"platform-test-annotations",
"truth-prebuilt",
+ "CtsMockInputMethodLib",
],
libs: ["android.test.runner"],
diff --git a/tests/tests/widget/AndroidTest.xml b/tests/tests/widget/AndroidTest.xml
index 27476fb..f2eb9ef 100644
--- a/tests/tests/widget/AndroidTest.xml
+++ b/tests/tests/widget/AndroidTest.xml
@@ -33,6 +33,16 @@
<option name="test-file-name" value="CtsWidgetTestCases.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
+ <option name="cleanup-apks" value="true" />
+ <!--
+ MockIME always needs to be installed as a full package, even when CTS is running
+ for instant apps.
+ -->
+ <option name="force-install-mode" value="FULL"/>
+ <option name="test-file-name" value="CtsMockInputMethod.apk" />
+ </target_preparer>
+
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
<!--
To fail-fast in case test-ime setup somehow failed. Consider increasing the
diff --git a/tests/tests/widget/src/android/widget/cts/EditTextTest.java b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
index b5f7263..2370562 100755
--- a/tests/tests/widget/src/android/widget/cts/EditTextTest.java
+++ b/tests/tests/widget/src/android/widget/cts/EditTextTest.java
@@ -16,6 +16,8 @@
package android.widget.cts;
+import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
@@ -27,6 +29,10 @@
import android.content.Context;
import android.content.res.Configuration;
import android.graphics.Point;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
import android.text.Editable;
import android.text.InputFilter;
import android.text.InputType;
@@ -41,6 +47,7 @@
import android.util.TypedValue;
import android.util.Xml;
import android.view.KeyEvent;
+import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
@@ -55,6 +62,9 @@
import com.android.compatibility.common.util.CtsKeyEventUtil;
import com.android.compatibility.common.util.CtsTouchUtils;
+import com.android.cts.mockime.ImeEventStream;
+import com.android.cts.mockime.ImeSettings;
+import com.android.cts.mockime.MockImeSession;
import org.junit.After;
import org.junit.Before;
@@ -64,6 +74,7 @@
import org.xmlpull.v1.XmlPullParser;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
@SmallTest
@@ -722,4 +733,31 @@
assertEquals(InputType.TYPE_CLASS_TEXT
| InputType.TYPE_TEXT_FLAG_ENABLE_TEXT_CONVERSION_SUGGESTIONS, tv.getInputType());
}
+
+ @Test
+ public void testClickTwice_showIme() throws Throwable {
+ try (MockImeSession imeSession = MockImeSession.create(
+ mInstrumentation.getContext(),
+ mInstrumentation.getUiAutomation(),
+ new ImeSettings.Builder())) {
+
+ clickOnEditText1();
+ mInstrumentation.waitForIdleSync();
+
+ clickOnEditText1();
+ mInstrumentation.waitForIdleSync();
+
+ final ImeEventStream stream = imeSession.openEventStream();
+ expectEvent(stream,
+ event -> "showSoftInput".equals(event.getEventName()),
+ TimeUnit.SECONDS.toMillis(2));
+ }
+ }
+
+ private void clickOnEditText1() throws Exception {
+ final UiObject2 object = UiDevice.getInstance(mInstrumentation)
+ .findObject(By.res("android.widget.cts", "edittext_simple1"));
+ object.click();
+ SystemClock.sleep(ViewConfiguration.getDoubleTapTimeout() + 50);
+ }
}
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 711e098..0f0c739 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
@@ -670,7 +670,7 @@
intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_RESOURCE_CHANGED);
WifiAwareResourcesBroadcastReceiver receiver = new WifiAwareResourcesBroadcastReceiver();
mContext.registerReceiver(receiver, intentFilter);
- final String serviceName = "ValidName";
+ final String serviceName = "PublishName";
WifiAwareSession session = attachAndGetSession();
@@ -735,7 +735,7 @@
return;
}
- final String serviceName = "ValidName";
+ final String serviceName = "PublishName";
final int ttlSec = 5;
WifiAwareSession session = attachAndGetSession();
@@ -774,7 +774,7 @@
return;
}
- final String serviceName = "ValidName";
+ final String serviceName = "PublishName";
final String passphrase = "SomePassword";
final byte[] pmk = "01234567890123456789012345678901".getBytes();
final byte[] pmkId = "0123456789012345".getBytes();
@@ -845,7 +845,7 @@
if (!characteristics.isInstantCommunicationModeSupported()) {
return;
}
- final String serviceName = "ValidName";
+ final String serviceName = "PublishName";
WifiAwareSession session = attachAndGetSession();
PublishConfig publishConfig = new PublishConfig.Builder()
@@ -887,7 +887,7 @@
intentFilter.addAction(WifiAwareManager.ACTION_WIFI_AWARE_RESOURCE_CHANGED);
WifiAwareResourcesBroadcastReceiver receiver = new WifiAwareResourcesBroadcastReceiver();
mContext.registerReceiver(receiver, intentFilter);
- final String serviceName = "ValidName";
+ final String serviceName = "SubscribeName";
WifiAwareSession session = attachAndGetSession();
@@ -963,7 +963,7 @@
if (!characteristics.isInstantCommunicationModeSupported()) {
return;
}
- final String serviceName = "ValidName";
+ final String serviceName = "SubscribeName";
WifiAwareSession session = attachAndGetSession();
SubscribeConfig subscribeConfig = new SubscribeConfig.Builder()
@@ -1003,7 +1003,7 @@
return;
}
- final String serviceName = "ValidName";
+ final String serviceName = "SubscribeName";
final int ttlSec = 5;
WifiAwareSession session = attachAndGetSession();
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 2c7605b..479c0eb3 100644
--- a/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
+++ b/tests/tests/wifi/src/android/net/wifi/cts/WifiManagerTest.java
@@ -177,7 +177,7 @@
private static final int SCAN_TEST_WAIT_DURATION_MS = 15_000;
private static final int TEST_WAIT_DURATION_MS = 10_000;
private static final int WIFI_CONNECT_TIMEOUT_MILLIS = 30_000;
- private static final int WIFI_PNO_CONNECT_TIMEOUT_MILLIS = 60_000;
+ private static final int WIFI_PNO_CONNECT_TIMEOUT_MILLIS = 90_000;
private static final int WAIT_MSEC = 60;
private static final int DURATION_SCREEN_TOGGLE = 2000;
private static final int DURATION_SETTINGS_TOGGLE = 1_000;
diff --git a/tests/tests/wifi/src/android/net/wifi/rtt/cts/TestBase.java b/tests/tests/wifi/src/android/net/wifi/rtt/cts/TestBase.java
index 87cd929..bdec62b 100644
--- a/tests/tests/wifi/src/android/net/wifi/rtt/cts/TestBase.java
+++ b/tests/tests/wifi/src/android/net/wifi/rtt/cts/TestBase.java
@@ -309,10 +309,10 @@
}
if (!ap5Ghz.isEmpty()) {
- s11McScanResult = getHighestRssiScanResult(ap5Ghz.values());
+ s11McScanResult = getRandomScanResult(ap5Ghz.values());
return;
}
- s11McScanResult = getHighestRssiScanResult(ap24Ghz.values());
+ s11McScanResult = getRandomScanResult(ap24Ghz.values());
}
static Context getContext() {
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 b40c379..2a3f67a 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
@@ -83,7 +83,7 @@
private static final MacAddress MAC = MacAddress.fromString("00:01:02:03:04:05");
// Interval between two ranging request.
- private static final int intervalMs = 200;
+ private static final int INTERVAL_MS = 1000;
/**
* Test Wi-Fi RTT ranging operation using ScanResults in request:
@@ -304,7 +304,7 @@
numFailures++;
}
// Sleep a while to avoid stress AP.
- Thread.sleep(intervalMs);
+ Thread.sleep(INTERVAL_MS);
}
// Save results to log
@@ -651,6 +651,7 @@
*/
private void rangeNon11mcApRequest(RangingRequest request, ScanResult testAp,
int variationLimit) throws InterruptedException {
+ Thread.sleep(5000);
List<RangingResult> allResults = new ArrayList<>();
int numFailures = 0;
int distanceSum = 0;
@@ -724,7 +725,7 @@
numFailures++;
}
// Sleep a while to avoid stress AP.
- Thread.sleep(intervalMs);
+ Thread.sleep(INTERVAL_MS);
}
// Save results to log
int numGoodResults = NUM_OF_RTT_ITERATIONS - numFailures;
diff --git a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
index a4c71f8..7a0d6f1 100644
--- a/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
+++ b/tests/translation/src/android/translation/cts/UiTranslationManagerTest.java
@@ -620,12 +620,194 @@
// Make sure onFinished will not be called twice.
mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
mActivityScenario = null;
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
// TODO(b/191417938): add a test to verify startUiTranslation + Activity destroyed.
}
@Test
+ public void testCallbackRegisteredAfterTranslationStarted() throws Throwable {
+ final Pair<List<AutofillId>, ContentCaptureContext> result =
+ enableServicesAndStartActivityForTranslation();
+
+ final List<AutofillId> views = result.first;
+ final ContentCaptureContext contentCaptureContext = result.second;
+
+ UiTranslationManager manager =
+ sContext.getSystemService(UiTranslationManager.class);
+ // Set response
+ sTranslationReplier.addResponse(createViewsTranslationResponse(views, "success"));
+
+ startUiTranslation(/* shouldPadContent */ false, views, contentCaptureContext);
+
+ // Register callback
+ final Executor executor = Executors.newSingleThreadExecutor();
+ UiTranslationStateCallback mockCallback = Mockito.mock(UiTranslationStateCallback.class);
+ manager.registerUiTranslationStateCallback(executor, mockCallback);
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+ // Callback should receive onStarted.
+ Mockito.verify(mockCallback, Mockito.times(1))
+ .onStarted(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ pauseUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1)).onPaused(any(String.class));
+
+ resumeUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1))
+ .onResumed(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ finishUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+
+ // Make sure onFinished will not be called twice.
+ mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
+ mActivityScenario = null;
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+ }
+
+ @Test
+ public void testCallbackRegisteredAfterTranslationStartedAndPaused() throws Throwable {
+ final Pair<List<AutofillId>, ContentCaptureContext> result =
+ enableServicesAndStartActivityForTranslation();
+
+ final List<AutofillId> views = result.first;
+ final ContentCaptureContext contentCaptureContext = result.second;
+
+ UiTranslationManager manager =
+ sContext.getSystemService(UiTranslationManager.class);
+ // Set response
+ sTranslationReplier.addResponse(createViewsTranslationResponse(views, "success"));
+
+ startUiTranslation(/* shouldPadContent */ false, views, contentCaptureContext);
+
+ pauseUiTranslation(contentCaptureContext);
+
+ // Register callback
+ final Executor executor = Executors.newSingleThreadExecutor();
+ UiTranslationStateCallback mockCallback = Mockito.mock(UiTranslationStateCallback.class);
+ manager.registerUiTranslationStateCallback(executor, mockCallback);
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+ // Callback should receive onStarted and onPaused events.
+ Mockito.verify(mockCallback, Mockito.times(1))
+ .onStarted(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.times(1)).onPaused(any(String.class));
+
+ resumeUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1))
+ .onResumed(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ finishUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+
+ // Make sure onFinished will not be called twice.
+ mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
+ mActivityScenario = null;
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+ }
+
+ @Test
+ public void testCallbackRegisteredAfterTranslationStartedPausedAndResumed() throws Throwable {
+ final Pair<List<AutofillId>, ContentCaptureContext> result =
+ enableServicesAndStartActivityForTranslation();
+
+ final List<AutofillId> views = result.first;
+ final ContentCaptureContext contentCaptureContext = result.second;
+
+ UiTranslationManager manager =
+ sContext.getSystemService(UiTranslationManager.class);
+ // Set response
+ sTranslationReplier.addResponse(createViewsTranslationResponse(views, "success"));
+
+ startUiTranslation(/* shouldPadContent */ false, views, contentCaptureContext);
+
+ pauseUiTranslation(contentCaptureContext);
+
+ resumeUiTranslation(contentCaptureContext);
+
+ // Register callback
+ final Executor executor = Executors.newSingleThreadExecutor();
+ UiTranslationStateCallback mockCallback = Mockito.mock(UiTranslationStateCallback.class);
+ manager.registerUiTranslationStateCallback(executor, mockCallback);
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+ // Callback should receive onStarted event but NOT an onPaused event, because translation
+ // was already resumed prior to registration.
+ Mockito.verify(mockCallback, Mockito.times(1))
+ .onStarted(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.never()).onPaused(any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.never())
+ .onResumed(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ finishUiTranslation(contentCaptureContext);
+
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+
+ // Make sure onFinished will not be called twice.
+ mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
+ mActivityScenario = null;
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ Mockito.verify(mockCallback, Mockito.times(1)).onFinished(any(String.class));
+ }
+
+ @Test
+ public void testCallbackRegisteredAfterTranslationFinished() throws Throwable {
+ final Pair<List<AutofillId>, ContentCaptureContext> result =
+ enableServicesAndStartActivityForTranslation();
+
+ final List<AutofillId> views = result.first;
+ final ContentCaptureContext contentCaptureContext = result.second;
+
+ UiTranslationManager manager =
+ sContext.getSystemService(UiTranslationManager.class);
+ // Set response
+ sTranslationReplier.addResponse(createViewsTranslationResponse(views, "success"));
+
+ startUiTranslation(/* shouldPadContent */ false, views, contentCaptureContext);
+
+ pauseUiTranslation(contentCaptureContext);
+
+ resumeUiTranslation(contentCaptureContext);
+
+ finishUiTranslation(contentCaptureContext);
+
+ // Register callback
+ final Executor executor = Executors.newSingleThreadExecutor();
+ UiTranslationStateCallback mockCallback = Mockito.mock(UiTranslationStateCallback.class);
+ manager.registerUiTranslationStateCallback(executor, mockCallback);
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+
+ // Callback should receive no events.
+ Mockito.verify(mockCallback, Mockito.never())
+ .onStarted(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.never()).onPaused(any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.never())
+ .onResumed(any(ULocale.class), any(ULocale.class), any(String.class));
+
+ Mockito.verify(mockCallback, Mockito.never()).onFinished(any(String.class));
+
+ // Make sure onFinished will not be called at all.
+ mActivityScenario.moveToState(Lifecycle.State.DESTROYED);
+ mActivityScenario = null;
+ SystemClock.sleep(UI_WAIT_TIMEOUT);
+ Mockito.verify(mockCallback, Mockito.never()).onFinished(any(String.class));
+ }
+
+ @Test
public void testVirtualViewUiTranslation() throws Throwable {
// Enable CTS ContentCaptureService
CtsContentCaptureService contentcaptureService = enableContentCaptureService();
diff --git a/tests/video/Android.bp b/tests/video/Android.bp
index 55eaa74..b41a32c 100644
--- a/tests/video/Android.bp
+++ b/tests/video/Android.bp
@@ -24,6 +24,7 @@
"ctsmediautil",
"compatibility-device-util-axt",
"ctstestrunner-axt",
+ "cts-media-common",
],
libs: [
"android.test.runner",
diff --git a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
index 4755b9a..401468f 100644
--- a/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
+++ b/tests/video/src/android/video/cts/VideoEncoderDecoderTest.java
@@ -27,6 +27,7 @@
import android.media.MediaFormat;
import android.media.cts.CodecImage;
import android.media.cts.CodecUtils;
+import android.media.cts.TestUtils;
import android.media.cts.YUVImage;
import android.os.Build;
import android.util.Log;
@@ -124,6 +125,8 @@
private double mRmsErrorMargin;
private Random mRandom;
+ private boolean mUpdatedSwCodec = false;
+
private class TestConfig {
public boolean mTestPixels = true;
public boolean mReportFrameTime = false;
@@ -157,6 +160,8 @@
protected void setUp() throws Exception {
mEncodedOutputBuffer = new LinkedList<Pair<ByteBuffer, BufferInfo>>();
mRmsErrorMargin = PIXEL_RMS_ERROR_MARGIN;
+ mUpdatedSwCodec =
+ !TestUtils.isMainlineModuleFactoryVersion("com.google.android.media.swcodec");
// Use time as a seed, hoping to prevent checking pixels in the same pattern
long now = System.currentTimeMillis();
mRandom = new Random(now);
@@ -819,8 +824,10 @@
}
if (isPerf) {
+ // allow improvements in mainline-updated google-supplied software codecs.
+ boolean fasterIsOk = mUpdatedSwCodec & encoderName.startsWith("c2.android.");
String error = MediaPerfUtils.verifyAchievableFrameRates(
- encoderName, mimeType, w, h, measuredFps);
+ encoderName, mimeType, w, h, fasterIsOk, measuredFps);
// Performance numbers only make sense on real devices, so skip on non-real devices
//
// Also ignore verification on non-preferred ABIs due to the possibility of
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 f4da75b..16287c5 100644
--- a/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
+++ b/tools/cts-tradefed/res/config/cts-on-gsi-exclude.xml
@@ -70,6 +70,7 @@
<!-- b/183234756, b/80388296, b/110260628, b/159295445, b/159294948 CtsDevicePolicyManagerTestCases -->
<option name="compatibility:exclude-filter" value="CtsDevicePolicyManagerTestCases" />
+ <option name="compatibility:exclude-filter" value="CtsDevicePolicyTestCases" />
<!-- b/183985653 -->
<option name="compatibility:module-arg" value="CtsDeqpTestCases:include-filter:dEQP-EGL.*" />
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
index ced6aaf..90ffb97 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/ApkPackageNameCheck.java
@@ -129,7 +129,7 @@
File apkFile = FileUtil.findFile(config.getParentFile(), apkName);
if (apkFile == null || !apkFile.exists()) {
fail(String.format("Module %s is trying to install %s which does not "
- + "exists in testcases/", config.getName(), apkFile));
+ + "exists in testcases/", config.getName(), apkName));
}
AaptParser res = AaptParser.parse(apkFile);
assertNotNull(res);
diff --git a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
index 7b6e0d1..8e21bf7 100644
--- a/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
+++ b/tools/cts-tradefed/tests/src/com/android/compatibility/common/tradefed/presubmit/CtsConfigLoadingTest.java
@@ -33,6 +33,7 @@
import com.android.tradefed.invoker.shard.token.TokenProperty;
import com.android.tradefed.targetprep.DeviceSetup;
import com.android.tradefed.targetprep.ITargetPreparer;
+import com.android.tradefed.targetprep.PythonVirtualenvPreparer;
import com.android.tradefed.testtype.AndroidJUnitTest;
import com.android.tradefed.testtype.GTest;
import com.android.tradefed.testtype.HostTest;
@@ -48,6 +49,7 @@
import org.junit.runners.JUnit4;
import java.io.File;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -55,6 +57,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.regex.Pattern;
/**
* Test that configuration in CTS can load and have expected properties.
@@ -62,6 +65,7 @@
@RunWith(JUnit4.class)
public class CtsConfigLoadingTest {
+ private static final Pattern TODO_BUG_PATTERN = Pattern.compile(".*TODO\\(b/[0-9]+\\).*", Pattern.DOTALL);
private static final String METADATA_COMPONENT = "component";
private static final Set<String> KNOWN_COMPONENTS =
new HashSet<>(
@@ -211,6 +215,7 @@
if (c.getDeviceConfig().size() > 2) {
throw new ConfigurationException(String.format("%s declares more than 2 devices.", config));
}
+ int deviceCount = 0;
for (IDeviceConfiguration dConfig : c.getDeviceConfig()) {
// Ensure the deprecated ApkInstaller is not used anymore.
for (ITargetPreparer prep : dConfig.getTargetPreparers()) {
@@ -238,7 +243,12 @@
config.getName(), prep.getClass()));
}
}
+ if (prep.getClass().isAssignableFrom(PythonVirtualenvPreparer.class)) {
+ // Ensure each modules has a tracking bug to be imported.
+ checkPythonModules(config, deviceCount);
+ }
}
+ deviceCount++;
}
// We can ensure that Host side tests are not empty.
for (IRemoteTest test : c.getTests()) {
@@ -412,4 +422,22 @@
}
return families;
}
+
+ /**
+ * For each usage of python virtualenv preparer, make sure we have tracking bugs to import as
+ * source the python libs.
+ */
+ private void checkPythonModules(File config, int deviceCount)
+ throws IOException, ConfigurationException {
+ if (deviceCount != 0) {
+ throw new ConfigurationException(
+ String.format("%s: PythonVirtualenvPreparer should only be declared for "
+ + "the first <device> tag in the config", config.getName()));
+ }
+ if (!TODO_BUG_PATTERN.matcher(FileUtil.readStringFromFile(config)).matches()) {
+ throw new ConfigurationException(
+ String.format("%s: Contains some virtualenv python lib usage but no "
+ + "tracking bug to import them as source.", config.getName()));
+ }
+ }
}